diff --git a/.editorconfig b/.editorconfig index db30a6257e..55bfe517da 100644 --- a/.editorconfig +++ b/.editorconfig @@ -9,5 +9,11 @@ indent_size = 4 indent_style = space insert_final_newline = true max_line_length = 120 + # Ktlint-specific config -disabled_rules = filename, max-line-length, argument-list-wrapping, parameter-list-wrapping +ktlint_standard = enabled +ktlint_experimental = disabled +ktlint_standard_filename = disabled +ktlint_standard_max-line-length = disabled +ktlint_standard_argument-list-wrapping = disabled +ktlint_standard_parameter-list-wrapping = disabled diff --git a/.github/scripts/docker-image-hash b/.github/scripts/docker-image-hash index 55b532332d..71fae16318 100755 --- a/.github/scripts/docker-image-hash +++ b/.github/scripts/docker-image-hash @@ -11,4 +11,4 @@ set -eo pipefail cd "$(dirname "$0")" cd "$(git rev-parse --show-toplevel)" -git ls-files -s --full-name "tools" | git hash-object --stdin +git ls-files -s --full-name "tools/ci-build" | git hash-object --stdin diff --git a/.github/scripts/get-or-create-release-branch.sh b/.github/scripts/get-or-create-release-branch.sh index 6cdc829911..7dcc87676d 100755 --- a/.github/scripts/get-or-create-release-branch.sh +++ b/.github/scripts/get-or-create-release-branch.sh @@ -8,8 +8,6 @@ set -eux # Compute the name of the release branch starting from the version that needs to be released ($SEMANTIC_VERSION). # If it's the beginning of a new release series, the branch is created and pushed to the remote (chosen according to # the value $DRY_RUN). -# If it isn't the beginning of a new release series, the script makes sure that the commit that will be tagged is at -# the tip of the (pre-existing) release branch. # # The script populates an output file with key-value pairs that are needed in the release CI workflow to carry out # the next steps in the release flow: the name of the release branch and a boolean flag that is set to 'true' if this @@ -57,16 +55,7 @@ if [[ "${DRY_RUN}" == "true" ]]; then git push --force origin "HEAD:refs/heads/${branch_name}" else commit_sha=$(git rev-parse --short HEAD) - if git ls-remote --exit-code --heads origin "${branch_name}"; then - # The release branch already exists, we need to make sure that our commit is its current tip - branch_head_sha=$(git rev-parse --verify --short "refs/heads/${branch_name}") - if [[ "${branch_head_sha}" != "${commit_sha}" ]]; then - echo "The release branch - ${branch_name} - already exists. ${commit_sha}, the commit you chose when " - echo "launching this release, is not its current HEAD (${branch_head_sha}). This is not allowed: you " - echo "MUST release from the HEAD of the release branch if it already exists." - exit 1 - fi - else + if ! git ls-remote --exit-code --heads origin "${branch_name}"; then # The release branch does not exist. # We need to make sure that the commit SHA that we are releasing is on `main`. git fetch origin main @@ -75,7 +64,7 @@ else git checkout -b "${branch_name}" git push origin "${branch_name}" else - echo "You must choose a commit from main to create a new release series!" + echo "You must choose a commit from main to create a new release branch!" exit 1 fi fi diff --git a/.github/workflows/ci-merge-queue.yml b/.github/workflows/ci-merge-queue.yml new file mode 100644 index 0000000000..bedab9beb5 --- /dev/null +++ b/.github/workflows/ci-merge-queue.yml @@ -0,0 +1,93 @@ +# Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +# SPDX-License-Identifier: Apache-2.0 + +# This workflow runs CI for the GitHub merge queue. + +name: Merge Queue CI +on: + merge_group: + types: [checks_requested] + +# Allow one instance of this workflow per merge +concurrency: + group: ci-merge-queue-yml-${{ github.ref }} + cancel-in-progress: true + +env: + ecr_repository: public.ecr.aws/w0m4q9l7/github-awslabs-smithy-rs-ci + +jobs: + # This job will, if possible, save a docker login password to the job outputs. The token will + # be encrypted with the passphrase stored as a GitHub secret. The login password expires after 12h. + # The login password is encrypted with the repo secret DOCKER_LOGIN_TOKEN_PASSPHRASE + save-docker-login-token: + name: Save a docker login token + outputs: + docker-login-password: ${{ steps.set-token.outputs.docker-login-password }} + permissions: + id-token: write + contents: read + continue-on-error: true + runs-on: ubuntu-latest + steps: + - name: Attempt to load a docker login password + uses: aws-actions/configure-aws-credentials@v1-node16 + with: + role-to-assume: ${{ secrets.SMITHY_RS_PUBLIC_ECR_PUSH_ROLE_ARN }} + role-session-name: GitHubActions + aws-region: us-west-2 + - name: Save the docker login password to the output + id: set-token + run: | + ENCRYPTED_PAYLOAD=$( + gpg --symmetric --batch --passphrase "${{ secrets.DOCKER_LOGIN_TOKEN_PASSPHRASE }}" --output - <(aws ecr-public get-login-password --region us-east-1) | base64 -w0 + ) + echo "docker-login-password=$ENCRYPTED_PAYLOAD" >> $GITHUB_OUTPUT + + # This job detects if the PR made changes to build tools. If it did, then it builds a new + # build Docker image. Otherwise, it downloads a build image from Public ECR. In both cases, + # it uploads the image as a build artifact for other jobs to download and use. + acquire-base-image: + name: Acquire Base Image + needs: save-docker-login-token + runs-on: ubuntu-latest + env: + ENCRYPTED_DOCKER_PASSWORD: ${{ needs.save-docker-login-token.outputs.docker-login-password }} + DOCKER_LOGIN_TOKEN_PASSPHRASE: ${{ secrets.DOCKER_LOGIN_TOKEN_PASSPHRASE }} + permissions: + id-token: write + contents: read + steps: + - uses: actions/checkout@v3 + with: + path: smithy-rs + - name: Acquire base image + id: acquire + env: + DOCKER_BUILDKIT: 1 + run: ./smithy-rs/.github/scripts/acquire-build-image + - name: Acquire credentials + uses: aws-actions/configure-aws-credentials@v1-node16 + with: + role-to-assume: ${{ secrets.SMITHY_RS_PUBLIC_ECR_PUSH_ROLE_ARN }} + role-session-name: GitHubActions + aws-region: us-west-2 + - name: Upload image + run: | + IMAGE_TAG="$(./smithy-rs/.github/scripts/docker-image-hash)" + docker tag "smithy-rs-base-image:${IMAGE_TAG}" "${{ env.ecr_repository }}:${IMAGE_TAG}" + aws ecr-public get-login-password --region us-east-1 | docker login --username AWS --password-stdin public.ecr.aws + docker push "${{ env.ecr_repository }}:${IMAGE_TAG}" + + # Run shared CI after the Docker build image has either been rebuilt or found in ECR + ci: + needs: + - save-docker-login-token + - acquire-base-image + if: ${{ github.event.pull_request.head.repo.full_name == 'awslabs/smithy-rs' || toJSON(github.event.merge_group) != '{}' }} + uses: ./.github/workflows/ci.yml + with: + run_sdk_examples: true + secrets: + ENCRYPTED_DOCKER_PASSWORD: ${{ needs.save-docker-login-token.outputs.docker-login-password }} + DOCKER_LOGIN_TOKEN_PASSPHRASE: ${{ secrets.DOCKER_LOGIN_TOKEN_PASSPHRASE }} diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 774c4f3c7a..048e577fcf 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -29,7 +29,7 @@ on: required: false env: - rust_version: 1.62.1 + rust_version: 1.66.1 rust_toolchain_components: clippy,rustfmt ENCRYPTED_DOCKER_PASSWORD: ${{ secrets.ENCRYPTED_DOCKER_PASSWORD }} DOCKER_LOGIN_TOKEN_PASSPHRASE: ${{ secrets.DOCKER_LOGIN_TOKEN_PASSPHRASE }} diff --git a/.github/workflows/claim-crate-names.yml b/.github/workflows/claim-crate-names.yml index 650b8cac7c..ef24446bb7 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.62.1 + rust_version: 1.66.1 name: Claim unpublished crate names on crates.io run-name: ${{ github.workflow }} diff --git a/.github/workflows/pull-request-bot.yml b/.github/workflows/pull-request-bot.yml index 7bcd2e833f..6d22213eb5 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.62.1 + rust_version: 1.66.1 rust_toolchain_components: clippy,rustfmt apt_dependencies: libssl-dev gnuplot jq diff --git a/.github/workflows/release-scripts/create-release.js b/.github/workflows/release-scripts/create-release.js index ad4f328e8f..fb10ea1b2c 100644 --- a/.github/workflows/release-scripts/create-release.js +++ b/.github/workflows/release-scripts/create-release.js @@ -44,10 +44,13 @@ module.exports = async ({ isDryRun, // Release manifest file path releaseManifestPath, + // The commit-like reference that we want to release (e.g. a commit SHA or a branch name) + releaseCommitish, }) => { assert(github !== undefined, "The `github` argument is required"); assert(isDryRun !== undefined, "The `isDryRun` argument is required"); assert(releaseManifestPath !== undefined, "The `releaseManifestPath` argument is required"); + assert(releaseCommitish !== undefined, "The `releaseCommitish` argument is required"); console.info(`Starting GitHub release creation with isDryRun: ${isDryRun}, and releaseManifestPath: '${releaseManifestPath}'`); @@ -74,6 +77,7 @@ module.exports = async ({ name: releaseManifest.name, body: releaseManifest.body, prerelease: releaseManifest.prerelease, + target_commitish: releaseCommitish, }); console.info(`SUCCESS: Created release with ID: ${response.data.id}, URL: ${response.data.html_url} `); } else { diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 580eb26245..52ddfdfe7a 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -10,7 +10,7 @@ concurrency: cancel-in-progress: true env: - rust_version: 1.62.1 + rust_version: 1.66.1 name: Release smithy-rs run-name: ${{ github.workflow }} ${{ inputs.semantic_version }} (${{ inputs.commit_sha }}) - ${{ inputs.dry_run && 'Dry run' || 'Production run' }} @@ -18,8 +18,8 @@ on: workflow_dispatch: inputs: commit_sha: - description: | - The SHA of the git commit that you want to release. + description: | + The SHA of the git commit that you want to release. You must use the non-abbreviated SHA (e.g. b2318b0 won't work!). required: true type: string @@ -75,8 +75,8 @@ jobs: # We need `always` here otherwise this job won't run if the previous job has been skipped # See https://samanpavel.medium.com/github-actions-conditional-job-execution-e6aa363d2867 if: | - always() && - needs.acquire-base-image.result == 'success' && + always() && + needs.acquire-base-image.result == 'success' && (needs.release-ci.result == 'success' || needs.release-ci.result == 'skipped') runs-on: ubuntu-latest outputs: @@ -87,6 +87,7 @@ jobs: with: ref: ${{ inputs.commit_sha }} token: ${{ secrets.RELEASE_AUTOMATION_BOT_PAT }} + fetch-depth: 0 - name: Get or create release branch id: branch-push shell: bash @@ -112,11 +113,13 @@ jobs: runs-on: ubuntu-latest outputs: release_branch: ${{ needs.get-or-create-release-branch.outputs.release_branch }} + commit_sha: ${{ steps.gradle-push.outputs.commit_sha }} steps: - uses: actions/checkout@v3 with: - ref: ${{ needs.get-or-create-release-branch.outputs.release_branch }} + ref: ${{ inputs.commit_sha }} path: smithy-rs + fetch-depth: 0 token: ${{ secrets.RELEASE_AUTOMATION_BOT_PAT }} - name: Upgrade gradle.properties uses: ./smithy-rs/.github/actions/docker-build @@ -131,13 +134,30 @@ jobs: shell: bash env: SEMANTIC_VERSION: ${{ inputs.semantic_version }} + RELEASE_COMMIT_SHA: ${{ inputs.commit_sha }} + RELEASE_BRANCH_NAME: ${{ needs.get-or-create-release-branch.outputs.release_branch }} DRY_RUN: ${{ inputs.dry_run }} run: | set -x + # For debugging purposes git status - # The file was actually changed, we need to commit the changes - git diff-index --quiet HEAD || { git -c 'user.name=AWS SDK Rust Bot' -c 'user.email=aws-sdk-rust-primary@amazon.com' commit gradle.properties --message "Upgrade the smithy-rs runtime crates version to ${SEMANTIC_VERSION}" && git push origin; } + + if ! git diff-index --quiet HEAD; then + # gradle.properties was changed, we need to commit and push the diff + git -c 'user.name=AWS SDK Rust Bot' -c 'user.email=aws-sdk-rust-primary@amazon.com' commit gradle.properties --message "Upgrade the smithy-rs runtime crates version to ${SEMANTIC_VERSION}" + + # This will fail if we tried to release from a non-HEAD commit on the release branch. + # The only scenario where we would try to release a non-HEAD commit from the release branch is + # to retry a release action execution that failed due to a transient issue. + # In that case, we expect the commit to be releasable as-is, i.e. the runtime crate version in gradle.properties + # should already be the expected one. + git push origin "HEAD:refs/heads/${RELEASE_BRANCH_NAME}" + + echo "commit_sha=$(git rev-parse HEAD)" > $GITHUB_OUTPUT + else + echo "commit_sha=${RELEASE_COMMIT_SHA}" > $GITHUB_OUTPUT + fi release: name: Release @@ -158,7 +178,7 @@ jobs: - name: Checkout smithy-rs uses: actions/checkout@v3 with: - ref: ${{ needs.upgrade-gradle-properties.outputs.release_branch }} + ref: ${{ needs.upgrade-gradle-properties.outputs.commit_sha }} path: smithy-rs token: ${{ secrets.RELEASE_AUTOMATION_BOT_PAT }} - name: Generate release artifacts @@ -170,9 +190,20 @@ jobs: - name: Push smithy-rs changes shell: bash working-directory: smithy-rs-release/smithy-rs + id: push-changelog + env: + RELEASE_BRANCH_NAME: ${{ needs.upgrade-gradle-properties.outputs.release_branch }} run: | - echo "Pushing release commits..." - git push origin + if ! git diff-index --quiet HEAD; then + echo "Pushing release commits..." + # This will fail if we tried to release from a non-HEAD commit on the release branch. + # The only scenario where we would try to release a non-HEAD commit from the release branch is + # to retry a release action execution that failed due to a transient issue. + # In that case, we expect the commit to be releasable as-is, i.e. the changelog should have already + # been processed. + git push origin "HEAD:refs/heads/${RELEASE_BRANCH_NAME}" + fi + echo "commit_sha=$(git rev-parse HEAD)" > $GITHUB_OUTPUT - name: Tag release uses: actions/github-script@v6 with: @@ -182,7 +213,8 @@ jobs: await createReleaseScript({ github, isDryRun: ${{ inputs.dry_run }}, - releaseManifestPath: "smithy-rs-release/smithy-rs-release-manifest.json" + releaseManifestPath: "smithy-rs-release/smithy-rs-release-manifest.json", + releaseCommitish: "${{ steps.push-changelog.outputs.commit_sha }}" }); - name: Publish to crates.io shell: bash @@ -232,7 +264,7 @@ jobs: shell: bash run: | set -eux - + # This will fail if other commits have been pushed to `main` after `commit_sha` # In particular, this will ALWAYS fail if you are creating a new release series from # a commit that is not the current tip of `main`. diff --git a/.github/workflows/update-sdk-next.yml b/.github/workflows/update-sdk-next.yml index 8fca2bbc62..c96b09b73f 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.62.1 + toolchain: 1.66.1 - name: Delete old SDK run: | - name: Generate a fresh SDK diff --git a/.gitignore b/.gitignore index 7e406dbcb5..710a23e16b 100644 --- a/.gitignore +++ b/.gitignore @@ -50,3 +50,9 @@ target/ # IDEs .idea/ +.project +.settings +.classpath + +# tools +.tool-versions diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index c6da8695fc..cce6e26327 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -20,10 +20,10 @@ repos: files: ^.*$ pass_filenames: false - repo: https://github.com/macisamuele/language-formatters-pre-commit-hooks - rev: v2.5.0 + rev: v2.6.0 hooks: - id: pretty-format-kotlin - args: [--autofix, --ktlint-version, 0.46.1] + args: [--autofix, --ktlint-version, 0.48.2] - id: pretty-format-yaml args: [--autofix, --indent, '2'] - id: pretty-format-rust diff --git a/CHANGELOG.md b/CHANGELOG.md index d08bd9d602..e4eaa57e2f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,4 +1,120 @@ +March 23rd, 2023 +================ +**Breaking Changes:** +- ⚠🎉 (all, [smithy-rs#2467](https://github.com/awslabs/smithy-rs/issues/2467)) Update MSRV to 1.66.1 +- ⚠ (client, [smithy-rs#76](https://github.com/awslabs/smithy-rs/issues/76), [smithy-rs#2129](https://github.com/awslabs/smithy-rs/issues/2129)) Generic clients no longer expose a `request_id()` function on errors. To get request ID functionality, use the SDK code generator. +- ⚠ (client, [smithy-rs#76](https://github.com/awslabs/smithy-rs/issues/76), [smithy-rs#2129](https://github.com/awslabs/smithy-rs/issues/2129)) The `message()` and `code()` methods on errors have been moved into `ProvideErrorMetadata` trait. This trait will need to be imported to continue calling these. +- ⚠ (client, [smithy-rs#76](https://github.com/awslabs/smithy-rs/issues/76), [smithy-rs#2129](https://github.com/awslabs/smithy-rs/issues/2129), [smithy-rs#2075](https://github.com/awslabs/smithy-rs/issues/2075)) The `*Error` and `*ErrorKind` types have been combined to make error matching simpler. +
+ Example with S3 + **Before:** + ```rust + let result = client + .get_object() + .bucket(BUCKET_NAME) + .key("some-key") + .send() + .await; + match result { + Ok(_output) => { /* Do something with the output */ } + Err(err) => match err.into_service_error() { + GetObjectError { kind, .. } => match kind { + GetObjectErrorKind::InvalidObjectState(value) => println!("invalid object state: {:?}", value), + GetObjectErrorKind::NoSuchKey(_) => println!("object didn't exist"), + } + err @ GetObjectError { .. } if err.code() == Some("SomeUnmodeledError") => {} + err @ _ => return Err(err.into()), + }, + } + ``` + **After:** + ```rust + // Needed to access the `.code()` function on the error type: + use aws_sdk_s3::types::ProvideErrorMetadata; + let result = client + .get_object() + .bucket(BUCKET_NAME) + .key("some-key") + .send() + .await; + match result { + Ok(_output) => { /* Do something with the output */ } + Err(err) => match err.into_service_error() { + GetObjectError::InvalidObjectState(value) => { + println!("invalid object state: {:?}", value); + } + GetObjectError::NoSuchKey(_) => { + println!("object didn't exist"); + } + err if err.code() == Some("SomeUnmodeledError") => {} + err @ _ => return Err(err.into()), + }, + } + ``` +
+- ⚠ (client, [smithy-rs#76](https://github.com/awslabs/smithy-rs/issues/76), [smithy-rs#2129](https://github.com/awslabs/smithy-rs/issues/2129)) `aws_smithy_types::Error` has been renamed to `aws_smithy_types::error::ErrorMetadata`. +- ⚠ (server, [smithy-rs#2436](https://github.com/awslabs/smithy-rs/issues/2436)) Remove unnecessary type parameter `B` from `Upgrade` service. +- 🐛⚠ (server, [smithy-rs#2382](https://github.com/awslabs/smithy-rs/issues/2382)) Smithy members named `send` were previously renamed to `send_value` at codegen time. These will now be called `send` in the generated code. +- ⚠ (client, [smithy-rs#2448](https://github.com/awslabs/smithy-rs/issues/2448)) The modules in generated client crates have been reorganized. See the [Client Crate Reorganization Upgrade Guidance](https://github.com/awslabs/smithy-rs/discussions/2449) to see how to fix your code after this change. +- ⚠ (server, [smithy-rs#2438](https://github.com/awslabs/smithy-rs/issues/2438)) Servers can send the `ServerRequestId` in the response headers. + Servers need to create their service using the new layer builder `ServerRequestIdProviderLayer::new_with_response_header`: + ``` + let app = app + .layer(&ServerRequestIdProviderLayer::new_with_response_header(HeaderName::from_static("x-request-id"))); + ``` + +**New this release:** +- 🐛🎉 (client, [aws-sdk-rust#740](https://github.com/awslabs/aws-sdk-rust/issues/740)) Fluent builder methods on the client are now marked as deprecated when the related operation is deprecated. +- 🎉 (all, [smithy-rs#2398](https://github.com/awslabs/smithy-rs/issues/2398)) Add support for the `awsQueryCompatible` trait. This allows services to continue supporting a custom error code (via the `awsQueryError` trait) when the services migrate their protocol from `awsQuery` to `awsJson1_0` annotated with `awsQueryCompatible`. +
+ Click to expand for more details... + + After the migration, services will include an additional header `x-amzn-query-error` in their responses whose value is in the form of `;`. An example response looks something like + ``` + HTTP/1.1 400 + x-amzn-query-error: AWS.SimpleQueueService.NonExistentQueue;Sender + Date: Wed, 08 Sep 2021 23:46:52 GMT + Content-Type: application/x-amz-json-1.0 + Content-Length: 163 + + { + "__type": "com.amazonaws.sqs#QueueDoesNotExist", + "message": "some user-visible message" + } + ``` + `` is `AWS.SimpleQueueService.NonExistentQueue` and `` is `Sender`. + + If an operation results in an error that causes a service to send back the response above, you can access `` and `` as follows: + ```rust + match client.some_operation().send().await { + Ok(_) => { /* success */ } + Err(sdk_err) => { + let err = sdk_err.into_service_error(); + assert_eq!( + error.meta().code(), + Some("AWS.SimpleQueueService.NonExistentQueue"), + ); + assert_eq!(error.meta().extra("type"), Some("Sender")); + } + } +
+ ``` +- 🎉 (client, [smithy-rs#2428](https://github.com/awslabs/smithy-rs/issues/2428), [smithy-rs#2208](https://github.com/awslabs/smithy-rs/issues/2208)) `SdkError` variants can now be constructed for easier unit testing. +- 🐛 (server, [smithy-rs#2441](https://github.com/awslabs/smithy-rs/issues/2441)) Fix `FilterByOperationName` plugin. This previous caused services with this applied to fail to compile due to mismatched bounds. +- (client, [smithy-rs#2437](https://github.com/awslabs/smithy-rs/issues/2437), [aws-sdk-rust#600](https://github.com/awslabs/aws-sdk-rust/issues/600)) 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}`. +- 🐛 (all, [smithy-rs#2226](https://github.com/awslabs/smithy-rs/issues/2226)) Fix bug in timestamp format resolution. Prior to this fix, the timestamp format may have been incorrect if set on the target instead of on the member. +- (all, [smithy-rs#2226](https://github.com/awslabs/smithy-rs/issues/2226)) Add support for offsets when parsing datetimes. RFC3339 date times now support offsets like `-0200` +- (client, [aws-sdk-rust#160](https://github.com/awslabs/aws-sdk-rust/issues/160), [smithy-rs#2445](https://github.com/awslabs/smithy-rs/issues/2445)) Reconnect on transient errors. + + Note: **this behavior is disabled by default for generic clients**. It can be enabled with + `aws_smithy_client::Builder::reconnect_on_transient_errors` + + If a transient error (timeout, 500, 503, 503) is encountered, the connection will be evicted from the pool and will not + be reused. +- (all, [smithy-rs#2474](https://github.com/awslabs/smithy-rs/issues/2474)) Increase Tokio version to 1.23.1 for all crates. This is to address [RUSTSEC-2023-0001](https://rustsec.org/advisories/RUSTSEC-2023-0001) + + January 25th, 2023 ================== **New this release:** diff --git a/CHANGELOG.next.toml b/CHANGELOG.next.toml index 7a5f6d7515..19c7b471d0 100644 --- a/CHANGELOG.next.toml +++ b/CHANGELOG.next.toml @@ -12,119 +12,25 @@ # author = "rcoh" [[aws-sdk-rust]] -message = """ -Provide a way to retrieve fallback credentials if a call to `provide_credentials` is interrupted. An interrupt can occur when a timeout future is raced against a future for `provide_credentials`, and the former wins the race. A new method, `fallback_on_interrupt` on the `ProvideCredentials` trait, can be used in that case. The following code snippet from `LazyCredentialsCache::provide_cached_credentials` has been updated like so: -Before: -```rust -let timeout_future = self.sleeper.sleep(self.load_timeout); -// --snip-- -let future = Timeout::new(provider.provide_credentials(), timeout_future); -let result = cache - .get_or_load(|| { - async move { - let credentials = future.await.map_err(|_err| { - CredentialsError::provider_timed_out(load_timeout) - })??; - // --snip-- - } - }).await; -// --snip-- -``` -After: -```rust -let timeout_future = self.sleeper.sleep(self.load_timeout); -// --snip-- -let future = Timeout::new(provider.provide_credentials(), timeout_future); -let result = cache - .get_or_load(|| { - async move { - let credentials = match future.await { - Ok(creds) => creds?, - Err(_err) => match provider.fallback_on_interrupt() { // can provide fallback credentials - Some(creds) => creds, - None => return Err(CredentialsError::provider_timed_out(load_timeout)), - } - }; - // --snip-- - } - }).await; -// --snip-- -``` -""" -references = ["smithy-rs#2246"] -meta = { "breaking" = false, "tada" = false, "bug" = false } -author = "ysaito1001" - -[[smithy-rs]] -message = "The [`@uniqueItems`](https://smithy.io/2.0/spec/constraint-traits.html#uniqueitems-trait) trait on `list` shapes is now supported in server SDKs." -references = ["smithy-rs#2232", "smithy-rs#1670"] -meta = { "breaking" = false, "tada" = true, "bug" = false, "target" = "server"} -author = "david-perez" - -[[aws-sdk-rust]] -message = """ -Add static stability support to IMDS credentials provider. It does not alter common use cases for the provider, but allows the provider to serve expired credentials in case IMDS is unreachable. This allows requests to be dispatched to a target service with expired credentials. This, in turn, allows the target service to make the ultimate decision as to whether requests sent are valid or not. -""" -references = ["smithy-rs#2258"] -meta = { "breaking" = false, "tada" = true, "bug" = false } -author = "ysaito1001" - -[[smithy-rs]] -message = "Fix broken doc link for `tokio_stream::Stream` that is a re-export of `futures_core::Stream`." -references = ["smithy-rs#2271"] -meta = { "breaking" = false, "tada" = false, "bug" = true, "target" = "client"} -author = "ysaito1001" - -[[aws-sdk-rust]] -message = "Fix broken doc link for `tokio_stream::Stream` that is a re-export of `futures_core::Stream`." -references = ["smithy-rs#2271"] +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 = "ysaito1001" +author = "jdisanti" [[smithy-rs]] -message = """ -Fix `name` and `absolute` methods on `OperationExtension`. -The older, [now removed](https://github.com/awslabs/smithy-rs/pull/2161), service builder would insert `OperationExtension` into the `http::Response` containing the [absolute shape ID](https://smithy.io/2.0/spec/model.html#grammar-token-smithy-AbsoluteRootShapeId) with the `#` symbol replaced with a `.`. When [reintroduced](https://github.com/awslabs/smithy-rs/pull/2157) into the new service builder machinery the behavior was changed - we now do _not_ perform the replace. This change fixes the documentation and `name`/`absolute` methods of the `OperationExtension` API to match this new behavior. -In the old service builder, `OperationExtension` was initialized, by the framework, and then used as follows: -```rust -let ext = OperationExtension::new("com.amazonaws.CompleteSnapshot"); -// This is expected -let name = ext.name(); // "CompleteSnapshot" -let namespace = ext.namespace(); // = "com.amazonaws"; -``` -When reintroduced, `OperationExtension` was initialized by the `Plugin` and then used as follows: -```rust -let ext = OperationExtension::new("com.amazonaws#CompleteSnapshot"); -// This is the bug -let name = ext.name(); // "amazonaws#CompleteSnapshot" -let namespace = ext.namespace(); // = "com"; -``` -The intended behavior is now restored: -```rust -let ext = OperationExtension::new("com.amazonaws#CompleteSnapshot"); -// This is expected -let name = ext.name(); // "CompleteSnapshot" -let namespace = ext.namespace(); // = "com.amazonaws"; -``` -The rationale behind this change is that the previous design was tailored towards a specific internal use case and shouldn't be enforced on all customers. -""" -references = ["smithy-rs#2276"] -meta = { "breaking" = true, "tada" = false, "bug" = true, "target" = "server"} -author = "hlbarber" +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 = """ -Fix request canonicalization for HTTP requests with repeated headers (for example S3's `GetObjectAttributes`). Previously requests with repeated headers would fail with a 403 signature mismatch due to this bug. -""" -references = ["smithy-rs#2261", "aws-sdk-rust#720"] +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 = "nipunn1313" +author = "jdisanti" [[smithy-rs]] -message = """Add serde crate to `aws-smithy-types`. - -It's behind the feature gate `aws_sdk_unstable` which can only be enabled via a `--cfg` flag. -""" -references = ["smithy-rs#1944"] -meta = { "breaking" = false, "tada" = false, "bug" = false } -author = "thomas-k-cameron" +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" diff --git a/README.md b/README.md index 32d5651266..3fabab8897 100644 --- a/README.md +++ b/README.md @@ -38,6 +38,7 @@ Project Layout * [`design`](design): Design documentation. See the [design/README.md](design/README.md) for details about building / viewing. * `codegen-server`: Whitelabel Smithy server code generation * `codegen-server-test`: Smithy protocol test generation & integration tests for Smithy server whitelabel code +* `examples`: A collection of server implementation examples Testing ------- diff --git a/aws/SDK_CHANGELOG.next.json b/aws/SDK_CHANGELOG.next.json index 5a4025621f..6d3e509f8e 100644 --- a/aws/SDK_CHANGELOG.next.json +++ b/aws/SDK_CHANGELOG.next.json @@ -6,165 +6,108 @@ "smithy-rs": [], "aws-sdk-rust": [ { - "message": "Add test to exercise excluded headers in aws-sigv4.", + "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", "meta": { "bug": false, - "breaking": false, - "tada": false - }, - "author": "ysaito1001", - "references": [ - "smithy-rs#1890" - ], - "since-commit": "c3de8a3f93201f969c28deb9313c903c1315054d", - "age": 5 - }, - { - "message": "Support Sigv4 signature generation on PowerPC 32 and 64 bit. This architecture cannot compile `ring`, so the implementation has been updated to rely on `hamc` + `sha2` to achive the same result with broader platform compatibility and higher performance. We also updated the CI which is now running as many tests as possible against i686 and PowerPC 32 and 64 bit.", - "meta": { - "bug": true, "breaking": true, - "tada": false - }, - "author": "crisidev", - "references": [ - "smithy-rs#1847" - ], - "since-commit": "c3de8a3f93201f969c28deb9313c903c1315054d", - "age": 5 - }, - { - "message": "Add test ensuring that a response will error if the response body returns an EOF before the entire body has been read.", - "meta": { - "bug": false, - "breaking": false, - "tada": false - }, - "author": "Velfi", - "references": [ - "smithy-rs#1801" - ], - "since-commit": "c3de8a3f93201f969c28deb9313c903c1315054d", - "age": 5 - }, - { - "message": "Fix cargo audit issue on criterion.", - "meta": { - "bug": false, - "breaking": false, - "tada": false + "tada": true }, - "author": "ysaito1001", + "author": "rcoh", "references": [ - "smithy-rs#1923" + "smithy-rs#1784", + "smithy-rs#2074" ], - "since-commit": "c3de8a3f93201f969c28deb9313c903c1315054d", + "since-commit": "40da9a32b38e198da6ca2223b86c314b26438230", "age": 5 }, { - "message": "
\nThe HTTP connector used when making requests is now configurable through `SdkConfig`.\n\n```rust\nuse std::time::Duration;\nuse aws_smithy_client::{Client, hyper_ext};\nuse aws_smithy_client::erase::DynConnector;\nuse aws_smithy_client::http_connector::ConnectorSettings;\nuse aws_types::SdkConfig;\n\nlet https_connector = hyper_rustls::HttpsConnectorBuilder::new()\n .with_webpki_roots()\n .https_only()\n .enable_http1()\n .enable_http2()\n .build();\n\nlet smithy_connector = hyper_ext::Adapter::builder()\n // Optionally set things like timeouts as well\n .connector_settings(\n ConnectorSettings::builder()\n .connect_timeout(Duration::from_secs(5))\n .build()\n )\n .build(https_connector);\n\nlet sdk_config = aws_config::from_env()\n .http_connector(smithy_connector)\n .load()\n .await;\n\nlet client = Client::new(&sdk_config);\n\n// When sent, this operation will go through the custom smithy connector instead of\n// the default HTTP connector.\nlet op = client\n .get_object()\n .bucket(\"some-test-bucket\")\n .key(\"test.txt\")\n .send()\n .await\n .unwrap();\n```\n\n
\n", + "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", "meta": { "bug": false, - "breaking": false, + "breaking": true, "tada": true }, - "author": "Velfi", + "author": "rcoh", "references": [ - "smithy-rs#1225", - "smithy-rs#1918" + "smithy-rs#1784", + "smithy-rs#2074" ], - "since-commit": "c3de8a3f93201f969c28deb9313c903c1315054d", + "since-commit": "40da9a32b38e198da6ca2223b86c314b26438230", "age": 5 }, { - "message": "`::Client::from_conf_conn` has been removed since it's now possible to configure the connection from the\nshared and service configs. To update your code, pass connections to the `http_connector` method during config creation.\n\n
\nExample\n\nbefore:\n\n```rust\n let conf = aws_sdk_sts::Config::builder()\n // The builder has no defaults but setting other fields is omitted for brevity...\n .build();\n let (server, request) = capture_request(None);\n let client = aws_sdk_sts::Client::from_conf_conn(conf, server);\n```\n\nafter:\n\n```rust\n let (server, request) = capture_request(None);\n let conf = aws_sdk_sts::Config::builder()\n // The builder has no defaults but setting other fields is omitted for brevity...\n .http_connector(server)\n .build();\n let client = aws_sdk_sts::Client::from_conf(conf);\n```\n\n
\n", + "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", "meta": { "bug": false, "breaking": true, "tada": false }, - "author": "Velfi", + "author": "ysaito1001", "references": [ - "smithy-rs#1225", - "smithy-rs#1918" + "smithy-rs#2108" ], - "since-commit": "c3de8a3f93201f969c28deb9313c903c1315054d", + "since-commit": "40da9a32b38e198da6ca2223b86c314b26438230", "age": 5 }, { - "message": "Add `to_vec` method to `aws_smithy_http::byte_stream::AggregatedBytes`.", + "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", "meta": { "bug": false, "breaking": false, "tada": false }, - "author": "Velfi", - "references": [ - "smithy-rs#1918" - ], - "since-commit": "c3de8a3f93201f969c28deb9313c903c1315054d", - "age": 5 - }, - { - "message": "Ability to add an inline policy or a list of policy ARNs to the `AssumeRoleProvider` builder.", - "meta": { - "bug": false, - "breaking": false, - "tada": true - }, - "author": "albe-rosado", + "author": "rcoh", "references": [ - "aws-sdk-rust#641", - "smithy-rs#1892" + "smithy-rs#2152" ], - "since-commit": "c3de8a3f93201f969c28deb9313c903c1315054d", + "since-commit": "40da9a32b38e198da6ca2223b86c314b26438230", "age": 5 }, { - "message": "Removed re-export of `aws_smithy_client::retry::Config` from the `middleware` module.", + "message": "`aws_config::profile::retry_config` && `aws_config::environment::retry_config` have been removed. Use `aws_config::default_provider::retry_config` instead.", "meta": { "bug": false, "breaking": true, "tada": false }, - "author": "jdisanti", + "author": "rcoh", "references": [ - "smithy-rs#1935" + "smithy-rs#2162" ], - "since-commit": "c3de8a3f93201f969c28deb9313c903c1315054d", + "since-commit": "40da9a32b38e198da6ca2223b86c314b26438230", "age": 5 }, { - "message": "It was possible in some cases to send some S3 requests without a required upload ID, causing a risk of unintended data\ndeletion and modification. Now, when an operation has query parameters that are marked as required, the omission of\nthose query parameters will cause a BuildError, preventing the invalid operation from being sent.\n", + "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", "meta": { - "bug": true, + "bug": false, "breaking": false, - "tada": false + "tada": true }, - "author": "Velfi", + "author": "rcoh", "references": [ - "smithy-rs#1957" + "smithy-rs#2168" ], - "since-commit": "c3de8a3f93201f969c28deb9313c903c1315054d", + "since-commit": "40da9a32b38e198da6ca2223b86c314b26438230", "age": 5 }, { - "message": "Several breaking changes have been made to errors. See [the upgrade guide](https://github.com/awslabs/aws-sdk-rust/issues/657) for more information.", + "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", "meta": { "bug": false, "breaking": true, "tada": false }, - "author": "jdisanti", + "author": "ysaito1001", "references": [ - "smithy-rs#1926", - "smithy-rs#1819" + "smithy-rs#2122", + "smithy-rs#2227" ], - "since-commit": "c3de8a3f93201f969c28deb9313c903c1315054d", - "age": 5 + "since-commit": "48ce90d3a32cc87337d87d1f291b41fc64f1e5bd", + "age": 3 }, { - "message": "Generate enums that guide the users to write match expressions in a forward-compatible way.\nBefore this change, users could write a match expression against an enum in a non-forward-compatible way:\n```rust\nmatch some_enum {\n SomeEnum::Variant1 => { /* ... */ },\n SomeEnum::Variant2 => { /* ... */ },\n Unknown(value) if value == \"NewVariant\" => { /* ... */ },\n _ => { /* ... */ },\n}\n```\nThis code can handle a case for \"NewVariant\" with a version of SDK where the enum does not yet include `SomeEnum::NewVariant`, but breaks with another version of SDK where the enum defines `SomeEnum::NewVariant` because the execution will hit a different match arm, i.e. the last one.\nAfter this change, users are guided to write the above match expression as follows:\n```rust\nmatch some_enum {\n SomeEnum::Variant1 => { /* ... */ },\n SomeEnum::Variant2 => { /* ... */ },\n other @ _ if other.as_str() == \"NewVariant\" => { /* ... */ },\n _ => { /* ... */ },\n}\n```\nThis is forward-compatible because the execution will hit the second last match arm regardless of whether the enum defines `SomeEnum::NewVariant` or not.\n", + "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", "meta": { "bug": false, "breaking": true, @@ -172,43 +115,42 @@ }, "author": "ysaito1001", "references": [ - "smithy-rs#1945" + "smithy-rs#2122", + "smithy-rs#2227" ], - "since-commit": "c3de8a3f93201f969c28deb9313c903c1315054d", - "age": 5 + "since-commit": "48ce90d3a32cc87337d87d1f291b41fc64f1e5bd", + "age": 3 }, { - "message": "Functions on `aws_smithy_http::endpoint::Endpoint` now return a `Result` instead of panicking.", + "message": "Fix endpoint for s3.write_get_object_response(). This bug was introduced in 0.53.", "meta": { - "bug": false, - "breaking": true, + "bug": true, + "breaking": false, "tada": false }, - "author": "jdisanti", + "author": "rcoh", "references": [ - "smithy-rs#1984", - "smithy-rs#1496" + "smithy-rs#2204" ], - "since-commit": "c3de8a3f93201f969c28deb9313c903c1315054d", - "age": 5 + "since-commit": "48ce90d3a32cc87337d87d1f291b41fc64f1e5bd", + "age": 3 }, { - "message": "`Endpoint::mutable` now takes `impl AsRef` instead of `Uri`. For the old functionality, use `Endpoint::mutable_uri`.", + "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.", "meta": { "bug": false, - "breaking": true, + "breaking": false, "tada": false }, - "author": "jdisanti", + "author": "rcoh", "references": [ - "smithy-rs#1984", - "smithy-rs#1496" + "smithy-rs#2204" ], - "since-commit": "c3de8a3f93201f969c28deb9313c903c1315054d", - "age": 5 + "since-commit": "48ce90d3a32cc87337d87d1f291b41fc64f1e5bd", + "age": 3 }, { - "message": "`Endpoint::immutable` now takes `impl AsRef` instead of `Uri`. For the old functionality, use `Endpoint::immutable_uri`.", + "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, @@ -216,43 +158,29 @@ }, "author": "jdisanti", "references": [ - "smithy-rs#1984", - "smithy-rs#1496" - ], - "since-commit": "c3de8a3f93201f969c28deb9313c903c1315054d", - "age": 5 - }, - { - "message": "Normalize URI paths per RFC3986 when constructing canonical requests, except for S3.", - "meta": { - "bug": true, - "breaking": false, - "tada": false - }, - "author": "ysaito1001", - "references": [ - "smithy-rs#2018" + "smithy-rs#76", + "smithy-rs#2129" ], - "since-commit": "c3de8a3f93201f969c28deb9313c903c1315054d", - "age": 5 + "since-commit": "562e196bbfb5c57270b2855479a5c365ba3d2dff", + "age": 1 }, { - "message": "Implementation of the Debug trait for container shapes now redacts what is printed per the sensitive trait.", + "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": false }, - "author": "ysaito1001", + "author": "jdisanti", "references": [ - "smithy-rs#1983", - "smithy-rs#2029" + "smithy-rs#76", + "smithy-rs#2129" ], - "since-commit": "c3de8a3f93201f969c28deb9313c903c1315054d", - "age": 5 + "since-commit": "562e196bbfb5c57270b2855479a5c365ba3d2dff", + "age": 1 }, { - "message": "`SdkBody` callbacks have been removed. If you were using these, please [file an issue](https://github.com/awslabs/aws-sdk-rust/issues/new) so that we can better understand your use-case and provide the support you need.", + "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, @@ -260,158 +188,118 @@ }, "author": "jdisanti", "references": [ - "smithy-rs#2065" + "smithy-rs#76", + "smithy-rs#2129" ], - "since-commit": "c3de8a3f93201f969c28deb9313c903c1315054d", - "age": 5 + "since-commit": "562e196bbfb5c57270b2855479a5c365ba3d2dff", + "age": 1 }, { - "message": "`AwsEndpointStage`, a middleware which set endpoints and auth has been split into `AwsAuthStage` and `SmithyEndpointStage`. Related types have also been renamed.", + "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": "rcoh", - "references": [ - "smithy-rs#2063" - ], - "since-commit": "c3de8a3f93201f969c28deb9313c903c1315054d", - "age": 5 - }, - { - "message": "The SDK clients now default max idle connections to 70 (previously unlimited) to reduce the likelihood of hitting max file handles in AWS Lambda.", - "meta": { - "bug": false, - "breaking": false, - "tada": false - }, "author": "jdisanti", "references": [ - "smithy-rs#2064", - "aws-sdk-rust#632" + "smithy-rs#76", + "smithy-rs#2129", + "smithy-rs#2075" ], - "since-commit": "c3de8a3f93201f969c28deb9313c903c1315054d", - "age": 5 + "since-commit": "562e196bbfb5c57270b2855479a5c365ba3d2dff", + "age": 1 }, { - "message": "The Unit type for a Union member is no longer rendered. The serializers and parsers generated now function accordingly in the absence of the inner data associated with the Unit type.\n", + "message": "`aws_smithy_types::Error` has been renamed to `aws_smithy_types::error::ErrorMetadata`.", "meta": { "bug": false, "breaking": true, "tada": false }, - "author": "ysaito1001", + "author": "jdisanti", "references": [ - "smithy-rs#1989" + "smithy-rs#76", + "smithy-rs#2129" ], - "since-commit": "c3de8a3f93201f969c28deb9313c903c1315054d", - "age": 5 + "since-commit": "562e196bbfb5c57270b2855479a5c365ba3d2dff", + "age": 1 }, { - "message": "Fixed and improved the request `tracing` span hierarchy to improve log messages, profiling, and debuggability.", + "message": "Fluent builder methods on the client are now marked as deprecated when the related operation is deprecated.", "meta": { - "bug": false, + "bug": true, "breaking": false, "tada": true }, - "author": "jdisanti", + "author": "Velfi", "references": [ - "smithy-rs#2044", - "smithy-rs#371" + "aws-sdk-rust#740" ], - "since-commit": "c3de8a3f93201f969c28deb9313c903c1315054d", - "age": 5 + "since-commit": "562e196bbfb5c57270b2855479a5c365ba3d2dff", + "age": 1 }, { - "message": "Add more `tracing` events to signing and event streams", + "message": "`SdkError` variants can now be constructed for easier unit testing.", "meta": { "bug": false, "breaking": false, - "tada": false + "tada": true }, "author": "jdisanti", "references": [ - "smithy-rs#2057", - "smithy-rs#371" + "smithy-rs#2428", + "smithy-rs#2208" ], - "since-commit": "c3de8a3f93201f969c28deb9313c903c1315054d", - "age": 5 + "since-commit": "562e196bbfb5c57270b2855479a5c365ba3d2dff", + "age": 1 }, { - "message": "Log an `info` on credentials cache miss and adjust level of some credential `tracing` spans/events.", + "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": "jdisanti", + "author": "ysaito1001", "references": [ - "smithy-rs#2062" + "smithy-rs#2437", + "aws-sdk-rust#600" ], - "since-commit": "c3de8a3f93201f969c28deb9313c903c1315054d", - "age": 5 + "since-commit": "562e196bbfb5c57270b2855479a5c365ba3d2dff", + "age": 1 }, { - "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": "Enable presigning for S3's `HeadObject` operation.", "meta": { "bug": false, - "breaking": true, + "breaking": false, "tada": true }, - "author": "rcoh", + "author": "Velfi", "references": [ - "smithy-rs#1784", - "smithy-rs#2074" + "aws-sdk-rust#753", + "smithy-rs#2451" ], - "since-commit": "40da9a32b38e198da6ca2223b86c314b26438230", - "age": 4 + "since-commit": "562e196bbfb5c57270b2855479a5c365ba3d2dff", + "age": 1 }, { - "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", - "meta": { - "bug": false, - "breaking": true, - "tada": true - }, - "author": "rcoh", - "references": [ - "smithy-rs#1784", - "smithy-rs#2074" - ], - "since-commit": "40da9a32b38e198da6ca2223b86c314b26438230", - "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 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": "ysaito1001", - "references": [ - "smithy-rs#2108" - ], - "since-commit": "40da9a32b38e198da6ca2223b86c314b26438230", - "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", - "meta": { - "bug": false, - "breaking": false, - "tada": false - }, - "author": "rcoh", + "author": "jdisanti", "references": [ - "smithy-rs#2152" + "smithy-rs#2433" ], - "since-commit": "40da9a32b38e198da6ca2223b86c314b26438230", - "age": 4 + "since-commit": "562e196bbfb5c57270b2855479a5c365ba3d2dff", + "age": 1 }, { - "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, @@ -419,71 +307,59 @@ }, "author": "rcoh", "references": [ - "smithy-rs#2162" + "aws-sdk-rust#160", + "smithy-rs#2445" ], - "since-commit": "40da9a32b38e198da6ca2223b86c314b26438230", - "age": 4 + "since-commit": "562e196bbfb5c57270b2855479a5c365ba3d2dff", + "age": 1 }, { - "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": "rcoh", + "author": "Velfi", "references": [ - "smithy-rs#2168" + "smithy-rs#2467" ], - "since-commit": "40da9a32b38e198da6ca2223b86c314b26438230", - "age": 4 + "since-commit": "562e196bbfb5c57270b2855479a5c365ba3d2dff", + "age": 1 }, { - "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": "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": false, - "breaking": true, + "bug": true, + "breaking": false, "tada": false }, - "author": "ysaito1001", + "author": "rcoh", "references": [ - "smithy-rs#2122", - "smithy-rs#2227" + "smithy-rs#2471", + "smithy-rs#2333", + "smithy-rs#2151" ], - "since-commit": "48ce90d3a32cc87337d87d1f291b41fc64f1e5bd", - "age": 2 + "since-commit": "562e196bbfb5c57270b2855479a5c365ba3d2dff", + "age": 1 }, { - "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": "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", - "references": [ - "smithy-rs#2122", - "smithy-rs#2227" - ], - "since-commit": "48ce90d3a32cc87337d87d1f291b41fc64f1e5bd", - "age": 2 - }, - { - "message": "Fix endpoint for s3.write_get_object_response(). This bug was introduced in 0.53.", - "meta": { - "bug": true, - "breaking": false, - "tada": false - }, "author": "rcoh", "references": [ - "smithy-rs#2204" + "smithy-rs#2390", + "smithy-rs#1784" ], - "since-commit": "48ce90d3a32cc87337d87d1f291b41fc64f1e5bd", - "age": 2 + "since-commit": "562e196bbfb5c57270b2855479a5c365ba3d2dff", + "age": 1 }, { - "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": "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, @@ -491,10 +367,10 @@ }, "author": "rcoh", "references": [ - "smithy-rs#2204" + "smithy-rs#2474" ], - "since-commit": "48ce90d3a32cc87337d87d1f291b41fc64f1e5bd", - "age": 2 + "since-commit": "562e196bbfb5c57270b2855479a5c365ba3d2dff", + "age": 1 } ], "aws-sdk-model": [] diff --git a/aws/rust-runtime/aws-config/Cargo.toml b/aws/rust-runtime/aws-config/Cargo.toml index 2a43647379..66c773ed75 100644 --- a/aws/rust-runtime/aws-config/Cargo.toml +++ b/aws/rust-runtime/aws-config/Cargo.toml @@ -28,9 +28,9 @@ 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.12", default-features = false } +hyper = { version = "0.14.25", default-features = false } time = { version = "0.3.4", features = ["parsing"] } -tokio = { version = "1.8.4", features = ["sync"] } +tokio = { version = "1.13.1", features = ["sync"] } tracing = { version = "0.1" } # implementation detail of SSO credential caching @@ -46,10 +46,10 @@ http = "0.2.4" tower = { version = "0.4.8" } [dev-dependencies] -futures-util = "0.3.16" +futures-util = { version = "0.3.16", default-features = false } tracing-test = "0.2.1" -tokio = { version = "1.8.4", features = ["full", "test-util"] } +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 diff --git a/aws/rust-runtime/aws-config/external-types.toml b/aws/rust-runtime/aws-config/external-types.toml index 9a7dbc128e..7e0257f46d 100644 --- a/aws/rust-runtime/aws-config/external-types.toml +++ b/aws/rust-runtime/aws-config/external-types.toml @@ -7,7 +7,7 @@ allowed_external_types = [ "aws_credential_types::provider::ProvideCredentials", "aws_credential_types::provider::Result", "aws_credential_types::provider::SharedCredentialsProvider", - "aws_sdk_sts::model::PolicyDescriptorType", + "aws_sdk_sts::types::_policy_descriptor_type::PolicyDescriptorType", "aws_smithy_async::rt::sleep::AsyncSleep", "aws_smithy_client::bounds::SmithyConnector", "aws_smithy_client::erase::DynConnector", @@ -29,9 +29,6 @@ allowed_external_types = [ "http::uri::Uri", "tower_service::Service", - # TODO(https://github.com/awslabs/smithy-rs/issues/1193): Decide if `InvalidUri` should be exposed - "http::uri::InvalidUri", - # TODO(https://github.com/awslabs/smithy-rs/issues/1193): Decide if the following should be exposed "hyper::client::connect::Connection", "tokio::io::async_read::AsyncRead", diff --git a/aws/rust-runtime/aws-config/src/connector.rs b/aws/rust-runtime/aws-config/src/connector.rs index e023695363..f03d44ab6e 100644 --- a/aws/rust-runtime/aws-config/src/connector.rs +++ b/aws/rust-runtime/aws-config/src/connector.rs @@ -35,6 +35,7 @@ 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)) } diff --git a/aws/rust-runtime/aws-config/src/imds/client.rs b/aws/rust-runtime/aws-config/src/imds/client.rs index ba5cea046c..e791ec929a 100644 --- a/aws/rust-runtime/aws-config/src/imds/client.rs +++ b/aws/rust-runtime/aws-config/src/imds/client.rs @@ -917,7 +917,7 @@ pub(crate) mod test { imds_request("http://169.254.169.254/latest/metadata", TOKEN_A), http::Response::builder() .status(200) - .body(SdkBody::from(vec![0xA0 as u8, 0xA1 as u8])) + .body(SdkBody::from(vec![0xA0, 0xA1])) .unwrap(), ), ]); diff --git a/aws/rust-runtime/aws-config/src/imds/credentials.rs b/aws/rust-runtime/aws-config/src/imds/credentials.rs index 8cd3df7d0e..c260ba51f1 100644 --- a/aws/rust-runtime/aws-config/src/imds/credentials.rs +++ b/aws/rust-runtime/aws-config/src/imds/credentials.rs @@ -452,11 +452,10 @@ mod test { sleeper.sleep(std::time::Duration::from_millis(100)), ); match timeout.await { - Ok(_) => assert!(false, "provide_credentials completed before timeout future"), + Ok(_) => panic!("provide_credentials completed before timeout future"), Err(_err) => match provider.fallback_on_interrupt() { Some(actual) => assert_eq!(actual, expected), - None => assert!( - false, + None => panic!( "provide_credentials timed out and no credentials returned from fallback_on_interrupt" ), }, diff --git a/aws/rust-runtime/aws-config/src/imds/region.rs b/aws/rust-runtime/aws-config/src/imds/region.rs index 8679e3e753..bc784f8d4f 100644 --- a/aws/rust-runtime/aws-config/src/imds/region.rs +++ b/aws/rust-runtime/aws-config/src/imds/region.rs @@ -117,7 +117,7 @@ mod test { use crate::imds::client::test::{imds_request, imds_response, token_request, token_response}; use crate::imds::region::ImdsRegionProvider; use crate::provider_config::ProviderConfig; - use aws_sdk_sts::Region; + use aws_sdk_sts::config::Region; use aws_smithy_async::rt::sleep::TokioSleep; use aws_smithy_client::erase::DynConnector; use aws_smithy_client::test_connection::TestConnection; diff --git a/aws/rust-runtime/aws-config/src/lib.rs b/aws/rust-runtime/aws-config/src/lib.rs index 8ca695a770..3c6a11fa1d 100644 --- a/aws/rust-runtime/aws-config/src/lib.rs +++ b/aws/rust-runtime/aws-config/src/lib.rs @@ -3,6 +3,7 @@ * SPDX-License-Identifier: Apache-2.0 */ +#![allow(clippy::derive_partial_eq_without_eq)] #![warn( missing_debug_implementations, missing_docs, @@ -153,12 +154,11 @@ 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_client::http_connector::{ConnectorSettings, HttpConnector}; + 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::endpoint::ResolveAwsEndpoint; use aws_types::SdkConfig; use crate::connector::default_connector; @@ -180,7 +180,6 @@ mod loader { app_name: Option, credentials_cache: Option, credentials_provider: Option, - endpoint_resolver: Option>, endpoint_url: Option, region: Option>, retry_config: Option, @@ -344,36 +343,6 @@ mod loader { self } - /// Override the endpoint resolver used for **all** AWS Services - /// - /// This method is deprecated. Use [`Self::endpoint_url`] instead. - /// - /// This method will override the endpoint resolver used for **all** AWS services. This mainly - /// exists to set a static endpoint for tools like `LocalStack`. For live traffic, AWS services - /// require the service-specific endpoint resolver they load by default. - /// - /// # Examples - /// - /// Use a static endpoint for all services - /// ```no_run - /// # async fn create_config() -> Result<(), aws_smithy_http::endpoint::error::InvalidEndpointError> { - /// use aws_config::endpoint::Endpoint; - /// - /// let sdk_config = aws_config::from_env() - /// .endpoint_resolver(Endpoint::immutable("http://localhost:1234")?) - /// .load() - /// .await; - /// # Ok(()) - /// # } - #[deprecated(note = "use `.endpoint_url(...)` instead")] - pub fn endpoint_resolver( - mut self, - endpoint_resolver: impl ResolveAwsEndpoint + 'static, - ) -> Self { - self.endpoint_resolver = Some(Arc::new(endpoint_resolver)); - self - } - /// Provides the ability to programmatically override the profile files that get loaded by the SDK. /// /// The [`Default`] for `ProfileFiles` includes the default SDK config and credential files located in @@ -569,12 +538,9 @@ mod loader { .await }; - let http_connector = self.http_connector.unwrap_or_else(|| { - HttpConnector::Prebuilt(default_connector( - &ConnectorSettings::from_timeout_config(&timeout_config), - sleep_impl.clone(), - )) - }); + let http_connector = self + .http_connector + .unwrap_or_else(|| HttpConnector::ConnectorFn(Arc::new(default_connector))); let credentials_cache = self.credentials_cache.unwrap_or_else(|| { let mut builder = CredentialsCache::lazy_builder().time_source(conf.time_source()); @@ -602,8 +568,6 @@ mod loader { SharedCredentialsProvider::new(builder.build().await) }; - let endpoint_resolver = self.endpoint_resolver; - let mut builder = SdkConfig::builder() .region(region) .retry_config(retry_config) @@ -612,7 +576,6 @@ mod loader { .credentials_provider(credentials_provider) .http_connector(http_connector); - builder.set_endpoint_resolver(endpoint_resolver); builder.set_app_name(app_name); builder.set_sleep_impl(sleep_impl); builder.set_endpoint_url(self.endpoint_url); 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 02ae424c25..193d9fb1ec 100644 --- a/aws/rust-runtime/aws-config/src/meta/credentials/chain.rs +++ b/aws/rust-runtime/aws-config/src/meta/credentials/chain.rs @@ -174,11 +174,10 @@ mod tests { tokio::time::sleep(Duration::from_millis(300)), ); match timeout.await { - Ok(_) => assert!(false, "provide_credentials completed before timeout future"), + Ok(_) => panic!("provide_credentials completed before timeout future"), Err(_err) => match chain.fallback_on_interrupt() { Some(actual) => assert_eq!(actual, expected), - None => assert!( - false, + None => panic!( "provide_credentials timed out and no credentials returned from fallback_on_interrupt" ), }, @@ -208,11 +207,10 @@ mod tests { tokio::time::sleep(Duration::from_millis(100)), ); match timeout.await { - Ok(_) => assert!(false, "provide_credentials completed before timeout future"), + Ok(_) => panic!("provide_credentials completed before timeout future"), Err(_err) => match chain.fallback_on_interrupt() { Some(actual) => assert_eq!(actual, expected), - None => assert!( - false, + None => panic!( "provide_credentials timed out and no credentials returned from fallback_on_interrupt" ), }, 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 a49fefc8b4..80a5f192ca 100644 --- a/aws/rust-runtime/aws-config/src/profile/app_name.rs +++ b/aws/rust-runtime/aws-config/src/profile/app_name.rs @@ -103,7 +103,7 @@ mod tests { use super::ProfileFileAppNameProvider; use crate::provider_config::ProviderConfig; use crate::test_case::no_traffic_connector; - use aws_sdk_sts::AppName; + use aws_sdk_sts::config::AppName; use aws_types::os_shim_internal::{Env, Fs}; use tracing_test::traced_test; diff --git a/aws/rust-runtime/aws-config/src/profile/credentials.rs b/aws/rust-runtime/aws-config/src/profile/credentials.rs index ba7fb9241e..9ce085503b 100644 --- a/aws/rust-runtime/aws-config/src/profile/credentials.rs +++ b/aws/rust-runtime/aws-config/src/profile/credentials.rs @@ -61,9 +61,8 @@ impl ProvideCredentials for ProfileFileCredentialsProvider { /// let provider = ProfileFileCredentialsProvider::builder().build(); /// ``` /// -/// _Note: Profile providers to not implement any caching. They will reload and reparse the profile -/// from the file system when called. See [CredentialsCache](aws_credential_types::cache::CredentialsCache) for -/// more information about caching._ +/// _Note: Profile providers, when called, will load and parse the profile from the file system +/// only once. Parsed file contents will be cached indefinitely._ /// /// This provider supports several different credentials formats: /// ### Credentials defined explicitly within the file 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 51449f22ef..fda62d7708 100644 --- a/aws/rust-runtime/aws-config/src/profile/credentials/exec.rs +++ b/aws/rust-runtime/aws-config/src/profile/credentials/exec.rs @@ -3,14 +3,7 @@ * SPDX-License-Identifier: Apache-2.0 */ -use std::sync::Arc; - -use aws_sdk_sts::operation::AssumeRole; -use aws_sdk_sts::{Config, Credentials}; -use aws_types::region::Region; - use super::repr::{self, BaseProvider}; - use crate::credential_process::CredentialProcessProvider; use crate::profile::credentials::ProfileFileError; use crate::provider_config::ProviderConfig; @@ -19,9 +12,12 @@ 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 std::fmt::Debug; +use std::sync::Arc; #[derive(Debug)] pub(super) struct AssumeRoleProvider { @@ -51,7 +47,7 @@ impl AssumeRoleProvider { .as_ref() .cloned() .unwrap_or_else(|| sts::util::default_session_name("assume-role-from-profile")); - let operation = AssumeRole::builder() + let operation = AssumeRoleInput::builder() .role_arn(&self.role_arn) .set_external_id(self.external_id.clone()) .role_session_name(session_name) diff --git a/aws/rust-runtime/aws-config/src/profile/parser.rs b/aws/rust-runtime/aws-config/src/profile/parser.rs index 0692bf376f..6d6dc204a3 100644 --- a/aws/rust-runtime/aws-config/src/profile/parser.rs +++ b/aws/rust-runtime/aws-config/src/profile/parser.rs @@ -334,14 +334,14 @@ mod test { fn flatten(profile: ProfileSet) -> HashMap> { profile .profiles - .into_iter() - .map(|(_name, profile)| { + .into_values() + .map(|profile| { ( profile.name, profile .properties - .into_iter() - .map(|(_, prop)| (prop.key, prop.value)) + .into_values() + .map(|prop| (prop.key, prop.value)) .collect(), ) }) diff --git a/aws/rust-runtime/aws-config/src/profile/parser/source.rs b/aws/rust-runtime/aws-config/src/profile/parser/source.rs index d626394aa1..1b859fcee9 100644 --- a/aws/rust-runtime/aws-config/src/profile/parser/source.rs +++ b/aws/rust-runtime/aws-config/src/profile/parser/source.rs @@ -216,7 +216,7 @@ mod tests { // ~ is only expanded as a single component (currently) let path = "~aws/config"; assert_eq!( - expand_home(&path, false, &None).to_str().unwrap(), + expand_home(path, false, &None).to_str().unwrap(), "~aws/config" ); } @@ -336,7 +336,7 @@ mod tests { fn test_expand_home() { let path = "~/.aws/config"; assert_eq!( - expand_home(&path, false, &Some("/user/foo".to_string())) + expand_home(path, false, &Some("/user/foo".to_string())) .to_str() .unwrap(), "/user/foo/.aws/config" @@ -366,7 +366,7 @@ mod tests { fn test_expand_home_windows() { let path = "~/.aws/config"; assert_eq!( - expand_home(&path, true, &Some("C:\\Users\\name".to_string()),) + expand_home(path, true, &Some("C:\\Users\\name".to_string()),) .to_str() .unwrap(), "C:\\Users\\name\\.aws\\config" diff --git a/aws/rust-runtime/aws-config/src/profile/region.rs b/aws/rust-runtime/aws-config/src/profile/region.rs index c58fce4039..3cdcf8f7e4 100644 --- a/aws/rust-runtime/aws-config/src/profile/region.rs +++ b/aws/rust-runtime/aws-config/src/profile/region.rs @@ -158,7 +158,7 @@ mod test { use crate::profile::ProfileFileRegionProvider; use crate::provider_config::ProviderConfig; use crate::test_case::no_traffic_connector; - use aws_sdk_sts::Region; + use aws_sdk_sts::config::Region; use aws_types::os_shim_internal::{Env, Fs}; use futures_util::FutureExt; use tracing_test::traced_test; diff --git a/aws/rust-runtime/aws-config/src/provider_config.rs b/aws/rust-runtime/aws-config/src/provider_config.rs index 9ca283d25f..cb4bd5aa17 100644 --- a/aws/rust-runtime/aws-config/src/provider_config.rs +++ b/aws/rust-runtime/aws-config/src/provider_config.rs @@ -124,7 +124,7 @@ impl ProviderConfig { /// # #[cfg(any(feature = "rustls", feature = "native-tls"))] /// # fn example() { /// use aws_config::provider_config::ProviderConfig; - /// use aws_sdk_sts::Region; + /// use aws_sdk_sts::config::Region; /// use aws_config::web_identity_token::WebIdentityTokenCredentialsProvider; /// let conf = ProviderConfig::without_region().with_region(Some(Region::new("us-east-1"))); /// @@ -156,7 +156,7 @@ impl ProviderConfig { /// ```no_run /// # async fn test() { /// use aws_config::provider_config::ProviderConfig; - /// use aws_sdk_sts::Region; + /// use aws_sdk_sts::config::Region; /// use aws_config::web_identity_token::WebIdentityTokenCredentialsProvider; /// let conf = ProviderConfig::with_default_region().await; /// let credential_provider = WebIdentityTokenCredentialsProvider::builder().configure(&conf).build(); diff --git a/aws/rust-runtime/aws-config/src/sso.rs b/aws/rust-runtime/aws-config/src/sso.rs index 0f58264645..7c693bdf2e 100644 --- a/aws/rust-runtime/aws-config/src/sso.rs +++ b/aws/rust-runtime/aws-config/src/sso.rs @@ -17,7 +17,8 @@ use crate::provider_config::ProviderConfig; 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::model::RoleCredentials; +use aws_sdk_sso::operation::get_role_credentials::GetRoleCredentialsInput; +use aws_sdk_sso::types::RoleCredentials; use aws_smithy_client::erase::DynConnector; use aws_smithy_json::deserialize::Token; use aws_smithy_types::date_time::Format; @@ -211,7 +212,7 @@ async fn load_sso_credentials( let config = aws_sdk_sso::Config::builder() .region(sso_config.region.clone()) .build(); - let operation = aws_sdk_sso::operation::GetRoleCredentials::builder() + let operation = GetRoleCredentialsInput::builder() .role_name(&sso_config.role_name) .access_token(&*token.access_token) .account_id(&sso_config.account_id) 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 422b644151..35f2b3fa27 100644 --- a/aws/rust-runtime/aws-config/src/sts/assume_role.rs +++ b/aws/rust-runtime/aws-config/src/sts/assume_role.rs @@ -5,19 +5,17 @@ //! Assume credentials for a role through the AWS Security Token Service (STS). +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::error::AssumeRoleErrorKind; use aws_sdk_sts::middleware::DefaultMiddleware; -use aws_sdk_sts::model::PolicyDescriptorType; -use aws_sdk_sts::operation::AssumeRole; +use aws_sdk_sts::operation::assume_role::{AssumeRoleError, AssumeRoleInput}; +use aws_sdk_sts::types::PolicyDescriptorType; use aws_smithy_client::erase::DynConnector; use aws_smithy_http::result::SdkError; +use aws_smithy_types::error::display::DisplayErrorContext; use aws_types::region::Region; use std::time::Duration; - -use crate::provider_config::ProviderConfig; -use aws_smithy_types::error::display::DisplayErrorContext; use tracing::Instrument; /// Credentials provider that uses credentials provided by another provider to assume a role @@ -50,7 +48,7 @@ pub struct AssumeRoleProvider { struct Inner { sts: aws_smithy_client::Client, conf: aws_sdk_sts::Config, - op: aws_sdk_sts::input::AssumeRoleInput, + op: AssumeRoleInput, } impl AssumeRoleProvider { @@ -129,7 +127,7 @@ impl AssumeRoleProviderBuilder { /// /// This parameter is optional /// For more information, see - /// [policy](aws_sdk_sts::input::assume_role_input::Builder::policy_arns) + /// [policy](aws_sdk_sts::operation::assume_role::builders::AssumeRoleInputBuilder::policy_arns) pub fn policy(mut self, policy: impl Into) -> Self { self.policy = Some(policy.into()); self @@ -139,7 +137,7 @@ impl AssumeRoleProviderBuilder { /// /// This parameter is optional. /// For more information, see - /// [policy_arns](aws_sdk_sts::input::assume_role_input::Builder::policy_arns) + /// [policy_arns](aws_sdk_sts::operation::assume_role::builders::AssumeRoleInputBuilder::policy_arns) pub fn policy_arns(mut self, policy_arns: Vec) -> Self { self.policy_arns = Some(policy_arns); self @@ -156,7 +154,7 @@ impl AssumeRoleProviderBuilder { /// but your administrator set the maximum session duration to 6 hours, you cannot assume the role. /// /// For more information, see - /// [duration_seconds](aws_sdk_sts::input::assume_role_input::Builder::duration_seconds) + /// [duration_seconds](aws_sdk_sts::operation::assume_role::builders::AssumeRoleInputBuilder::duration_seconds) pub fn session_length(mut self, length: Duration) -> Self { self.session_length = Some(length); self @@ -225,7 +223,7 @@ impl AssumeRoleProviderBuilder { .session_name .unwrap_or_else(|| super::util::default_session_name("assume-role-provider")); - let operation = AssumeRole::builder() + let operation = AssumeRoleInput::builder() .set_role_arn(Some(self.role_arn)) .set_external_id(self.external_id) .set_role_session_name(Some(session_name)) @@ -266,9 +264,9 @@ impl Inner { } Err(SdkError::ServiceError(ref context)) if matches!( - context.err().kind, - AssumeRoleErrorKind::RegionDisabledException(_) - | AssumeRoleErrorKind::MalformedPolicyDocumentException(_) + context.err(), + AssumeRoleError::RegionDisabledException(_) + | AssumeRoleError::MalformedPolicyDocumentException(_) ) => { Err(CredentialsError::invalid_configuration( diff --git a/aws/rust-runtime/aws-config/src/sts/util.rs b/aws/rust-runtime/aws-config/src/sts/util.rs index 9ebdbe6f16..426d3eb40a 100644 --- a/aws/rust-runtime/aws-config/src/sts/util.rs +++ b/aws/rust-runtime/aws-config/src/sts/util.rs @@ -5,7 +5,7 @@ use aws_credential_types::provider::{self, error::CredentialsError}; use aws_credential_types::Credentials as AwsCredentials; -use aws_sdk_sts::model::Credentials as StsCredentials; +use aws_sdk_sts::types::Credentials as StsCredentials; use std::convert::TryFrom; use std::time::{SystemTime, UNIX_EPOCH}; 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 e3c45c7bb8..dd13524b06 100644 --- a/aws/rust-runtime/aws-config/src/web_identity_token.rs +++ b/aws/rust-runtime/aws-config/src/web_identity_token.rs @@ -64,8 +64,9 @@ 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::Region; +use aws_sdk_sts::operation::assume_role_with_web_identity::AssumeRoleWithWebIdentityInput; use aws_smithy_client::erase::DynConnector; use aws_smithy_types::error::display::DisplayErrorContext; use aws_types::os_shim_internal::{Env, Fs}; @@ -236,7 +237,7 @@ async fn load_credentials( .region(region.clone()) .build(); - let operation = aws_sdk_sts::operation::AssumeRoleWithWebIdentity::builder() + let operation = AssumeRoleWithWebIdentityInput::builder() .role_arn(role_arn) .role_session_name(session_name) .web_identity_token(token) @@ -260,7 +261,7 @@ mod test { Builder, ENV_VAR_ROLE_ARN, ENV_VAR_SESSION_NAME, ENV_VAR_TOKEN_FILE, }; use aws_credential_types::provider::error::CredentialsError; - use aws_sdk_sts::Region; + use aws_sdk_sts::config::Region; use aws_smithy_async::rt::sleep::TokioSleep; use aws_smithy_types::error::display::DisplayErrorContext; use aws_types::os_shim_internal::{Env, Fs}; diff --git a/aws/rust-runtime/aws-credential-types/Cargo.toml b/aws/rust-runtime/aws-credential-types/Cargo.toml index bf7e332568..8fad933c2a 100644 --- a/aws/rust-runtime/aws-credential-types/Cargo.toml +++ b/aws/rust-runtime/aws-credential-types/Cargo.toml @@ -14,7 +14,8 @@ test-util = [] [dependencies] aws-smithy-async = { path = "../../../rust-runtime/aws-smithy-async" } aws-smithy-types = { path = "../../../rust-runtime/aws-smithy-types" } -tokio = { version = "1.8.4", features = ["sync"] } +fastrand = "1.4.0" +tokio = { version = "1.23.1", features = ["sync"] } tracing = "0.1" zeroize = "1" @@ -25,7 +26,7 @@ aws-smithy-async = { path = "../../../rust-runtime/aws-smithy-async", features = async-trait = "0.1.51" env_logger = "0.9.0" -tokio = { version = "1.8.4", features = ["full", "test-util", "rt"] } +tokio = { version = "1.23.1", features = ["full", "test-util", "rt"] } tracing-test = "0.2.1" [package.metadata.docs.rs] 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 3a2459c597..1081b8f336 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 @@ -20,6 +20,7 @@ use crate::time_source::TimeSource; const DEFAULT_LOAD_TIMEOUT: Duration = Duration::from_secs(5); const DEFAULT_CREDENTIAL_EXPIRATION: Duration = Duration::from_secs(15 * 60); const DEFAULT_BUFFER_TIME: Duration = Duration::from_secs(10); +const DEFAULT_BUFFER_TIME_JITTER_FRACTION: fn() -> f64 = fastrand::f64; #[derive(Debug)] pub(crate) struct LazyCredentialsCache { @@ -28,6 +29,8 @@ pub(crate) struct LazyCredentialsCache { cache: ExpiringCache, provider: SharedCredentialsProvider, load_timeout: Duration, + buffer_time: Duration, + buffer_time_jitter_fraction: fn() -> f64, default_credential_expiration: Duration, } @@ -37,8 +40,9 @@ impl LazyCredentialsCache { sleeper: Arc, provider: SharedCredentialsProvider, load_timeout: Duration, - default_credential_expiration: Duration, buffer_time: Duration, + buffer_time_jitter_fraction: fn() -> f64, + default_credential_expiration: Duration, ) -> Self { Self { time, @@ -46,6 +50,8 @@ impl LazyCredentialsCache { cache: ExpiringCache::new(buffer_time), provider, load_timeout, + buffer_time, + buffer_time_jitter_fraction, default_credential_expiration, } } @@ -95,17 +101,28 @@ impl ProvideCachedCredentials for LazyCredentialsCache { let expiry = credentials .expiry() .unwrap_or(now + default_credential_expiration); - Ok((credentials, expiry)) + + let jitter = self + .buffer_time + .mul_f64((self.buffer_time_jitter_fraction)()); + + // Logging for cache miss should be emitted here as opposed to after the call to + // `cache.get_or_load` above. In the case of multiple threads concurrently executing + // `cache.get_or_load`, logging inside `cache.get_or_load` ensures that it is emitted + // 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() + ); + + 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) }) .await; - info!( - "credentials cache miss occurred; retrieved new AWS credentials (took {:?})", - start_time.elapsed() - ); + debug!("loaded credentials"); result } }) @@ -125,8 +142,8 @@ mod builder { use super::TimeSource; use super::{ - LazyCredentialsCache, DEFAULT_BUFFER_TIME, DEFAULT_CREDENTIAL_EXPIRATION, - DEFAULT_LOAD_TIMEOUT, + LazyCredentialsCache, DEFAULT_BUFFER_TIME, DEFAULT_BUFFER_TIME_JITTER_FRACTION, + DEFAULT_CREDENTIAL_EXPIRATION, DEFAULT_LOAD_TIMEOUT, }; /// Builder for constructing a `LazyCredentialsCache`. @@ -147,6 +164,7 @@ mod builder { time_source: Option, load_timeout: Option, buffer_time: Option, + buffer_time_jitter_fraction: Option f64>, default_credential_expiration: Option, } @@ -228,6 +246,38 @@ mod builder { self } + /// A random percentage by which buffer time is jittered for randomization. + /// + /// For example, if credentials are expiring in 15 minutes, the buffer time is 10 seconds, + /// and buffer time jitter fraction is 0.2, then buffer time is adjusted to 8 seconds. + /// Therefore, any requests made after 14 minutes and 52 seconds will load new credentials. + /// + /// Defaults to a randomly generated value between 0.0 and 1.0. This setter is for testing only. + #[cfg(feature = "test-util")] + pub fn buffer_time_jitter_fraction( + mut self, + buffer_time_jitter_fraction: fn() -> f64, + ) -> Self { + self.set_buffer_time_jitter_fraction(Some(buffer_time_jitter_fraction)); + self + } + + /// A random percentage by which buffer time is jittered for randomization. + /// + /// For example, if credentials are expiring in 15 minutes, the buffer time is 10 seconds, + /// and buffer time jitter fraction is 0.2, then buffer time is adjusted to 8 seconds. + /// Therefore, any requests made after 14 minutes and 52 seconds will load new credentials. + /// + /// Defaults to a randomly generated value between 0.0 and 1.0. This setter is for testing only. + #[cfg(feature = "test-util")] + pub fn set_buffer_time_jitter_fraction( + &mut self, + buffer_time_jitter_fraction: Option f64>, + ) -> &mut Self { + self.buffer_time_jitter_fraction = buffer_time_jitter_fraction; + self + } + /// Default expiration time to set on credentials if they don't have an expiration time. /// /// This is only used if the given [`ProvideCredentials`](crate::provider::ProvideCredentials) returns @@ -283,8 +333,10 @@ mod builder { }), provider, self.load_timeout.unwrap_or(DEFAULT_LOAD_TIMEOUT), - default_credential_expiration, self.buffer_time.unwrap_or(DEFAULT_BUFFER_TIME), + self.buffer_time_jitter_fraction + .unwrap_or(DEFAULT_BUFFER_TIME_JITTER_FRACTION), + default_credential_expiration, ) } } @@ -310,8 +362,11 @@ mod tests { DEFAULT_LOAD_TIMEOUT, }; + const BUFFER_TIME_NO_JITTER: fn() -> f64 = || 0_f64; + fn test_provider( time: TimeSource, + buffer_time_jitter_fraction: fn() -> f64, load_list: Vec, ) -> LazyCredentialsCache { let load_list = Arc::new(Mutex::new(load_list)); @@ -327,8 +382,9 @@ mod tests { } })), DEFAULT_LOAD_TIMEOUT, - DEFAULT_CREDENTIAL_EXPIRATION, DEFAULT_BUFFER_TIME, + buffer_time_jitter_fraction, + DEFAULT_CREDENTIAL_EXPIRATION, ) } @@ -361,8 +417,9 @@ mod tests { Arc::new(TokioSleep::new()), provider, DEFAULT_LOAD_TIMEOUT, - DEFAULT_CREDENTIAL_EXPIRATION, DEFAULT_BUFFER_TIME, + BUFFER_TIME_NO_JITTER, + DEFAULT_CREDENTIAL_EXPIRATION, ); assert_eq!( epoch_secs(1000), @@ -381,6 +438,7 @@ mod tests { let mut time = TestingTimeSource::new(epoch_secs(100)); let credentials_cache = test_provider( TimeSource::testing(&time), + BUFFER_TIME_NO_JITTER, vec![ Ok(credentials(1000)), Ok(credentials(2000)), @@ -404,6 +462,7 @@ mod tests { let mut time = TestingTimeSource::new(epoch_secs(100)); let credentials_cache = test_provider( TimeSource::testing(&time), + BUFFER_TIME_NO_JITTER, vec![ Ok(credentials(1000)), Err(CredentialsError::not_loaded("failed")), @@ -430,6 +489,7 @@ mod tests { let time = TestingTimeSource::new(epoch_secs(0)); let credentials_cache = Arc::new(test_provider( TimeSource::testing(&time), + BUFFER_TIME_NO_JITTER, vec![ Ok(credentials(500)), Ok(credentials(1500)), @@ -480,8 +540,9 @@ mod tests { Ok(credentials(1000)) })), Duration::from_millis(5), - DEFAULT_CREDENTIAL_EXPIRATION, DEFAULT_BUFFER_TIME, + BUFFER_TIME_NO_JITTER, + DEFAULT_CREDENTIAL_EXPIRATION, ); assert!(matches!( @@ -489,4 +550,30 @@ mod tests { Err(CredentialsError::ProviderTimedOut { .. }) )); } + + #[tokio::test] + async fn buffer_time_jitter() { + let mut time = TestingTimeSource::new(epoch_secs(100)); + let buffer_time_jitter_fraction = || 0.5_f64; + let credentials_cache = test_provider( + TimeSource::testing(&time), + buffer_time_jitter_fraction, + vec![Ok(credentials(1000)), Ok(credentials(2000))], + ); + + expect_creds(1000, &credentials_cache).await; + let buffer_time_with_jitter = + (DEFAULT_BUFFER_TIME.as_secs_f64() * buffer_time_jitter_fraction()) as u64; + assert_eq!(buffer_time_with_jitter, 5); + // Advance time to the point where the first credentials are about to expire (but haven't). + let almost_expired_secs = 1000 - buffer_time_with_jitter - 1; + time.set_time(epoch_secs(almost_expired_secs)); + // We should still use the first credentials. + expect_creds(1000, &credentials_cache).await; + // Now let the first credentials expire. + let expired_secs = almost_expired_secs + 1; + time.set_time(epoch_secs(expired_secs)); + // Now that the first credentials have been expired, the second credentials will be retrieved. + expect_creds(2000, &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 1f790c3fca..b2f8330b58 100644 --- a/aws/rust-runtime/aws-credential-types/src/lib.rs +++ b/aws/rust-runtime/aws-credential-types/src/lib.rs @@ -8,6 +8,7 @@ //! * An opaque struct representing credentials //! * Concrete implementations of credentials caching +#![allow(clippy::derive_partial_eq_without_eq)] #![warn( missing_debug_implementations, missing_docs, diff --git a/aws/rust-runtime/aws-endpoint/src/lib.rs b/aws/rust-runtime/aws-endpoint/src/lib.rs index c895143184..8003d7c4aa 100644 --- a/aws/rust-runtime/aws-endpoint/src/lib.rs +++ b/aws/rust-runtime/aws-endpoint/src/lib.rs @@ -3,84 +3,19 @@ * SPDX-License-Identifier: Apache-2.0 */ -use std::collections::HashMap; +#![allow(clippy::derive_partial_eq_without_eq)] + use std::error::Error; use std::fmt; -use std::sync::Arc; -use aws_smithy_http::endpoint::error::ResolveEndpointError; -use aws_smithy_http::endpoint::ResolveEndpoint; use aws_smithy_http::middleware::MapRequest; use aws_smithy_http::operation::Request; use aws_smithy_types::endpoint::Endpoint as SmithyEndpoint; use aws_smithy_types::Document; -pub use aws_types::endpoint::{AwsEndpoint, BoxError, CredentialScope, ResolveAwsEndpoint}; use aws_types::region::{Region, SigningRegion}; use aws_types::SigningService; -#[doc(hidden)] -pub struct Params { - region: Option, -} - -impl Params { - pub fn new(region: Option) -> Self { - Self { region } - } -} - -#[doc(hidden)] -pub struct EndpointShim(Arc); -impl EndpointShim { - pub fn from_resolver(resolver: impl ResolveAwsEndpoint + 'static) -> Self { - Self(Arc::new(resolver)) - } - - pub fn from_arc(arc: Arc) -> Self { - Self(arc) - } -} - -impl ResolveEndpoint for EndpointShim -where - T: Clone + Into, -{ - fn resolve_endpoint(&self, params: &T) -> Result { - let params: Params = params.clone().into(); - let aws_endpoint = self - .0 - .resolve_endpoint( - params - .region - .as_ref() - .ok_or_else(|| ResolveEndpointError::message("no region in params"))?, - ) - .map_err(|err| { - ResolveEndpointError::message("failure resolving endpoint").with_source(Some(err)) - })?; - let uri = aws_endpoint.endpoint().uri(); - let mut auth_scheme = - HashMap::from([("name".to_string(), Document::String("sigv4".into()))]); - if let Some(region) = aws_endpoint.credential_scope().region() { - auth_scheme.insert( - "signingRegion".to_string(), - region.as_ref().to_string().into(), - ); - } - if let Some(service) = aws_endpoint.credential_scope().service() { - auth_scheme.insert( - "signingName".to_string(), - service.as_ref().to_string().into(), - ); - } - Ok(SmithyEndpoint::builder() - .url(uri.to_string()) - .property("authSchemes", vec![Document::Object(auth_scheme)]) - .build()) - } -} - /// Middleware Stage to add authentication information from a Smithy endpoint into the property bag /// /// AwsAuthStage implements [`MapRequest`](MapRequest). It will: @@ -93,7 +28,7 @@ pub struct AwsAuthStage; #[derive(Debug)] enum AwsAuthStageErrorKind { NoEndpointResolver, - EndpointResolutionError(BoxError), + EndpointResolutionError(Box), } #[derive(Debug)] @@ -270,7 +205,7 @@ mod test { let mut req = operation::Request::new(req); { let mut props = req.properties_mut(); - props.insert(region.clone()); + props.insert(region); props.insert(SigningService::from_static("qldb")); props.insert(endpoint); }; diff --git a/aws/rust-runtime/aws-http/Cargo.toml b/aws/rust-runtime/aws-http/Cargo.toml index 20e131a883..be5cedd5d8 100644 --- a/aws/rust-runtime/aws-http/Cargo.toml +++ b/aws/rust-runtime/aws-http/Cargo.toml @@ -29,7 +29,7 @@ aws-smithy-protocol-test = { path = "../../../rust-runtime/aws-smithy-protocol-t bytes-utils = "0.1.2" env_logger = "0.9" http = "0.2.3" -tokio = { version = "1.8.4", features = ["macros", "rt", "rt-multi-thread", "test-util", "time"] } +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" serde = { version = "1", features = ["derive"]} diff --git a/aws/rust-runtime/aws-http/src/auth.rs b/aws/rust-runtime/aws-http/src/auth.rs index c91b4c5bb5..98e0e219bb 100644 --- a/aws/rust-runtime/aws-http/src/auth.rs +++ b/aws/rust-runtime/aws-http/src/auth.rs @@ -188,10 +188,7 @@ mod tests { .create_cache(SharedCredentialsProvider::new(provide_credentials_fn( || async { Ok(Credentials::for_tests()) }, ))); - set_credentials_cache( - &mut req.properties_mut(), - SharedCredentialsCache::from(credentials_cache), - ); + set_credentials_cache(&mut req.properties_mut(), credentials_cache); let req = CredentialsStage::new() .apply(req) .await diff --git a/aws/rust-runtime/aws-http/src/lib.rs b/aws/rust-runtime/aws-http/src/lib.rs index b000c3d6ae..d5307bcba3 100644 --- a/aws/rust-runtime/aws-http/src/lib.rs +++ b/aws/rust-runtime/aws-http/src/lib.rs @@ -3,8 +3,9 @@ * SPDX-License-Identifier: Apache-2.0 */ -//! Provides user agent and credentials middleware for the AWS SDK. +//! AWS-specific middleware implementations and HTTP-related features. +#![allow(clippy::derive_partial_eq_without_eq)] #![warn( missing_docs, rustdoc::missing_crate_level_docs, @@ -27,3 +28,6 @@ pub mod user_agent; /// AWS-specific content-encoding tools pub mod content_encoding; + +/// AWS-specific request ID support +pub mod request_id; diff --git a/aws/rust-runtime/aws-http/src/request_id.rs b/aws/rust-runtime/aws-http/src/request_id.rs new file mode 100644 index 0000000000..7713328f7c --- /dev/null +++ b/aws/rust-runtime/aws-http/src/request_id.rs @@ -0,0 +1,188 @@ +/* + * 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_http::operation; +use aws_smithy_http::result::SdkError; +use aws_smithy_types::error::metadata::{ + Builder as ErrorMetadataBuilder, ErrorMetadata, ProvideErrorMetadata, +}; +use aws_smithy_types::error::Unhandled; +use http::{HeaderMap, HeaderValue}; + +/// Constant for the [`ErrorMetadata`] extra field that contains the request ID +const AWS_REQUEST_ID: &str = "aws_request_id"; + +/// Implementers add a function to return an AWS request ID +pub trait RequestId { + /// Returns the request ID, or `None` if the service could not be reached. + fn request_id(&self) -> Option<&str>; +} + +impl RequestId for SdkError +where + R: HttpHeaders, +{ + fn request_id(&self) -> Option<&str> { + match self { + Self::ResponseError(err) => extract_request_id(err.raw().http_headers()), + Self::ServiceError(err) => extract_request_id(err.raw().http_headers()), + _ => None, + } + } +} + +impl RequestId for ErrorMetadata { + fn request_id(&self) -> Option<&str> { + self.extra(AWS_REQUEST_ID) + } +} + +impl RequestId for Unhandled { + fn request_id(&self) -> Option<&str> { + self.meta().request_id() + } +} + +impl RequestId for operation::Response { + fn request_id(&self) -> Option<&str> { + extract_request_id(self.http().headers()) + } +} + +impl RequestId for http::Response { + fn request_id(&self) -> Option<&str> { + extract_request_id(self.headers()) + } +} + +impl RequestId for HeaderMap { + fn request_id(&self) -> Option<&str> { + extract_request_id(self) + } +} + +impl RequestId for Result +where + O: RequestId, + E: RequestId, +{ + fn request_id(&self) -> Option<&str> { + match self { + Ok(ok) => ok.request_id(), + Err(err) => err.request_id(), + } + } +} + +/// Applies a request ID to a generic error builder +#[doc(hidden)] +pub fn apply_request_id( + builder: ErrorMetadataBuilder, + headers: &HeaderMap, +) -> ErrorMetadataBuilder { + if let Some(request_id) = extract_request_id(headers) { + builder.custom(AWS_REQUEST_ID, request_id) + } else { + builder + } +} + +/// Extracts a request ID from HTTP response headers +fn extract_request_id(headers: &HeaderMap) -> Option<&str> { + headers + .get("x-amzn-requestid") + .or_else(|| headers.get("x-amz-request-id")) + .and_then(|value| value.to_str().ok()) +} + +#[cfg(test)] +mod tests { + use super::*; + use aws_smithy_http::body::SdkBody; + use http::Response; + + #[test] + fn test_request_id_sdk_error() { + let without_request_id = + || operation::Response::new(Response::builder().body(SdkBody::empty()).unwrap()); + let with_request_id = || { + operation::Response::new( + Response::builder() + .header( + "x-amzn-requestid", + HeaderValue::from_static("some-request-id"), + ) + .body(SdkBody::empty()) + .unwrap(), + ) + }; + assert_eq!( + None, + SdkError::<(), _>::response_error("test", without_request_id()).request_id() + ); + assert_eq!( + Some("some-request-id"), + SdkError::<(), _>::response_error("test", with_request_id()).request_id() + ); + assert_eq!( + None, + SdkError::service_error((), without_request_id()).request_id() + ); + assert_eq!( + Some("some-request-id"), + SdkError::service_error((), with_request_id()).request_id() + ); + } + + #[test] + fn test_extract_request_id() { + let mut headers = HeaderMap::new(); + assert_eq!(None, extract_request_id(&headers)); + + headers.append( + "x-amzn-requestid", + HeaderValue::from_static("some-request-id"), + ); + assert_eq!(Some("some-request-id"), extract_request_id(&headers)); + + headers.append( + "x-amz-request-id", + HeaderValue::from_static("other-request-id"), + ); + assert_eq!(Some("some-request-id"), extract_request_id(&headers)); + + headers.remove("x-amzn-requestid"); + assert_eq!(Some("other-request-id"), extract_request_id(&headers)); + } + + #[test] + fn test_apply_request_id() { + let mut headers = HeaderMap::new(); + assert_eq!( + ErrorMetadata::builder().build(), + apply_request_id(ErrorMetadata::builder(), &headers).build(), + ); + + headers.append( + "x-amzn-requestid", + HeaderValue::from_static("some-request-id"), + ); + assert_eq!( + ErrorMetadata::builder() + .custom(AWS_REQUEST_ID, "some-request-id") + .build(), + apply_request_id(ErrorMetadata::builder(), &headers).build(), + ); + } + + #[test] + fn test_error_metadata_request_id_impl() { + let err = ErrorMetadata::builder() + .custom(AWS_REQUEST_ID, "some-request-id") + .build(); + assert_eq!(Some("some-request-id"), err.request_id()); + } +} diff --git a/aws/rust-runtime/aws-inlineable/Cargo.toml b/aws/rust-runtime/aws-inlineable/Cargo.toml index 4c67b6bb5f..a5ce695bda 100644 --- a/aws/rust-runtime/aws-inlineable/Cargo.toml +++ b/aws/rust-runtime/aws-inlineable/Cargo.toml @@ -29,7 +29,7 @@ http = "0.2.4" http-body = "0.4.5" md-5 = "0.10.1" ring = "0.16" -tokio = { version = "1.8.4", features = ["full"] } +tokio = { version = "1.23.1", features = ["full"] } tokio-stream = "0.1.5" tower = { version = "0.4", default-features = false } tracing = "0.1" diff --git a/aws/rust-runtime/aws-inlineable/src/http_body_checksum.rs b/aws/rust-runtime/aws-inlineable/src/http_body_checksum.rs index 59dea2ca41..d99e1b1123 100644 --- a/aws/rust-runtime/aws-inlineable/src/http_body_checksum.rs +++ b/aws/rust-runtime/aws-inlineable/src/http_body_checksum.rs @@ -269,7 +269,7 @@ mod tests { for i in 0..10000 { let line = format!("This is a large file created for testing purposes {}", i); - file.as_file_mut().write(line.as_bytes()).unwrap(); + file.as_file_mut().write_all(line.as_bytes()).unwrap(); crc32c_checksum.update(line.as_bytes()); } diff --git a/aws/rust-runtime/aws-inlineable/src/lib.rs b/aws/rust-runtime/aws-inlineable/src/lib.rs index b4e00994d4..d5ed3b8be3 100644 --- a/aws/rust-runtime/aws-inlineable/src/lib.rs +++ b/aws/rust-runtime/aws-inlineable/src/lib.rs @@ -10,6 +10,7 @@ //! This is _NOT_ intended to be an actual crate. It is a cargo project to solely to aid //! with local development of the SDK. +#![allow(clippy::derive_partial_eq_without_eq)] #![warn( missing_docs, rustdoc::missing_crate_level_docs, @@ -23,9 +24,11 @@ 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; -/// Special logic for handling S3's error responses. -pub mod s3_errors; +/// Special logic for extracting request IDs from S3's responses. +pub mod s3_request_id; /// Glacier-specific checksumming behavior pub mod glacier_checksums; diff --git a/aws/rust-runtime/aws-inlineable/src/old_presigning.rs b/aws/rust-runtime/aws-inlineable/src/old_presigning.rs new file mode 100644 index 0000000000..cf95c3901d --- /dev/null +++ b/aws/rust-runtime/aws-inlineable/src/old_presigning.rs @@ -0,0 +1,282 @@ +/* + * 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 5a97a19902..da0997d591 100644 --- a/aws/rust-runtime/aws-inlineable/src/presigning.rs +++ b/aws/rust-runtime/aws-inlineable/src/presigning.rs @@ -5,229 +5,221 @@ //! Presigned request types and configuration. -/// Presigning config and builder -pub mod config { - use std::fmt; - use std::time::{Duration, SystemTime}; +use std::fmt; +use std::time::{Duration, SystemTime}; - const ONE_WEEK: Duration = Duration::from_secs(604800); +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, - } +/// 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() - } +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() - } + /// Creates a new builder for creating a `PresigningConfig`. + pub fn builder() -> PresigningConfigBuilder { + PresigningConfigBuilder::default() + } - /// Returns the amount of time the presigned request should be valid for. - pub fn expires(&self) -> Duration { - self.expires_in - } + /// 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 - } + /// 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, +#[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, - } + /// The `PresigningConfig` builder requires a value for `expires_in`. + ExpiresInRequired, +} - /// `PresigningConfig` build errors. - #[derive(Debug)] - pub struct Error { - kind: ErrorKind, - } +/// `PresigningConfig` build errors. +#[derive(Debug)] +pub struct PresigningConfigError { + kind: ErrorKind, +} - impl std::error::Error for Error {} +impl std::error::Error for PresigningConfigError {} - 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 fmt::Display for PresigningConfigError { + 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 From for PresigningConfigError { + fn from(kind: ErrorKind) -> Self { + Self { kind } } +} - 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; - } +/// Builder used to create `PresigningConfig`. +#[non_exhaustive] +#[derive(Default, Debug)] +pub struct PresigningConfigBuilder { + start_time: Option, + expires_in: Option, +} - /// 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, - }) - } +impl PresigningConfigBuilder { + /// 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 } -} -/// Presigned request -pub mod request { - use std::fmt::{Debug, Formatter}; + /// 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; + } - /// Represents a presigned request. This only includes the HTTP request method, URI, and headers. + /// Sets how long the request should be valid after the `start_time` (which defaults + /// to the current time). /// - /// **This struct has conversion convenience functions:** + /// 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. /// - /// - [`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<()>); + /// Required. + pub fn expires_in(mut self, expires_in: Duration) -> Self { + self.set_expires_in(Some(expires_in)); + self + } - impl PresignedRequest { - pub(crate) fn new(inner: http::Request<()>) -> Self { - Self(inner) - } + /// 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; + } - /// Returns the HTTP request method. - pub fn method(&self) -> &http::Method { - self.0.method() + /// 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, + }) + } +} - /// Returns the HTTP request URI. - pub fn uri(&self) -> &http::Uri { - self.0.uri() - } +/// 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 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() - } + /// Returns the HTTP request method. + pub fn method(&self) -> &http::Method { + self.0.method() + } - /// 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(); + /// Returns the HTTP request URI. + pub fn uri(&self) -> &http::Uri { + self.0.uri() + } - builder.body(body) - } + /// 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() } - 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() - } + /// 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 From for http::request::Builder { - fn from(req: PresignedRequest) -> Self { - let mut builder = http::request::Builder::new() - .uri(req.uri()) - .method(req.method()); +impl fmt::Debug for PresignedRequest { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("PresignedRequest") + .field("method", self.method()) + .field("uri", self.uri()) + .field("headers", self.headers()) + .finish() + } +} - if let Some(headers) = builder.headers_mut() { - *headers = req.headers().clone(); - } +impl From for http::request::Builder { + fn from(req: PresignedRequest) -> Self { + let mut builder = http::request::Builder::new() + .uri(req.uri()) + .method(req.method()); - builder + 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 crate::presigning::request::PresignedRequest; + use super::PresignedRequest; use aws_smithy_http::operation; use http::header::USER_AGENT; use std::future::{ready, Ready}; diff --git a/aws/rust-runtime/aws-inlineable/src/s3_errors.rs b/aws/rust-runtime/aws-inlineable/src/s3_errors.rs deleted file mode 100644 index ca15ddc42b..0000000000 --- a/aws/rust-runtime/aws-inlineable/src/s3_errors.rs +++ /dev/null @@ -1,77 +0,0 @@ -/* - * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. - * SPDX-License-Identifier: Apache-2.0 - */ - -use http::{HeaderMap, HeaderValue}; - -const EXTENDED_REQUEST_ID: &str = "s3_extended_request_id"; - -/// S3-specific service error additions. -pub trait ErrorExt { - /// Returns the S3 Extended Request ID necessary when contacting AWS Support. - /// Read more at . - fn extended_request_id(&self) -> Option<&str>; -} - -impl ErrorExt for aws_smithy_types::Error { - fn extended_request_id(&self) -> Option<&str> { - self.extra(EXTENDED_REQUEST_ID) - } -} - -/// Parses the S3 Extended Request ID out of S3 error response headers. -pub fn parse_extended_error( - error: aws_smithy_types::Error, - headers: &HeaderMap, -) -> aws_smithy_types::Error { - let mut builder = error.into_builder(); - let host_id = headers - .get("x-amz-id-2") - .and_then(|header_value| header_value.to_str().ok()); - if let Some(host_id) = host_id { - builder.custom(EXTENDED_REQUEST_ID, host_id); - } - builder.build() -} - -#[cfg(test)] -mod test { - use crate::s3_errors::{parse_extended_error, ErrorExt}; - - #[test] - fn add_error_fields() { - let resp = http::Response::builder() - .header( - "x-amz-id-2", - "eftixk72aD6Ap51TnqcoF8eFidJG9Z/2mkiDFu8yU9AS1ed4OpIszj7UDNEHGran", - ) - .status(400) - .body("") - .unwrap(); - let error = aws_smithy_types::Error::builder() - .message("123") - .request_id("456") - .build(); - - let error = parse_extended_error(error, resp.headers()); - assert_eq!( - error - .extended_request_id() - .expect("extended request id should be set"), - "eftixk72aD6Ap51TnqcoF8eFidJG9Z/2mkiDFu8yU9AS1ed4OpIszj7UDNEHGran" - ); - } - - #[test] - fn handle_missing_header() { - let resp = http::Response::builder().status(400).body("").unwrap(); - let error = aws_smithy_types::Error::builder() - .message("123") - .request_id("456") - .build(); - - let error = parse_extended_error(error, resp.headers()); - assert_eq!(error.extended_request_id(), None); - } -} diff --git a/aws/rust-runtime/aws-inlineable/src/s3_request_id.rs b/aws/rust-runtime/aws-inlineable/src/s3_request_id.rs new file mode 100644 index 0000000000..f19ad434d1 --- /dev/null +++ b/aws/rust-runtime/aws-inlineable/src/s3_request_id.rs @@ -0,0 +1,184 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +use aws_smithy_client::SdkError; +use aws_smithy_http::http::HttpHeaders; +use aws_smithy_http::operation; +use aws_smithy_types::error::metadata::{ + Builder as ErrorMetadataBuilder, ErrorMetadata, ProvideErrorMetadata, +}; +use aws_smithy_types::error::Unhandled; +use http::{HeaderMap, HeaderValue}; + +const EXTENDED_REQUEST_ID: &str = "s3_extended_request_id"; + +/// Trait to retrieve the S3-specific extended request ID +/// +/// Read more at . +pub trait RequestIdExt { + /// Returns the S3 Extended Request ID necessary when contacting AWS Support. + fn extended_request_id(&self) -> Option<&str>; +} + +impl RequestIdExt for SdkError +where + R: HttpHeaders, +{ + fn extended_request_id(&self) -> Option<&str> { + match self { + Self::ResponseError(err) => extract_extended_request_id(err.raw().http_headers()), + Self::ServiceError(err) => extract_extended_request_id(err.raw().http_headers()), + _ => None, + } + } +} + +impl RequestIdExt for ErrorMetadata { + fn extended_request_id(&self) -> Option<&str> { + self.extra(EXTENDED_REQUEST_ID) + } +} + +impl RequestIdExt for Unhandled { + fn extended_request_id(&self) -> Option<&str> { + self.meta().extended_request_id() + } +} + +impl RequestIdExt for operation::Response { + fn extended_request_id(&self) -> Option<&str> { + extract_extended_request_id(self.http().headers()) + } +} + +impl RequestIdExt for http::Response { + fn extended_request_id(&self) -> Option<&str> { + extract_extended_request_id(self.headers()) + } +} + +impl RequestIdExt for HeaderMap { + fn extended_request_id(&self) -> Option<&str> { + extract_extended_request_id(self) + } +} + +impl RequestIdExt for Result +where + O: RequestIdExt, + E: RequestIdExt, +{ + fn extended_request_id(&self) -> Option<&str> { + match self { + Ok(ok) => ok.extended_request_id(), + Err(err) => err.extended_request_id(), + } + } +} + +/// Applies the extended request ID to a generic error builder +#[doc(hidden)] +pub fn apply_extended_request_id( + builder: ErrorMetadataBuilder, + headers: &HeaderMap, +) -> ErrorMetadataBuilder { + if let Some(extended_request_id) = extract_extended_request_id(headers) { + builder.custom(EXTENDED_REQUEST_ID, extended_request_id) + } else { + builder + } +} + +/// Extracts the S3 Extended Request ID from HTTP response headers +fn extract_extended_request_id(headers: &HeaderMap) -> Option<&str> { + headers + .get("x-amz-id-2") + .and_then(|value| value.to_str().ok()) +} + +#[cfg(test)] +mod test { + use super::*; + use aws_smithy_client::SdkError; + use aws_smithy_http::body::SdkBody; + use http::Response; + + #[test] + fn handle_missing_header() { + let resp = http::Response::builder().status(400).body("").unwrap(); + let mut builder = aws_smithy_types::Error::builder().message("123"); + builder = apply_extended_request_id(builder, resp.headers()); + assert_eq!(builder.build().extended_request_id(), None); + } + + #[test] + fn test_extended_request_id_sdk_error() { + let without_extended_request_id = + || operation::Response::new(Response::builder().body(SdkBody::empty()).unwrap()); + let with_extended_request_id = || { + operation::Response::new( + Response::builder() + .header("x-amz-id-2", HeaderValue::from_static("some-request-id")) + .body(SdkBody::empty()) + .unwrap(), + ) + }; + assert_eq!( + None, + SdkError::<(), _>::response_error("test", without_extended_request_id()) + .extended_request_id() + ); + assert_eq!( + Some("some-request-id"), + SdkError::<(), _>::response_error("test", with_extended_request_id()) + .extended_request_id() + ); + assert_eq!( + None, + SdkError::service_error((), without_extended_request_id()).extended_request_id() + ); + assert_eq!( + Some("some-request-id"), + SdkError::service_error((), with_extended_request_id()).extended_request_id() + ); + } + + #[test] + fn test_extract_extended_request_id() { + let mut headers = HeaderMap::new(); + assert_eq!(None, extract_extended_request_id(&headers)); + + headers.append("x-amz-id-2", HeaderValue::from_static("some-request-id")); + assert_eq!( + Some("some-request-id"), + extract_extended_request_id(&headers) + ); + } + + #[test] + fn test_apply_extended_request_id() { + let mut headers = HeaderMap::new(); + assert_eq!( + ErrorMetadata::builder().build(), + apply_extended_request_id(ErrorMetadata::builder(), &headers).build(), + ); + + headers.append("x-amz-id-2", HeaderValue::from_static("some-request-id")); + assert_eq!( + ErrorMetadata::builder() + .custom(EXTENDED_REQUEST_ID, "some-request-id") + .build(), + apply_extended_request_id(ErrorMetadata::builder(), &headers).build(), + ); + } + + #[test] + fn test_error_metadata_extended_request_id_impl() { + let err = ErrorMetadata::builder() + .custom(EXTENDED_REQUEST_ID, "some-request-id") + .build(); + assert_eq!(Some("some-request-id"), err.extended_request_id()); + } +} diff --git a/aws/rust-runtime/aws-sig-auth/src/lib.rs b/aws/rust-runtime/aws-sig-auth/src/lib.rs index 61ae88e81c..643b3ff216 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 */ +#![allow(clippy::derive_partial_eq_without_eq)] + //! AWS Signature Authentication Package //! //! This crate may be used to generate presigned URLs for unmodeled behavior such as `rds-iam-token` diff --git a/aws/rust-runtime/aws-sigv4/Cargo.toml b/aws/rust-runtime/aws-sigv4/Cargo.toml index ad1570bb86..90cf5f42f3 100644 --- a/aws/rust-runtime/aws-sigv4/Cargo.toml +++ b/aws/rust-runtime/aws-sigv4/Cargo.toml @@ -32,7 +32,7 @@ sha2 = "0.10" criterion = "0.4" bytes = "1" httparse = "1.5" -pretty_assertions = "1.0" +pretty_assertions = "1.3" proptest = "1" time = { version = "0.3.4", features = ["parsing"] } diff --git a/aws/rust-runtime/aws-sigv4/src/event_stream.rs b/aws/rust-runtime/aws-sigv4/src/event_stream.rs index 349ba919f0..a8da19ab03 100644 --- a/aws/rust-runtime/aws-sigv4/src/event_stream.rs +++ b/aws/rust-runtime/aws-sigv4/src/event_stream.rs @@ -74,7 +74,7 @@ fn calculate_string_to_sign( let mut date_buffer = Vec::new(); write_headers_to(&[date_header], &mut date_buffer).unwrap(); writeln!(sts, "{}", sha256_hex_string(&date_buffer)).unwrap(); - write!(sts, "{}", sha256_hex_string(&message_payload)).unwrap(); + write!(sts, "{}", sha256_hex_string(message_payload)).unwrap(); sts } 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 172f2cbce7..08ac6c8f54 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 @@ -5,7 +5,6 @@ use crate::date_time::{format_date, format_date_time}; use crate::http_request::error::CanonicalRequestError; -use crate::http_request::query_writer::QueryWriter; use crate::http_request::settings::UriPathNormalizationMode; use crate::http_request::sign::SignableRequest; use crate::http_request::uri_path_normalization::normalize_uri_path; @@ -13,6 +12,7 @@ use crate::http_request::url_escape::percent_encode_path; use crate::http_request::PercentEncodingMode; use crate::http_request::{PayloadChecksumKind, SignableBody, SignatureLocation, SigningParams}; use crate::sign::sha256_hex_string; +use aws_smithy_http::query_writer::QueryWriter; use http::header::{AsHeaderName, HeaderName, HOST}; use http::{HeaderMap, HeaderValue, Method, Uri}; use std::borrow::Cow; @@ -519,13 +519,13 @@ mod tests { use crate::http_request::canonical_request::{ normalize_header_value, trim_all, CanonicalRequest, SigningScope, StringToSign, }; - use crate::http_request::query_writer::QueryWriter; use crate::http_request::test::{test_canonical_request, test_request, test_sts}; use crate::http_request::{ PayloadChecksumKind, SignableBody, SignableRequest, SigningSettings, }; use crate::http_request::{SignatureLocation, SigningParams}; use crate::sign::sha256_hex_string; + use aws_smithy_http::query_writer::QueryWriter; use http::Uri; use http::{header::HeaderName, HeaderValue}; use pretty_assertions::assert_eq; 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 c93a052887..543d58fb2d 100644 --- a/aws/rust-runtime/aws-sigv4/src/http_request/mod.rs +++ b/aws/rust-runtime/aws-sigv4/src/http_request/mod.rs @@ -43,7 +43,6 @@ mod canonical_request; mod error; -mod query_writer; mod settings; mod sign; mod uri_path_normalization; 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 69f5c00819..dbe7624294 100644 --- a/aws/rust-runtime/aws-sigv4/src/http_request/sign.rs +++ b/aws/rust-runtime/aws-sigv4/src/http_request/sign.rs @@ -8,10 +8,10 @@ use super::{PayloadChecksumKind, SignatureLocation}; use crate::http_request::canonical_request::header; use crate::http_request::canonical_request::param; use crate::http_request::canonical_request::{CanonicalRequest, StringToSign, HMAC_256}; -use crate::http_request::query_writer::QueryWriter; use crate::http_request::SigningParams; use crate::sign::{calculate_signature, generate_signing_key, sha256_hex_string}; use crate::SigningOutput; +use aws_smithy_http::query_writer::QueryWriter; use http::header::HeaderValue; use http::{HeaderMap, Method, Uri}; use std::borrow::Cow; @@ -380,9 +380,11 @@ mod tests { #[test] fn test_sign_vanilla_with_query_params() { - let mut settings = SigningSettings::default(); - settings.signature_location = SignatureLocation::QueryParams; - settings.expires_in = Some(Duration::from_secs(35)); + let settings = SigningSettings { + signature_location: SignatureLocation::QueryParams, + expires_in: Some(Duration::from_secs(35)), + ..Default::default() + }; let params = SigningParams { access_key: "AKIDEXAMPLE", secret_key: "wJalrXUtnFEMI/K7MDENG+bPxRfiCYEXAMPLEKEY", diff --git a/aws/rust-runtime/aws-sigv4/src/http_request/url_escape.rs b/aws/rust-runtime/aws-sigv4/src/http_request/url_escape.rs index 651c62c44a..d7656c355b 100644 --- a/aws/rust-runtime/aws-sigv4/src/http_request/url_escape.rs +++ b/aws/rust-runtime/aws-sigv4/src/http_request/url_escape.rs @@ -3,11 +3,7 @@ * SPDX-License-Identifier: Apache-2.0 */ -use aws_smithy_http::{label, query}; - -pub(super) fn percent_encode_query(value: &str) -> String { - query::fmt_string(value) -} +use aws_smithy_http::label; pub(super) fn percent_encode_path(value: &str) -> String { label::fmt_string(value, label::EncodingStrategy::Greedy) diff --git a/aws/rust-runtime/aws-sigv4/src/lib.rs b/aws/rust-runtime/aws-sigv4/src/lib.rs index be2552f578..14d7a7b5fd 100644 --- a/aws/rust-runtime/aws-sigv4/src/lib.rs +++ b/aws/rust-runtime/aws-sigv4/src/lib.rs @@ -6,6 +6,7 @@ //! Provides functions for calculating Sigv4 signing keys, signatures, and //! optional utilities for signing HTTP requests and Event Stream messages. +#![allow(clippy::derive_partial_eq_without_eq)] #![warn( missing_docs, rustdoc::missing_crate_level_docs, diff --git a/aws/rust-runtime/aws-sigv4/src/sign.rs b/aws/rust-runtime/aws-sigv4/src/sign.rs index a140f6e955..43e9da1ac1 100644 --- a/aws/rust-runtime/aws-sigv4/src/sign.rs +++ b/aws/rust-runtime/aws-sigv4/src/sign.rs @@ -84,7 +84,7 @@ mod tests { #[test] fn sign_payload_empty_string() { let expected = "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855"; - let actual = sha256_hex_string(&[]); + let actual = sha256_hex_string([]); assert_eq!(expected, actual); } } diff --git a/aws/rust-runtime/aws-types/Cargo.toml b/aws/rust-runtime/aws-types/Cargo.toml index 9004790827..5c3d39b909 100644 --- a/aws/rust-runtime/aws-types/Cargo.toml +++ b/aws/rust-runtime/aws-types/Cargo.toml @@ -25,7 +25,7 @@ http = "0.2.6" hyper-rustls = { version = "0.23.0", optional = true, features = ["rustls-native-certs", "http2", "webpki-roots"] } [dev-dependencies] -futures-util = "0.3.16" +futures-util = { version = "0.3.16", default-features = false } http = "0.2.4" tracing-test = "0.2.1" diff --git a/aws/rust-runtime/aws-types/build.rs b/aws/rust-runtime/aws-types/build.rs index 533271f769..593ac054b3 100644 --- a/aws/rust-runtime/aws-types/build.rs +++ b/aws/rust-runtime/aws-types/build.rs @@ -11,8 +11,8 @@ use std::path::Path; fn generate_build_vars(output_path: &Path) { let rust_version = rustc_version::version().expect("Could not retrieve rustc version"); - let mut f = File::create(&output_path.join("build_env.rs")) - .expect("Could not create build environment"); + let mut f = + File::create(output_path.join("build_env.rs")).expect("Could not create build environment"); f.write_all(format!("const RUST_VERSION: &str = \"{}\";", rust_version).as_bytes()) .expect("Unable to write rust version"); f.flush().expect("failed to flush"); diff --git a/aws/rust-runtime/aws-types/src/endpoint.rs b/aws/rust-runtime/aws-types/src/endpoint.rs deleted file mode 100644 index 9bd5bb3ceb..0000000000 --- a/aws/rust-runtime/aws-types/src/endpoint.rs +++ /dev/null @@ -1,192 +0,0 @@ -/* - * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. - * SPDX-License-Identifier: Apache-2.0 - */ - -//! AWS SDK endpoint support. -#![allow(deprecated)] - -use crate::region::{Region, SigningRegion}; -use crate::SigningService; -use aws_smithy_http::endpoint::error::InvalidEndpointError; -use aws_smithy_http::endpoint::{Endpoint, EndpointPrefix}; -use std::error::Error; -use std::fmt::Debug; - -/// Endpoint to connect to an AWS Service -/// -/// An `AwsEndpoint` captures all necessary information needed to connect to an AWS service, including: -/// - The URI of the endpoint (needed to actually send the request) -/// - The name of the service (needed downstream for signing) -/// - The signing region (which may differ from the actual region) -#[derive(Clone, Debug)] -pub struct AwsEndpoint { - endpoint: Endpoint, - credential_scope: CredentialScope, -} - -impl AwsEndpoint { - /// Constructs a new AWS endpoint. - pub fn new(endpoint: Endpoint, credential_scope: CredentialScope) -> AwsEndpoint { - AwsEndpoint { - endpoint, - credential_scope, - } - } - - /// Returns the underlying endpoint. - pub fn endpoint(&self) -> &Endpoint { - &self.endpoint - } - - /// Returns the credential scope. - pub fn credential_scope(&self) -> &CredentialScope { - &self.credential_scope - } - - /// Sets the endpoint on a given `uri` based on this endpoint - pub fn set_endpoint( - &self, - uri: &mut http::Uri, - endpoint_prefix: Option<&EndpointPrefix>, - ) -> Result<(), InvalidEndpointError> { - self.endpoint.set_endpoint(uri, endpoint_prefix) - } -} - -/// A boxed error. -pub type BoxError = Box; - -/// Resolve the AWS Endpoint for a given region -/// -/// To provide a static endpoint, [`Endpoint`](aws_smithy_http::endpoint::Endpoint) implements this trait. -/// Example usage: -/// ```rust -/// # mod dynamodb { -/// # use aws_types::endpoint::ResolveAwsEndpoint; -/// # pub struct ConfigBuilder; -/// # impl ConfigBuilder { -/// # pub fn endpoint(&mut self, resolver: impl ResolveAwsEndpoint + 'static) { -/// # // ... -/// # } -/// # } -/// # pub struct Config; -/// # impl Config { -/// # pub fn builder() -> ConfigBuilder { -/// # ConfigBuilder -/// # } -/// # } -/// # } -/// # fn wrapper() -> Result<(), aws_smithy_http::endpoint::error::InvalidEndpointError> { -/// use aws_smithy_http::endpoint::Endpoint; -/// let config = dynamodb::Config::builder() -/// .endpoint(Endpoint::immutable("http://localhost:8080")?); -/// # Ok(()) -/// # } -/// ``` -/// Each AWS service generates their own implementation of `ResolveAwsEndpoint`. -pub trait ResolveAwsEndpoint: Send + Sync + Debug { - /// Resolves the AWS endpoint for a given region. - fn resolve_endpoint(&self, region: &Region) -> Result; -} - -/// The scope for AWS credentials. -#[derive(Clone, Default, Debug)] -pub struct CredentialScope { - region: Option, - service: Option, -} - -impl CredentialScope { - /// Creates a builder for [`CredentialScope`]. - pub fn builder() -> credential_scope::Builder { - credential_scope::Builder::default() - } -} - -/// Types associated with [`CredentialScope`]. -pub mod credential_scope { - use crate::endpoint::CredentialScope; - use crate::region::SigningRegion; - use crate::SigningService; - - /// A builder for [`CredentialScope`]. - #[derive(Debug, Default)] - pub struct Builder { - region: Option, - service: Option, - } - - impl Builder { - /// Sets the signing region. - pub fn region(mut self, region: impl Into) -> Self { - self.region = Some(region.into()); - self - } - - /// Sets the signing service. - pub fn service(mut self, service: impl Into) -> Self { - self.service = Some(service.into()); - self - } - - /// Constructs a [`CredentialScope`] from the builder. - pub fn build(self) -> CredentialScope { - CredentialScope { - region: self.region, - service: self.service, - } - } - } -} - -impl CredentialScope { - /// Returns the signing region. - pub fn region(&self) -> Option<&SigningRegion> { - self.region.as_ref() - } - - /// Returns the signing service. - pub fn service(&self) -> Option<&SigningService> { - self.service.as_ref() - } - - /// Uses the values from `other` to fill in unconfigured parameters on this - /// credential scope object. - pub fn merge(&self, other: &CredentialScope) -> CredentialScope { - CredentialScope { - region: self.region.clone().or_else(|| other.region.clone()), - service: self.service.clone().or_else(|| other.service.clone()), - } - } -} - -/// An `Endpoint` can be its own resolver to support static endpoints -impl ResolveAwsEndpoint for Endpoint { - fn resolve_endpoint(&self, _region: &Region) -> Result { - Ok(AwsEndpoint { - endpoint: self.clone(), - credential_scope: Default::default(), - }) - } -} - -#[cfg(test)] -mod test { - use crate::endpoint::CredentialScope; - use crate::region::SigningRegion; - use crate::SigningService; - - #[test] - fn create_credentials_scope_from_strs() { - let scope = CredentialScope::builder() - .service("s3") - .region("us-east-1") - .build(); - assert_eq!(scope.service(), Some(&SigningService::from_static("s3"))); - assert_eq!( - scope.region(), - Some(&SigningRegion::from_static("us-east-1")) - ); - } -} diff --git a/aws/rust-runtime/aws-types/src/lib.rs b/aws/rust-runtime/aws-types/src/lib.rs index 795ff08849..27860df1be 100644 --- a/aws/rust-runtime/aws-types/src/lib.rs +++ b/aws/rust-runtime/aws-types/src/lib.rs @@ -5,6 +5,7 @@ //! Cross-service types for the AWS SDK. +#![allow(clippy::derive_partial_eq_without_eq)] #![warn( missing_docs, rustdoc::missing_crate_level_docs, @@ -15,9 +16,6 @@ pub mod app_name; pub mod build_metadata; -#[deprecated(since = "0.9.0", note = "renamed to sdk_config")] -pub mod config; -pub mod endpoint; #[doc(hidden)] pub mod os_shim_internal; pub mod region; diff --git a/aws/rust-runtime/aws-types/src/sdk_config.rs b/aws/rust-runtime/aws-types/src/sdk_config.rs index 77df879722..3c6c69b612 100644 --- a/aws/rust-runtime/aws-types/src/sdk_config.rs +++ b/aws/rust-runtime/aws-types/src/sdk_config.rs @@ -20,7 +20,6 @@ use aws_smithy_types::timeout::TimeoutConfig; use crate::app_name::AppName; use crate::docs_for; -use crate::endpoint::ResolveAwsEndpoint; use crate::region::Region; #[doc(hidden)] @@ -51,7 +50,6 @@ pub struct SdkConfig { credentials_cache: Option, credentials_provider: Option, region: Option, - endpoint_resolver: Option>, endpoint_url: Option, retry_config: Option, sleep_impl: Option>, @@ -72,7 +70,6 @@ pub struct Builder { credentials_cache: Option, credentials_provider: Option, region: Option, - endpoint_resolver: Option>, endpoint_url: Option, retry_config: Option, sleep_impl: Option>, @@ -117,31 +114,6 @@ impl Builder { self } - /// Set the endpoint resolver to use when making requests - /// - /// This method is deprecated. Use [`Self::endpoint_url`] instead. - /// - /// # Examples - /// ``` - /// # fn wrapper() -> Result<(), aws_smithy_http::endpoint::error::InvalidEndpointError> { - /// use std::sync::Arc; - /// use aws_types::SdkConfig; - /// use aws_smithy_http::endpoint::Endpoint; - /// let config = SdkConfig::builder().endpoint_resolver( - /// Endpoint::immutable("http://localhost:8080")? - /// ).build(); - /// # Ok(()) - /// # } - /// ``` - #[deprecated(note = "use `endpoint_url` instead")] - pub fn endpoint_resolver( - mut self, - endpoint_resolver: impl ResolveAwsEndpoint + 'static, - ) -> Self { - self.set_endpoint_resolver(Some(Arc::new(endpoint_resolver))); - self - } - /// Set the endpoint url to use when making requests. /// # Examples /// ``` @@ -159,29 +131,6 @@ impl Builder { self } - /// Set the endpoint resolver to use when making requests - /// - /// # Examples - /// ``` - /// use std::sync::Arc; - /// use aws_types::SdkConfig; - /// use aws_types::endpoint::ResolveAwsEndpoint; - /// fn endpoint_resolver_override() -> Option> { - /// // ... - /// # None - /// } - /// let mut config = SdkConfig::builder(); - /// config.set_endpoint_resolver(endpoint_resolver_override()); - /// config.build(); - /// ``` - pub fn set_endpoint_resolver( - &mut self, - endpoint_resolver: Option>, - ) -> &mut Self { - self.endpoint_resolver = endpoint_resolver; - self - } - /// Set the retry_config for the builder /// /// _Note:_ Retries require a sleep implementation in order to work. When enabling retry, make @@ -557,7 +506,6 @@ impl Builder { credentials_cache: self.credentials_cache, credentials_provider: self.credentials_provider, region: self.region, - endpoint_resolver: self.endpoint_resolver, endpoint_url: self.endpoint_url, retry_config: self.retry_config, sleep_impl: self.sleep_impl, @@ -575,11 +523,6 @@ impl SdkConfig { self.region.as_ref() } - /// Configured endpoint resolver - pub fn endpoint_resolver(&self) -> Option> { - self.endpoint_resolver.clone() - } - /// Configured endpoint URL pub fn endpoint_url(&self) -> Option<&str> { self.endpoint_url.as_deref() diff --git a/aws/sdk-adhoc-test/build.gradle.kts b/aws/sdk-adhoc-test/build.gradle.kts index 41fcbdc885..98ad953105 100644 --- a/aws/sdk-adhoc-test/build.gradle.kts +++ b/aws/sdk-adhoc-test/build.gradle.kts @@ -3,8 +3,8 @@ * SPDX-License-Identifier: Apache-2.0 */ -extra["displayName"] = "Smithy :: Rust :: Codegen :: Test" -extra["moduleName"] = "software.amazon.smithy.kotlin.codegen.test" +extra["displayName"] = "Smithy :: Rust :: AWS-SDK :: Ad-hoc Test" +extra["moduleName"] = "software.amazon.smithy.rust.awssdk.adhoc.test" tasks["jar"].enabled = false @@ -45,7 +45,8 @@ val allCodegenTests = listOf( extraConfig = """ , "codegen": { - "includeFluentClient": false + "includeFluentClient": false, + "enableNewCrateOrganizationScheme": true }, "customizationConfig": { "awsSdk": { @@ -61,7 +62,8 @@ val allCodegenTests = listOf( extraConfig = """ , "codegen": { - "includeFluentClient": false + "includeFluentClient": false, + "enableNewCrateOrganizationScheme": true }, "customizationConfig": { "awsSdk": { 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 75d6fd7cbd..802b55b04a 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 @@ -9,55 +9,66 @@ import software.amazon.smithy.rust.codegen.client.smithy.customizations.DocsRsMe import software.amazon.smithy.rust.codegen.client.smithy.customizations.DocsRsMetadataSettings import software.amazon.smithy.rust.codegen.client.smithy.customize.ClientCodegenDecorator import software.amazon.smithy.rust.codegen.client.smithy.customize.CombinedClientCodegenDecorator +import software.amazon.smithy.rustsdk.customize.DisabledAuthDecorator import software.amazon.smithy.rustsdk.customize.apigateway.ApiGatewayDecorator -import software.amazon.smithy.rustsdk.customize.auth.DisabledAuthDecorator +import software.amazon.smithy.rustsdk.customize.applyDecorators import software.amazon.smithy.rustsdk.customize.ec2.Ec2Decorator import software.amazon.smithy.rustsdk.customize.glacier.GlacierDecorator +import software.amazon.smithy.rustsdk.customize.onlyApplyTo 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.sts.STSDecorator -import software.amazon.smithy.rustsdk.endpoints.AwsEndpointDecorator import software.amazon.smithy.rustsdk.endpoints.AwsEndpointsStdLib import software.amazon.smithy.rustsdk.endpoints.OperationInputTestDecorator +import software.amazon.smithy.rustsdk.endpoints.RequireEndpointRules val DECORATORS: List = listOf( // General AWS Decorators - CredentialsCacheDecorator(), - CredentialsProviderDecorator(), - RegionDecorator(), - AwsEndpointDecorator(), - UserAgentDecorator(), - SigV4SigningDecorator(), - HttpRequestChecksumDecorator(), - HttpResponseChecksumDecorator(), - RetryClassifierDecorator(), - IntegrationTestDecorator(), - AwsFluentClientDecorator(), - CrateLicenseDecorator(), - SdkConfigDecorator(), - ServiceConfigDecorator(), - AwsPresigningDecorator(), - AwsReadmeDecorator(), - HttpConnectorDecorator(), - AwsEndpointsStdLib(), - *PromotedBuiltInsDecorators, - GenericSmithySdkConfigSettings(), - OperationInputTestDecorator(), + listOf( + CredentialsCacheDecorator(), + CredentialsProviderDecorator(), + RegionDecorator(), + RequireEndpointRules(), + UserAgentDecorator(), + SigV4SigningDecorator(), + HttpRequestChecksumDecorator(), + HttpResponseChecksumDecorator(), + RetryClassifierDecorator(), + IntegrationTestDecorator(), + AwsFluentClientDecorator(), + CrateLicenseDecorator(), + SdkConfigDecorator(), + ServiceConfigDecorator(), + AwsPresigningDecorator(), + AwsCrateDocsDecorator(), + HttpConnectorDecorator(), + AwsEndpointsStdLib(), + *PromotedBuiltInsDecorators, + GenericSmithySdkConfigSettings(), + OperationInputTestDecorator(), + AwsRequestIdDecorator(), + DisabledAuthDecorator(), + ), // Service specific decorators - ApiGatewayDecorator(), - DisabledAuthDecorator(), - Ec2Decorator(), - GlacierDecorator(), - Route53Decorator(), - S3Decorator(), - S3ControlDecorator(), - STSDecorator(), + ApiGatewayDecorator().onlyApplyTo("com.amazonaws.apigateway#BackplaneControlService"), + Ec2Decorator().onlyApplyTo("com.amazonaws.ec2#AmazonEC2"), + GlacierDecorator().onlyApplyTo("com.amazonaws.glacier#Glacier"), + Route53Decorator().onlyApplyTo("com.amazonaws.route53#AWSDnsV20130401"), + "com.amazonaws.s3#AmazonS3".applyDecorators( + S3Decorator(), + S3ExtendedRequestIdDecorator(), + ), + S3ControlDecorator().onlyApplyTo("com.amazonaws.s3control#AWSS3ControlServiceV20180820"), + STSDecorator().onlyApplyTo("com.amazonaws.sts#AWSSecurityTokenServiceV20110615"), // Only build docs-rs for linux to reduce load on docs.rs - DocsRsMetadataDecorator(DocsRsMetadataSettings(targets = listOf("x86_64-unknown-linux-gnu"), allFeatures = true)), -) + listOf( + DocsRsMetadataDecorator(DocsRsMetadataSettings(targets = listOf("x86_64-unknown-linux-gnu"), allFeatures = true)), + ), +).flatten() class AwsCodegenDecorator : CombinedClientCodegenDecorator(DECORATORS) { override val name: String = "AwsSdkCodegenDecorator" diff --git a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/AwsReadmeDecorator.kt b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/AwsCrateDocsDecorator.kt similarity index 52% rename from aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/AwsReadmeDecorator.kt rename to aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/AwsCrateDocsDecorator.kt index d8868bb94c..a810b3e8a5 100644 --- a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/AwsReadmeDecorator.kt +++ b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/AwsCrateDocsDecorator.kt @@ -11,10 +11,22 @@ import org.jsoup.nodes.TextNode import software.amazon.smithy.model.traits.DocumentationTrait 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.raw +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.containerDocsTemplate +import software.amazon.smithy.rust.codegen.core.rustlang.docs +import software.amazon.smithy.rust.codegen.core.rustlang.escape +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.generators.LibRsCustomization +import software.amazon.smithy.rust.codegen.core.smithy.generators.LibRsSection import software.amazon.smithy.rust.codegen.core.smithy.generators.ManifestCustomizations +import software.amazon.smithy.rust.codegen.core.smithy.generators.ModuleDocSection import software.amazon.smithy.rust.codegen.core.util.getTrait +import software.amazon.smithy.rust.codegen.core.util.serviceNameOrDefault import java.util.logging.Logger // Use a sigil that should always be unique in the text to fix line breaks and spaces @@ -23,9 +35,9 @@ private const val LINE_BREAK_SIGIL = "[[smithy-rs-br]]" private const val SPACE_SIGIL = "[[smithy-rs-nbsp]]" /** - * Generates a README.md for each service crate for display on crates.io. + * Generates a README.md and top-level crate documentation for each service crate for display on crates.io and docs.rs. */ -class AwsReadmeDecorator : ClientCodegenDecorator { +class AwsCrateDocsDecorator : ClientCodegenDecorator { override val name: String = "AwsReadmeDecorator" override val order: Byte = 0 @@ -36,96 +48,173 @@ class AwsReadmeDecorator : ClientCodegenDecorator { emptyMap() } + override fun libRsCustomizations( + codegenContext: ClientCodegenContext, + baseCustomizations: List, + ): List = baseCustomizations + listOf( + object : LibRsCustomization() { + override fun section(section: LibRsSection): Writable = when { + section is LibRsSection.ModuleDoc && section.subsection is ModuleDocSection.ServiceDocs -> writable { + // Include README contents in crate docs if they are to be generated + if (generateReadme(codegenContext)) { + AwsCrateDocGenerator(codegenContext).generateCrateDocComment()(this) + } + } + + else -> emptySection + } + }, + ) + override fun extras(codegenContext: ClientCodegenContext, rustCrate: RustCrate) { if (generateReadme(codegenContext)) { - AwsSdkReadmeGenerator().generateReadme(codegenContext, rustCrate) + AwsCrateDocGenerator(codegenContext).generateReadme(rustCrate) } } + override fun clientConstructionDocs(codegenContext: ClientCodegenContext, baseDocs: Writable): Writable = + writable { + val serviceName = codegenContext.serviceShape.serviceNameOrDefault("the service") + docs("Client for calling $serviceName.") + if (generateReadme(codegenContext)) { + AwsDocs.clientConstructionDocs(codegenContext)(this) + } + } + private fun generateReadme(codegenContext: ClientCodegenContext) = SdkSettings.from(codegenContext.settings).generateReadme } -internal class AwsSdkReadmeGenerator { +internal class AwsCrateDocGenerator(private val codegenContext: ClientCodegenContext) { private val logger: Logger = Logger.getLogger(javaClass.name) - - internal fun generateReadme(codegenContext: ClientCodegenContext, rustCrate: RustCrate) { - val awsConfigVersion = SdkSettings.from(codegenContext.settings).awsConfigVersion + private val awsConfigVersion by lazy { + SdkSettings.from(codegenContext.settings).awsConfigVersion ?: throw IllegalStateException("missing `awsConfigVersion` codegen setting") - rustCrate.withFile("README.md") { - val description = normalizeDescription( - codegenContext.moduleName, - codegenContext.settings.getService(codegenContext.model).getTrait()?.value ?: "", - ) - val moduleName = codegenContext.settings.moduleName - val snakeCaseModuleName = moduleName.replace('-', '_') - val shortModuleName = moduleName.removePrefix("aws-sdk-") + } - raw( - """ - # $moduleName + private fun RustWriter.template(asComments: Boolean, text: String, vararg args: Pair) = + when (asComments) { + true -> containerDocsTemplate(text, *args) + else -> rawTemplate(text + "\n", *args) + } - **Please Note: The SDK is currently in Developer Preview and is intended strictly for - feedback purposes only. Do not use this SDK for production workloads.** - """.trimIndent() + - "\n\n$description\n\n" + - """ - ## Getting Started + private fun docText( + includeHeader: Boolean, + includeLicense: Boolean, + asComments: Boolean, + ): Writable = writable { + val moduleName = codegenContext.settings.moduleName + val description = normalizeDescription( + codegenContext.moduleName, + codegenContext.settings.getService(codegenContext.model).getTrait()?.value ?: "", + ) + val snakeCaseModuleName = moduleName.replace('-', '_') + val shortModuleName = moduleName.removePrefix("aws-sdk-") - > Examples are available for many services and operations, check out the - > [examples folder in GitHub](https://github.com/awslabs/aws-sdk-rust/tree/main/examples). + if (includeHeader) { + template(asComments, escape("# $moduleName\n")) + } + template( + asComments, + """ + **Please Note: The SDK is currently in Developer Preview and is intended strictly for + feedback purposes only. Do not use this SDK for production workloads.**${"\n"} + """.trimIndent(), + ) + + if (description.isNotBlank()) { + template(asComments, escape("$description\n")) + } - The SDK provides one crate per AWS service. You must add [Tokio](https://crates.io/crates/tokio) - as a dependency within your Rust project to execute asynchronous code. To add `$moduleName` to - your project, add the following to your **Cargo.toml** file: + val compileExample = AwsDocs.canRelyOnAwsConfig(codegenContext) + val exampleMode = if (compileExample) "no_run" else "ignore" + template( + asComments, + """ + #### Getting Started - ```toml - [dependencies] - aws-config = "$awsConfigVersion" - $moduleName = "${codegenContext.settings.moduleVersion}" - tokio = { version = "1", features = ["full"] } - ``` + > Examples are available for many services and operations, check out the + > [examples folder in GitHub](https://github.com/awslabs/aws-sdk-rust/tree/main/examples). - Then in code, a client can be created with the following: + The SDK provides one crate per AWS service. You must add [Tokio](https://crates.io/crates/tokio) + as a dependency within your Rust project to execute asynchronous code. To add `$moduleName` to + your project, add the following to your **Cargo.toml** file: - ```rust - use $snakeCaseModuleName as $shortModuleName; + ```toml + [dependencies] + aws-config = "$awsConfigVersion" + $moduleName = "${codegenContext.settings.moduleVersion}" + tokio = { version = "1", features = ["full"] } + ``` - #[tokio::main] - async fn main() -> Result<(), $shortModuleName::Error> { - let config = aws_config::load_from_env().await; - let client = $shortModuleName::Client::new(&config); + Then in code, a client can be created with the following: - // ... make some calls with the client + ```rust,$exampleMode + use $snakeCaseModuleName as $shortModuleName; - Ok(()) - } - ``` + ##[#{tokio}::main] + async fn main() -> Result<(), $shortModuleName::Error> { + let config = #{aws_config}::load_from_env().await; + let client = $shortModuleName::Client::new(&config); - See the [client documentation](https://docs.rs/$moduleName/latest/$snakeCaseModuleName/client/struct.Client.html) - for information on what calls can be made, and the inputs and outputs for each of those calls. + // ... make some calls with the client + + Ok(()) + } + ``` + + See the [client documentation](https://docs.rs/$moduleName/latest/$snakeCaseModuleName/client/struct.Client.html) + for information on what calls can be made, and the inputs and outputs for each of those calls.${"\n"} + """.trimIndent().trimStart(), + "tokio" to CargoDependency.Tokio.toDevDependency().toType(), + "aws_config" to when (compileExample) { + true -> AwsCargoDependency.awsConfig(codegenContext.runtimeConfig).toDevDependency().toType() + else -> writable { rust("aws_config") } + }, + ) - ## Using the SDK + template( + asComments, + """ + #### Using the SDK - Until the SDK is released, we will be adding information about using the SDK to the - [Developer Guide](https://docs.aws.amazon.com/sdk-for-rust/latest/dg/welcome.html). Feel free to suggest - additional sections for the guide by opening an issue and describing what you are trying to do. + Until the SDK is released, we will be adding information about using the SDK to the + [Developer Guide](https://docs.aws.amazon.com/sdk-for-rust/latest/dg/welcome.html). Feel free to suggest + additional sections for the guide by opening an issue and describing what you are trying to do.${"\n"} + """.trimIndent(), + ) - ## Getting Help + template( + asComments, + """ + #### Getting Help - * [GitHub discussions](https://github.com/awslabs/aws-sdk-rust/discussions) - For ideas, RFCs & general questions - * [GitHub issues](https://github.com/awslabs/aws-sdk-rust/issues/new/choose) – For bug reports & feature requests - * [Generated Docs (latest version)](https://awslabs.github.io/aws-sdk-rust/) - * [Usage examples](https://github.com/awslabs/aws-sdk-rust/tree/main/examples) + * [GitHub discussions](https://github.com/awslabs/aws-sdk-rust/discussions) - For ideas, RFCs & general questions + * [GitHub issues](https://github.com/awslabs/aws-sdk-rust/issues/new/choose) - For bug reports & feature requests + * [Generated Docs (latest version)](https://awslabs.github.io/aws-sdk-rust/) + * [Usage examples](https://github.com/awslabs/aws-sdk-rust/tree/main/examples)${"\n"} + """.trimIndent(), + ) - ## License + if (includeLicense) { + template( + asComments, + """ + #### License - This project is licensed under the Apache-2.0 License. - """.trimIndent(), + This project is licensed under the Apache-2.0 License. + """.trimIndent(), ) } } + internal fun generateCrateDocComment(): Writable = + docText(includeHeader = false, includeLicense = false, asComments = true) + + internal fun generateReadme(rustCrate: RustCrate) = rustCrate.withFile("README.md") { + docText(includeHeader = true, includeLicense = true, asComments = false)(this) + } + /** * Strips HTML from the description and makes it human-readable Markdown. */ 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 new file mode 100644 index 0000000000..a8dcdc33d8 --- /dev/null +++ b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/AwsDocs.kt @@ -0,0 +1,82 @@ +/* + * 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.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.util.toSnakeCase + +object AwsDocs { + /** + * If no `aws-config` version is provided, assume that docs referencing `aws-config` cannot be given. + * Also, STS and SSO must NOT reference `aws-config` since that would create a circular dependency. + */ + fun canRelyOnAwsConfig(codegenContext: ClientCodegenContext): Boolean = + SdkSettings.from(codegenContext.settings).awsConfigVersion != null && + !setOf( + ShapeId.from("com.amazonaws.sts#AWSSecurityTokenServiceV20110615"), + ShapeId.from("com.amazonaws.sso#SWBPortalService"), + ).contains(codegenContext.serviceShape.id) + + fun clientConstructionDocs(codegenContext: ClientCodegenContext): Writable = { + if (canRelyOnAwsConfig(codegenContext)) { + val crateName = codegenContext.moduleName.toSnakeCase() + docsTemplate( + """ + #### Constructing a `Client` + + A [`Config`] is required to construct a client. For most use cases, the [`aws-config`] + crate should be used to automatically resolve this config using + [`aws_config::load_from_env()`], since this will resolve an [`SdkConfig`] which can be shared + across multiple different AWS SDK clients. This config resolution process can be customized + by calling [`aws_config::from_env()`] instead, which returns a [`ConfigLoader`] that uses + the [builder pattern] to customize the default config. + + 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); + ## } + ``` + + Occasionally, SDKs may have additional service-specific that can be set on the [`Config`] that + is absent from [`SdkConfig`], or slightly different settings for a specific client may be desired. + The [`Config`] struct implements `From<&SdkConfig>`, so setting these specific settings can be + done as follows: + + ```rust,no_run + ## async fn wrapper() { + let sdk_config = #{aws_config}::load_from_env().await; + let config = $crateName::config::Builder::from(&sdk_config) + ## /* + .some_service_specific_setting("value") + ## */ + .build(); + ## } + ``` + + See the [`aws-config` docs] and [`Config`] for more information on customizing configuration. + + _Note:_ Client construction is expensive due to connection thread pool initialization, and should + be done once at application start-up. + + [`Config`]: crate::Config + [`ConfigLoader`]: https://docs.rs/aws-config/*/aws_config/struct.ConfigLoader.html + [`SdkConfig`]: https://docs.rs/aws-config/*/aws_config/struct.SdkConfig.html + [`aws-config` docs]: https://docs.rs/aws-config/* + [`aws-config`]: https://crates.io/crates/aws-config + [`aws_config::from_env()`]: https://docs.rs/aws-config/*/aws_config/fn.from_env.html + [`aws_config::load_from_env()`]: https://docs.rs/aws-config/*/aws_config/fn.load_from_env.html + [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(), + ) + } + } +} 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 c06b58994b..1c3ceed72e 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 @@ -6,17 +6,16 @@ package software.amazon.smithy.rustsdk import software.amazon.smithy.codegen.core.Symbol -import software.amazon.smithy.model.shapes.ShapeId -import software.amazon.smithy.model.traits.TitleTrait 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.CustomizableOperationGenerator +import software.amazon.smithy.rust.codegen.client.smithy.featureGatedCustomizeModule 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.core.rustlang.Attribute -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.GenericTypeArg import software.amazon.smithy.rust.codegen.core.rustlang.RustGenerics @@ -26,13 +25,12 @@ 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.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.RustCrate 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.util.expectTrait +import software.amazon.smithy.rust.codegen.core.util.serviceNameOrDefault import software.amazon.smithy.rustsdk.AwsRuntimeType.defaultMiddleware private class Types(runtimeConfig: RuntimeConfig) { @@ -76,7 +74,7 @@ private class AwsClientGenerics(private val types: Types) : FluentClientGenerics override fun sendBounds( operation: Symbol, operationOutput: Symbol, - operationError: RuntimeType, + operationError: Symbol, retryClassifier: RuntimeType, ): Writable = writable { } @@ -96,17 +94,18 @@ class AwsFluentClientDecorator : ClientCodegenDecorator { val generics = AwsClientGenerics(types) FluentClientGenerator( codegenContext, - generics, + reexportSmithyClientBuilder = false, + generics = generics, customizations = listOf( - AwsPresignedFluentBuilderMethod(runtimeConfig), + AwsPresignedFluentBuilderMethod(codegenContext, runtimeConfig), AwsFluentClientDocs(codegenContext), ), retryClassifier = AwsRuntimeType.awsHttp(runtimeConfig).resolve("retry::AwsResponseRetryClassifier"), ).render(rustCrate) - rustCrate.withModule(CustomizableOperationGenerator.CustomizeModule) { + rustCrate.withModule(codegenContext.featureGatedCustomizeModule()) { renderCustomizableOperationSendMethod(runtimeConfig, generics, this) } - rustCrate.withModule(FluentClientGenerator.clientModule) { + rustCrate.withModule(ClientRustModule.client) { AwsFluentClientExtensions(types).render(this) } val awsSmithyClient = "aws-smithy-client" @@ -209,6 +208,7 @@ private class AwsFluentClientExtensions(types: Types) { }; 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); @@ -223,21 +223,8 @@ private class AwsFluentClientExtensions(types: Types) { } } -private class AwsFluentClientDocs(private val codegenContext: CodegenContext) : FluentClientCustomization() { - private val serviceName = codegenContext.serviceShape.expectTrait().value - private val serviceShape = codegenContext.serviceShape - private val crateName = codegenContext.moduleUseName() - private val codegenScope = - arrayOf("aws_config" to AwsCargoDependency.awsConfig(codegenContext.runtimeConfig).copy(scope = DependencyScope.Dev).toType()) - - // If no `aws-config` version is provided, assume that docs referencing `aws-config` cannot be given. - // Also, STS and SSO must NOT reference `aws-config` since that would create a circular dependency. - private fun suppressUsageDocs(): Boolean = - SdkSettings.from(codegenContext.settings).awsConfigVersion == null || - setOf( - ShapeId.from("com.amazonaws.sts#AWSSecurityTokenServiceV20110615"), - ShapeId.from("com.amazonaws.sso#SWBPortalService"), - ).contains(serviceShape.id) +private class AwsFluentClientDocs(private val codegenContext: ClientCodegenContext) : FluentClientCustomization() { + private val serviceName = codegenContext.serviceShape.serviceNameOrDefault("the service") override fun section(section: FluentClientSection): Writable { return when (section) { @@ -249,38 +236,8 @@ private class AwsFluentClientDocs(private val codegenContext: CodegenContext) : /// Client for invoking operations on $serviceName. Each operation on $serviceName is a method on this /// this struct. `.send()` MUST be invoked on the generated operations to dispatch the request to the service.""", ) - if (!suppressUsageDocs()) { - rustTemplate( - """ - /// - /// ## Examples - /// **Constructing a client and invoking an operation** - /// ```rust,no_run - /// ## async fn docs() { - /// // create a shared configuration. This can be used & shared between multiple service clients. - /// let shared_config = #{aws_config}::load_from_env().await; - /// let client = $crateName::Client::new(&shared_config); - /// // invoke an operation - /// /* let rsp = client - /// .(). - /// .("some value") - /// .send().await; */ - /// ## } - /// ``` - /// **Constructing a client with custom configuration** - /// ```rust,no_run - /// use #{aws_config}::retry::RetryConfig; - /// ## async fn docs() { - /// let shared_config = #{aws_config}::load_from_env().await; - /// let config = $crateName::config::Builder::from(&shared_config) - /// .retry_config(RetryConfig::disabled()) - /// .build(); - /// let client = $crateName::Client::from_conf(config); - /// ## } - """, - *codegenScope, - ) - } + AwsDocs.clientConstructionDocs(codegenContext)(this) + FluentClientDocs.clientUsageDocs(codegenContext)(this) } else -> emptySection @@ -301,6 +258,7 @@ private fun renderCustomizableOperationSendMethod( "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), ) @@ -316,7 +274,7 @@ private fun renderCustomizableOperationSendMethod( where E: std::error::Error + Send + Sync + 'static, O: #{ParseHttpResponse}> + Send + Sync + Clone + 'static, - Retry: #{ClassifyRetry}<#{SdkSuccess}, SdkError> + Send + Sync + Clone, + Retry: #{ClassifyRetry}<#{SdkSuccess}, #{SdkError}> + Send + Sync + Clone, { self.handle.client.call(self.operation).await } 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 c733138076..7e359f40e3 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 @@ -30,12 +30,10 @@ 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.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.error.errorSymbol import software.amazon.smithy.rust.codegen.core.smithy.protocols.HttpBoundProtocolPayloadGenerator import software.amazon.smithy.rust.codegen.core.util.cloneOperation import software.amazon.smithy.rust.codegen.core.util.expectTrait @@ -64,6 +62,7 @@ internal val PRESIGNABLE_OPERATIONS by lazy { mapOf( // S3 // TODO(https://github.com/awslabs/aws-sdk-rust/issues/488) Technically, all S3 operations support presigning + ShapeId.from("com.amazonaws.s3#HeadObject") to PresignableOperation(PayloadSigningType.UNSIGNED_PAYLOAD), ShapeId.from("com.amazonaws.s3#GetObject") to PresignableOperation(PayloadSigningType.UNSIGNED_PAYLOAD), ShapeId.from("com.amazonaws.s3#PutObject") to PresignableOperation(PayloadSigningType.UNSIGNED_PAYLOAD), ShapeId.from("com.amazonaws.s3#UploadPart") to PresignableOperation(PayloadSigningType.UNSIGNED_PAYLOAD), @@ -129,23 +128,23 @@ class AwsPresigningDecorator internal constructor( } class AwsInputPresignedMethod( - private val codegenContext: CodegenContext, + private val codegenContext: ClientCodegenContext, private val operationShape: OperationShape, ) : OperationCustomization() { private val runtimeConfig = codegenContext.runtimeConfig private val symbolProvider = codegenContext.symbolProvider - private val codegenScope = arrayOf( - "Error" to AwsRuntimeType.Presigning.resolve("config::Error"), - "PresignedRequest" to AwsRuntimeType.Presigning.resolve("request::PresignedRequest"), - "PresignedRequestService" to AwsRuntimeType.Presigning.resolve("service::PresignedRequestService"), - "PresigningConfig" to AwsRuntimeType.Presigning.resolve("config::PresigningConfig"), - "SdkError" to RuntimeType.sdkError(runtimeConfig), - "aws_sigv4" to AwsRuntimeType.awsSigv4(runtimeConfig), - "sig_auth" to AwsRuntimeType.awsSigAuth(runtimeConfig), - "tower" to RuntimeType.Tower, - "Middleware" to runtimeConfig.defaultMiddleware(), - ) + private val codegenScope = ( + presigningTypes(codegenContext) + listOf( + "PresignedRequestService" to AwsRuntimeType.presigning(codegenContext) + .resolve("service::PresignedRequestService"), + "SdkError" to RuntimeType.sdkError(runtimeConfig), + "aws_sigv4" to AwsRuntimeType.awsSigv4(runtimeConfig), + "sig_auth" to AwsRuntimeType.awsSigAuth(runtimeConfig), + "tower" to RuntimeType.Tower, + "Middleware" to runtimeConfig.defaultMiddleware(), + ) + ).toTypedArray() override fun section(section: OperationSection): Writable = writable { @@ -155,7 +154,7 @@ class AwsInputPresignedMethod( } private fun RustWriter.writeInputPresignedMethod(section: OperationSection.InputImpl) { - val operationError = operationShape.errorSymbol(symbolProvider) + val operationError = symbolProvider.symbolForOperationError(operationShape) val presignableOp = PRESIGNABLE_OPERATIONS.getValue(operationShape.id) val makeOperationOp = if (presignableOp.hasModelTransforms()) { @@ -242,14 +241,15 @@ class AwsInputPresignedMethod( } class AwsPresignedFluentBuilderMethod( + codegenContext: ClientCodegenContext, runtimeConfig: RuntimeConfig, ) : FluentClientCustomization() { - private val codegenScope = arrayOf( - "Error" to AwsRuntimeType.Presigning.resolve("config::Error"), - "PresignedRequest" to AwsRuntimeType.Presigning.resolve("request::PresignedRequest"), - "PresigningConfig" to AwsRuntimeType.Presigning.resolve("config::PresigningConfig"), - "SdkError" to RuntimeType.sdkError(runtimeConfig), - ) + private val codegenScope = ( + presigningTypes(codegenContext) + arrayOf( + "Error" to AwsRuntimeType.presigning(codegenContext).resolve("config::Error"), + "SdkError" to RuntimeType.sdkError(runtimeConfig), + ) + ).toTypedArray() override fun section(section: FluentClientSection): Writable = writable { @@ -365,3 +365,15 @@ private fun RustWriter.documentPresignedMethod(hasConfigArg: Boolean) { """, ) } + +private fun presigningTypes(codegenContext: ClientCodegenContext): List> = + when (codegenContext.settings.codegenConfig.enableNewCrateOrganizationScheme) { + true -> listOf( + "PresignedRequest" to AwsRuntimeType.presigning(codegenContext).resolve("PresignedRequest"), + "PresigningConfig" to AwsRuntimeType.presigning(codegenContext).resolve("PresigningConfig"), + ) + else -> listOf( + "PresignedRequest" to AwsRuntimeType.presigning(codegenContext).resolve("request::PresignedRequest"), + "PresigningConfig" to AwsRuntimeType.presigning(codegenContext).resolve("config::PresigningConfig"), + ) + } diff --git a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/AwsRequestIdDecorator.kt b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/AwsRequestIdDecorator.kt new file mode 100644 index 0000000000..0b496ce2c9 --- /dev/null +++ b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/AwsRequestIdDecorator.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.rustsdk + +import software.amazon.smithy.rust.codegen.client.smithy.ClientCodegenContext +import software.amazon.smithy.rust.codegen.core.smithy.RuntimeType + +/** + * Customizes response parsing logic to add AWS request IDs to error metadata and outputs + */ +class AwsRequestIdDecorator : BaseRequestIdDecorator() { + override val name: String = "AwsRequestIdDecorator" + override val order: Byte = 0 + + override val fieldName: String = "request_id" + override val accessorFunctionName: String = "request_id" + + private fun requestIdModule(codegenContext: ClientCodegenContext): RuntimeType = + AwsRuntimeType.awsHttp(codegenContext.runtimeConfig).resolve("request_id") + + override fun accessorTrait(codegenContext: ClientCodegenContext): RuntimeType = + requestIdModule(codegenContext).resolve("RequestId") + + override fun applyToError(codegenContext: ClientCodegenContext): RuntimeType = + requestIdModule(codegenContext).resolve("apply_request_id") +} 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 f4b05b7baf..e07607a722 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 @@ -6,8 +6,8 @@ package software.amazon.smithy.rustsdk import software.amazon.smithy.codegen.core.CodegenException +import software.amazon.smithy.rust.codegen.client.smithy.ClientCodegenContext 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.smithy.RuntimeConfig import software.amazon.smithy.rust.codegen.core.smithy.RuntimeCrateLocation @@ -42,10 +42,17 @@ fun RuntimeConfig.awsRoot(): RuntimeCrateLocation { } object AwsRuntimeType { - val S3Errors by lazy { RuntimeType.forInlineDependency(InlineAwsDependency.forRustFile("s3_errors")) } - val Presigning by lazy { - RuntimeType.forInlineDependency(InlineAwsDependency.forRustFile("presigning", visibility = Visibility.PUBLIC)) - } + fun presigning(codegenContext: ClientCodegenContext): RuntimeType = + when (codegenContext.settings.codegenConfig.enableNewCrateOrganizationScheme) { + true -> RuntimeType.forInlineDependency(InlineAwsDependency.forRustFile("presigning", visibility = Visibility.PUBLIC)) + else -> RuntimeType.forInlineDependency( + InlineAwsDependency.forRustFileAs( + file = "old_presigning", + moduleName = "presigning", + visibility = Visibility.PUBLIC, + ), + ) + } fun RuntimeConfig.defaultMiddleware() = RuntimeType.forInlineDependency( InlineAwsDependency.forRustFile( @@ -63,7 +70,7 @@ object AwsRuntimeType { fun awsCredentialTypes(runtimeConfig: RuntimeConfig) = AwsCargoDependency.awsCredentialTypes(runtimeConfig).toType() fun awsCredentialTypesTestUtil(runtimeConfig: RuntimeConfig) = - AwsCargoDependency.awsCredentialTypes(runtimeConfig).copy(scope = DependencyScope.Dev).withFeature("test-util").toType() + AwsCargoDependency.awsCredentialTypes(runtimeConfig).toDevDependency().withFeature("test-util").toType() fun awsEndpoint(runtimeConfig: RuntimeConfig) = AwsCargoDependency.awsEndpoint(runtimeConfig).toType() fun awsHttp(runtimeConfig: RuntimeConfig) = AwsCargoDependency.awsHttp(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 new file mode 100644 index 0000000000..80a66b7989 --- /dev/null +++ b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/BaseRequestIdDecorator.kt @@ -0,0 +1,226 @@ +/* + * 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.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.error.ErrorCustomization +import software.amazon.smithy.rust.codegen.client.smithy.generators.error.ErrorSection +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.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 +import software.amazon.smithy.rust.codegen.core.smithy.generators.StructureSection +import software.amazon.smithy.rust.codegen.core.smithy.generators.error.ErrorImplCustomization +import software.amazon.smithy.rust.codegen.core.smithy.generators.error.ErrorImplSection +import software.amazon.smithy.rust.codegen.core.smithy.traits.SyntheticOutputTrait +import software.amazon.smithy.rust.codegen.core.util.hasTrait + +/** + * Base customization for adding a request ID (or extended request ID) to outputs and errors. + */ +abstract class BaseRequestIdDecorator : ClientCodegenDecorator { + abstract val accessorFunctionName: String + abstract val fieldName: String + abstract fun accessorTrait(codegenContext: ClientCodegenContext): RuntimeType + abstract fun applyToError(codegenContext: ClientCodegenContext): RuntimeType + + override fun operationCustomizations( + codegenContext: ClientCodegenContext, + operation: OperationShape, + baseCustomizations: List, + ): List = baseCustomizations + listOf(RequestIdOperationCustomization(codegenContext)) + + override fun errorCustomizations( + codegenContext: ClientCodegenContext, + baseCustomizations: List, + ): List = + baseCustomizations + listOf(RequestIdErrorCustomization(codegenContext)) + + override fun errorImplCustomizations( + codegenContext: ClientCodegenContext, + baseCustomizations: List, + ): List = baseCustomizations + listOf(RequestIdErrorImplCustomization(codegenContext)) + + override fun structureCustomizations( + codegenContext: ClientCodegenContext, + baseCustomizations: List, + ): List = baseCustomizations + listOf(RequestIdStructureCustomization(codegenContext)) + + override fun builderCustomizations( + codegenContext: ClientCodegenContext, + baseCustomizations: List, + ): List = baseCustomizations + listOf(RequestIdBuilderCustomization()) + + override fun extras(codegenContext: ClientCodegenContext, rustCrate: RustCrate) { + rustCrate.withModule( + when (codegenContext.settings.codegenConfig.enableNewCrateOrganizationScheme) { + true -> ClientRustModule.Operation + else -> ClientRustModule.types + }, + ) { + // Re-export RequestId in generated crate + rust("pub use #T;", accessorTrait(codegenContext)) + } + } + + private inner class RequestIdOperationCustomization(private val codegenContext: ClientCodegenContext) : + OperationCustomization() { + override fun section(section: OperationSection): Writable = writable { + when (section) { + is OperationSection.PopulateErrorMetadataExtras -> { + rustTemplate( + "${section.builderName} = #{apply_to_error}(${section.builderName}, ${section.responseHeadersName});", + "apply_to_error" to applyToError(codegenContext), + ) + } + is OperationSection.MutateOutput -> { + rust( + "output._set_$fieldName(#T::$accessorFunctionName(${section.responseHeadersName}).map(str::to_string));", + accessorTrait(codegenContext), + ) + } + is OperationSection.BeforeParseResponse -> { + rustTemplate( + "#{tracing}::debug!($fieldName = ?#{trait}::$accessorFunctionName(${section.responseName}));", + "tracing" to RuntimeType.Tracing, + "trait" to accessorTrait(codegenContext), + ) + } + else -> {} + } + } + } + + private inner class RequestIdErrorCustomization(private val codegenContext: ClientCodegenContext) : + ErrorCustomization() { + override fun section(section: ErrorSection): Writable = writable { + when (section) { + is ErrorSection.OperationErrorAdditionalTraitImpls -> { + rustTemplate( + """ + impl #{AccessorTrait} for #{error} { + fn $accessorFunctionName(&self) -> Option<&str> { + self.meta().$accessorFunctionName() + } + } + """, + "AccessorTrait" to accessorTrait(codegenContext), + "error" to section.errorSymbol, + ) + } + + is ErrorSection.ServiceErrorAdditionalTraitImpls -> { + rustBlock("impl #T for Error", accessorTrait(codegenContext)) { + rustBlock("fn $accessorFunctionName(&self) -> Option<&str>") { + rustBlock("match self") { + section.allErrors.forEach { error -> + val sym = codegenContext.symbolProvider.toSymbol(error) + rust("Self::${sym.name}(e) => e.$accessorFunctionName(),") + } + rust("Self::Unhandled(e) => e.$accessorFunctionName(),") + } + } + } + } + } + } + } + + private inner class RequestIdErrorImplCustomization(private val codegenContext: ClientCodegenContext) : + ErrorImplCustomization() { + override fun section(section: ErrorImplSection): Writable = writable { + when (section) { + is ErrorImplSection.ErrorAdditionalTraitImpls -> { + rustBlock("impl #1T for #2T", accessorTrait(codegenContext), section.errorType) { + rustBlock("fn $accessorFunctionName(&self) -> Option<&str>") { + rust("use #T;", RuntimeType.provideErrorMetadataTrait(codegenContext.runtimeConfig)) + rust("self.meta().$accessorFunctionName()") + } + } + } + + else -> {} + } + } + } + + private inner class RequestIdStructureCustomization(private val codegenContext: ClientCodegenContext) : + StructureCustomization() { + override fun section(section: StructureSection): Writable = writable { + if (section.shape.hasTrait()) { + when (section) { + is StructureSection.AdditionalFields -> { + rust("_$fieldName: Option,") + } + + is StructureSection.AdditionalTraitImpls -> { + rustTemplate( + """ + impl #{AccessorTrait} for ${section.structName} { + fn $accessorFunctionName(&self) -> Option<&str> { + self._$fieldName.as_deref() + } + } + """, + "AccessorTrait" to accessorTrait(codegenContext), + ) + } + + is StructureSection.AdditionalDebugFields -> { + rust("""${section.formatterName}.field("_$fieldName", &self._$fieldName);""") + } + } + } + } + } + + private inner class RequestIdBuilderCustomization : BuilderCustomization() { + override fun section(section: BuilderSection): Writable = writable { + if (section.shape.hasTrait()) { + when (section) { + is BuilderSection.AdditionalFields -> { + rust("_$fieldName: Option,") + } + + is BuilderSection.AdditionalMethods -> { + rust( + """ + pub(crate) fn _$fieldName(mut self, $fieldName: impl Into) -> Self { + self._$fieldName = Some($fieldName.into()); + self + } + + pub(crate) fn _set_$fieldName(&mut self, $fieldName: Option) -> &mut Self { + self._$fieldName = $fieldName; + self + } + """, + ) + } + + is BuilderSection.AdditionalDebugFields -> { + rust("""${section.formatterName}.field("_$fieldName", &self._$fieldName);""") + } + + is BuilderSection.AdditionalFieldsInBuild -> { + rust("_$fieldName: self._$fieldName,") + } + } + } + } + } +} 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 c9499c3920..30e15a0ca9 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 @@ -8,9 +8,9 @@ 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.customize.TestUtilFeature +import software.amazon.smithy.rust.codegen.client.smithy.featureGatedConfigModule 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 @@ -19,8 +19,6 @@ 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.smithy.generators.LibRsCustomization -import software.amazon.smithy.rust.codegen.core.smithy.generators.LibRsSection class CredentialsProviderDecorator : ClientCodegenDecorator { override val name: String = "CredentialsProvider" @@ -33,13 +31,6 @@ class CredentialsProviderDecorator : ClientCodegenDecorator { return baseCustomizations + CredentialProviderConfig(codegenContext.runtimeConfig) } - override fun libRsCustomizations( - codegenContext: ClientCodegenContext, - baseCustomizations: List, - ): List { - return baseCustomizations + PubUseCredentials(codegenContext.runtimeConfig) - } - override fun extraSections(codegenContext: ClientCodegenContext): List = listOf( adhocCustomization { section -> @@ -49,6 +40,13 @@ class CredentialsProviderDecorator : ClientCodegenDecorator { override fun extras(codegenContext: ClientCodegenContext, rustCrate: RustCrate) { rustCrate.mergeFeature(TestUtilFeature.copy(deps = listOf("aws-credential-types/test-util"))) + + rustCrate.withModule(codegenContext.featureGatedConfigModule()) { + rust( + "pub use #T::Credentials;", + AwsRuntimeType.awsCredentialTypes(codegenContext.runtimeConfig), + ) + } } } @@ -95,20 +93,5 @@ class CredentialProviderConfig(runtimeConfig: RuntimeConfig) : ConfigCustomizati } } -class PubUseCredentials(private val runtimeConfig: RuntimeConfig) : LibRsCustomization() { - override fun section(section: LibRsSection): Writable { - return when (section) { - is LibRsSection.Body -> writable { - rust( - "pub use #T::Credentials;", - AwsRuntimeType.awsCredentialTypes(runtimeConfig), - ) - } - - else -> emptySection - } - } -} - fun defaultProvider() = RuntimeType.forInlineDependency(InlineAwsDependency.forRustFile("no_credentials")).resolve("NoCredentials") 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 0a5a4cfd02..f2913a1aa7 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 @@ -22,11 +22,12 @@ import software.amazon.smithy.rust.codegen.core.smithy.generators.operationBuild 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( InlineAwsDependency.forRustFile( - "http_body_checksum", visibility = Visibility.PUBLIC, + "http_body_checksum", visibility = Visibility.PUBCRATE, CargoDependency.Http, CargoDependency.HttpBody, CargoDependency.smithyHttp(this), @@ -41,12 +42,16 @@ 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 { - return baseCustomizations + HttpRequestChecksumCustomization(codegenContext, operation) + ): List = baseCustomizations.letIf(applies(codegenContext)) { + it + HttpRequestChecksumCustomization(codegenContext, operation) } } 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 f1a907a316..568abfa191 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 @@ -17,6 +17,7 @@ import software.amazon.smithy.rust.codegen.core.smithy.customize.OperationSectio 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 HttpChecksumTrait.requestValidationModeMember( @@ -31,12 +32,16 @@ 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): Boolean = + !codegenContext.settings.codegenConfig.enableNewSmithyRuntime + override fun operationCustomizations( codegenContext: ClientCodegenContext, operation: OperationShape, baseCustomizations: List, - ): List { - return baseCustomizations + HttpResponseChecksumCustomization(codegenContext, operation) + ): List = baseCustomizations.letIf(applies(codegenContext)) { + it + HttpResponseChecksumCustomization(codegenContext, operation) } } diff --git a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/InlineAwsDependency.kt b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/InlineAwsDependency.kt index fa2554655a..4f34f3a750 100644 --- a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/InlineAwsDependency.kt +++ b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/InlineAwsDependency.kt @@ -12,5 +12,12 @@ import software.amazon.smithy.rust.codegen.core.rustlang.Visibility object InlineAwsDependency { fun forRustFile(file: String, visibility: Visibility = Visibility.PRIVATE, vararg additionalDependency: RustDependency): InlineDependency = - InlineDependency.Companion.forRustFile(RustModule.new(file, visibility), "/aws-inlineable/src/$file.rs", *additionalDependency) + forRustFileAs(file, file, visibility, *additionalDependency) + + fun forRustFileAs(file: String, moduleName: String, visibility: Visibility = Visibility.PRIVATE, vararg additionalDependency: RustDependency): InlineDependency = + InlineDependency.Companion.forRustFile( + RustModule.new(moduleName, visibility, documentationOverride = ""), + "/aws-inlineable/src/$file.rs", + *additionalDependency, + ) } 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 0eafec8fcd..9cbddde250 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 @@ -30,6 +30,7 @@ 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.LibRsCustomization import software.amazon.smithy.rust.codegen.core.smithy.generators.LibRsSection +import software.amazon.smithy.rust.codegen.core.testutil.testDependenciesOnly import java.nio.file.Files import java.nio.file.Paths import kotlin.io.path.absolute @@ -72,7 +73,7 @@ class IntegrationTestDependencies( private val hasBenches: Boolean, ) : LibRsCustomization() { override fun section(section: LibRsSection) = when (section) { - is LibRsSection.Body -> writable { + is LibRsSection.Body -> testDependenciesOnly { if (hasTests) { val smithyClient = CargoDependency.smithyClient(runtimeConfig) .copy(features = setOf("test-util"), scope = DependencyScope.Dev) @@ -81,7 +82,7 @@ class IntegrationTestDependencies( addDependency(SerdeJson) addDependency(Tokio) addDependency(FuturesUtil) - addDependency(Tracing) + addDependency(Tracing.toDevDependency()) addDependency(TracingSubscriber) } if (hasBenches) { @@ -91,6 +92,7 @@ class IntegrationTestDependencies( serviceSpecific.section(section)(this) } } + else -> emptySection } @@ -114,8 +116,8 @@ class S3TestDependencies : LibRsCustomization() { override fun section(section: LibRsSection): Writable = writable { addDependency(AsyncStd) - addDependency(BytesUtils) - addDependency(FastRand) + addDependency(BytesUtils.toDevDependency()) + addDependency(FastRand.toDevDependency()) addDependency(HdrHistogram) addDependency(Smol) addDependency(TempFile) 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 70775e2cda..af625e7979 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 @@ -12,6 +12,7 @@ import software.amazon.smithy.rulesengine.language.syntax.parameters.Parameter 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.EndpointCustomization +import software.amazon.smithy.rust.codegen.client.smithy.featureGatedConfigModule 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 @@ -20,12 +21,11 @@ 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.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.smithy.generators.LibRsCustomization -import software.amazon.smithy.rust.codegen.core.smithy.generators.LibRsSection 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.thenSingletonListOf @@ -101,13 +101,6 @@ class RegionDecorator : ClientCodegenDecorator { return baseCustomizations.extendIf(usesRegion(codegenContext)) { RegionConfigPlugin() } } - override fun libRsCustomizations( - codegenContext: ClientCodegenContext, - baseCustomizations: List, - ): List { - return baseCustomizations.extendIf(usesRegion(codegenContext)) { PubUseRegion(codegenContext.runtimeConfig) } - } - override fun extraSections(codegenContext: ClientCodegenContext): List { return usesRegion(codegenContext).thenSingletonListOf { adhocCustomization { section -> @@ -121,6 +114,14 @@ class RegionDecorator : ClientCodegenDecorator { } } + override fun extras(codegenContext: ClientCodegenContext, rustCrate: RustCrate) { + if (usesRegion(codegenContext)) { + rustCrate.withModule(codegenContext.featureGatedConfigModule()) { + rust("pub use #T::Region;", region(codegenContext.runtimeConfig)) + } + } + } + override fun endpointCustomizations(codegenContext: ClientCodegenContext): List { if (!usesRegion(codegenContext)) { return listOf() @@ -129,7 +130,9 @@ class RegionDecorator : ClientCodegenDecorator { object : EndpointCustomization { 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())") } + Builtins.REGION.builtIn -> writable { + rust("$configRef.region.as_ref().map(|r|r.as_ref().to_owned())") + } else -> null } } @@ -221,19 +224,4 @@ class RegionConfigPlugin : OperationCustomization() { } } -class PubUseRegion(private val runtimeConfig: RuntimeConfig) : LibRsCustomization() { - override fun section(section: LibRsSection): Writable { - return when (section) { - is LibRsSection.Body -> writable { - rust( - "pub use #T::Region;", - region(runtimeConfig), - ) - } - - else -> emptySection - } - } -} - fun region(runtimeConfig: RuntimeConfig) = AwsRuntimeType.awsTypes(runtimeConfig).resolve("region") 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 f0e2a7e7d6..1d94cd6082 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 @@ -6,10 +6,10 @@ package software.amazon.smithy.rustsdk 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.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 @@ -105,7 +105,7 @@ class SdkConfigDecorator : ClientCodegenDecorator { val codegenScope = arrayOf( "SdkConfig" to AwsRuntimeType.awsTypes(codegenContext.runtimeConfig).resolve("sdk_config::SdkConfig"), ) - rustCrate.withModule(RustModule.Config) { + rustCrate.withModule(ClientRustModule.Config) { rustTemplate( """ impl From<&#{SdkConfig}> for Builder { diff --git a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/SdkSettings.kt b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/SdkSettings.kt index ce1f0f015f..62bb927a04 100644 --- a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/SdkSettings.kt +++ b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/SdkSettings.kt @@ -5,6 +5,7 @@ package software.amazon.smithy.rustsdk import software.amazon.smithy.model.node.ObjectNode +import software.amazon.smithy.rust.codegen.client.smithy.ClientCodegenContext import software.amazon.smithy.rust.codegen.core.smithy.CoreRustSettings import software.amazon.smithy.rust.codegen.core.util.orNull import java.nio.file.Path @@ -40,10 +41,17 @@ class SdkSettings private constructor(private val awsSdk: ObjectNode?) { awsSdk?.getStringMember("integrationTestPath")?.orNull()?.value ?: "aws/sdk/integration-tests" /** Version number of the `aws-config` crate */ - val awsConfigVersion: String? get() = - awsSdk?.getStringMember("awsConfigVersion")?.orNull()?.value + val awsConfigVersion: String? + get() = + awsSdk?.getStringMember("awsConfigVersion")?.orNull()?.value /** Whether to generate a README */ - val generateReadme: Boolean get() = - awsSdk?.getBooleanMember("generateReadme")?.orNull()?.value ?: false + val generateReadme: Boolean + get() = + awsSdk?.getBooleanMember("generateReadme")?.orNull()?.value ?: false + + val requireEndpointResolver: Boolean + get() = awsSdk?.getBooleanMember("requireEndpointResolver")?.orNull()?.value ?: true } + +fun ClientCodegenContext.sdkSettings() = SdkSettings.from(this.settings) 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 8a2a27f66f..0fa2c5f66b 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 @@ -9,6 +9,8 @@ import software.amazon.smithy.aws.traits.ServiceTrait 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.featureGatedConfigModule +import software.amazon.smithy.rust.codegen.client.smithy.featureGatedMetaModule 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 @@ -16,12 +18,12 @@ 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.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.smithy.generators.LibRsCustomization -import software.amazon.smithy.rust.codegen.core.smithy.generators.LibRsSection import software.amazon.smithy.rust.codegen.core.util.dq import software.amazon.smithy.rust.codegen.core.util.expectTrait @@ -39,21 +41,12 @@ class UserAgentDecorator : ClientCodegenDecorator { return baseCustomizations + AppNameCustomization(codegenContext.runtimeConfig) } - override fun libRsCustomizations( - codegenContext: ClientCodegenContext, - baseCustomizations: List, - ): List { - // We are generating an AWS SDK, the service needs to have the AWS service trait - val serviceTrait = codegenContext.serviceShape.expectTrait() - return baseCustomizations + ApiVersionAndPubUse(codegenContext.runtimeConfig, serviceTrait) - } - override fun operationCustomizations( codegenContext: ClientCodegenContext, operation: OperationShape, baseCustomizations: List, ): List { - return baseCustomizations + UserAgentFeature(codegenContext.runtimeConfig) + return baseCustomizations + UserAgentFeature(codegenContext) } override fun extraSections(codegenContext: ClientCodegenContext): List { @@ -65,44 +58,54 @@ class UserAgentDecorator : ClientCodegenDecorator { } /** - * Adds a static `API_METADATA` variable to the crate root containing the serviceId & the version of the crate for this individual service + * Adds a static `API_METADATA` variable to the crate `config` containing the serviceId & the version of the crate for this individual service */ - private class ApiVersionAndPubUse(private val runtimeConfig: RuntimeConfig, serviceTrait: ServiceTrait) : - LibRsCustomization() { - private val serviceId = serviceTrait.sdkId.lowercase().replace(" ", "") - override fun section(section: LibRsSection): Writable = when (section) { - is LibRsSection.Body -> writable { - // PKG_VERSION comes from CrateVersionGenerator - rust( - "static API_METADATA: #1T::ApiMetadata = #1T::ApiMetadata::new(${serviceId.dq()}, PKG_VERSION);", - AwsRuntimeType.awsHttp(runtimeConfig).resolve("user_agent"), - ) + override fun extras(codegenContext: ClientCodegenContext, rustCrate: RustCrate) { + val runtimeConfig = codegenContext.runtimeConfig - // Re-export the app name so that it can be specified in config programmatically without an explicit dependency - rustTemplate( - "pub use #{AppName};", - "AppName" to AwsRuntimeType.awsTypes(runtimeConfig).resolve("app_name::AppName"), - ) - } + // We are generating an AWS SDK, the service needs to have the AWS service trait + val serviceTrait = codegenContext.serviceShape.expectTrait() + val serviceId = serviceTrait.sdkId.lowercase().replace(" ", "") + + rustCrate.withModule(codegenContext.featureGatedMetaModule()) { + rustTemplate( + """ + pub(crate) static API_METADATA: #{user_agent}::ApiMetadata = + #{user_agent}::ApiMetadata::new(${serviceId.dq()}, #{PKG_VERSION}); + """, + "user_agent" to AwsRuntimeType.awsHttp(runtimeConfig).resolve("user_agent"), + "PKG_VERSION" to CrateVersionCustomization.pkgVersion(codegenContext.featureGatedMetaModule()), + ) + } - else -> emptySection + rustCrate.withModule(codegenContext.featureGatedConfigModule()) { + // Re-export the app name so that it can be specified in config programmatically without an explicit dependency + rustTemplate( + "pub use #{AppName};", + "AppName" to AwsRuntimeType.awsTypes(runtimeConfig).resolve("app_name::AppName"), + ) } } - private class UserAgentFeature(private val runtimeConfig: RuntimeConfig) : OperationCustomization() { + private class UserAgentFeature( + private val codegenContext: ClientCodegenContext, + ) : OperationCustomization() { + private val runtimeConfig = codegenContext.runtimeConfig + override fun section(section: OperationSection): Writable = when (section) { is OperationSection.MutateRequest -> writable { rustTemplate( """ let mut user_agent = #{ua_module}::AwsUserAgent::new_from_environment( #{Env}::real(), - crate::API_METADATA.clone(), + #{meta}::API_METADATA.clone(), ); if let Some(app_name) = _config.app_name() { user_agent = user_agent.with_app_name(app_name.clone()); } ${section.request}.properties_mut().insert(user_agent); """, + "meta" to codegenContext.featureGatedMetaModule(), "ua_module" to AwsRuntimeType.awsHttp(runtimeConfig).resolve("user_agent"), "Env" to AwsRuntimeType.awsTypes(runtimeConfig).resolve("os_shim_internal::Env"), ) diff --git a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/customize/auth/DisabledAuthDecorator.kt b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/customize/DisabledAuthDecorator.kt similarity index 91% rename from aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/customize/auth/DisabledAuthDecorator.kt rename to aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/customize/DisabledAuthDecorator.kt index 2c65f95bd3..dfbd2ca597 100644 --- a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/customize/auth/DisabledAuthDecorator.kt +++ b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/customize/DisabledAuthDecorator.kt @@ -3,17 +3,15 @@ * SPDX-License-Identifier: Apache-2.0 */ -package software.amazon.smithy.rustsdk.customize.auth +package software.amazon.smithy.rustsdk.customize 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.shapes.ShapeId import software.amazon.smithy.model.traits.AuthTrait import software.amazon.smithy.model.transform.ModelTransformer import software.amazon.smithy.rust.codegen.client.smithy.customize.ClientCodegenDecorator - -private fun String.shapeId() = ShapeId.from(this) +import software.amazon.smithy.rust.codegen.core.util.shapeId // / STS (and possibly other services) need to have auth manually set to [] class DisabledAuthDecorator : ClientCodegenDecorator { 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 new file mode 100644 index 0000000000..8e957b3f59 --- /dev/null +++ b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/customize/ServiceSpecificDecorator.kt @@ -0,0 +1,133 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +package software.amazon.smithy.rustsdk.customize + +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.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.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.config.ConfigCustomization +import software.amazon.smithy.rust.codegen.client.smithy.generators.error.ErrorCustomization +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.generators.BuilderCustomization +import software.amazon.smithy.rust.codegen.core.smithy.generators.LibRsCustomization +import software.amazon.smithy.rust.codegen.core.smithy.generators.ManifestCustomizations +import software.amazon.smithy.rust.codegen.core.smithy.generators.StructureCustomization +import software.amazon.smithy.rust.codegen.core.smithy.generators.error.ErrorImplCustomization + +/** Only apply this decorator to the given service ID */ +fun ClientCodegenDecorator.onlyApplyTo(serviceId: String): List = + listOf(ServiceSpecificDecorator(ShapeId.from(serviceId), this)) + +/** Apply the given decorators only to this service ID */ +fun String.applyDecorators(vararg decorators: ClientCodegenDecorator): List = + decorators.map { it.onlyApplyTo(this) }.flatten() + +/** + * Delegating decorator that only applies to a configured service ID + */ +class ServiceSpecificDecorator( + /** Service ID this decorator is active for */ + private val appliesToServiceId: ShapeId, + /** Decorator to delegate to */ + private val delegateTo: ClientCodegenDecorator, + /** Decorator name */ + override val name: String = "${appliesToServiceId.namespace}.${appliesToServiceId.name}", + /** Decorator order */ + override val order: Byte = 0, +) : ClientCodegenDecorator { + private fun T.maybeApply(serviceId: ToShapeId, delegatedValue: () -> T): T = + if (appliesToServiceId == serviceId.toShapeId()) { + delegatedValue() + } else { + this + } + + // This kind of decorator gets explicitly added to the root sdk-codegen decorator + override fun classpathDiscoverable(): Boolean = false + + override fun builderCustomizations( + codegenContext: ClientCodegenContext, + baseCustomizations: List, + ): List = baseCustomizations.maybeApply(codegenContext.serviceShape) { + delegateTo.builderCustomizations(codegenContext, baseCustomizations) + } + + override fun configCustomizations( + codegenContext: ClientCodegenContext, + baseCustomizations: List, + ): List = baseCustomizations.maybeApply(codegenContext.serviceShape) { + delegateTo.configCustomizations(codegenContext, baseCustomizations) + } + + override fun crateManifestCustomizations(codegenContext: ClientCodegenContext): ManifestCustomizations = + emptyMap().maybeApply(codegenContext.serviceShape) { + delegateTo.crateManifestCustomizations(codegenContext) + } + + override fun endpointCustomizations(codegenContext: ClientCodegenContext): List = + emptyList().maybeApply(codegenContext.serviceShape) { + delegateTo.endpointCustomizations(codegenContext) + } + + override fun errorCustomizations( + codegenContext: ClientCodegenContext, + baseCustomizations: List, + ): List = baseCustomizations.maybeApply(codegenContext.serviceShape) { + delegateTo.errorCustomizations(codegenContext, baseCustomizations) + } + + override fun errorImplCustomizations( + codegenContext: ClientCodegenContext, + baseCustomizations: List, + ): List = baseCustomizations.maybeApply(codegenContext.serviceShape) { + delegateTo.errorImplCustomizations(codegenContext, baseCustomizations) + } + + override fun extras(codegenContext: ClientCodegenContext, rustCrate: RustCrate) { + maybeApply(codegenContext.serviceShape) { + delegateTo.extras(codegenContext, rustCrate) + } + } + + override fun libRsCustomizations( + codegenContext: ClientCodegenContext, + baseCustomizations: List, + ): List = baseCustomizations.maybeApply(codegenContext.serviceShape) { + delegateTo.libRsCustomizations(codegenContext, baseCustomizations) + } + + override fun operationCustomizations( + codegenContext: ClientCodegenContext, + operation: OperationShape, + baseCustomizations: List, + ): List = baseCustomizations.maybeApply(codegenContext.serviceShape) { + delegateTo.operationCustomizations(codegenContext, operation, baseCustomizations) + } + + override fun protocols(serviceId: ShapeId, currentProtocols: ClientProtocolMap): ClientProtocolMap = + currentProtocols.maybeApply(serviceId) { + delegateTo.protocols(serviceId, currentProtocols) + } + + override fun structureCustomizations( + codegenContext: ClientCodegenContext, + baseCustomizations: List, + ): List = baseCustomizations.maybeApply(codegenContext.serviceShape) { + delegateTo.structureCustomizations(codegenContext, baseCustomizations) + } + + override fun transformModel(service: ServiceShape, model: Model): Model = + model.maybeApply(service) { + delegateTo.transformModel(service, model) + } +} 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 9fc0f3e4c7..5959918ef7 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 @@ -6,34 +6,24 @@ package software.amazon.smithy.rustsdk.customize.apigateway 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.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.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.letIf class ApiGatewayDecorator : ClientCodegenDecorator { override val name: String = "ApiGateway" override val order: Byte = 0 - private fun applies(codegenContext: CodegenContext) = - codegenContext.serviceShape.id == ShapeId.from("com.amazonaws.apigateway#BackplaneControlService") - override fun operationCustomizations( codegenContext: ClientCodegenContext, operation: OperationShape, baseCustomizations: List, - ): List { - return baseCustomizations.letIf(applies(codegenContext)) { - it + ApiGatewayAddAcceptHeader() - } - } + ): List = baseCustomizations + ApiGatewayAddAcceptHeader() } class ApiGatewayAddAcceptHeader : OperationCustomization() { 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 ee005da318..e788920e1d 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,24 +7,14 @@ package software.amazon.smithy.rustsdk.customize.ec2 import software.amazon.smithy.model.Model import software.amazon.smithy.model.shapes.ServiceShape -import software.amazon.smithy.model.shapes.ShapeId import software.amazon.smithy.rust.codegen.client.smithy.customize.ClientCodegenDecorator -import software.amazon.smithy.rust.codegen.core.util.letIf class Ec2Decorator : ClientCodegenDecorator { override val name: String = "Ec2" override val order: Byte = 0 - private val ec2 = ShapeId.from("com.amazonaws.ec2#AmazonEC2") - private fun applies(serviceShape: ServiceShape) = - serviceShape.id == ec2 - - override fun transformModel(service: ServiceShape, model: Model): Model { - // EC2 incorrectly models primitive shapes as unboxed when they actually - // need to be boxed for the API to work properly - return model.letIf( - applies(service), - EC2MakePrimitivesOptional::processModel, - ) - } + // 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 = + 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 65e4f3a455..63ef01d1b1 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 @@ -37,7 +37,9 @@ class AccountIdAutofill : OperationCustomization() { val input = operation.inputShape(model) return if (input.memberNames.contains("accountId")) { AccountIdAutofill() - } else null + } else { + null + } } } } 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 7bfc3c4e42..5ba71e2f20 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,35 +6,21 @@ 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.ClientCodegenContext import software.amazon.smithy.rust.codegen.client.smithy.customize.ClientCodegenDecorator -import software.amazon.smithy.rust.codegen.core.smithy.CodegenContext import software.amazon.smithy.rust.codegen.core.smithy.customize.OperationCustomization -val Glacier: ShapeId = ShapeId.from("com.amazonaws.glacier#Glacier") - class GlacierDecorator : ClientCodegenDecorator { override val name: String = "Glacier" override val order: Byte = 0 - private fun applies(codegenContext: CodegenContext) = codegenContext.serviceShape.id == Glacier - override fun operationCustomizations( codegenContext: ClientCodegenContext, operation: OperationShape, baseCustomizations: List, - ): List { - val extras = if (applies(codegenContext)) { - val apiVersion = codegenContext.serviceShape.version - listOfNotNull( - ApiVersionHeader(apiVersion), - TreeHashHeader.forOperation(operation, codegenContext.runtimeConfig), - AccountIdAutofill.forOperation(operation, codegenContext.model), - ) - } else { - emptyList() - } - return baseCustomizations + extras - } + ): List = baseCustomizations + listOfNotNull( + ApiVersionHeader(codegenContext.serviceShape.version), + TreeHashHeader.forOperation(operation, codegenContext.runtimeConfig), + AccountIdAutofill.forOperation(operation, codegenContext.model), + ) } 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 023a663c42..c97357a2fd 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 @@ -33,13 +33,16 @@ private val UploadMultipartPart: ShapeId = ShapeId.from("com.amazonaws.glacier#U private val Applies = setOf(UploadArchive, UploadMultipartPart) class TreeHashHeader(private val runtimeConfig: RuntimeConfig) : OperationCustomization() { - private val glacierChecksums = RuntimeType.forInlineDependency(InlineAwsDependency.forRustFile("glacier_checksums")) + private val glacierChecksums = RuntimeType.forInlineDependency( + InlineAwsDependency.forRustFile( + "glacier_checksums", + additionalDependency = TreeHashDependencies.toTypedArray(), + ), + ) + override fun section(section: OperationSection): Writable { return when (section) { is OperationSection.MutateRequest -> writable { - TreeHashDependencies.forEach { dep -> - addDependency(dep) - } rustTemplate( """ #{glacier_checksums}::add_checksum_treehash( @@ -49,6 +52,7 @@ class TreeHashHeader(private val runtimeConfig: RuntimeConfig) : OperationCustom "glacier_checksums" to glacierChecksums, "BuildError" to runtimeConfig.operationBuildError(), ) } + else -> emptySection } } 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 e1adcf46af..007254c479 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 @@ -26,26 +26,19 @@ import software.amazon.smithy.rust.codegen.core.util.letIf import software.amazon.smithy.rustsdk.InlineAwsDependency import java.util.logging.Logger -val Route53: ShapeId = ShapeId.from("com.amazonaws.route53#AWSDnsV20130401") - class Route53Decorator : ClientCodegenDecorator { override val name: String = "Route53" override val order: Byte = 0 private val logger: Logger = Logger.getLogger(javaClass.name) private val resourceShapes = setOf(ShapeId.from("com.amazonaws.route53#ResourceId"), ShapeId.from("com.amazonaws.route53#ChangeId")) - private fun applies(service: ServiceShape) = service.id == Route53 - - override fun transformModel(service: ServiceShape, model: Model): Model { - return model.letIf(applies(service)) { - ModelTransformer.create().mapShapes(model) { shape -> - shape.letIf(isResourceId(shape)) { - logger.info("Adding TrimResourceId trait to $shape") - (shape as MemberShape).toBuilder().addTrait(TrimResourceId()).build() - } + override fun transformModel(service: ServiceShape, model: Model): Model = + ModelTransformer.create().mapShapes(model) { shape -> + shape.letIf(isResourceId(shape)) { + logger.info("Adding TrimResourceId trait to $shape") + (shape as MemberShape).toBuilder().addTrait(TrimResourceId()).build() } } - } override fun operationCustomizations( codegenContext: ClientCodegenContext, @@ -56,7 +49,9 @@ class Route53Decorator : ClientCodegenDecorator { operation.inputShape(codegenContext.model).members().find { it.hasTrait() } return if (hostedZoneMember != null) { baseCustomizations + TrimResourceIdCustomization(codegenContext.symbolProvider.toMemberName(hostedZoneMember)) - } else baseCustomizations + } else { + baseCustomizations + } } private fun isResourceId(shape: Shape): Boolean { 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 d6c0f8257f..7e78f0d47a 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 @@ -20,21 +20,17 @@ import software.amazon.smithy.rust.codegen.client.smithy.endpoint.EndpointCustom 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.protocols.ClientRestXmlFactory -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.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.RuntimeType -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.protocols.ProtocolFunctions 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.AwsRuntimeType import software.amazon.smithy.rustsdk.endpoints.stripEndpointTrait import software.amazon.smithy.rustsdk.getBuiltIn import software.amazon.smithy.rustsdk.toWritable @@ -52,38 +48,22 @@ class S3Decorator : ClientCodegenDecorator { ShapeId.from("com.amazonaws.s3#GetObjectAttributesOutput"), ) - private fun applies(serviceId: ShapeId) = - serviceId == ShapeId.from("com.amazonaws.s3#AmazonS3") - override fun protocols( serviceId: ShapeId, currentProtocols: ProtocolMap, - ): ProtocolMap = - currentProtocols.letIf(applies(serviceId)) { - it + mapOf( - RestXmlTrait.ID to ClientRestXmlFactory { protocolConfig -> - S3(protocolConfig) - }, - ) - } - - override fun transformModel(service: ServiceShape, model: Model): Model { - return model.letIf(applies(service.id)) { - 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")) - } - } + ): ProtocolMap = currentProtocols + mapOf( + RestXmlTrait.ID to ClientRestXmlFactory { protocolConfig -> + S3ProtocolOverride(protocolConfig) + }, + ) - override fun libRsCustomizations( - codegenContext: ClientCodegenContext, - baseCustomizations: List, - ): List = baseCustomizations.letIf(applies(codegenContext.serviceShape.id)) { - it + S3PubUse() - } + override fun transformModel(service: ServiceShape, model: Model): 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")) override fun endpointCustomizations(codegenContext: ClientCodegenContext): List { return listOf(object : EndpointCustomization { @@ -108,35 +88,36 @@ class S3Decorator : ClientCodegenDecorator { } } -class S3(codegenContext: CodegenContext) : RestXml(codegenContext) { +class S3ProtocolOverride(codegenContext: CodegenContext) : RestXml(codegenContext) { private val runtimeConfig = codegenContext.runtimeConfig private val errorScope = arrayOf( "Bytes" to RuntimeType.Bytes, - "Error" to RuntimeType.genericError(runtimeConfig), + "ErrorMetadata" to RuntimeType.errorMetadata(runtimeConfig), + "ErrorBuilder" to RuntimeType.errorMetadataBuilder(runtimeConfig), "HeaderMap" to RuntimeType.HttpHeaderMap, "Response" to RuntimeType.HttpResponse, "XmlDecodeError" to RuntimeType.smithyXml(runtimeConfig).resolve("decode::XmlDecodeError"), "base_errors" to restXmlErrors, - "s3_errors" to AwsRuntimeType.S3Errors, ) - override fun parseHttpGenericError(operationShape: OperationShape): RuntimeType { - return RuntimeType.forInlineFun("parse_http_generic_error", RustModule.private("xml_deser")) { + override fun parseHttpErrorMetadata(operationShape: OperationShape): RuntimeType { + return ProtocolFunctions.crossOperationFn("parse_http_error_metadata") { fnName -> rustBlockTemplate( - "pub fn parse_http_generic_error(response: &#{Response}<#{Bytes}>) -> Result<#{Error}, #{XmlDecodeError}>", + "pub fn $fnName(response_status: u16, _response_headers: &#{HeaderMap}, response_body: &[u8]) -> Result<#{ErrorBuilder}, #{XmlDecodeError}>", *errorScope, ) { rustTemplate( """ - if response.body().is_empty() { - let mut err = #{Error}::builder(); - if response.status().as_u16() == 404 { - err.code("NotFound"); + // S3 HEAD responses have no response body to for an error code. Therefore, + // check the HTTP response status and populate an error code for 404s. + if response_body.is_empty() { + let mut builder = #{ErrorMetadata}::builder(); + if response_status == 404 { + builder = builder.code("NotFound"); } - Ok(err.build()) + Ok(builder) } else { - let base_err = #{base_errors}::parse_generic_error(response.body().as_ref())?; - Ok(#{s3_errors}::parse_extended_error(base_err, response.headers())) + #{base_errors}::parse_error_metadata(response_body) } """, *errorScope, @@ -145,16 +126,3 @@ class S3(codegenContext: CodegenContext) : RestXml(codegenContext) { } } } - -class S3PubUse : LibRsCustomization() { - override fun section(section: LibRsSection): Writable = when (section) { - is LibRsSection.Body -> writable { - rust( - "pub use #T::ErrorExt;", - AwsRuntimeType.S3Errors, - ) - } - - else -> emptySection - } -} diff --git a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/customize/s3/S3ExtendedRequestIdDecorator.kt b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/customize/s3/S3ExtendedRequestIdDecorator.kt new file mode 100644 index 0000000000..6b117b60da --- /dev/null +++ b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/customize/s3/S3ExtendedRequestIdDecorator.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.rustsdk.customize.s3 + +import software.amazon.smithy.rust.codegen.client.smithy.ClientCodegenContext +import software.amazon.smithy.rust.codegen.core.smithy.RuntimeType +import software.amazon.smithy.rustsdk.BaseRequestIdDecorator +import software.amazon.smithy.rustsdk.InlineAwsDependency + +class S3ExtendedRequestIdDecorator : BaseRequestIdDecorator() { + override val name: String = "S3ExtendedRequestIdDecorator" + override val order: Byte = 0 + + override val fieldName: String = "extended_request_id" + override val accessorFunctionName: String = "extended_request_id" + + private val requestIdModule: RuntimeType = + RuntimeType.forInlineDependency(InlineAwsDependency.forRustFile("s3_request_id")) + + override fun accessorTrait(codegenContext: ClientCodegenContext): RuntimeType = + requestIdModule.resolve("RequestIdExt") + + override fun applyToError(codegenContext: ClientCodegenContext): RuntimeType = + requestIdModule.resolve("apply_extended_request_id") +} 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 39e85d7898..3534258b18 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 @@ -6,21 +6,41 @@ package software.amazon.smithy.rustsdk.customize.s3control import software.amazon.smithy.model.Model +import software.amazon.smithy.model.node.Node import software.amazon.smithy.model.shapes.ServiceShape -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.endpoint.EndpointCustomization +import software.amazon.smithy.rust.codegen.client.smithy.endpoint.rustName +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.rustsdk.endpoints.stripEndpointTrait +import software.amazon.smithy.rustsdk.getBuiltIn +import software.amazon.smithy.rustsdk.toWritable class S3ControlDecorator : ClientCodegenDecorator { override val name: String = "S3Control" override val order: Byte = 0 - private fun applies(service: ServiceShape) = - service.id == ShapeId.from("com.amazonaws.s3control#AWSS3ControlServiceV20180820") - override fun transformModel(service: ServiceShape, model: Model): Model { - if (!applies(service)) { - return model - } - return stripEndpointTrait("AccountId")(model) + override fun transformModel(service: ServiceShape, model: Model): Model = + stripEndpointTrait("AccountId")(model) + + override fun endpointCustomizations(codegenContext: ClientCodegenContext): List { + return listOf(object : EndpointCustomization { + override fun setBuiltInOnServiceConfig(name: String, value: Node, configBuilderRef: String): Writable? { + if (!name.startsWith("AWS::S3Control")) { + return null + } + val builtIn = codegenContext.getBuiltIn(name) ?: return null + return writable { + rustTemplate( + "let $configBuilderRef = $configBuilderRef.${builtIn.name.rustName()}(#{value});", + "value" to value.toWritable(), + ) + } + } + }, + ) } } 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 75d0555c8f..a332dd3035 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,7 +7,6 @@ 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 @@ -22,24 +21,18 @@ class STSDecorator : ClientCodegenDecorator { override val order: Byte = 0 private val logger: Logger = Logger.getLogger(javaClass.name) - private fun applies(serviceId: ShapeId) = - serviceId == ShapeId.from("com.amazonaws.sts#AWSSecurityTokenServiceV20110615") - private fun isIdpCommunicationError(shape: Shape): Boolean = shape is StructureShape && shape.hasTrait() && shape.id.namespace == "com.amazonaws.sts" && shape.id.name == "IDPCommunicationErrorException" - override fun transformModel(service: ServiceShape, model: Model): Model { - return model.letIf(applies(service.id)) { - ModelTransformer.create().mapShapes(model) { shape -> - shape.letIf(isIdpCommunicationError(shape)) { - logger.info("Adding @retryable trait to $shape and setting its error type to 'server'") - (shape as StructureShape).toBuilder() - .removeTrait(ErrorTrait.ID) - .addTrait(ErrorTrait("server")) - .addTrait(RetryableTrait.builder().build()).build() - } + override fun transformModel(service: ServiceShape, model: Model): Model = + ModelTransformer.create().mapShapes(model) { shape -> + shape.letIf(isIdpCommunicationError(shape)) { + logger.info("Adding @retryable trait to $shape and setting its error type to 'server'") + (shape as StructureShape).toBuilder() + .removeTrait(ErrorTrait.ID) + .addTrait(ErrorTrait("server")) + .addTrait(RetryableTrait.builder().build()).build() } } - } } diff --git a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/endpoints/AwsEndpointDecorator.kt b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/endpoints/AwsEndpointDecorator.kt deleted file mode 100644 index ab9ce5bf1f..0000000000 --- a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/endpoints/AwsEndpointDecorator.kt +++ /dev/null @@ -1,277 +0,0 @@ -/* - * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. - * SPDX-License-Identifier: Apache-2.0 - */ - -package software.amazon.smithy.rustsdk.endpoints - -import software.amazon.smithy.codegen.core.CodegenException -import software.amazon.smithy.model.Model -import software.amazon.smithy.model.shapes.ServiceShape -import software.amazon.smithy.model.shapes.ShapeId -import software.amazon.smithy.model.transform.ModelTransformer -import software.amazon.smithy.rulesengine.language.EndpointRuleSet -import software.amazon.smithy.rulesengine.language.syntax.parameters.Builtins -import software.amazon.smithy.rulesengine.language.syntax.parameters.Parameters -import software.amazon.smithy.rulesengine.traits.EndpointRuleSetTrait -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.endpoint.generators.EndpointsModule -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.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.CodegenContext -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.customize.AdHocCustomization -import software.amazon.smithy.rust.codegen.core.smithy.customize.adhocCustomization -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.util.extendIf -import software.amazon.smithy.rust.codegen.core.util.letIf -import software.amazon.smithy.rust.codegen.core.util.thenSingletonListOf -import software.amazon.smithy.rustsdk.AwsRuntimeType -import software.amazon.smithy.rustsdk.SdkConfigSection -import software.amazon.smithy.rustsdk.getBuiltIn - -class AwsEndpointDecorator : ClientCodegenDecorator { - override val name: String = "AwsEndpoint" - override val order: Byte = 100 - - override fun transformModel(service: ServiceShape, model: Model): Model { - val customServices = setOf( - ShapeId.from("com.amazonaws.s3#AmazonS3"), - ShapeId.from("com.amazonaws.s3control#AWSS3ControlServiceV20180820"), - ShapeId.from("com.amazonaws.codecatalyst#CodeCatalyst"), - ) - if (customServices.contains(service.id)) { - return model - } - // currently, most models incorrectly model region is optional when it is actually required—fix these models: - return ModelTransformer.create().mapTraits(model) { _, trait -> - when (trait) { - is EndpointRuleSetTrait -> { - val epRules = EndpointRuleSet.fromNode(trait.ruleSet) - val newParameters = Parameters.builder() - epRules.parameters.toList() - .map { param -> - param.letIf(param.builtIn == Builtins.REGION.builtIn) { parameter -> - val builder = parameter.toBuilder().required(true) - // TODO(https://github.com/awslabs/smithy-rs/issues/2187): undo this workaround - parameter.defaultValue.ifPresent { default -> builder.defaultValue(default) } - - builder.build() - } - } - .forEach(newParameters::addParameter) - - val newTrait = epRules.toBuilder().parameters( - newParameters.build(), - ).build() - EndpointRuleSetTrait.builder().ruleSet(newTrait.toNode()).build() - } - - else -> trait - } - } - } - - override fun configCustomizations( - codegenContext: ClientCodegenContext, - baseCustomizations: List, - ): List { - return baseCustomizations.extendIf(codegenContext.isRegionalized()) { - AwsEndpointShimCustomization(codegenContext) - } - } - - override fun libRsCustomizations( - codegenContext: ClientCodegenContext, - baseCustomizations: List, - ): List { - return baseCustomizations + PubUseEndpoint(codegenContext.runtimeConfig) - } - - override fun extras(codegenContext: ClientCodegenContext, rustCrate: RustCrate) { - val epTypes = EndpointTypesGenerator.fromContext(codegenContext) - if (epTypes.defaultResolver() == null) { - throw CodegenException( - "${codegenContext.serviceShape} did not provide endpoint rules. " + - "This is a bug and the generated client will not work. All AWS services MUST define endpoint rules.", - ) - } - // generate a region converter if params has a region - if (!codegenContext.isRegionalized()) { - println("not generating a resolver for ${codegenContext.serviceShape}") - return - } - rustCrate.withModule(EndpointsModule) { - // TODO(https://github.com/awslabs/smithy-rs/issues/1784) cleanup task - rustTemplate( - """ - /// Temporary shim to allow new and old endpoint resolvers to co-exist - /// - /// This enables converting from the actual parameters type to the placeholder parameters type that - /// contains a region - ##[doc(hidden)] - impl From<#{Params}> for #{PlaceholderParams} { - fn from(params: #{Params}) -> Self { - Self::new(params.region().map(|r|#{Region}::new(r.to_string()))) - } - } - """, - "Params" to epTypes.paramsStruct(), - "Region" to AwsRuntimeType.awsTypes(codegenContext.runtimeConfig).resolve("region::Region"), - "PlaceholderParams" to AwsRuntimeType.awsEndpoint(codegenContext.runtimeConfig).resolve("Params"), - ) - } - } - - override fun extraSections(codegenContext: ClientCodegenContext): List { - return codegenContext.isRegionalized().thenSingletonListOf { - adhocCustomization { section -> - rust( - """ - ${section.serviceConfigBuilder}.set_aws_endpoint_resolver(${section.sdkConfig}.endpoint_resolver().clone()); - """, - ) - } - } - } - - class AwsEndpointShimCustomization(codegenContext: ClientCodegenContext) : ConfigCustomization() { - private val moduleUseName = codegenContext.moduleUseName() - private val runtimeConfig = codegenContext.runtimeConfig - private val resolveAwsEndpoint = AwsRuntimeType.awsEndpoint(runtimeConfig).resolve("ResolveAwsEndpoint") - private val endpointShim = AwsRuntimeType.awsEndpoint(runtimeConfig).resolve("EndpointShim") - private val codegenScope = arrayOf( - "ResolveAwsEndpoint" to resolveAwsEndpoint, - "EndpointShim" to endpointShim, - "aws_types" to AwsRuntimeType.awsTypes(runtimeConfig), - ) - - override fun section(section: ServiceConfig) = writable { - when (section) { - ServiceConfig.BuilderImpl -> rustTemplate( - """ - /// Overrides the endpoint resolver to use when making requests. - /// - /// This method is deprecated, use [`Builder::endpoint_url`] or [`Builder::endpoint_resolver`] instead. - /// - /// When unset, the client will used a generated endpoint resolver based on the endpoint metadata - /// for `$moduleUseName`. - /// - /// ## Examples - /// ```no_run - /// ## fn wrapper() -> Result<(), aws_smithy_http::endpoint::error::InvalidEndpointError> { - /// use #{aws_types}::region::Region; - /// use $moduleUseName::config::{Builder, Config}; - /// use $moduleUseName::Endpoint; - /// - /// let config = $moduleUseName::Config::builder() - /// .endpoint_resolver(Endpoint::immutable("http://localhost:8080")?) - /// .build(); - /// ## Ok(()) - /// ## } - /// ``` - ##[deprecated(note = "use endpoint_url or set the endpoint resolver directly")] - pub fn aws_endpoint_resolver(mut self, endpoint_resolver: impl #{ResolveAwsEndpoint} + 'static) -> Self { - self.endpoint_resolver = Some(std::sync::Arc::new(#{EndpointShim}::from_resolver(endpoint_resolver)) as _); - self - } - - ##[deprecated(note = "use endpoint_url or set the endpoint resolver directly")] - /// Sets the endpoint resolver to use when making requests. - /// - /// This method is deprecated, use [`Builder::endpoint_url`] or [`Builder::endpoint_resolver`] instead. - pub fn set_aws_endpoint_resolver(&mut self, endpoint_resolver: Option>) -> &mut Self { - self.endpoint_resolver = endpoint_resolver.map(|res|std::sync::Arc::new(#{EndpointShim}::from_arc(res) ) as _); - self - } - - """, - *codegenScope, - ) - - else -> emptySection - } - } - } - - class SdkEndpointCustomization( - codegenContext: CodegenContext, - ) : - ConfigCustomization() { - private val runtimeConfig = codegenContext.runtimeConfig - private val resolveAwsEndpoint = AwsRuntimeType.awsEndpoint(runtimeConfig).resolve("ResolveAwsEndpoint") - private val endpointShim = AwsRuntimeType.awsEndpoint(runtimeConfig).resolve("EndpointShim") - private val codegenScope = arrayOf( - "ResolveAwsEndpoint" to resolveAwsEndpoint, - "EndpointShim" to endpointShim, - "aws_types" to AwsRuntimeType.awsTypes(runtimeConfig), - ) - - override fun section(section: ServiceConfig): Writable = writable { - when (section) { - ServiceConfig.BuilderImpl -> rustTemplate( - """ - /// 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 - /// [`Builder::endpoint_resolver`]. - pub fn endpoint_url(mut self, endpoint_url: impl Into) -> Self { - self.endpoint_url = Some(endpoint_url.into()); - self - } - - /// 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 - /// [`Builder::endpoint_resolver`]. - pub fn set_endpoint_url(&mut self, endpoint_url: Option) -> &mut Self { - self.endpoint_url = endpoint_url; - self - } - """, - *codegenScope, - ) - - ServiceConfig.BuilderBuild -> rust("endpoint_url: self.endpoint_url,") - ServiceConfig.BuilderStruct -> rust("endpoint_url: Option,") - ServiceConfig.ConfigImpl -> { - Attribute.AllowDeadCode.render(this) - rust("pub(crate) fn endpoint_url(&self) -> Option<&str> { self.endpoint_url.as_deref() }") - } - - ServiceConfig.ConfigStruct -> rust("endpoint_url: Option,") - ServiceConfig.ConfigStructAdditionalDocs -> emptySection - ServiceConfig.Extras -> emptySection - } - } - } - - class PubUseEndpoint(private val runtimeConfig: RuntimeConfig) : LibRsCustomization() { - override fun section(section: LibRsSection): Writable { - return when (section) { - is LibRsSection.Body -> writable { - rust( - "pub use #T::endpoint::Endpoint;", - CargoDependency.smithyHttp(runtimeConfig).toType(), - ) - } - - else -> emptySection - } - } - } -} - -fun ClientCodegenContext.isRegionalized() = getBuiltIn(Builtins.REGION) != null 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 37ecbad73c..9d620e11cb 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 @@ -17,7 +17,6 @@ import software.amazon.smithy.rust.codegen.client.smithy.endpoint.EndpointTypesG 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.CargoDependency import software.amazon.smithy.rust.codegen.core.rustlang.escape import software.amazon.smithy.rust.codegen.core.rustlang.join import software.amazon.smithy.rust.codegen.core.rustlang.rust @@ -25,6 +24,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.PublicImportSymbolProvider +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.generators.setterName import software.amazon.smithy.rust.codegen.core.testutil.integrationTest @@ -146,8 +146,7 @@ class OperationInputTestGenerator(_ctx: ClientCodegenContext, private val test: let _result = dbg!(#{invoke_operation}); #{assertion} """, - "capture_request" to CargoDependency.smithyClient(runtimeConfig) - .withFeature("test-util").toType().resolve("test_connection::capture_request"), + "capture_request" to RuntimeType.captureRequest(runtimeConfig), "conf" to config(testOperationInput), "invoke_operation" to operationInvocation(testOperationInput), "assertion" to writable { diff --git a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/endpoints/RequireEndpointRules.kt b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/endpoints/RequireEndpointRules.kt new file mode 100644 index 0000000000..3b3b81aecc --- /dev/null +++ b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/endpoints/RequireEndpointRules.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.rustsdk.endpoints + +import software.amazon.smithy.codegen.core.CodegenException +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.core.smithy.RustCrate +import software.amazon.smithy.rustsdk.sdkSettings + +class RequireEndpointRules : ClientCodegenDecorator { + override val name: String = "RequireEndpointRules" + override val order: Byte = 100 + override fun extras(codegenContext: ClientCodegenContext, rustCrate: RustCrate) { + if (!codegenContext.sdkSettings().requireEndpointResolver) { + return + } + val epTypes = EndpointTypesGenerator.fromContext(codegenContext) + if (epTypes.defaultResolver() == null) { + throw CodegenException( + "${codegenContext.serviceShape} did not provide endpoint rules. To explicitly allow this, set `awsSdk.requireEndpointResolver: false` in smithy-build.json.", + ) + } + } +} diff --git a/aws/sdk-codegen/src/test/kotlin/AwsReadmeDecoratorTest.kt b/aws/sdk-codegen/src/test/kotlin/AwsCrateDocsDecoratorTest.kt similarity index 83% rename from aws/sdk-codegen/src/test/kotlin/AwsReadmeDecoratorTest.kt rename to aws/sdk-codegen/src/test/kotlin/AwsCrateDocsDecoratorTest.kt index 5717fcc2fe..aa2485e6ae 100644 --- a/aws/sdk-codegen/src/test/kotlin/AwsReadmeDecoratorTest.kt +++ b/aws/sdk-codegen/src/test/kotlin/AwsCrateDocsDecoratorTest.kt @@ -5,9 +5,13 @@ import org.junit.jupiter.api.Assertions.assertEquals import org.junit.jupiter.api.Test -import software.amazon.smithy.rustsdk.AwsSdkReadmeGenerator +import software.amazon.smithy.model.loader.ModelAssembler +import software.amazon.smithy.rust.codegen.client.testutil.testClientCodegenContext +import software.amazon.smithy.rustsdk.AwsCrateDocGenerator + +class AwsCrateDocsDecoratorTest { + private val codegenContext = testClientCodegenContext(ModelAssembler().assemble().unwrap()) -class AwsReadmeDecoratorTest { @Test fun `it converts description HTML into Markdown`() { assertEquals( @@ -18,7 +22,7 @@ class AwsReadmeDecoratorTest { More information [can be found here](https://example.com). """.trimIndent(), - AwsSdkReadmeGenerator().normalizeDescription( + AwsCrateDocGenerator(codegenContext).normalizeDescription( "", """ Some service @@ -44,7 +48,7 @@ class AwsReadmeDecoratorTest { More text. """.trimIndent(), - AwsSdkReadmeGenerator().normalizeDescription( + AwsCrateDocGenerator(codegenContext).normalizeDescription( "", """

Some text introducing a list: @@ -81,7 +85,7 @@ class AwsReadmeDecoratorTest { Some trailing text. """.trimIndent(), - AwsSdkReadmeGenerator().normalizeDescription( + AwsCrateDocGenerator(codegenContext).normalizeDescription( "", """

Some text introducing a description list: 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 1df1a0c0e0..ea2bfe026f 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 @@ -6,8 +6,8 @@ package software.amazon.smithy.rustsdk 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.testutil.asSmithyModel import software.amazon.smithy.rust.codegen.core.testutil.integrationTest import software.amazon.smithy.rust.codegen.core.testutil.tokioTest @@ -96,8 +96,7 @@ class EndpointsCredentialsTest { let auth_header = req.headers().get("AUTHORIZATION").unwrap().to_str().unwrap(); assert!(auth_header.contains("/us-west-2/foobaz/aws4_request"), "{}", auth_header); """, - "capture_request" to CargoDependency.smithyClient(context.runtimeConfig) - .withFeature("test-util").toType().resolve("test_connection::capture_request"), + "capture_request" to RuntimeType.captureRequest(context.runtimeConfig), "Credentials" to AwsCargoDependency.awsCredentialTypes(context.runtimeConfig) .withFeature("test-util").toType().resolve("Credentials"), "Region" to AwsRuntimeType.awsTypes(context.runtimeConfig).resolve("region::Region"), @@ -120,8 +119,7 @@ class EndpointsCredentialsTest { 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); """, - "capture_request" to CargoDependency.smithyClient(context.runtimeConfig) - .withFeature("test-util").toType().resolve("test_connection::capture_request"), + "capture_request" to RuntimeType.captureRequest(context.runtimeConfig), "Credentials" to AwsCargoDependency.awsCredentialTypes(context.runtimeConfig) .withFeature("test-util").toType().resolve("Credentials"), "Region" to AwsRuntimeType.awsTypes(context.runtimeConfig).resolve("region::Region"), 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 1307a46fbb..b97952e002 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 @@ -7,30 +7,13 @@ package software.amazon.smithy.rustsdk import org.junit.jupiter.api.Test import software.amazon.smithy.rust.codegen.client.testutil.validateConfigCustomizations -import software.amazon.smithy.rust.codegen.core.smithy.CoreRustSettings import software.amazon.smithy.rust.codegen.core.testutil.TestWorkspace -import software.amazon.smithy.rust.codegen.core.testutil.rustSettings class HttpConnectorConfigCustomizationTest { @Test fun `generates a valid config`() { val project = TestWorkspace.testProject() - val projectSettings = project.rustSettings() - val codegenContext = awsTestCodegenContext( - coreRustSettings = CoreRustSettings( - service = projectSettings.service, - moduleName = projectSettings.moduleName, - moduleVersion = projectSettings.moduleVersion, - moduleAuthors = projectSettings.moduleAuthors, - moduleDescription = projectSettings.moduleDescription, - moduleRepository = projectSettings.moduleRepository, - runtimeConfig = AwsTestRuntimeConfig, - codegenConfig = projectSettings.codegenConfig, - license = projectSettings.license, - examplesUri = projectSettings.examplesUri, - customizationConfig = projectSettings.customizationConfig, - ), - ) + val codegenContext = awsTestCodegenContext() validateConfigCustomizations(HttpConnectorConfigCustomization(codegenContext), project) } } 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 8d69fb2c86..9d2e865a64 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 @@ -6,8 +6,8 @@ package software.amazon.smithy.rustsdk import org.junit.jupiter.api.Test +import software.amazon.smithy.rust.codegen.client.testutil.testClientRustSettings import software.amazon.smithy.rust.codegen.client.testutil.validateConfigCustomizations -import software.amazon.smithy.rust.codegen.core.smithy.CoreRustSettings import software.amazon.smithy.rust.codegen.core.testutil.TestWorkspace import software.amazon.smithy.rust.codegen.core.testutil.rustSettings @@ -15,21 +15,12 @@ internal class RegionProviderConfigTest { @Test fun `generates a valid config`() { val project = TestWorkspace.testProject() - val projectSettings = project.rustSettings() - val coreRustSettings = CoreRustSettings( - service = projectSettings.service, - moduleName = projectSettings.moduleName, - moduleVersion = projectSettings.moduleVersion, - moduleAuthors = projectSettings.moduleAuthors, - moduleDescription = projectSettings.moduleDescription, - moduleRepository = projectSettings.moduleRepository, - runtimeConfig = AwsTestRuntimeConfig, - codegenConfig = projectSettings.codegenConfig, - license = projectSettings.license, - examplesUri = projectSettings.examplesUri, - customizationConfig = projectSettings.customizationConfig, + val codegenContext = awsTestCodegenContext( + settings = testClientRustSettings( + moduleName = project.rustSettings().moduleName, + runtimeConfig = AwsTestRuntimeConfig, + ), ) - val codegenContext = awsTestCodegenContext(coreRustSettings = coreRustSettings) validateConfigCustomizations(RegionProviderConfig(codegenContext), project) } } 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 8db0d6a1df..f24dd88408 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 @@ -8,14 +8,15 @@ package software.amazon.smithy.rustsdk import software.amazon.smithy.model.Model import software.amazon.smithy.model.node.ObjectNode 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 -import software.amazon.smithy.rust.codegen.client.testutil.testCodegenContext -import software.amazon.smithy.rust.codegen.core.smithy.CoreRustSettings +import software.amazon.smithy.rust.codegen.client.testutil.testClientCodegenContext +import software.amazon.smithy.rust.codegen.client.testutil.testClientRustSettings import software.amazon.smithy.rust.codegen.core.smithy.RuntimeCrateLocation 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.testutil.testRustSettings 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 @@ -28,10 +29,10 @@ val AwsTestRuntimeConfig = TestRuntimeConfig.copy( }, ) -fun awsTestCodegenContext(model: Model? = null, coreRustSettings: CoreRustSettings?) = - testCodegenContext( +fun awsTestCodegenContext(model: Model? = null, settings: ClientRustSettings? = null) = + testClientCodegenContext( model ?: "namespace test".asSmithyModel(), - settings = coreRustSettings ?: testRustSettings(runtimeConfig = AwsTestRuntimeConfig), + settings = settings ?: testClientRustSettings(runtimeConfig = AwsTestRuntimeConfig), ) fun awsSdkIntegrationTest( @@ -39,18 +40,27 @@ fun awsSdkIntegrationTest( test: (ClientCodegenContext, RustCrate) -> Unit = { _, _ -> }, ) = clientIntegrationTest( - model, runtimeConfig = AwsTestRuntimeConfig, - additionalSettings = ObjectNode.builder() - .withMember( + model, + IntegrationTestParams( + runtimeConfig = AwsTestRuntimeConfig, + additionalSettings = ObjectNode.builder().withMember( "customizationConfig", ObjectNode.builder() .withMember( "awsSdk", ObjectNode.builder() + .withMember("generateReadme", false) .withMember("integrationTestPath", "../sdk/integration-tests") .build(), ).build(), ) - .withMember("codegen", ObjectNode.builder().withMember("includeFluentClient", false).build()).build(), + .withMember( + "codegen", + ObjectNode.builder() + .withMember("includeFluentClient", false) + .withMember("enableNewCrateOrganizationScheme", true) + .build(), + ).build(), + ), test = test, ) diff --git a/aws/sdk/aws-models/config.json b/aws/sdk/aws-models/config.json index 5cc02d27fb..7ca3f50448 100644 --- a/aws/sdk/aws-models/config.json +++ b/aws/sdk/aws-models/config.json @@ -12896,7 +12896,7 @@ "parameters": { "Region": { "builtIn": "AWS::Region", - "required": true, + "required": false, "documentation": "The AWS region used to dispatch the request.", "type": "String" }, @@ -12925,13 +12925,12 @@ { "conditions": [ { - "fn": "aws.partition", + "fn": "isSet", "argv": [ { - "ref": "Region" + "ref": "Endpoint" } - ], - "assign": "PartitionResult" + ] } ], "type": "tree", @@ -12939,14 +12938,20 @@ { "conditions": [ { - "fn": "isSet", + "fn": "booleanEquals", "argv": [ { - "ref": "Endpoint" - } + "ref": "UseFIPS" + }, + true ] } ], + "error": "Invalid Configuration: FIPS and custom endpoint are not supported", + "type": "error" + }, + { + "conditions": [], "type": "tree", "rules": [ { @@ -12955,67 +12960,42 @@ "fn": "booleanEquals", "argv": [ { - "ref": "UseFIPS" + "ref": "UseDualStack" }, true ] } ], - "error": "Invalid Configuration: FIPS and custom endpoint are not supported", + "error": "Invalid Configuration: Dualstack 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" + "endpoint": { + "url": { + "ref": "Endpoint" }, - { - "conditions": [], - "endpoint": { - "url": { - "ref": "Endpoint" - }, - "properties": {}, - "headers": {} - }, - "type": "endpoint" - } - ] + "properties": {}, + "headers": {} + }, + "type": "endpoint" } ] - }, + } + ] + }, + { + "conditions": [], + "type": "tree", + "rules": [ { "conditions": [ { - "fn": "booleanEquals", - "argv": [ - { - "ref": "UseFIPS" - }, - true - ] - }, - { - "fn": "booleanEquals", + "fn": "isSet", "argv": [ { - "ref": "UseDualStack" - }, - true + "ref": "Region" + } ] } ], @@ -13024,179 +13004,240 @@ { "conditions": [ { - "fn": "booleanEquals", + "fn": "aws.partition", "argv": [ - true, { - "fn": "getAttr", + "ref": "Region" + } + ], + "assign": "PartitionResult" + } + ], + "type": "tree", + "rules": [ + { + "conditions": [ + { + "fn": "booleanEquals", "argv": [ { - "ref": "PartitionResult" + "ref": "UseFIPS" }, - "supportsFIPS" + true ] - } - ] - }, - { - "fn": "booleanEquals", - "argv": [ - true, + }, { - "fn": "getAttr", + "fn": "booleanEquals", "argv": [ { - "ref": "PartitionResult" + "ref": "UseDualStack" }, - "supportsDualStack" + true ] } - ] - } - ], - "type": "tree", - "rules": [ - { - "conditions": [], + ], "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://config-fips.{Region}.{PartitionResult#dualStackDnsSuffix}", + "properties": {}, + "headers": {} + }, + "type": "endpoint" + } + ] + } + ] + }, { "conditions": [], - "endpoint": { - "url": "https://config-fips.{Region}.{PartitionResult#dualStackDnsSuffix}", - "properties": {}, - "headers": {} - }, - "type": "endpoint" + "error": "FIPS and DualStack are enabled, but this partition does not support one or both", + "type": "error" } ] - } - ] - }, - { - "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, + "conditions": [ { - "fn": "getAttr", + "fn": "booleanEquals", "argv": [ { - "ref": "PartitionResult" + "ref": "UseFIPS" }, - "supportsFIPS" + true ] } - ] - } - ], - "type": "tree", - "rules": [ - { - "conditions": [], + ], "type": "tree", "rules": [ { "conditions": [ { - "fn": "stringEquals", + "fn": "booleanEquals", "argv": [ - "aws-us-gov", + true, { "fn": "getAttr", "argv": [ { "ref": "PartitionResult" }, - "name" + "supportsFIPS" ] } ] } ], - "endpoint": { - "url": "https://config.{Region}.amazonaws.com", - "properties": {}, - "headers": {} - }, - "type": "endpoint" + "type": "tree", + "rules": [ + { + "conditions": [], + "type": "tree", + "rules": [ + { + "conditions": [ + { + "fn": "stringEquals", + "argv": [ + "aws-us-gov", + { + "fn": "getAttr", + "argv": [ + { + "ref": "PartitionResult" + }, + "name" + ] + } + ] + } + ], + "endpoint": { + "url": "https://config.{Region}.amazonaws.com", + "properties": {}, + "headers": {} + }, + "type": "endpoint" + }, + { + "conditions": [], + "endpoint": { + "url": "https://config-fips.{Region}.{PartitionResult#dnsSuffix}", + "properties": {}, + "headers": {} + }, + "type": "endpoint" + } + ] + } + ] }, { "conditions": [], - "endpoint": { - "url": "https://config-fips.{Region}.{PartitionResult#dnsSuffix}", - "properties": {}, - "headers": {} - }, - "type": "endpoint" + "error": "FIPS is enabled but this partition does not support FIPS", + "type": "error" } ] - } - ] - }, - { - "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, + "conditions": [ { - "fn": "getAttr", + "fn": "booleanEquals", "argv": [ { - "ref": "PartitionResult" + "ref": "UseDualStack" }, - "supportsDualStack" + 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://config.{Region}.{PartitionResult#dualStackDnsSuffix}", + "properties": {}, + "headers": {} + }, + "type": "endpoint" + } + ] + } + ] + }, + { + "conditions": [], + "error": "DualStack is enabled but this partition does not support DualStack", + "type": "error" + } ] - } - ], - "type": "tree", - "rules": [ + }, { "conditions": [], "type": "tree", @@ -13204,7 +13245,7 @@ { "conditions": [], "endpoint": { - "url": "https://config.{Region}.{PartitionResult#dualStackDnsSuffix}", + "url": "https://config.{Region}.{PartitionResult#dnsSuffix}", "properties": {}, "headers": {} }, @@ -13213,28 +13254,13 @@ ] } ] - }, - { - "conditions": [], - "error": "DualStack is enabled but this partition does not support DualStack", - "type": "error" } ] }, { "conditions": [], - "type": "tree", - "rules": [ - { - "conditions": [], - "endpoint": { - "url": "https://config.{Region}.{PartitionResult#dnsSuffix}", - "properties": {}, - "headers": {} - }, - "type": "endpoint" - } - ] + "error": "Invalid Configuration: Missing Region", + "type": "error" } ] } @@ -13243,16 +13269,16 @@ "smithy.rules#endpointTests": { "testCases": [ { - "documentation": "For region sa-east-1 with FIPS disabled and DualStack disabled", + "documentation": "For region af-south-1 with FIPS disabled and DualStack disabled", "expect": { "endpoint": { - "url": "https://config.sa-east-1.amazonaws.com" + "url": "https://config.af-south-1.amazonaws.com" } }, "params": { - "UseFIPS": false, + "Region": "af-south-1", "UseDualStack": false, - "Region": "sa-east-1" + "UseFIPS": false } }, { @@ -13263,321 +13289,321 @@ } }, "params": { - "UseFIPS": false, + "Region": "ap-east-1", "UseDualStack": false, - "Region": "ap-east-1" + "UseFIPS": false } }, { - "documentation": "For region eu-south-1 with FIPS disabled and DualStack disabled", + "documentation": "For region ap-northeast-1 with FIPS disabled and DualStack disabled", "expect": { "endpoint": { - "url": "https://config.eu-south-1.amazonaws.com" + "url": "https://config.ap-northeast-1.amazonaws.com" } }, "params": { - "UseFIPS": false, + "Region": "ap-northeast-1", "UseDualStack": false, - "Region": "eu-south-1" + "UseFIPS": false } }, { - "documentation": "For region eu-central-1 with FIPS disabled and DualStack disabled", + "documentation": "For region ap-northeast-2 with FIPS disabled and DualStack disabled", "expect": { "endpoint": { - "url": "https://config.eu-central-1.amazonaws.com" + "url": "https://config.ap-northeast-2.amazonaws.com" } }, "params": { - "UseFIPS": false, + "Region": "ap-northeast-2", "UseDualStack": false, - "Region": "eu-central-1" + "UseFIPS": false } }, { - "documentation": "For region ap-southeast-1 with FIPS disabled and DualStack disabled", + "documentation": "For region ap-northeast-3 with FIPS disabled and DualStack disabled", "expect": { "endpoint": { - "url": "https://config.ap-southeast-1.amazonaws.com" + "url": "https://config.ap-northeast-3.amazonaws.com" } }, "params": { - "UseFIPS": false, + "Region": "ap-northeast-3", "UseDualStack": false, - "Region": "ap-southeast-1" + "UseFIPS": false } }, { - "documentation": "For region ap-southeast-2 with FIPS disabled and DualStack disabled", + "documentation": "For region ap-south-1 with FIPS disabled and DualStack disabled", "expect": { "endpoint": { - "url": "https://config.ap-southeast-2.amazonaws.com" + "url": "https://config.ap-south-1.amazonaws.com" } }, "params": { - "UseFIPS": false, + "Region": "ap-south-1", "UseDualStack": false, - "Region": "ap-southeast-2" + "UseFIPS": false } }, { - "documentation": "For region ap-southeast-3 with FIPS disabled and DualStack disabled", + "documentation": "For region ap-southeast-1 with FIPS disabled and DualStack disabled", "expect": { "endpoint": { - "url": "https://config.ap-southeast-3.amazonaws.com" + "url": "https://config.ap-southeast-1.amazonaws.com" } }, "params": { - "UseFIPS": false, + "Region": "ap-southeast-1", "UseDualStack": false, - "Region": "ap-southeast-3" + "UseFIPS": false } }, { - "documentation": "For region ca-central-1 with FIPS disabled and DualStack disabled", + "documentation": "For region ap-southeast-2 with FIPS disabled and DualStack disabled", "expect": { "endpoint": { - "url": "https://config.ca-central-1.amazonaws.com" + "url": "https://config.ap-southeast-2.amazonaws.com" } }, "params": { - "UseFIPS": false, + "Region": "ap-southeast-2", "UseDualStack": false, - "Region": "ca-central-1" + "UseFIPS": false } }, { - "documentation": "For region us-west-1 with FIPS disabled and DualStack disabled", + "documentation": "For region ap-southeast-3 with FIPS disabled and DualStack disabled", "expect": { "endpoint": { - "url": "https://config.us-west-1.amazonaws.com" + "url": "https://config.ap-southeast-3.amazonaws.com" } }, "params": { - "UseFIPS": false, + "Region": "ap-southeast-3", "UseDualStack": false, - "Region": "us-west-1" + "UseFIPS": false } }, { - "documentation": "For region us-west-1 with FIPS enabled and DualStack disabled", + "documentation": "For region ca-central-1 with FIPS disabled and DualStack disabled", "expect": { "endpoint": { - "url": "https://config-fips.us-west-1.amazonaws.com" + "url": "https://config.ca-central-1.amazonaws.com" } }, "params": { - "UseFIPS": true, + "Region": "ca-central-1", "UseDualStack": false, - "Region": "us-west-1" + "UseFIPS": false } }, { - "documentation": "For region us-west-2 with FIPS disabled and DualStack disabled", + "documentation": "For region eu-central-1 with FIPS disabled and DualStack disabled", "expect": { "endpoint": { - "url": "https://config.us-west-2.amazonaws.com" + "url": "https://config.eu-central-1.amazonaws.com" } }, "params": { - "UseFIPS": false, + "Region": "eu-central-1", "UseDualStack": false, - "Region": "us-west-2" + "UseFIPS": false } }, { - "documentation": "For region us-west-2 with FIPS enabled and DualStack disabled", + "documentation": "For region eu-north-1 with FIPS disabled and DualStack disabled", "expect": { "endpoint": { - "url": "https://config-fips.us-west-2.amazonaws.com" + "url": "https://config.eu-north-1.amazonaws.com" } }, "params": { - "UseFIPS": true, + "Region": "eu-north-1", "UseDualStack": false, - "Region": "us-west-2" + "UseFIPS": false } }, { - "documentation": "For region af-south-1 with FIPS disabled and DualStack disabled", + "documentation": "For region eu-south-1 with FIPS disabled and DualStack disabled", "expect": { "endpoint": { - "url": "https://config.af-south-1.amazonaws.com" + "url": "https://config.eu-south-1.amazonaws.com" } }, "params": { - "UseFIPS": false, + "Region": "eu-south-1", "UseDualStack": false, - "Region": "af-south-1" + "UseFIPS": false } }, { - "documentation": "For region ap-south-1 with FIPS disabled and DualStack disabled", + "documentation": "For region eu-west-1 with FIPS disabled and DualStack disabled", "expect": { "endpoint": { - "url": "https://config.ap-south-1.amazonaws.com" + "url": "https://config.eu-west-1.amazonaws.com" } }, "params": { - "UseFIPS": false, + "Region": "eu-west-1", "UseDualStack": false, - "Region": "ap-south-1" + "UseFIPS": false } }, { - "documentation": "For region ap-northeast-1 with FIPS disabled and DualStack disabled", + "documentation": "For region eu-west-2 with FIPS disabled and DualStack disabled", "expect": { "endpoint": { - "url": "https://config.ap-northeast-1.amazonaws.com" + "url": "https://config.eu-west-2.amazonaws.com" } }, "params": { - "UseFIPS": false, + "Region": "eu-west-2", "UseDualStack": false, - "Region": "ap-northeast-1" + "UseFIPS": false } }, { - "documentation": "For region ap-northeast-2 with FIPS disabled and DualStack disabled", + "documentation": "For region eu-west-3 with FIPS disabled and DualStack disabled", "expect": { "endpoint": { - "url": "https://config.ap-northeast-2.amazonaws.com" + "url": "https://config.eu-west-3.amazonaws.com" } }, "params": { - "UseFIPS": false, + "Region": "eu-west-3", "UseDualStack": false, - "Region": "ap-northeast-2" + "UseFIPS": false } }, { - "documentation": "For region ap-northeast-3 with FIPS disabled and DualStack disabled", + "documentation": "For region me-south-1 with FIPS disabled and DualStack disabled", "expect": { "endpoint": { - "url": "https://config.ap-northeast-3.amazonaws.com" + "url": "https://config.me-south-1.amazonaws.com" } }, "params": { - "UseFIPS": false, + "Region": "me-south-1", "UseDualStack": false, - "Region": "ap-northeast-3" + "UseFIPS": false } }, { - "documentation": "For region us-east-1 with FIPS disabled and DualStack disabled", + "documentation": "For region sa-east-1 with FIPS disabled and DualStack disabled", "expect": { "endpoint": { - "url": "https://config.us-east-1.amazonaws.com" + "url": "https://config.sa-east-1.amazonaws.com" } }, "params": { - "UseFIPS": false, + "Region": "sa-east-1", "UseDualStack": false, - "Region": "us-east-1" + "UseFIPS": false } }, { - "documentation": "For region us-east-1 with FIPS enabled and DualStack disabled", + "documentation": "For region us-east-1 with FIPS disabled and DualStack disabled", "expect": { "endpoint": { - "url": "https://config-fips.us-east-1.amazonaws.com" + "url": "https://config.us-east-1.amazonaws.com" } }, "params": { - "UseFIPS": true, + "Region": "us-east-1", "UseDualStack": false, - "Region": "us-east-1" + "UseFIPS": false } }, { - "documentation": "For region eu-west-1 with FIPS disabled and DualStack disabled", + "documentation": "For region us-east-1 with FIPS enabled and DualStack disabled", "expect": { "endpoint": { - "url": "https://config.eu-west-1.amazonaws.com" + "url": "https://config-fips.us-east-1.amazonaws.com" } }, "params": { - "UseFIPS": false, + "Region": "us-east-1", "UseDualStack": false, - "Region": "eu-west-1" + "UseFIPS": true } }, { - "documentation": "For region eu-west-2 with FIPS disabled and DualStack disabled", + "documentation": "For region us-east-2 with FIPS disabled and DualStack disabled", "expect": { "endpoint": { - "url": "https://config.eu-west-2.amazonaws.com" + "url": "https://config.us-east-2.amazonaws.com" } }, "params": { - "UseFIPS": false, + "Region": "us-east-2", "UseDualStack": false, - "Region": "eu-west-2" + "UseFIPS": false } }, { - "documentation": "For region eu-west-3 with FIPS disabled and DualStack disabled", + "documentation": "For region us-east-2 with FIPS enabled and DualStack disabled", "expect": { "endpoint": { - "url": "https://config.eu-west-3.amazonaws.com" + "url": "https://config-fips.us-east-2.amazonaws.com" } }, "params": { - "UseFIPS": false, + "Region": "us-east-2", "UseDualStack": false, - "Region": "eu-west-3" + "UseFIPS": true } }, { - "documentation": "For region me-south-1 with FIPS disabled and DualStack disabled", + "documentation": "For region us-west-1 with FIPS disabled and DualStack disabled", "expect": { "endpoint": { - "url": "https://config.me-south-1.amazonaws.com" + "url": "https://config.us-west-1.amazonaws.com" } }, "params": { - "UseFIPS": false, + "Region": "us-west-1", "UseDualStack": false, - "Region": "me-south-1" + "UseFIPS": false } }, { - "documentation": "For region eu-north-1 with FIPS disabled and DualStack disabled", + "documentation": "For region us-west-1 with FIPS enabled and DualStack disabled", "expect": { "endpoint": { - "url": "https://config.eu-north-1.amazonaws.com" + "url": "https://config-fips.us-west-1.amazonaws.com" } }, "params": { - "UseFIPS": false, + "Region": "us-west-1", "UseDualStack": false, - "Region": "eu-north-1" + "UseFIPS": true } }, { - "documentation": "For region us-east-2 with FIPS disabled and DualStack disabled", + "documentation": "For region us-west-2 with FIPS disabled and DualStack disabled", "expect": { "endpoint": { - "url": "https://config.us-east-2.amazonaws.com" + "url": "https://config.us-west-2.amazonaws.com" } }, "params": { - "UseFIPS": false, + "Region": "us-west-2", "UseDualStack": false, - "Region": "us-east-2" + "UseFIPS": false } }, { - "documentation": "For region us-east-2 with FIPS enabled and DualStack disabled", + "documentation": "For region us-west-2 with FIPS enabled and DualStack disabled", "expect": { "endpoint": { - "url": "https://config-fips.us-east-2.amazonaws.com" + "url": "https://config-fips.us-west-2.amazonaws.com" } }, "params": { - "UseFIPS": true, + "Region": "us-west-2", "UseDualStack": false, - "Region": "us-east-2" + "UseFIPS": true } }, { @@ -13588,9 +13614,9 @@ } }, "params": { - "UseFIPS": true, + "Region": "us-east-1", "UseDualStack": true, - "Region": "us-east-1" + "UseFIPS": true } }, { @@ -13601,230 +13627,243 @@ } }, "params": { - "UseFIPS": false, + "Region": "us-east-1", "UseDualStack": true, - "Region": "us-east-1" + "UseFIPS": false } }, { - "documentation": "For region us-gov-west-1 with FIPS disabled and DualStack disabled", + "documentation": "For region cn-north-1 with FIPS disabled and DualStack disabled", "expect": { "endpoint": { - "url": "https://config.us-gov-west-1.amazonaws.com" + "url": "https://config.cn-north-1.amazonaws.com.cn" } }, "params": { - "UseFIPS": false, + "Region": "cn-north-1", "UseDualStack": false, - "Region": "us-gov-west-1" + "UseFIPS": false } }, { - "documentation": "For region us-gov-west-1 with FIPS enabled and DualStack disabled", + "documentation": "For region cn-northwest-1 with FIPS disabled and DualStack disabled", "expect": { "endpoint": { - "url": "https://config.us-gov-west-1.amazonaws.com" + "url": "https://config.cn-northwest-1.amazonaws.com.cn" } }, "params": { - "UseFIPS": true, + "Region": "cn-northwest-1", "UseDualStack": false, - "Region": "us-gov-west-1" + "UseFIPS": false } }, { - "documentation": "For region us-gov-east-1 with FIPS disabled and DualStack disabled", + "documentation": "For region cn-north-1 with FIPS enabled and DualStack enabled", "expect": { "endpoint": { - "url": "https://config.us-gov-east-1.amazonaws.com" + "url": "https://config-fips.cn-north-1.api.amazonwebservices.com.cn" } }, "params": { - "UseFIPS": false, - "UseDualStack": false, - "Region": "us-gov-east-1" + "Region": "cn-north-1", + "UseDualStack": true, + "UseFIPS": true } }, { - "documentation": "For region us-gov-east-1 with FIPS enabled and DualStack disabled", + "documentation": "For region cn-north-1 with FIPS enabled and DualStack disabled", "expect": { "endpoint": { - "url": "https://config.us-gov-east-1.amazonaws.com" + "url": "https://config-fips.cn-north-1.amazonaws.com.cn" } }, "params": { - "UseFIPS": true, + "Region": "cn-north-1", "UseDualStack": false, - "Region": "us-gov-east-1" + "UseFIPS": true } }, { - "documentation": "For region us-gov-east-1 with FIPS enabled and DualStack enabled", + "documentation": "For region cn-north-1 with FIPS disabled and DualStack enabled", "expect": { "endpoint": { - "url": "https://config-fips.us-gov-east-1.api.aws" + "url": "https://config.cn-north-1.api.amazonwebservices.com.cn" } }, "params": { - "UseFIPS": true, + "Region": "cn-north-1", "UseDualStack": true, - "Region": "us-gov-east-1" + "UseFIPS": false } }, { - "documentation": "For region us-gov-east-1 with FIPS disabled and DualStack enabled", + "documentation": "For region us-gov-east-1 with FIPS disabled and DualStack disabled", "expect": { "endpoint": { - "url": "https://config.us-gov-east-1.api.aws" + "url": "https://config.us-gov-east-1.amazonaws.com" } }, "params": { - "UseFIPS": false, - "UseDualStack": true, - "Region": "us-gov-east-1" + "Region": "us-gov-east-1", + "UseDualStack": false, + "UseFIPS": false } }, { - "documentation": "For region us-isob-east-1 with FIPS disabled and DualStack disabled", + "documentation": "For region us-gov-east-1 with FIPS enabled and DualStack disabled", "expect": { "endpoint": { - "url": "https://config.us-isob-east-1.sc2s.sgov.gov" + "url": "https://config.us-gov-east-1.amazonaws.com" } }, "params": { - "UseFIPS": false, + "Region": "us-gov-east-1", "UseDualStack": false, - "Region": "us-isob-east-1" + "UseFIPS": true } }, { - "documentation": "For region us-isob-east-1 with FIPS enabled and DualStack disabled", + "documentation": "For region us-gov-west-1 with FIPS disabled and DualStack disabled", "expect": { "endpoint": { - "url": "https://config-fips.us-isob-east-1.sc2s.sgov.gov" + "url": "https://config.us-gov-west-1.amazonaws.com" } }, "params": { - "UseFIPS": true, + "Region": "us-gov-west-1", "UseDualStack": false, - "Region": "us-isob-east-1" + "UseFIPS": false } }, { - "documentation": "For region cn-northwest-1 with FIPS disabled and DualStack disabled", + "documentation": "For region us-gov-west-1 with FIPS enabled and DualStack disabled", "expect": { "endpoint": { - "url": "https://config.cn-northwest-1.amazonaws.com.cn" + "url": "https://config.us-gov-west-1.amazonaws.com" } }, "params": { - "UseFIPS": false, + "Region": "us-gov-west-1", "UseDualStack": false, - "Region": "cn-northwest-1" + "UseFIPS": true } }, { - "documentation": "For region cn-north-1 with FIPS disabled and DualStack disabled", + "documentation": "For region us-gov-east-1 with FIPS enabled and DualStack enabled", "expect": { "endpoint": { - "url": "https://config.cn-north-1.amazonaws.com.cn" + "url": "https://config-fips.us-gov-east-1.api.aws" } }, "params": { - "UseFIPS": false, - "UseDualStack": false, - "Region": "cn-north-1" + "Region": "us-gov-east-1", + "UseDualStack": true, + "UseFIPS": true } }, { - "documentation": "For region cn-north-1 with FIPS enabled and DualStack enabled", + "documentation": "For region us-gov-east-1 with FIPS disabled and DualStack enabled", "expect": { "endpoint": { - "url": "https://config-fips.cn-north-1.api.amazonwebservices.com.cn" + "url": "https://config.us-gov-east-1.api.aws" } }, "params": { - "UseFIPS": true, + "Region": "us-gov-east-1", "UseDualStack": true, - "Region": "cn-north-1" + "UseFIPS": false } }, { - "documentation": "For region cn-north-1 with FIPS enabled and DualStack disabled", + "documentation": "For region us-iso-east-1 with FIPS disabled and DualStack disabled", "expect": { "endpoint": { - "url": "https://config-fips.cn-north-1.amazonaws.com.cn" + "url": "https://config.us-iso-east-1.c2s.ic.gov" } }, "params": { - "UseFIPS": true, + "Region": "us-iso-east-1", "UseDualStack": false, - "Region": "cn-north-1" + "UseFIPS": false } }, { - "documentation": "For region cn-north-1 with FIPS disabled and DualStack enabled", + "documentation": "For region us-iso-west-1 with FIPS disabled and DualStack disabled", "expect": { "endpoint": { - "url": "https://config.cn-north-1.api.amazonwebservices.com.cn" + "url": "https://config.us-iso-west-1.c2s.ic.gov" } }, "params": { - "UseFIPS": false, - "UseDualStack": true, - "Region": "cn-north-1" + "Region": "us-iso-west-1", + "UseDualStack": false, + "UseFIPS": false } }, { - "documentation": "For region us-iso-west-1 with FIPS disabled and DualStack disabled", + "documentation": "For region us-iso-east-1 with FIPS enabled and DualStack disabled", "expect": { "endpoint": { - "url": "https://config.us-iso-west-1.c2s.ic.gov" + "url": "https://config-fips.us-iso-east-1.c2s.ic.gov" } }, "params": { - "UseFIPS": false, + "Region": "us-iso-east-1", "UseDualStack": false, - "Region": "us-iso-west-1" + "UseFIPS": true } }, { - "documentation": "For region us-iso-east-1 with FIPS disabled and DualStack disabled", + "documentation": "For region us-isob-east-1 with FIPS disabled and DualStack disabled", "expect": { "endpoint": { - "url": "https://config.us-iso-east-1.c2s.ic.gov" + "url": "https://config.us-isob-east-1.sc2s.sgov.gov" } }, "params": { - "UseFIPS": false, + "Region": "us-isob-east-1", "UseDualStack": false, - "Region": "us-iso-east-1" + "UseFIPS": false } }, { - "documentation": "For region us-iso-east-1 with FIPS enabled and DualStack disabled", + "documentation": "For region us-isob-east-1 with FIPS enabled and DualStack disabled", "expect": { "endpoint": { - "url": "https://config-fips.us-iso-east-1.c2s.ic.gov" + "url": "https://config-fips.us-isob-east-1.sc2s.sgov.gov" } }, "params": { - "UseFIPS": true, + "Region": "us-isob-east-1", "UseDualStack": false, - "Region": "us-iso-east-1" + "UseFIPS": true } }, { - "documentation": "For custom endpoint with fips disabled and dualstack disabled", + "documentation": "For custom endpoint with region set and fips disabled and dualstack disabled", "expect": { "endpoint": { "url": "https://example.com" } }, "params": { + "Region": "us-east-1", + "UseDualStack": false, "UseFIPS": 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": { "UseDualStack": false, - "Region": "us-east-1", + "UseFIPS": false, "Endpoint": "https://example.com" } }, @@ -13834,9 +13873,9 @@ "error": "Invalid Configuration: FIPS and custom endpoint are not supported" }, "params": { - "UseFIPS": true, - "UseDualStack": false, "Region": "us-east-1", + "UseDualStack": false, + "UseFIPS": true, "Endpoint": "https://example.com" } }, @@ -13846,9 +13885,9 @@ "error": "Invalid Configuration: Dualstack and custom endpoint are not supported" }, "params": { - "UseFIPS": false, - "UseDualStack": true, "Region": "us-east-1", + "UseDualStack": true, + "UseFIPS": false, "Endpoint": "https://example.com" } } diff --git a/aws/sdk/aws-models/dynamodb.json b/aws/sdk/aws-models/dynamodb.json index 49728c9f9e..c6a6550e5b 100644 --- a/aws/sdk/aws-models/dynamodb.json +++ b/aws/sdk/aws-models/dynamodb.json @@ -44,7 +44,7 @@ "ArchivalReason": { "target": "com.amazonaws.dynamodb#ArchivalReason", "traits": { - "smithy.api#documentation": "

The reason DynamoDB archived the table. Currently, the only possible value is:

\n\n
    \n
  • \n

    \n INACCESSIBLE_ENCRYPTION_CREDENTIALS - The table was archived due\n to the table's KMS key being inaccessible for more than seven\n days. An On-Demand backup was created at the archival time.

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

The reason DynamoDB archived the table. Currently, the only possible value is:

\n
    \n
  • \n

    \n INACCESSIBLE_ENCRYPTION_CREDENTIALS - The table was archived due\n to the table's KMS key being inaccessible for more than seven\n days. An On-Demand backup was created at the archival time.

    \n
  • \n
" } }, "ArchivalBackupArn": { @@ -94,7 +94,7 @@ "AttributeType": { "target": "com.amazonaws.dynamodb#ScalarAttributeType", "traits": { - "smithy.api#documentation": "

The data type for the attribute, where:

\n
    \n
  • \n

    \n S - the attribute is of type String

    \n
  • \n
  • \n

    \n N - the attribute is of type Number

    \n
  • \n
  • \n

    \n B - the attribute is of type Binary

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

The data type for the attribute, where:

\n
    \n
  • \n

    \n S - the attribute is of type String

    \n
  • \n
  • \n

    \n N - the attribute is of type Number

    \n
  • \n
  • \n

    \n B - the attribute is of type Binary

    \n
  • \n
", "smithy.api#required": {} } } @@ -153,66 +153,66 @@ "S": { "target": "com.amazonaws.dynamodb#StringAttributeValue", "traits": { - "smithy.api#documentation": "

An attribute of type String. For example:

\n

\n \"S\": \"Hello\"\n

" + "smithy.api#documentation": "

An attribute of type String. For example:

\n

\n \"S\": \"Hello\"\n

" } }, "N": { "target": "com.amazonaws.dynamodb#NumberAttributeValue", "traits": { - "smithy.api#documentation": "

An attribute of type Number. For example:

\n

\n \"N\": \"123.45\"\n

\n

Numbers are sent across the network to DynamoDB as strings, to maximize compatibility\n across languages and libraries. However, DynamoDB treats them as number type attributes\n for mathematical operations.

" + "smithy.api#documentation": "

An attribute of type Number. For example:

\n

\n \"N\": \"123.45\"\n

\n

Numbers are sent across the network to DynamoDB as strings, to maximize compatibility\n across languages and libraries. However, DynamoDB treats them as number type attributes\n for mathematical operations.

" } }, "B": { "target": "com.amazonaws.dynamodb#BinaryAttributeValue", "traits": { - "smithy.api#documentation": "

An attribute of type Binary. For example:

\n

\n \"B\": \"dGhpcyB0ZXh0IGlzIGJhc2U2NC1lbmNvZGVk\"\n

" + "smithy.api#documentation": "

An attribute of type Binary. For example:

\n

\n \"B\": \"dGhpcyB0ZXh0IGlzIGJhc2U2NC1lbmNvZGVk\"\n

" } }, "SS": { "target": "com.amazonaws.dynamodb#StringSetAttributeValue", "traits": { - "smithy.api#documentation": "

An attribute of type String Set. For example:

\n

\n \"SS\": [\"Giraffe\", \"Hippo\" ,\"Zebra\"]\n

" + "smithy.api#documentation": "

An attribute of type String Set. For example:

\n

\n \"SS\": [\"Giraffe\", \"Hippo\" ,\"Zebra\"]\n

" } }, "NS": { "target": "com.amazonaws.dynamodb#NumberSetAttributeValue", "traits": { - "smithy.api#documentation": "

An attribute of type Number Set. For example:

\n

\n \"NS\": [\"42.2\", \"-19\", \"7.5\", \"3.14\"]\n

\n

Numbers are sent across the network to DynamoDB as strings, to maximize compatibility\n across languages and libraries. However, DynamoDB treats them as number type attributes\n for mathematical operations.

" + "smithy.api#documentation": "

An attribute of type Number Set. For example:

\n

\n \"NS\": [\"42.2\", \"-19\", \"7.5\", \"3.14\"]\n

\n

Numbers are sent across the network to DynamoDB as strings, to maximize compatibility\n across languages and libraries. However, DynamoDB treats them as number type attributes\n for mathematical operations.

" } }, "BS": { "target": "com.amazonaws.dynamodb#BinarySetAttributeValue", "traits": { - "smithy.api#documentation": "

An attribute of type Binary Set. For example:

\n

\n \"BS\": [\"U3Vubnk=\", \"UmFpbnk=\", \"U25vd3k=\"]\n

" + "smithy.api#documentation": "

An attribute of type Binary Set. For example:

\n

\n \"BS\": [\"U3Vubnk=\", \"UmFpbnk=\", \"U25vd3k=\"]\n

" } }, "M": { "target": "com.amazonaws.dynamodb#MapAttributeValue", "traits": { - "smithy.api#documentation": "

An attribute of type Map. For example:

\n

\n \"M\": {\"Name\": {\"S\": \"Joe\"}, \"Age\": {\"N\": \"35\"}}\n

" + "smithy.api#documentation": "

An attribute of type Map. For example:

\n

\n \"M\": {\"Name\": {\"S\": \"Joe\"}, \"Age\": {\"N\": \"35\"}}\n

" } }, "L": { "target": "com.amazonaws.dynamodb#ListAttributeValue", "traits": { - "smithy.api#documentation": "

An attribute of type List. For example:

\n

\n \"L\": [ {\"S\": \"Cookies\"} , {\"S\": \"Coffee\"}, {\"N\": \"3.14159\"}]\n

" + "smithy.api#documentation": "

An attribute of type List. For example:

\n

\n \"L\": [ {\"S\": \"Cookies\"} , {\"S\": \"Coffee\"}, {\"N\": \"3.14159\"}]\n

" } }, "NULL": { "target": "com.amazonaws.dynamodb#NullAttributeValue", "traits": { - "smithy.api#documentation": "

An attribute of type Null. For example:

\n

\n \"NULL\": true\n

" + "smithy.api#documentation": "

An attribute of type Null. For example:

\n

\n \"NULL\": true\n

" } }, "BOOL": { "target": "com.amazonaws.dynamodb#BooleanAttributeValue", "traits": { - "smithy.api#documentation": "

An attribute of type Boolean. For example:

\n

\n \"BOOL\": true\n

" + "smithy.api#documentation": "

An attribute of type Boolean. For example:

\n

\n \"BOOL\": true\n

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

Represents the data for an attribute.

\n

Each attribute value is described as a name-value pair. The name is the data type, and\n the value is the data itself.

\n

For more information, see Data Types in the Amazon DynamoDB Developer\n Guide.

" + "smithy.api#documentation": "

Represents the data for an attribute.

\n

Each attribute value is described as a name-value pair. The name is the data type, and\n the value is the data itself.

\n

For more information, see Data Types in the Amazon DynamoDB Developer\n Guide.

" } }, "com.amazonaws.dynamodb#AttributeValueList": { @@ -227,18 +227,18 @@ "Value": { "target": "com.amazonaws.dynamodb#AttributeValue", "traits": { - "smithy.api#documentation": "

Represents the data for an attribute.

\n

Each attribute value is described as a name-value pair. The name is the data type, and\n the value is the data itself.

\n

For more information, see Data Types in the Amazon DynamoDB Developer Guide.\n

" + "smithy.api#documentation": "

Represents the data for an attribute.

\n

Each attribute value is described as a name-value pair. The name is the data type, and\n the value is the data itself.

\n

For more information, see Data Types in the Amazon DynamoDB Developer Guide.\n

" } }, "Action": { "target": "com.amazonaws.dynamodb#AttributeAction", "traits": { - "smithy.api#documentation": "

Specifies how to perform the update. Valid values are PUT (default),\n DELETE, and ADD. The behavior depends on whether the\n specified primary key already exists in the table.

\n\n

\n If an item with the specified Key is found in\n the table:\n

\n\n
    \n
  • \n

    \n PUT - Adds the specified attribute to the item. If the attribute\n already exists, it is replaced by the new value.

    \n
  • \n
  • \n

    \n DELETE - If no value is specified, the attribute and its value are\n removed from the item. The data type of the specified value must match the\n existing value's data type.

    \n

    If a set of values is specified, then those values are\n subtracted from the old set. For example, if the attribute value was the set\n [a,b,c] and the DELETE action specified\n [a,c], then the final attribute value would be\n [b]. Specifying an empty set is an error.

    \n
  • \n
  • \n

    \n ADD - If the attribute does not already exist, then the attribute\n and its values are added to the item. If the attribute does exist, then the\n behavior of ADD depends on the data type of the attribute:

    \n
      \n
    • \n

      If the existing attribute is a number, and if Value is\n also a number, then the Value is mathematically added to\n the existing attribute. If Value is a negative number, then\n it is subtracted from the existing attribute.

      \n \n

      If you use ADD to increment or decrement a number\n value for an item that doesn't exist before the update, DynamoDB\n uses 0 as the initial value.

      \n

      In addition, if you use ADD to update an existing\n item, and intend to increment or decrement an attribute value which\n does not yet exist, DynamoDB uses 0 as the initial\n value. For example, suppose that the item you want to update does\n not yet have an attribute named itemcount, but\n you decide to ADD the number 3 to this\n attribute anyway, even though it currently does not exist. DynamoDB\n will create the itemcount attribute, set its\n initial value to 0, and finally add 3 to\n it. The result will be a new itemcount\n attribute in the item, with a value of 3.

      \n
      \n
    • \n
    • \n

      If the existing data type is a set, and if the Value is\n also a set, then the Value is added to the existing set.\n (This is a set operation, not mathematical\n addition.) For example, if the attribute value was the set\n [1,2], and the ADD action specified\n [3], then the final attribute value would be\n [1,2,3]. An error occurs if an Add action is specified\n for a set attribute and the attribute type specified does not match the\n existing set type.

      \n

      Both sets must have the same primitive data type. For example, if the\n existing data type is a set of strings, the Value must also\n be a set of strings. The same holds true for number sets and binary\n sets.

      \n
    • \n
    \n

    This action is only valid for an existing attribute whose data type is number\n or is a set. Do not use ADD for any other data types.

    \n
  • \n
\n\n

\n If no item with the specified Key is\n found:\n

\n\n
    \n
  • \n

    \n PUT - DynamoDB creates a new item with the specified primary key,\n and then adds the attribute.

    \n
  • \n
  • \n

    \n DELETE - Nothing happens; there is no attribute to delete.

    \n
  • \n
  • \n

    \n ADD - DynamoDB creates a new item with the supplied primary key and\n number (or set) for the attribute value. The only data types allowed are number,\n number set, string set or binary set.

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

Specifies how to perform the update. Valid values are PUT (default),\n DELETE, and ADD. The behavior depends on whether the\n specified primary key already exists in the table.

\n

\n If an item with the specified Key is found in\n the table:\n

\n
    \n
  • \n

    \n PUT - Adds the specified attribute to the item. If the attribute\n already exists, it is replaced by the new value.

    \n
  • \n
  • \n

    \n DELETE - If no value is specified, the attribute and its value are\n removed from the item. The data type of the specified value must match the\n existing value's data type.

    \n

    If a set of values is specified, then those values are\n subtracted from the old set. For example, if the attribute value was the set\n [a,b,c] and the DELETE action specified\n [a,c], then the final attribute value would be\n [b]. Specifying an empty set is an error.

    \n
  • \n
  • \n

    \n ADD - If the attribute does not already exist, then the attribute\n and its values are added to the item. If the attribute does exist, then the\n behavior of ADD depends on the data type of the attribute:

    \n
      \n
    • \n

      If the existing attribute is a number, and if Value is\n also a number, then the Value is mathematically added to\n the existing attribute. If Value is a negative number, then\n it is subtracted from the existing attribute.

      \n \n

      If you use ADD to increment or decrement a number\n value for an item that doesn't exist before the update, DynamoDB\n uses 0 as the initial value.

      \n

      In addition, if you use ADD to update an existing\n item, and intend to increment or decrement an attribute value which\n does not yet exist, DynamoDB uses 0 as the initial\n value. For example, suppose that the item you want to update does\n not yet have an attribute named itemcount, but\n you decide to ADD the number 3 to this\n attribute anyway, even though it currently does not exist. DynamoDB\n will create the itemcount attribute, set its\n initial value to 0, and finally add 3 to\n it. The result will be a new itemcount\n attribute in the item, with a value of 3.

      \n
      \n
    • \n
    • \n

      If the existing data type is a set, and if the Value is\n also a set, then the Value is added to the existing set.\n (This is a set operation, not mathematical\n addition.) For example, if the attribute value was the set\n [1,2], and the ADD action specified\n [3], then the final attribute value would be\n [1,2,3]. An error occurs if an Add action is specified\n for a set attribute and the attribute type specified does not match the\n existing set type.

      \n

      Both sets must have the same primitive data type. For example, if the\n existing data type is a set of strings, the Value must also\n be a set of strings. The same holds true for number sets and binary\n sets.

      \n
    • \n
    \n

    This action is only valid for an existing attribute whose data type is number\n or is a set. Do not use ADD for any other data types.

    \n
  • \n
\n

\n If no item with the specified Key is\n found:\n

\n
    \n
  • \n

    \n PUT - DynamoDB creates a new item with the specified primary key,\n and then adds the attribute.

    \n
  • \n
  • \n

    \n DELETE - Nothing happens; there is no attribute to delete.

    \n
  • \n
  • \n

    \n ADD - DynamoDB creates a new item with the supplied primary key and\n number (or set) for the attribute value. The only data types allowed are number,\n number set, string set or binary set.

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

For the UpdateItem operation, represents the attributes to be modified,\n the action to perform on each, and the new value for each.

\n \n

You cannot use UpdateItem to update any primary key attributes.\n Instead, you will need to delete the item, and then use PutItem to\n create a new item with new attributes.

\n
\n

Attribute values cannot be null; string and binary type attributes must have lengths\n greater than zero; and set type attributes must not be empty. Requests with empty values\n will be rejected with a ValidationException exception.

" + "smithy.api#documentation": "

For the UpdateItem operation, represents the attributes to be modified,\n the action to perform on each, and the new value for each.

\n \n

You cannot use UpdateItem to update any primary key attributes.\n Instead, you will need to delete the item, and then use PutItem to\n create a new item with new attributes.

\n
\n

Attribute values cannot be null; string and binary type attributes must have lengths\n greater than zero; and set type attributes must not be empty. Requests with empty values\n will be rejected with a ValidationException exception.

" } }, "com.amazonaws.dynamodb#AutoScalingPolicyDescription": { @@ -406,7 +406,7 @@ } }, "TargetValue": { - "target": "com.amazonaws.dynamodb#Double", + "target": "com.amazonaws.dynamodb#DoubleObject", "traits": { "smithy.api#documentation": "

The target value for the metric. The range is 8.515920e-109 to 1.174271e+108 (Base 10)\n or 2e-360 to 2e360 (Base 2).

", "smithy.api#required": {} @@ -439,7 +439,7 @@ } }, "TargetValue": { - "target": "com.amazonaws.dynamodb#Double", + "target": "com.amazonaws.dynamodb#DoubleObject", "traits": { "smithy.api#documentation": "

The target value for the metric. The range is 8.515920e-109 to 1.174271e+108 (Base 10)\n or 2e-360 to 2e360 (Base 2).

", "smithy.api#required": {} @@ -524,7 +524,7 @@ "BackupType": { "target": "com.amazonaws.dynamodb#BackupType", "traits": { - "smithy.api#documentation": "

BackupType:

\n
    \n
  • \n

    \n USER - You create and manage these using the on-demand backup\n feature.

    \n
  • \n
  • \n

    \n SYSTEM - If you delete a table with point-in-time recovery enabled,\n a SYSTEM backup is automatically created and is retained for 35\n days (at no additional cost). System backups allow you to restore the deleted\n table to the state it was in just before the point of deletion.

    \n
  • \n
  • \n

    \n AWS_BACKUP - On-demand backup created by you from Backup service.

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

BackupType:

\n
    \n
  • \n

    \n USER - You create and manage these using the on-demand backup\n feature.

    \n
  • \n
  • \n

    \n SYSTEM - If you delete a table with point-in-time recovery enabled,\n a SYSTEM backup is automatically created and is retained for 35\n days (at no additional cost). System backups allow you to restore the deleted\n table to the state it was in just before the point of deletion.

    \n
  • \n
  • \n

    \n AWS_BACKUP - On-demand backup created by you from Backup service.

    \n
  • \n
", "smithy.api#required": {} } }, @@ -671,7 +671,7 @@ "BackupType": { "target": "com.amazonaws.dynamodb#BackupType", "traits": { - "smithy.api#documentation": "

BackupType:

\n
    \n
  • \n

    \n USER - You create and manage these using the on-demand backup\n feature.

    \n
  • \n
  • \n

    \n SYSTEM - If you delete a table with point-in-time recovery enabled,\n a SYSTEM backup is automatically created and is retained for 35\n days (at no additional cost). System backups allow you to restore the deleted\n table to the state it was in just before the point of deletion.

    \n
  • \n
  • \n

    \n AWS_BACKUP - On-demand backup created by you from Backup service.

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

BackupType:

\n
    \n
  • \n

    \n USER - You create and manage these using the on-demand backup\n feature.

    \n
  • \n
  • \n

    \n SYSTEM - If you delete a table with point-in-time recovery enabled,\n a SYSTEM backup is automatically created and is retained for 35\n days (at no additional cost). System backups allow you to restore the deleted\n table to the state it was in just before the point of deletion.

    \n
  • \n
  • \n

    \n AWS_BACKUP - On-demand backup created by you from Backup service.

    \n
  • \n
" } }, "BackupSizeBytes": { @@ -763,7 +763,7 @@ } ], "traits": { - "smithy.api#documentation": "

This operation allows you to perform batch reads or writes on data stored in DynamoDB,\n using PartiQL. Each read statement in a BatchExecuteStatement must specify\n an equality condition on all key attributes. This enforces that each SELECT\n statement in a batch returns at most a single item.

\n \n

The entire batch must consist of either read statements or write statements, you\n cannot mix both in one batch.

\n
\n \n

A HTTP 200 response does not mean that all statements in the BatchExecuteStatement\n succeeded. Error details for individual statements can be found under the Error field of the BatchStatementResponse for each\n statement.

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

This operation allows you to perform batch reads or writes on data stored in DynamoDB,\n using PartiQL. Each read statement in a BatchExecuteStatement must specify\n an equality condition on all key attributes. This enforces that each SELECT\n statement in a batch returns at most a single item.

\n \n

The entire batch must consist of either read statements or write statements, you\n cannot mix both in one batch.

\n
\n \n

A HTTP 200 response does not mean that all statements in the BatchExecuteStatement\n succeeded. Error details for individual statements can be found under the Error field of the BatchStatementResponse for each\n statement.

\n
" } }, "com.amazonaws.dynamodb#BatchExecuteStatementInput": { @@ -779,6 +779,9 @@ "ReturnConsumedCapacity": { "target": "com.amazonaws.dynamodb#ReturnConsumedCapacity" } + }, + "traits": { + "smithy.api#input": {} } }, "com.amazonaws.dynamodb#BatchExecuteStatementOutput": { @@ -796,6 +799,9 @@ "smithy.api#documentation": "

The capacity units consumed by the entire operation. The values of the list are\n ordered according to the ordering of the statements.

" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.dynamodb#BatchGetItem": { @@ -827,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, 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.

" } }, "com.amazonaws.dynamodb#BatchGetItemInput": { @@ -836,7 +842,7 @@ "RequestItems": { "target": "com.amazonaws.dynamodb#BatchGetRequestMap", "traits": { - "smithy.api#documentation": "

A map of one or more table names and, for each table, a map that describes one or more\n items to retrieve from that table. Each table name can be used only once per\n BatchGetItem request.

\n

Each element in the map of items to retrieve consists of the following:

\n
    \n
  • \n

    \n ConsistentRead - If true, a strongly consistent read\n is used; if false (the default), an eventually consistent read is\n used.

    \n
  • \n
  • \n

    \n ExpressionAttributeNames - One or more substitution tokens for\n attribute names in the ProjectionExpression parameter. The\n following are some use cases for using\n ExpressionAttributeNames:

    \n
      \n
    • \n

      To access an attribute whose name conflicts with a DynamoDB reserved\n word.

      \n
    • \n
    • \n

      To create a placeholder for repeating occurrences of an attribute name\n in an expression.

      \n
    • \n
    • \n

      To prevent special characters in an attribute name from being\n misinterpreted in an expression.

      \n
    • \n
    \n

    Use the # character in an expression to\n dereference an attribute name. For example, consider the following attribute\n name:

    \n
      \n
    • \n

      \n Percentile\n

      \n
    • \n
    \n

    The name of this attribute conflicts with a reserved word, so it cannot be\n used directly in an expression. (For the complete list of reserved words, see\n Reserved\n Words in the Amazon DynamoDB Developer Guide).\n To work around this, you could specify the following for\n ExpressionAttributeNames:

    \n
      \n
    • \n

      \n {\"#P\":\"Percentile\"}\n

      \n
    • \n
    \n

    You could then use this substitution in an expression, as in this\n example:

    \n
      \n
    • \n

      \n #P = :val\n

      \n
    • \n
    \n \n

    Tokens that begin with the : character\n are expression attribute values, which are placeholders\n for the actual value at runtime.

    \n
    \n

    For more information about expression attribute names, see Accessing Item Attributes in the Amazon DynamoDB\n Developer Guide.

    \n
  • \n
  • \n

    \n Keys - An array of primary key attribute values that define\n specific items in the table. For each primary key, you must provide\n all of the key attributes. For example, with a simple\n primary key, you only need to provide the partition key value. For a composite\n key, you must provide both the partition key value and the\n sort key value.

    \n
  • \n
  • \n

    \n ProjectionExpression - A string that identifies one or more\n attributes to retrieve from the table. These attributes can include scalars,\n sets, or elements of a JSON document. The attributes in the expression must be\n separated by commas.

    \n

    If no attribute names are specified, then all attributes are returned. If any\n of the requested attributes are not found, they do not appear in the\n result.

    \n

    For more information, see Accessing Item Attributes in the Amazon DynamoDB\n Developer Guide.

    \n
  • \n
  • \n

    \n AttributesToGet - This is a legacy parameter. Use\n ProjectionExpression instead. For more information, see AttributesToGet in the Amazon DynamoDB Developer\n Guide.

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

A map of one or more table names and, for each table, a map that describes one or more\n items to retrieve from that table. Each table name can be used only once per\n BatchGetItem request.

\n

Each element in the map of items to retrieve consists of the following:

\n
    \n
  • \n

    \n ConsistentRead - If true, a strongly consistent read\n is used; if false (the default), an eventually consistent read is\n used.

    \n
  • \n
  • \n

    \n ExpressionAttributeNames - One or more substitution tokens for\n attribute names in the ProjectionExpression parameter. The\n following are some use cases for using\n ExpressionAttributeNames:

    \n
      \n
    • \n

      To access an attribute whose name conflicts with a DynamoDB reserved\n word.

      \n
    • \n
    • \n

      To create a placeholder for repeating occurrences of an attribute name\n in an expression.

      \n
    • \n
    • \n

      To prevent special characters in an attribute name from being\n misinterpreted in an expression.

      \n
    • \n
    \n

    Use the # character in an expression to\n dereference an attribute name. For example, consider the following attribute\n name:

    \n
      \n
    • \n

      \n Percentile\n

      \n
    • \n
    \n

    The name of this attribute conflicts with a reserved word, so it cannot be\n used directly in an expression. (For the complete list of reserved words, see\n Reserved\n Words in the Amazon DynamoDB Developer Guide).\n To work around this, you could specify the following for\n ExpressionAttributeNames:

    \n
      \n
    • \n

      \n {\"#P\":\"Percentile\"}\n

      \n
    • \n
    \n

    You could then use this substitution in an expression, as in this\n example:

    \n
      \n
    • \n

      \n #P = :val\n

      \n
    • \n
    \n \n

    Tokens that begin with the : character\n are expression attribute values, which are placeholders\n for the actual value at runtime.

    \n
    \n

    For more information about expression attribute names, see Accessing Item Attributes in the Amazon DynamoDB\n Developer Guide.

    \n
  • \n
  • \n

    \n Keys - An array of primary key attribute values that define\n specific items in the table. For each primary key, you must provide\n all of the key attributes. For example, with a simple\n primary key, you only need to provide the partition key value. For a composite\n key, you must provide both the partition key value and the\n sort key value.

    \n
  • \n
  • \n

    \n ProjectionExpression - A string that identifies one or more\n attributes to retrieve from the table. These attributes can include scalars,\n sets, or elements of a JSON document. The attributes in the expression must be\n separated by commas.

    \n

    If no attribute names are specified, then all attributes are returned. If any\n of the requested attributes are not found, they do not appear in the\n result.

    \n

    For more information, see Accessing Item Attributes in the Amazon DynamoDB\n Developer Guide.

    \n
  • \n
  • \n

    \n AttributesToGet - This is a legacy parameter. Use\n ProjectionExpression instead. For more information, see AttributesToGet in the Amazon DynamoDB Developer\n Guide.

    \n
  • \n
", "smithy.api#required": {} } }, @@ -845,7 +851,8 @@ } }, "traits": { - "smithy.api#documentation": "

Represents the input of a BatchGetItem operation.

" + "smithy.api#documentation": "

Represents the input of a BatchGetItem operation.

", + "smithy.api#input": {} } }, "com.amazonaws.dynamodb#BatchGetItemOutput": { @@ -860,18 +867,19 @@ "UnprocessedKeys": { "target": "com.amazonaws.dynamodb#BatchGetRequestMap", "traits": { - "smithy.api#documentation": "

A map of tables and their respective keys that were not processed with the current\n response. The UnprocessedKeys value is in the same form as\n RequestItems, so the value can be provided directly to a subsequent\n BatchGetItem operation. For more information, see\n RequestItems in the Request Parameters section.

\n

Each element consists of:

\n
    \n
  • \n

    \n Keys - An array of primary key attribute values that define\n specific items in the table.

    \n
  • \n
  • \n

    \n ProjectionExpression - One or more attributes to be retrieved from\n the table or index. By default, all attributes are returned. If a requested\n attribute is not found, it does not appear in the result.

    \n
  • \n
  • \n

    \n ConsistentRead - The consistency of a read operation. If set to\n true, then a strongly consistent read is used; otherwise, an\n eventually consistent read is used.

    \n
  • \n
\n

If there are no unprocessed keys remaining, the response contains an empty\n UnprocessedKeys map.

" + "smithy.api#documentation": "

A map of tables and their respective keys that were not processed with the current\n response. The UnprocessedKeys value is in the same form as\n RequestItems, so the value can be provided directly to a subsequent\n BatchGetItem operation. For more information, see\n RequestItems in the Request Parameters section.

\n

Each element consists of:

\n
    \n
  • \n

    \n Keys - An array of primary key attribute values that define\n specific items in the table.

    \n
  • \n
  • \n

    \n ProjectionExpression - One or more attributes to be retrieved from\n the table or index. By default, all attributes are returned. If a requested\n attribute is not found, it does not appear in the result.

    \n
  • \n
  • \n

    \n ConsistentRead - The consistency of a read operation. If set to\n true, then a strongly consistent read is used; otherwise, an\n eventually consistent read is used.

    \n
  • \n
\n

If there are no unprocessed keys remaining, the response contains an empty\n UnprocessedKeys map.

" } }, "ConsumedCapacity": { "target": "com.amazonaws.dynamodb#ConsumedCapacityMultiple", "traits": { - "smithy.api#documentation": "

The read capacity units consumed by the entire BatchGetItem\n operation.

\n

Each element consists of:

\n
    \n
  • \n

    \n TableName - The table that consumed the provisioned\n throughput.

    \n
  • \n
  • \n

    \n CapacityUnits - The total number of capacity units consumed.

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

The read capacity units consumed by the entire BatchGetItem\n operation.

\n

Each element consists of:

\n
    \n
  • \n

    \n TableName - The table that consumed the provisioned\n throughput.

    \n
  • \n
  • \n

    \n CapacityUnits - The total number of capacity units consumed.

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

Represents the output of a BatchGetItem operation.

" + "smithy.api#documentation": "

Represents the output of a BatchGetItem operation.

", + "smithy.api#output": {} } }, "com.amazonaws.dynamodb#BatchGetRequestMap": { @@ -1074,7 +1082,7 @@ "aws.api#clientDiscoveredEndpoint": { "required": false }, - "smithy.api#documentation": "

The BatchWriteItem operation puts or deletes multiple items in one or\n more tables. A single call to BatchWriteItem can transmit up to 16MB of\n data over the network, consisting of up to 25 item put or delete operations. While\n individual items can be up to 400 KB once stored, it's important to note that an item's\n representation might be greater than 400KB while being sent in DynamoDB's JSON format\n for the API call. For more details on this distinction, see Naming Rules and Data Types.

\n \n

\n BatchWriteItem cannot update items. If you perform a BatchWriteItem\n operation on an existing item, that item's values will be overwritten by the\n operation and it will appear like it was updated. To update items, we recommend you\n use the UpdateItem action.

\n
\n

The individual PutItem and DeleteItem operations specified\n in BatchWriteItem are atomic; however BatchWriteItem as a\n whole is not. If any requested operations fail because the table's provisioned\n throughput is exceeded or an internal processing failure occurs, the failed operations\n are returned in the UnprocessedItems response parameter. You can\n investigate and optionally resend the requests. Typically, you would call\n BatchWriteItem in a loop. Each iteration would check for unprocessed\n items and submit a new BatchWriteItem request with those unprocessed items\n until all items have been processed.

\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 BatchWriteItem returns a\n ProvisionedThroughputExceededException.

\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\n

With BatchWriteItem, you can efficiently write or delete large amounts of\n data, such as from Amazon EMR, or copy data from another database into DynamoDB. In\n order to improve performance with these large-scale operations,\n BatchWriteItem does not behave in the same way as individual\n PutItem and DeleteItem calls would. For example, you\n cannot specify conditions on individual put and delete requests, and\n BatchWriteItem does not return deleted items in the response.

\n

If you use a programming language that supports concurrency, you can use threads to\n write items in parallel. Your application must include the necessary logic to manage the\n threads. With languages that don't support threading, you must update or delete the\n specified items one at a time. In both situations, BatchWriteItem performs\n the specified put and delete operations in parallel, giving you the power of the thread\n pool approach without having to introduce complexity into your application.

\n

Parallel processing reduces latency, but each specified put and delete request\n consumes the same number of write capacity units whether it is processed in parallel or\n not. Delete operations on nonexistent items consume one write capacity unit.

\n

If one or more of the following is true, DynamoDB rejects the entire batch write\n operation:

\n
    \n
  • \n

    One or more tables specified in the BatchWriteItem request does\n not exist.

    \n
  • \n
  • \n

    Primary key attributes specified on an item in the request do not match those\n in the corresponding table's primary key schema.

    \n
  • \n
  • \n

    You try to perform multiple operations on the same item in the same\n BatchWriteItem request. For example, you cannot put and delete\n the same item in the same BatchWriteItem request.

    \n
  • \n
  • \n

    Your request contains at least two items with identical hash and range keys\n (which essentially is two put operations).

    \n
  • \n
  • \n

    There are more than 25 requests in the batch.

    \n
  • \n
  • \n

    Any individual item in a batch exceeds 400 KB.

    \n
  • \n
  • \n

    The total request size exceeds 16 MB.

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

The BatchWriteItem operation puts or deletes multiple items in one or\n more tables. A single call to BatchWriteItem can transmit up to 16MB of\n data over the network, consisting of up to 25 item put or delete operations. While\n individual items can be up to 400 KB once stored, it's important to note that an item's\n representation might be greater than 400KB while being sent in DynamoDB's JSON format\n for the API call. For more details on this distinction, see Naming Rules and Data Types.

\n \n

\n BatchWriteItem cannot update items. If you perform a BatchWriteItem\n operation on an existing item, that item's values will be overwritten by the\n operation and it will appear like it was updated. To update items, we recommend you\n use the UpdateItem action.

\n
\n

The individual PutItem and DeleteItem operations specified\n in BatchWriteItem are atomic; however BatchWriteItem as a\n whole is not. If any requested operations fail because the table's provisioned\n throughput is exceeded or an internal processing failure occurs, the failed operations\n are returned in the UnprocessedItems response parameter. You can\n investigate and optionally resend the requests. Typically, you would call\n BatchWriteItem in a loop. Each iteration would check for unprocessed\n items and submit a new BatchWriteItem request with those unprocessed items\n until all items have been processed.

\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 BatchWriteItem returns a\n ProvisionedThroughputExceededException.

\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

With BatchWriteItem, you can efficiently write or delete large amounts of\n data, such as from Amazon EMR, or copy data from another database into DynamoDB. In\n order to improve performance with these large-scale operations,\n BatchWriteItem does not behave in the same way as individual\n PutItem and DeleteItem calls would. For example, you\n cannot specify conditions on individual put and delete requests, and\n BatchWriteItem does not return deleted items in the response.

\n

If you use a programming language that supports concurrency, you can use threads to\n write items in parallel. Your application must include the necessary logic to manage the\n threads. With languages that don't support threading, you must update or delete the\n specified items one at a time. In both situations, BatchWriteItem performs\n the specified put and delete operations in parallel, giving you the power of the thread\n pool approach without having to introduce complexity into your application.

\n

Parallel processing reduces latency, but each specified put and delete request\n consumes the same number of write capacity units whether it is processed in parallel or\n not. Delete operations on nonexistent items consume one write capacity unit.

\n

If one or more of the following is true, DynamoDB rejects the entire batch write\n operation:

\n
    \n
  • \n

    One or more tables specified in the BatchWriteItem request does\n not exist.

    \n
  • \n
  • \n

    Primary key attributes specified on an item in the request do not match those\n in the corresponding table's primary key schema.

    \n
  • \n
  • \n

    You try to perform multiple operations on the same item in the same\n BatchWriteItem request. For example, you cannot put and delete\n the same item in the same BatchWriteItem request.

    \n
  • \n
  • \n

    Your request contains at least two items with identical hash and range keys\n (which essentially is two put operations).

    \n
  • \n
  • \n

    There are more than 25 requests in the batch.

    \n
  • \n
  • \n

    Any individual item in a batch exceeds 400 KB.

    \n
  • \n
  • \n

    The total request size exceeds 16 MB.

    \n
  • \n
" } }, "com.amazonaws.dynamodb#BatchWriteItemInput": { @@ -1083,7 +1091,7 @@ "RequestItems": { "target": "com.amazonaws.dynamodb#BatchWriteItemRequestMap", "traits": { - "smithy.api#documentation": "

A map of one or more table names and, for each table, a list of operations to be\n performed (DeleteRequest or PutRequest). Each element in the\n map consists of the following:

\n
    \n
  • \n

    \n DeleteRequest - Perform a DeleteItem operation on the\n specified item. The item to be deleted is identified by a Key\n subelement:

    \n
      \n
    • \n

      \n Key - A map of primary key attribute values that uniquely\n identify the item. Each entry in this map consists of an attribute name\n and an attribute value. For each primary key, you must provide\n all of the key attributes. For example, with a\n simple primary key, you only need to provide a value for the partition\n key. For a composite primary key, you must provide values for\n both the partition key and the sort key.

      \n
    • \n
    \n
  • \n
  • \n

    \n PutRequest - Perform a PutItem operation on the\n specified item. The item to be put is identified by an Item\n subelement:

    \n
      \n
    • \n

      \n Item - A map of attributes and their values. Each entry in\n this map consists of an attribute name and an attribute value. Attribute\n values must not be null; string and binary type attributes must have\n lengths greater than zero; and set type attributes must not be empty.\n Requests that contain empty values are rejected with a\n ValidationException exception.

      \n

      If you specify any attributes that are part of an index key, then the\n data types for those attributes must match those of the schema in the\n table's attribute definition.

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

A map of one or more table names and, for each table, a list of operations to be\n performed (DeleteRequest or PutRequest). Each element in the\n map consists of the following:

\n
    \n
  • \n

    \n DeleteRequest - Perform a DeleteItem operation on the\n specified item. The item to be deleted is identified by a Key\n subelement:

    \n
      \n
    • \n

      \n Key - A map of primary key attribute values that uniquely\n identify the item. Each entry in this map consists of an attribute name\n and an attribute value. For each primary key, you must provide\n all of the key attributes. For example, with a\n simple primary key, you only need to provide a value for the partition\n key. For a composite primary key, you must provide values for\n both the partition key and the sort key.

      \n
    • \n
    \n
  • \n
  • \n

    \n PutRequest - Perform a PutItem operation on the\n specified item. The item to be put is identified by an Item\n subelement:

    \n
      \n
    • \n

      \n Item - A map of attributes and their values. Each entry in\n this map consists of an attribute name and an attribute value. Attribute\n values must not be null; string and binary type attributes must have\n lengths greater than zero; and set type attributes must not be empty.\n Requests that contain empty values are rejected with a\n ValidationException exception.

      \n

      If you specify any attributes that are part of an index key, then the\n data types for those attributes must match those of the schema in the\n table's attribute definition.

      \n
    • \n
    \n
  • \n
", "smithy.api#required": {} } }, @@ -1098,7 +1106,8 @@ } }, "traits": { - "smithy.api#documentation": "

Represents the input of a BatchWriteItem operation.

" + "smithy.api#documentation": "

Represents the input of a BatchWriteItem operation.

", + "smithy.api#input": {} } }, "com.amazonaws.dynamodb#BatchWriteItemOutput": { @@ -1107,24 +1116,25 @@ "UnprocessedItems": { "target": "com.amazonaws.dynamodb#BatchWriteItemRequestMap", "traits": { - "smithy.api#documentation": "

A map of tables and requests against those tables that were not processed. The\n UnprocessedItems value is in the same form as\n RequestItems, so you can provide this value directly to a subsequent\n BatchWriteItem operation. For more information, see\n RequestItems in the Request Parameters section.

\n

Each UnprocessedItems entry consists of a table name and, for that table,\n a list of operations to perform (DeleteRequest or\n PutRequest).

\n
    \n
  • \n

    \n DeleteRequest - Perform a DeleteItem operation on the\n specified item. The item to be deleted is identified by a Key\n subelement:

    \n
      \n
    • \n

      \n Key - A map of primary key attribute values that uniquely\n identify the item. Each entry in this map consists of an attribute name\n and an attribute value.

      \n
    • \n
    \n
  • \n
  • \n

    \n PutRequest - Perform a PutItem operation on the\n specified item. The item to be put is identified by an Item\n subelement:

    \n
      \n
    • \n

      \n Item - A map of attributes and their values. Each entry in\n this map consists of an attribute name and an attribute value. Attribute\n values must not be null; string and binary type attributes must have\n lengths greater than zero; and set type attributes must not be empty.\n Requests that contain empty values will be rejected with a\n ValidationException exception.

      \n

      If you specify any attributes that are part of an index key, then the\n data types for those attributes must match those of the schema in the\n table's attribute definition.

      \n
    • \n
    \n
  • \n
\n

If there are no unprocessed items remaining, the response contains an empty\n UnprocessedItems map.

" + "smithy.api#documentation": "

A map of tables and requests against those tables that were not processed. The\n UnprocessedItems value is in the same form as\n RequestItems, so you can provide this value directly to a subsequent\n BatchWriteItem operation. For more information, see\n RequestItems in the Request Parameters section.

\n

Each UnprocessedItems entry consists of a table name and, for that table,\n a list of operations to perform (DeleteRequest or\n PutRequest).

\n
    \n
  • \n

    \n DeleteRequest - Perform a DeleteItem operation on the\n specified item. The item to be deleted is identified by a Key\n subelement:

    \n
      \n
    • \n

      \n Key - A map of primary key attribute values that uniquely\n identify the item. Each entry in this map consists of an attribute name\n and an attribute value.

      \n
    • \n
    \n
  • \n
  • \n

    \n PutRequest - Perform a PutItem operation on the\n specified item. The item to be put is identified by an Item\n subelement:

    \n
      \n
    • \n

      \n Item - A map of attributes and their values. Each entry in\n this map consists of an attribute name and an attribute value. Attribute\n values must not be null; string and binary type attributes must have\n lengths greater than zero; and set type attributes must not be empty.\n Requests that contain empty values will be rejected with a\n ValidationException exception.

      \n

      If you specify any attributes that are part of an index key, then the\n data types for those attributes must match those of the schema in the\n table's attribute definition.

      \n
    • \n
    \n
  • \n
\n

If there are no unprocessed items remaining, the response contains an empty\n UnprocessedItems map.

" } }, "ItemCollectionMetrics": { "target": "com.amazonaws.dynamodb#ItemCollectionMetricsPerTable", "traits": { - "smithy.api#documentation": "

A list of tables that were processed by BatchWriteItem and, for each\n table, information about any item collections that were affected by individual\n DeleteItem or PutItem operations.

\n

Each entry consists of the following subelements:

\n
    \n
  • \n

    \n ItemCollectionKey - The partition key value of the item collection.\n This is the same as the partition key value of the item.

    \n
  • \n
  • \n

    \n SizeEstimateRangeGB - An estimate of item collection size,\n expressed in GB. This is a two-element array containing a lower bound and an\n upper bound for the estimate. The estimate includes the size of all the items in\n the table, plus the size of all attributes projected into all of the local\n secondary indexes on the table. Use this estimate to measure whether a local\n secondary index is approaching its size limit.

    \n

    The estimate is subject to change over time; therefore, do not rely on the\n precision or accuracy of the estimate.

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

A list of tables that were processed by BatchWriteItem and, for each\n table, information about any item collections that were affected by individual\n DeleteItem or PutItem operations.

\n

Each entry consists of the following subelements:

\n
    \n
  • \n

    \n ItemCollectionKey - The partition key value of the item collection.\n This is the same as the partition key value of the item.

    \n
  • \n
  • \n

    \n SizeEstimateRangeGB - An estimate of item collection size,\n expressed in GB. This is a two-element array containing a lower bound and an\n upper bound for the estimate. The estimate includes the size of all the items in\n the table, plus the size of all attributes projected into all of the local\n secondary indexes on the table. Use this estimate to measure whether a local\n secondary index is approaching its size limit.

    \n

    The estimate is subject to change over time; therefore, do not rely on the\n precision or accuracy of the estimate.

    \n
  • \n
" } }, "ConsumedCapacity": { "target": "com.amazonaws.dynamodb#ConsumedCapacityMultiple", "traits": { - "smithy.api#documentation": "

The capacity units consumed by the entire BatchWriteItem\n operation.

\n

Each element consists of:

\n
    \n
  • \n

    \n TableName - The table that consumed the provisioned\n throughput.

    \n
  • \n
  • \n

    \n CapacityUnits - The total number of capacity units consumed.

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

The capacity units consumed by the entire BatchWriteItem\n operation.

\n

Each element consists of:

\n
    \n
  • \n

    \n TableName - The table that consumed the provisioned\n throughput.

    \n
  • \n
  • \n

    \n CapacityUnits - The total number of capacity units consumed.

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

Represents the output of a BatchWriteItem operation.

" + "smithy.api#documentation": "

Represents the output of a BatchWriteItem operation.

", + "smithy.api#output": {} } }, "com.amazonaws.dynamodb#BatchWriteItemRequestMap": { @@ -1173,7 +1183,7 @@ "BillingMode": { "target": "com.amazonaws.dynamodb#BillingMode", "traits": { - "smithy.api#documentation": "

Controls how you are charged for read and write throughput and how you manage\n capacity. This setting can be changed later.

\n
    \n
  • \n

    \n PROVISIONED - Sets the read/write capacity mode to\n PROVISIONED. We recommend using PROVISIONED for\n predictable workloads.

    \n
  • \n
  • \n

    \n PAY_PER_REQUEST - Sets the read/write capacity mode to\n PAY_PER_REQUEST. We recommend using\n PAY_PER_REQUEST for unpredictable workloads.

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

Controls how you are charged for read and write throughput and how you manage\n capacity. This setting can be changed later.

\n
    \n
  • \n

    \n PROVISIONED - Sets the read/write capacity mode to\n PROVISIONED. We recommend using PROVISIONED for\n predictable workloads.

    \n
  • \n
  • \n

    \n PAY_PER_REQUEST - Sets the read/write capacity mode to\n PAY_PER_REQUEST. We recommend using\n PAY_PER_REQUEST for unpredictable workloads.

    \n
  • \n
" } }, "LastUpdateToPayPerRequestDateTime": { @@ -1184,7 +1194,7 @@ } }, "traits": { - "smithy.api#documentation": "

Contains the details for the read/write capacity mode. This page talks about\n PROVISIONED and PAY_PER_REQUEST billing modes. For more\n information about these modes, see Read/write capacity mode.

\n \n

You may need to switch to on-demand mode at least once in order to return a\n BillingModeSummary response.

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

Contains the details for the read/write capacity mode. This page talks about\n PROVISIONED and PAY_PER_REQUEST billing modes. For more\n information about these modes, see Read/write capacity mode.

\n \n

You may need to switch to on-demand mode at least once in order to return a\n BillingModeSummary response.

\n
" } }, "com.amazonaws.dynamodb#BinaryAttributeValue": { @@ -1382,19 +1392,19 @@ "AttributeValueList": { "target": "com.amazonaws.dynamodb#AttributeValueList", "traits": { - "smithy.api#documentation": "

One or more values to evaluate against the supplied attribute. The number of values in\n the list depends on the ComparisonOperator being used.

\n

For type Number, value comparisons are numeric.

\n

String value comparisons for greater than, equals, or less than are based on ASCII\n character code values. For example, a is greater than A, and\n a is greater than B. For a list of code values, see http://en.wikipedia.org/wiki/ASCII#ASCII_printable_characters.

\n

For Binary, DynamoDB treats each byte of the binary data as unsigned when it\n compares binary values.

" + "smithy.api#documentation": "

One or more values to evaluate against the supplied attribute. The number of values in\n the list depends on the ComparisonOperator being used.

\n

For type Number, value comparisons are numeric.

\n

String value comparisons for greater than, equals, or less than are based on ASCII\n character code values. For example, a is greater than A, and\n a is greater than B. For a list of code values, see http://en.wikipedia.org/wiki/ASCII#ASCII_printable_characters.

\n

For Binary, DynamoDB treats each byte of the binary data as unsigned when it\n compares binary values.

" } }, "ComparisonOperator": { "target": "com.amazonaws.dynamodb#ComparisonOperator", "traits": { - "smithy.api#documentation": "

A comparator for evaluating attributes. For example, equals, greater than, less than,\n etc.

\n

The following comparison operators are available:

\n

\n EQ | NE | LE | LT | GE | GT | NOT_NULL | NULL | CONTAINS | NOT_CONTAINS |\n BEGINS_WITH | IN | BETWEEN\n

\n

The following are descriptions of each comparison operator.

\n
    \n
  • \n

    \n EQ : Equal. EQ is supported for all data types,\n including lists and maps.

    \n

    \n AttributeValueList can contain only one AttributeValue\n element of type String, Number, Binary, String Set, Number Set, or Binary Set.\n If an item contains an AttributeValue element of a different type\n than the one provided in the request, the value does not match. For example,\n {\"S\":\"6\"} does not equal {\"N\":\"6\"}. Also,\n {\"N\":\"6\"} does not equal {\"NS\":[\"6\", \"2\",\n \"1\"]}.

    \n

    \n
  • \n
  • \n

    \n NE : Not equal. NE is supported for all data types,\n including lists and maps.

    \n

    \n AttributeValueList can contain only one AttributeValue\n of type String, Number, Binary, String Set, Number Set, or Binary Set. If an\n item contains an AttributeValue of a different type than the one\n provided in the request, the value does not match. For example,\n {\"S\":\"6\"} does not equal {\"N\":\"6\"}. Also,\n {\"N\":\"6\"} does not equal {\"NS\":[\"6\", \"2\",\n \"1\"]}.

    \n

    \n
  • \n
  • \n

    \n LE : Less than or equal.

    \n

    \n AttributeValueList can contain only one AttributeValue\n element of type String, Number, or Binary (not a set type). If an item contains\n an AttributeValue element of a different type than the one provided\n in the request, the value does not match. For example, {\"S\":\"6\"}\n does not equal {\"N\":\"6\"}. Also, {\"N\":\"6\"} does not\n compare to {\"NS\":[\"6\", \"2\", \"1\"]}.

    \n

    \n
  • \n
  • \n

    \n LT : Less than.

    \n

    \n AttributeValueList can contain only one AttributeValue\n of type String, Number, or Binary (not a set type). If an item contains an\n AttributeValue element of a different type than the one\n provided in the request, the value does not match. For example,\n {\"S\":\"6\"} does not equal {\"N\":\"6\"}. Also,\n {\"N\":\"6\"} does not compare to {\"NS\":[\"6\", \"2\",\n \"1\"]}.

    \n

    \n
  • \n
  • \n

    \n GE : Greater than or equal.

    \n

    \n AttributeValueList can contain only one AttributeValue\n element of type String, Number, or Binary (not a set type). If an item contains\n an AttributeValue element of a different type than the one provided\n in the request, the value does not match. For example, {\"S\":\"6\"}\n does not equal {\"N\":\"6\"}. Also, {\"N\":\"6\"} does not\n compare to {\"NS\":[\"6\", \"2\", \"1\"]}.

    \n

    \n
  • \n
  • \n

    \n GT : Greater than.

    \n

    \n AttributeValueList can contain only one AttributeValue\n element of type String, Number, or Binary (not a set type). If an item contains\n an AttributeValue element of a different type than the one provided\n in the request, the value does not match. For example, {\"S\":\"6\"}\n does not equal {\"N\":\"6\"}. Also, {\"N\":\"6\"} does not\n compare to {\"NS\":[\"6\", \"2\", \"1\"]}.

    \n

    \n
  • \n
  • \n

    \n NOT_NULL : The attribute exists. NOT_NULL is supported\n for all data types, including lists and maps.

    \n \n

    This operator tests for the existence of an attribute, not its data type.\n If the data type of attribute \"a\" is null, and you evaluate it\n using NOT_NULL, the result is a Boolean true. This\n result is because the attribute \"a\" exists; its data type is\n not relevant to the NOT_NULL comparison operator.

    \n
    \n
  • \n
  • \n

    \n NULL : The attribute does not exist. NULL is supported\n for all data types, including lists and maps.

    \n \n

    This operator tests for the nonexistence of an attribute, not its data\n type. If the data type of attribute \"a\" is null, and you\n evaluate it using NULL, the result is a Boolean\n false. This is because the attribute \"a\"\n exists; its data type is not relevant to the NULL comparison\n operator.

    \n
    \n
  • \n
  • \n

    \n CONTAINS : Checks for a subsequence, or value in a set.

    \n

    \n AttributeValueList can contain only one AttributeValue\n element of type String, Number, or Binary (not a set type). If the target\n attribute of the comparison is of type String, then the operator checks for a\n substring match. If the target attribute of the comparison is of type Binary,\n then the operator looks for a subsequence of the target that matches the input.\n If the target attribute of the comparison is a set (\"SS\",\n \"NS\", or \"BS\"), then the operator evaluates to\n true if it finds an exact match with any member of the set.

    \n

    CONTAINS is supported for lists: When evaluating \"a CONTAINS b\",\n \"a\" can be a list; however, \"b\" cannot be a set, a\n map, or a list.

    \n
  • \n
  • \n

    \n NOT_CONTAINS : Checks for absence of a subsequence, or absence of a\n value in a set.

    \n

    \n AttributeValueList can contain only one AttributeValue\n element of type String, Number, or Binary (not a set type). If the target\n attribute of the comparison is a String, then the operator checks for the\n absence of a substring match. If the target attribute of the comparison is\n Binary, then the operator checks for the absence of a subsequence of the target\n that matches the input. If the target attribute of the comparison is a set\n (\"SS\", \"NS\", or \"BS\"), then the\n operator evaluates to true if it does not find an exact\n match with any member of the set.

    \n

    NOT_CONTAINS is supported for lists: When evaluating \"a NOT CONTAINS\n b\", \"a\" can be a list; however, \"b\" cannot\n be a set, a map, or a list.

    \n
  • \n
  • \n

    \n BEGINS_WITH : Checks for a prefix.

    \n

    \n AttributeValueList can contain only one AttributeValue\n of type String or Binary (not a Number or a set type). The target attribute of\n the comparison must be of type String or Binary (not a Number or a set\n type).

    \n

    \n
  • \n
  • \n

    \n IN : Checks for matching elements in a list.

    \n

    \n AttributeValueList can contain one or more\n AttributeValue elements of type String, Number, or Binary.\n These attributes are compared against an existing attribute of an item. If any\n elements of the input are equal to the item attribute, the expression evaluates\n to true.

    \n
  • \n
  • \n

    \n BETWEEN : Greater than or equal to the first value, and less than\n or equal to the second value.

    \n

    \n AttributeValueList must contain two AttributeValue\n elements of the same type, either String, Number, or Binary (not a set type). A\n target attribute matches if the target value is greater than, or equal to, the\n first element and less than, or equal to, the second element. If an item\n contains an AttributeValue element of a different type than the one\n provided in the request, the value does not match. For example,\n {\"S\":\"6\"} does not compare to {\"N\":\"6\"}. Also,\n {\"N\":\"6\"} does not compare to {\"NS\":[\"6\", \"2\",\n \"1\"]}\n

    \n
  • \n
\n

For usage examples of AttributeValueList and\n ComparisonOperator, see Legacy\n Conditional Parameters in the Amazon DynamoDB Developer\n Guide.

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

A comparator for evaluating attributes. For example, equals, greater than, less than,\n etc.

\n

The following comparison operators are available:

\n

\n EQ | NE | LE | LT | GE | GT | NOT_NULL | NULL | CONTAINS | NOT_CONTAINS |\n BEGINS_WITH | IN | BETWEEN\n

\n

The following are descriptions of each comparison operator.

\n
    \n
  • \n

    \n EQ : Equal. EQ is supported for all data types,\n including lists and maps.

    \n

    \n AttributeValueList can contain only one AttributeValue\n element of type String, Number, Binary, String Set, Number Set, or Binary Set.\n If an item contains an AttributeValue element of a different type\n than the one provided in the request, the value does not match. For example,\n {\"S\":\"6\"} does not equal {\"N\":\"6\"}. Also,\n {\"N\":\"6\"} does not equal {\"NS\":[\"6\", \"2\",\n \"1\"]}.

    \n

    \n
  • \n
  • \n

    \n NE : Not equal. NE is supported for all data types,\n including lists and maps.

    \n

    \n AttributeValueList can contain only one AttributeValue\n of type String, Number, Binary, String Set, Number Set, or Binary Set. If an\n item contains an AttributeValue of a different type than the one\n provided in the request, the value does not match. For example,\n {\"S\":\"6\"} does not equal {\"N\":\"6\"}. Also,\n {\"N\":\"6\"} does not equal {\"NS\":[\"6\", \"2\",\n \"1\"]}.

    \n

    \n
  • \n
  • \n

    \n LE : Less than or equal.

    \n

    \n AttributeValueList can contain only one AttributeValue\n element of type String, Number, or Binary (not a set type). If an item contains\n an AttributeValue element of a different type than the one provided\n in the request, the value does not match. For example, {\"S\":\"6\"}\n does not equal {\"N\":\"6\"}. Also, {\"N\":\"6\"} does not\n compare to {\"NS\":[\"6\", \"2\", \"1\"]}.

    \n

    \n
  • \n
  • \n

    \n LT : Less than.

    \n

    \n AttributeValueList can contain only one AttributeValue\n of type String, Number, or Binary (not a set type). If an item contains an\n AttributeValue element of a different type than the one\n provided in the request, the value does not match. For example,\n {\"S\":\"6\"} does not equal {\"N\":\"6\"}. Also,\n {\"N\":\"6\"} does not compare to {\"NS\":[\"6\", \"2\",\n \"1\"]}.

    \n

    \n
  • \n
  • \n

    \n GE : Greater than or equal.

    \n

    \n AttributeValueList can contain only one AttributeValue\n element of type String, Number, or Binary (not a set type). If an item contains\n an AttributeValue element of a different type than the one provided\n in the request, the value does not match. For example, {\"S\":\"6\"}\n does not equal {\"N\":\"6\"}. Also, {\"N\":\"6\"} does not\n compare to {\"NS\":[\"6\", \"2\", \"1\"]}.

    \n

    \n
  • \n
  • \n

    \n GT : Greater than.

    \n

    \n AttributeValueList can contain only one AttributeValue\n element of type String, Number, or Binary (not a set type). If an item contains\n an AttributeValue element of a different type than the one provided\n in the request, the value does not match. For example, {\"S\":\"6\"}\n does not equal {\"N\":\"6\"}. Also, {\"N\":\"6\"} does not\n compare to {\"NS\":[\"6\", \"2\", \"1\"]}.

    \n

    \n
  • \n
  • \n

    \n NOT_NULL : The attribute exists. NOT_NULL is supported\n for all data types, including lists and maps.

    \n \n

    This operator tests for the existence of an attribute, not its data type.\n If the data type of attribute \"a\" is null, and you evaluate it\n using NOT_NULL, the result is a Boolean true. This\n result is because the attribute \"a\" exists; its data type is\n not relevant to the NOT_NULL comparison operator.

    \n
    \n
  • \n
  • \n

    \n NULL : The attribute does not exist. NULL is supported\n for all data types, including lists and maps.

    \n \n

    This operator tests for the nonexistence of an attribute, not its data\n type. If the data type of attribute \"a\" is null, and you\n evaluate it using NULL, the result is a Boolean\n false. This is because the attribute \"a\"\n exists; its data type is not relevant to the NULL comparison\n operator.

    \n
    \n
  • \n
  • \n

    \n CONTAINS : Checks for a subsequence, or value in a set.

    \n

    \n AttributeValueList can contain only one AttributeValue\n element of type String, Number, or Binary (not a set type). If the target\n attribute of the comparison is of type String, then the operator checks for a\n substring match. If the target attribute of the comparison is of type Binary,\n then the operator looks for a subsequence of the target that matches the input.\n If the target attribute of the comparison is a set (\"SS\",\n \"NS\", or \"BS\"), then the operator evaluates to\n true if it finds an exact match with any member of the set.

    \n

    CONTAINS is supported for lists: When evaluating \"a CONTAINS b\",\n \"a\" can be a list; however, \"b\" cannot be a set, a\n map, or a list.

    \n
  • \n
  • \n

    \n NOT_CONTAINS : Checks for absence of a subsequence, or absence of a\n value in a set.

    \n

    \n AttributeValueList can contain only one AttributeValue\n element of type String, Number, or Binary (not a set type). If the target\n attribute of the comparison is a String, then the operator checks for the\n absence of a substring match. If the target attribute of the comparison is\n Binary, then the operator checks for the absence of a subsequence of the target\n that matches the input. If the target attribute of the comparison is a set\n (\"SS\", \"NS\", or \"BS\"), then the\n operator evaluates to true if it does not find an exact\n match with any member of the set.

    \n

    NOT_CONTAINS is supported for lists: When evaluating \"a NOT CONTAINS\n b\", \"a\" can be a list; however, \"b\" cannot\n be a set, a map, or a list.

    \n
  • \n
  • \n

    \n BEGINS_WITH : Checks for a prefix.

    \n

    \n AttributeValueList can contain only one AttributeValue\n of type String or Binary (not a Number or a set type). The target attribute of\n the comparison must be of type String or Binary (not a Number or a set\n type).

    \n

    \n
  • \n
  • \n

    \n IN : Checks for matching elements in a list.

    \n

    \n AttributeValueList can contain one or more\n AttributeValue elements of type String, Number, or Binary.\n These attributes are compared against an existing attribute of an item. If any\n elements of the input are equal to the item attribute, the expression evaluates\n to true.

    \n
  • \n
  • \n

    \n BETWEEN : Greater than or equal to the first value, and less than\n or equal to the second value.

    \n

    \n AttributeValueList must contain two AttributeValue\n elements of the same type, either String, Number, or Binary (not a set type). A\n target attribute matches if the target value is greater than, or equal to, the\n first element and less than, or equal to, the second element. If an item\n contains an AttributeValue element of a different type than the one\n provided in the request, the value does not match. For example,\n {\"S\":\"6\"} does not compare to {\"N\":\"6\"}. Also,\n {\"N\":\"6\"} does not compare to {\"NS\":[\"6\", \"2\",\n \"1\"]}\n

    \n
  • \n
\n

For usage examples of AttributeValueList and\n ComparisonOperator, see Legacy\n Conditional Parameters in the Amazon DynamoDB Developer\n Guide.

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

Represents the selection criteria for a Query or Scan\n operation:

\n
    \n
  • \n

    For a Query operation, Condition is used for\n specifying the KeyConditions to use when querying a table or an\n index. For KeyConditions, only the following comparison operators\n are supported:

    \n

    \n EQ | LE | LT | GE | GT | BEGINS_WITH | BETWEEN\n

    \n

    \n Condition is also used in a QueryFilter, which\n evaluates the query results and returns only the desired values.

    \n
  • \n
  • \n

    For a Scan operation, Condition is used in a\n ScanFilter, which evaluates the scan results and returns only\n the desired values.

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

Represents the selection criteria for a Query or Scan\n operation:

\n
    \n
  • \n

    For a Query operation, Condition is used for\n specifying the KeyConditions to use when querying a table or an\n index. For KeyConditions, only the following comparison operators\n are supported:

    \n

    \n EQ | LE | LT | GE | GT | BEGINS_WITH | BETWEEN\n

    \n

    \n Condition is also used in a QueryFilter, which\n evaluates the query results and returns only the desired values.

    \n
  • \n
  • \n

    For a Scan operation, Condition is used in a\n ScanFilter, which evaluates the scan results and returns only\n the desired values.

    \n
  • \n
" } }, "com.amazonaws.dynamodb#ConditionCheck": { @@ -1417,20 +1427,20 @@ "ConditionExpression": { "target": "com.amazonaws.dynamodb#ConditionExpression", "traits": { - "smithy.api#documentation": "

A condition that must be satisfied in order for a conditional update to\n succeed.

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

A condition that must be satisfied in order for a conditional update to\n succeed. For more information, see Condition expressions in the Amazon DynamoDB Developer\n Guide.

", "smithy.api#required": {} } }, "ExpressionAttributeNames": { "target": "com.amazonaws.dynamodb#ExpressionAttributeNameMap", "traits": { - "smithy.api#documentation": "

One or more substitution tokens for attribute names in an expression.

" + "smithy.api#documentation": "

One or more substitution tokens for attribute names in an expression. For more information, see\n Expression attribute names \n in the Amazon DynamoDB Developer Guide.

" } }, "ExpressionAttributeValues": { "target": "com.amazonaws.dynamodb#ExpressionAttributeValueMap", "traits": { - "smithy.api#documentation": "

One or more values that can be substituted in an expression.

" + "smithy.api#documentation": "

One or more values that can be substituted in an expression. For more information, see Condition expressions in the Amazon DynamoDB Developer Guide.

" } }, "ReturnValuesOnConditionCheckFailure": { @@ -1722,7 +1732,7 @@ "aws.api#clientDiscoveredEndpoint": { "required": false }, - "smithy.api#documentation": "

Creates a backup for an existing table.

\n

Each time you create an on-demand backup, the entire table data is backed up. There\n is no limit to the number of on-demand backups that can be taken.

\n

When you create an on-demand backup, a time marker of the request is cataloged, and\n the backup is created asynchronously, by applying all changes until the time of the\n request to the last full table snapshot. Backup requests are processed instantaneously\n and become available for restore within minutes.

\n

You can call CreateBackup at a maximum rate of 50 times per\n second.

\n

All backups in DynamoDB work without consuming any provisioned throughput on the\n table.

\n

If you submit a backup request on 2018-12-14 at 14:25:00, the backup is guaranteed to\n contain all data committed to the table up to 14:24:00, and data committed after\n 14:26:00 will not be. The backup might contain data modifications made between 14:24:00\n and 14:26:00. On-demand backup does not support causal consistency.

\n

Along with data, the following are also included on the backups:

\n
    \n
  • \n

    Global secondary indexes (GSIs)

    \n
  • \n
  • \n

    Local secondary indexes (LSIs)

    \n
  • \n
  • \n

    Streams

    \n
  • \n
  • \n

    Provisioned read and write capacity

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

Creates a backup for an existing table.

\n

Each time you create an on-demand backup, the entire table data is backed up. There\n is no limit to the number of on-demand backups that can be taken.

\n

When you create an on-demand backup, a time marker of the request is cataloged, and\n the backup is created asynchronously, by applying all changes until the time of the\n request to the last full table snapshot. Backup requests are processed instantaneously\n and become available for restore within minutes.

\n

You can call CreateBackup at a maximum rate of 50 times per\n second.

\n

All backups in DynamoDB work without consuming any provisioned throughput on the\n table.

\n

If you submit a backup request on 2018-12-14 at 14:25:00, the backup is guaranteed to\n contain all data committed to the table up to 14:24:00, and data committed after\n 14:26:00 will not be. The backup might contain data modifications made between 14:24:00\n and 14:26:00. On-demand backup does not support causal consistency.

\n

Along with data, the following are also included on the backups:

\n
    \n
  • \n

    Global secondary indexes (GSIs)

    \n
  • \n
  • \n

    Local secondary indexes (LSIs)

    \n
  • \n
  • \n

    Streams

    \n
  • \n
  • \n

    Provisioned read and write capacity

    \n
  • \n
" } }, "com.amazonaws.dynamodb#CreateBackupInput": { @@ -1742,6 +1752,9 @@ "smithy.api#required": {} } } + }, + "traits": { + "smithy.api#input": {} } }, "com.amazonaws.dynamodb#CreateBackupOutput": { @@ -1753,6 +1766,9 @@ "smithy.api#documentation": "

Contains the details of the backup created for the table.

" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.dynamodb#CreateGlobalSecondaryIndexAction": { @@ -1782,7 +1798,7 @@ "ProvisionedThroughput": { "target": "com.amazonaws.dynamodb#ProvisionedThroughput", "traits": { - "smithy.api#documentation": "

Represents the provisioned throughput settings for the specified global secondary\n index.

\n

For current minimum and maximum provisioned throughput values, see Service,\n Account, and Table Quotas in the Amazon DynamoDB Developer\n Guide.

" + "smithy.api#documentation": "

Represents the provisioned throughput settings for the specified global secondary\n index.

\n

For current minimum and maximum provisioned throughput values, see Service,\n Account, and Table Quotas in the Amazon DynamoDB Developer\n Guide.

" } } }, @@ -1819,7 +1835,7 @@ "aws.api#clientDiscoveredEndpoint": { "required": false }, - "smithy.api#documentation": "

Creates a global table from an existing table. A global table creates a replication\n relationship between two or more DynamoDB tables with the same table name in the\n provided Regions.

\n \n

This operation only applies to Version\n 2017.11.29 of global tables.

\n
\n\n

If you want to add a new replica table to a global table, each of the following\n conditions must be true:

\n
    \n
  • \n

    The table must have the same primary key as all of the other replicas.

    \n
  • \n
  • \n

    The table must have the same name as all of the other replicas.

    \n
  • \n
  • \n

    The table must have DynamoDB Streams enabled, with the stream containing both\n the new and the old images of the item.

    \n
  • \n
  • \n

    None of the replica tables in the global table can contain any data.

    \n
  • \n
\n

If global secondary indexes are specified, then the following conditions must also be\n met:

\n
    \n
  • \n

    The global secondary indexes must have the same name.

    \n
  • \n
  • \n

    The global secondary indexes must have the same hash key and sort key (if\n present).

    \n
  • \n
\n

If local secondary indexes are specified, then the following conditions must also be\n met:

\n
    \n
  • \n

    The local secondary indexes must have the same name.

    \n
  • \n
  • \n

    The local secondary indexes must have the same hash key and sort key (if\n present).

    \n
  • \n
\n\n \n

Write capacity settings should be set consistently across your replica tables and\n secondary indexes. DynamoDB strongly recommends enabling auto scaling to manage the\n write capacity settings for all of your global tables replicas and indexes.

\n

If you prefer to manage write capacity settings manually, you should provision\n equal replicated write capacity units to your replica tables. You should also\n provision equal replicated write capacity units to matching secondary indexes across\n your global table.

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

Creates a global table from an existing table. A global table creates a replication\n relationship between two or more DynamoDB tables with the same table name in the\n provided Regions.

\n \n

This operation only applies to Version\n 2017.11.29 (Legacy) of global tables. We recommend using\n Version 2019.11.21 (Current)\n when creating new global tables, as it provides greater flexibility, higher efficiency and consumes less write capacity than \n 2017.11.29 (Legacy). To determine which version you are using, see \n Determining the version. \n To update existing global tables from version 2017.11.29 (Legacy) to version\n 2019.11.21 (Current), see \n Updating global tables.\n

\n
\n

If you want to add a new replica table to a global table, each of the following\n conditions must be true:

\n
    \n
  • \n

    The table must have the same primary key as all of the other replicas.

    \n
  • \n
  • \n

    The table must have the same name as all of the other replicas.

    \n
  • \n
  • \n

    The table must have DynamoDB Streams enabled, with the stream containing both\n the new and the old images of the item.

    \n
  • \n
  • \n

    None of the replica tables in the global table can contain any data.

    \n
  • \n
\n

If global secondary indexes are specified, then the following conditions must also be\n met:

\n
    \n
  • \n

    The global secondary indexes must have the same name.

    \n
  • \n
  • \n

    The global secondary indexes must have the same hash key and sort key (if\n present).

    \n
  • \n
\n

If local secondary indexes are specified, then the following conditions must also be\n met:

\n
    \n
  • \n

    The local secondary indexes must have the same name.

    \n
  • \n
  • \n

    The local secondary indexes must have the same hash key and sort key (if\n present).

    \n
  • \n
\n \n

Write capacity settings should be set consistently across your replica tables and\n secondary indexes. DynamoDB strongly recommends enabling auto scaling to manage the\n write capacity settings for all of your global tables replicas and indexes.

\n

If you prefer to manage write capacity settings manually, you should provision\n equal replicated write capacity units to your replica tables. You should also\n provision equal replicated write capacity units to matching secondary indexes across\n your global table.

\n
" } }, "com.amazonaws.dynamodb#CreateGlobalTableInput": { @@ -1839,6 +1855,9 @@ "smithy.api#required": {} } } + }, + "traits": { + "smithy.api#input": {} } }, "com.amazonaws.dynamodb#CreateGlobalTableOutput": { @@ -1850,6 +1869,9 @@ "smithy.api#documentation": "

Contains the details of the global table.

" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.dynamodb#CreateReplicaAction": { @@ -1932,7 +1954,7 @@ "aws.api#clientDiscoveredEndpoint": { "required": false }, - "smithy.api#documentation": "

The CreateTable operation adds a new table to your account. In an Amazon Web Services account, table names must be unique within each Region. That is, you can\n have two tables with same name if you create the tables in different Regions.

\n

\n CreateTable is an asynchronous operation. Upon receiving a\n CreateTable request, DynamoDB immediately returns a response with a\n TableStatus of CREATING. After the table is created,\n DynamoDB sets the TableStatus to ACTIVE. You can perform read\n and write operations only on an ACTIVE table.

\n

You can optionally define secondary indexes on the new table, as part of the\n CreateTable operation. If you want to create multiple tables with\n secondary indexes on them, you must create the tables sequentially. Only one table with\n secondary indexes can be in the CREATING state at any given time.

\n

You can use the DescribeTable action to check the table status.

" + "smithy.api#documentation": "

The CreateTable operation adds a new table to your account. In an Amazon Web Services account, table names must be unique within each Region. That is, you can\n have two tables with same name if you create the tables in different Regions.

\n

\n CreateTable is an asynchronous operation. Upon receiving a\n CreateTable request, DynamoDB immediately returns a response with a\n TableStatus of CREATING. After the table is created,\n DynamoDB sets the TableStatus to ACTIVE. You can perform read\n and write operations only on an ACTIVE table.

\n

You can optionally define secondary indexes on the new table, as part of the\n CreateTable operation. If you want to create multiple tables with\n secondary indexes on them, you must create the tables sequentially. Only one table with\n secondary indexes can be in the CREATING state at any given time.

\n

You can use the DescribeTable action to check the table status.

" } }, "com.amazonaws.dynamodb#CreateTableInput": { @@ -1955,38 +1977,38 @@ "KeySchema": { "target": "com.amazonaws.dynamodb#KeySchema", "traits": { - "smithy.api#documentation": "

Specifies the attributes that make up the primary key for a table or an index. The\n attributes in KeySchema must also be defined in the\n AttributeDefinitions array. For more information, see Data\n Model in the Amazon DynamoDB Developer Guide.

\n

Each KeySchemaElement in the array is composed of:

\n
    \n
  • \n

    \n AttributeName - The name of this key attribute.

    \n
  • \n
  • \n

    \n KeyType - The role that the key attribute will assume:

    \n
      \n
    • \n

      \n HASH - partition key

      \n
    • \n
    • \n

      \n RANGE - sort key

      \n
    • \n
    \n
  • \n
\n \n

The partition key of an item is also known as its hash\n attribute. The term \"hash attribute\" derives from the DynamoDB usage\n of an internal hash function to evenly distribute data items across partitions,\n based on their partition key values.

\n

The sort key of an item is also known as its range attribute.\n The term \"range attribute\" derives from the way DynamoDB stores items with the same\n partition key physically close together, in sorted order by the sort key\n value.

\n
\n\n

For a simple primary key (partition key), you must provide exactly one element with a\n KeyType of HASH.

\n

For a composite primary key (partition key and sort key), you must provide exactly two\n elements, in this order: The first element must have a KeyType of\n HASH, and the second element must have a KeyType of\n RANGE.

\n

For more information, see Working with Tables in the Amazon DynamoDB Developer\n Guide.

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

Specifies the attributes that make up the primary key for a table or an index. The\n attributes in KeySchema must also be defined in the\n AttributeDefinitions array. For more information, see Data\n Model in the Amazon DynamoDB Developer Guide.

\n

Each KeySchemaElement in the array is composed of:

\n
    \n
  • \n

    \n AttributeName - The name of this key attribute.

    \n
  • \n
  • \n

    \n KeyType - The role that the key attribute will assume:

    \n
      \n
    • \n

      \n HASH - partition key

      \n
    • \n
    • \n

      \n RANGE - sort key

      \n
    • \n
    \n
  • \n
\n \n

The partition key of an item is also known as its hash\n attribute. The term \"hash attribute\" derives from the DynamoDB usage\n of an internal hash function to evenly distribute data items across partitions,\n based on their partition key values.

\n

The sort key of an item is also known as its range attribute.\n The term \"range attribute\" derives from the way DynamoDB stores items with the same\n partition key physically close together, in sorted order by the sort key\n value.

\n
\n

For a simple primary key (partition key), you must provide exactly one element with a\n KeyType of HASH.

\n

For a composite primary key (partition key and sort key), you must provide exactly two\n elements, in this order: The first element must have a KeyType of\n HASH, and the second element must have a KeyType of\n RANGE.

\n

For more information, see Working with Tables in the Amazon DynamoDB Developer\n Guide.

", "smithy.api#required": {} } }, "LocalSecondaryIndexes": { "target": "com.amazonaws.dynamodb#LocalSecondaryIndexList", "traits": { - "smithy.api#documentation": "

One or more local secondary indexes (the maximum is 5) to be created on the table.\n Each index is scoped to a given partition key value. There is a 10 GB size limit per\n partition key value; otherwise, the size of a local secondary index is\n unconstrained.

\n

Each local secondary index in the array includes the following:

\n
    \n
  • \n

    \n IndexName - The name of the local secondary index. Must be unique\n only for this table.

    \n

    \n
  • \n
  • \n

    \n KeySchema - Specifies the key schema for the local secondary index.\n The key schema must begin with the same partition key as the table.

    \n
  • \n
  • \n

    \n Projection - Specifies attributes that are copied (projected) from\n the table into the index. These are in addition to the primary key attributes\n and index key attributes, which are automatically projected. Each attribute\n specification is composed of:

    \n
      \n
    • \n

      \n ProjectionType - One of the following:

      \n
        \n
      • \n

        \n KEYS_ONLY - Only the index and primary keys are\n projected into the index.

        \n
      • \n
      • \n

        \n INCLUDE - Only the specified table attributes are\n projected into the index. The list of projected attributes is in\n NonKeyAttributes.

        \n
      • \n
      • \n

        \n ALL - All of the table attributes are projected\n into the index.

        \n
      • \n
      \n
    • \n
    • \n

      \n NonKeyAttributes - A list of one or more non-key attribute\n names that are projected into the secondary index. The total count of\n attributes provided in NonKeyAttributes, summed across all\n of the secondary indexes, must not exceed 100. If you project the same\n attribute into two different indexes, this counts as two distinct\n attributes when determining the total.

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

One or more local secondary indexes (the maximum is 5) to be created on the table.\n Each index is scoped to a given partition key value. There is a 10 GB size limit per\n partition key value; otherwise, the size of a local secondary index is\n unconstrained.

\n

Each local secondary index in the array includes the following:

\n
    \n
  • \n

    \n IndexName - The name of the local secondary index. Must be unique\n only for this table.

    \n

    \n
  • \n
  • \n

    \n KeySchema - Specifies the key schema for the local secondary index.\n The key schema must begin with the same partition key as the table.

    \n
  • \n
  • \n

    \n Projection - Specifies attributes that are copied (projected) from\n the table into the index. These are in addition to the primary key attributes\n and index key attributes, which are automatically projected. Each attribute\n specification is composed of:

    \n
      \n
    • \n

      \n ProjectionType - One of the following:

      \n
        \n
      • \n

        \n KEYS_ONLY - Only the index and primary keys are\n projected into the index.

        \n
      • \n
      • \n

        \n INCLUDE - Only the specified table attributes are\n projected into the index. The list of projected attributes is in\n NonKeyAttributes.

        \n
      • \n
      • \n

        \n ALL - All of the table attributes are projected\n into the index.

        \n
      • \n
      \n
    • \n
    • \n

      \n NonKeyAttributes - A list of one or more non-key attribute\n names that are projected into the secondary index. The total count of\n attributes provided in NonKeyAttributes, summed across all\n of the secondary indexes, must not exceed 100. If you project the same\n attribute into two different indexes, this counts as two distinct\n attributes when determining the total.

      \n
    • \n
    \n
  • \n
" } }, "GlobalSecondaryIndexes": { "target": "com.amazonaws.dynamodb#GlobalSecondaryIndexList", "traits": { - "smithy.api#documentation": "

One or more global secondary indexes (the maximum is 20) to be created on the table.\n Each global secondary index in the array includes the following:

\n
    \n
  • \n

    \n IndexName - The name of the global secondary index. Must be unique\n only for this table.

    \n

    \n
  • \n
  • \n

    \n KeySchema - Specifies the key schema for the global secondary\n index.

    \n
  • \n
  • \n

    \n Projection - Specifies attributes that are copied (projected) from\n the table into the index. These are in addition to the primary key attributes\n and index key attributes, which are automatically projected. Each attribute\n specification is composed of:

    \n
      \n
    • \n

      \n ProjectionType - One of the following:

      \n
        \n
      • \n

        \n KEYS_ONLY - Only the index and primary keys are\n projected into the index.

        \n
      • \n
      • \n

        \n INCLUDE - Only the specified table attributes are\n projected into the index. The list of projected attributes is in\n NonKeyAttributes.

        \n
      • \n
      • \n

        \n ALL - All of the table attributes are projected\n into the index.

        \n
      • \n
      \n
    • \n
    • \n

      \n NonKeyAttributes - A list of one or more non-key attribute\n names that are projected into the secondary index. The total count of\n attributes provided in NonKeyAttributes, summed across all\n of the secondary indexes, must not exceed 100. If you project the same\n attribute into two different indexes, this counts as two distinct\n attributes when determining the total.

      \n
    • \n
    \n
  • \n
  • \n

    \n ProvisionedThroughput - The provisioned throughput settings for the\n global secondary index, consisting of read and write capacity units.

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

One or more global secondary indexes (the maximum is 20) to be created on the table.\n Each global secondary index in the array includes the following:

\n
    \n
  • \n

    \n IndexName - The name of the global secondary index. Must be unique\n only for this table.

    \n

    \n
  • \n
  • \n

    \n KeySchema - Specifies the key schema for the global secondary\n index.

    \n
  • \n
  • \n

    \n Projection - Specifies attributes that are copied (projected) from\n the table into the index. These are in addition to the primary key attributes\n and index key attributes, which are automatically projected. Each attribute\n specification is composed of:

    \n
      \n
    • \n

      \n ProjectionType - One of the following:

      \n
        \n
      • \n

        \n KEYS_ONLY - Only the index and primary keys are\n projected into the index.

        \n
      • \n
      • \n

        \n INCLUDE - Only the specified table attributes are\n projected into the index. The list of projected attributes is in\n NonKeyAttributes.

        \n
      • \n
      • \n

        \n ALL - All of the table attributes are projected\n into the index.

        \n
      • \n
      \n
    • \n
    • \n

      \n NonKeyAttributes - A list of one or more non-key attribute\n names that are projected into the secondary index. The total count of\n attributes provided in NonKeyAttributes, summed across all\n of the secondary indexes, must not exceed 100. If you project the same\n attribute into two different indexes, this counts as two distinct\n attributes when determining the total.

      \n
    • \n
    \n
  • \n
  • \n

    \n ProvisionedThroughput - The provisioned throughput settings for the\n global secondary index, consisting of read and write capacity units.

    \n
  • \n
" } }, "BillingMode": { "target": "com.amazonaws.dynamodb#BillingMode", "traits": { - "smithy.api#documentation": "

Controls how you are charged for read and write throughput and how you manage\n capacity. This setting can be changed later.

\n
    \n
  • \n

    \n PROVISIONED - We recommend using PROVISIONED for\n predictable workloads. PROVISIONED sets the billing mode to Provisioned Mode.

    \n
  • \n
  • \n

    \n PAY_PER_REQUEST - We recommend using PAY_PER_REQUEST\n for unpredictable workloads. PAY_PER_REQUEST sets the billing mode\n to On-Demand Mode.

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

Controls how you are charged for read and write throughput and how you manage\n capacity. This setting can be changed later.

\n
    \n
  • \n

    \n PROVISIONED - We recommend using PROVISIONED for\n predictable workloads. PROVISIONED sets the billing mode to Provisioned Mode.

    \n
  • \n
  • \n

    \n PAY_PER_REQUEST - We recommend using PAY_PER_REQUEST\n for unpredictable workloads. PAY_PER_REQUEST sets the billing mode\n to On-Demand Mode.

    \n
  • \n
" } }, "ProvisionedThroughput": { "target": "com.amazonaws.dynamodb#ProvisionedThroughput", "traits": { - "smithy.api#documentation": "

Represents the provisioned throughput settings for a specified table or index. The\n settings can be modified using the UpdateTable operation.

\n

If you set BillingMode as PROVISIONED, you must specify this property.\n If you set BillingMode as PAY_PER_REQUEST, you cannot specify this\n property.

\n

For current minimum and maximum provisioned throughput values, see Service,\n Account, and Table Quotas in the Amazon DynamoDB Developer\n Guide.

" + "smithy.api#documentation": "

Represents the provisioned throughput settings for a specified table or index. The\n settings can be modified using the UpdateTable operation.

\n

If you set BillingMode as PROVISIONED, you must specify this property.\n If you set BillingMode as PAY_PER_REQUEST, you cannot specify this\n property.

\n

For current minimum and maximum provisioned throughput values, see Service,\n Account, and Table Quotas in the Amazon DynamoDB Developer\n Guide.

" } }, "StreamSpecification": { "target": "com.amazonaws.dynamodb#StreamSpecification", "traits": { - "smithy.api#documentation": "

The settings for DynamoDB Streams on the table. These settings consist of:

\n
    \n
  • \n

    \n StreamEnabled - Indicates whether DynamoDB Streams is to be enabled\n (true) or disabled (false).

    \n
  • \n
  • \n

    \n StreamViewType - When an item in the table is modified,\n StreamViewType determines what information is written to the\n table's stream. Valid values for StreamViewType are:

    \n
      \n
    • \n

      \n KEYS_ONLY - Only the key attributes of the modified item\n are written to the stream.

      \n
    • \n
    • \n

      \n NEW_IMAGE - The entire item, as it appears after it was\n modified, is written to the stream.

      \n
    • \n
    • \n

      \n OLD_IMAGE - The entire item, as it appeared before it was\n modified, is written to the stream.

      \n
    • \n
    • \n

      \n NEW_AND_OLD_IMAGES - Both the new and the old item images\n of the item are written to the stream.

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

The settings for DynamoDB Streams on the table. These settings consist of:

\n
    \n
  • \n

    \n StreamEnabled - Indicates whether DynamoDB Streams is to be enabled\n (true) or disabled (false).

    \n
  • \n
  • \n

    \n StreamViewType - When an item in the table is modified,\n StreamViewType determines what information is written to the\n table's stream. Valid values for StreamViewType are:

    \n
      \n
    • \n

      \n KEYS_ONLY - Only the key attributes of the modified item\n are written to the stream.

      \n
    • \n
    • \n

      \n NEW_IMAGE - The entire item, as it appears after it was\n modified, is written to the stream.

      \n
    • \n
    • \n

      \n OLD_IMAGE - The entire item, as it appeared before it was\n modified, is written to the stream.

      \n
    • \n
    • \n

      \n NEW_AND_OLD_IMAGES - Both the new and the old item images\n of the item are written to the stream.

      \n
    • \n
    \n
  • \n
" } }, "SSESpecification": { @@ -2006,10 +2028,17 @@ "traits": { "smithy.api#documentation": "

The table class of the new table. Valid values are STANDARD and\n STANDARD_INFREQUENT_ACCESS.

" } + }, + "DeletionProtectionEnabled": { + "target": "com.amazonaws.dynamodb#DeletionProtectionEnabled", + "traits": { + "smithy.api#documentation": "

Indicates whether deletion protection is to be enabled (true) or disabled (false) on the table.

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

Represents the input of a CreateTable operation.

" + "smithy.api#documentation": "

Represents the input of a CreateTable operation.

", + "smithy.api#input": {} } }, "com.amazonaws.dynamodb#CreateTableOutput": { @@ -2023,7 +2052,8 @@ } }, "traits": { - "smithy.api#documentation": "

Represents the output of a CreateTable operation.

" + "smithy.api#documentation": "

Represents the output of a CreateTable operation.

", + "smithy.api#output": {} } }, "com.amazonaws.dynamodb#CsvDelimiter": { @@ -2156,7 +2186,7 @@ "aws.api#clientDiscoveredEndpoint": { "required": false }, - "smithy.api#documentation": "

Deletes an existing backup of a table.

\n

You can call DeleteBackup at a maximum rate of 10 times per\n second.

" + "smithy.api#documentation": "

Deletes an existing backup of a table.

\n

You can call DeleteBackup at a maximum rate of 10 times per\n second.

" } }, "com.amazonaws.dynamodb#DeleteBackupInput": { @@ -2169,6 +2199,9 @@ "smithy.api#required": {} } } + }, + "traits": { + "smithy.api#input": {} } }, "com.amazonaws.dynamodb#DeleteBackupOutput": { @@ -2180,6 +2213,9 @@ "smithy.api#documentation": "

Contains the description of the backup created for the table.

" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.dynamodb#DeleteGlobalSecondaryIndexAction": { @@ -2235,7 +2271,7 @@ "aws.api#clientDiscoveredEndpoint": { "required": false }, - "smithy.api#documentation": "

Deletes a single item in a table by primary key. You can perform a conditional delete\n operation that deletes the item if it exists, or if it has an expected attribute\n value.

\n

In addition to deleting an item, you can also return the item's attribute values in\n the same operation, using the ReturnValues parameter.

\n

Unless you specify conditions, the DeleteItem is an idempotent operation;\n running it multiple times on the same item or attribute does not\n result in an error response.

\n

Conditional deletes are useful for deleting items only if specific conditions are met.\n If those conditions are met, DynamoDB performs the delete. Otherwise, the item is not\n deleted.

" + "smithy.api#documentation": "

Deletes a single item in a table by primary key. You can perform a conditional delete\n operation that deletes the item if it exists, or if it has an expected attribute\n value.

\n

In addition to deleting an item, you can also return the item's attribute values in\n the same operation, using the ReturnValues parameter.

\n

Unless you specify conditions, the DeleteItem is an idempotent operation;\n running it multiple times on the same item or attribute does not\n result in an error response.

\n

Conditional deletes are useful for deleting items only if specific conditions are met.\n If those conditions are met, DynamoDB performs the delete. Otherwise, the item is not\n deleted.

" } }, "com.amazonaws.dynamodb#DeleteItemInput": { @@ -2251,7 +2287,7 @@ "Key": { "target": "com.amazonaws.dynamodb#Key", "traits": { - "smithy.api#documentation": "

A map of attribute names to AttributeValue objects, representing the\n primary key of the item to delete.

\n

For the primary key, you must provide all of the attributes. For example, with a\n simple primary key, you only need to provide a value for the partition key. For a\n composite primary key, you must provide values for both the partition key and the sort\n key.

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

A map of attribute names to AttributeValue objects, representing the\n primary key of the item to delete.

\n

For the primary key, you must provide all of the key attributes. For example, with a\n simple primary key, you only need to provide a value for the partition key. For a\n composite primary key, you must provide values for both the partition key and the sort\n key.

", "smithy.api#required": {} } }, @@ -2270,7 +2306,7 @@ "ReturnValues": { "target": "com.amazonaws.dynamodb#ReturnValue", "traits": { - "smithy.api#documentation": "

Use ReturnValues if you want to get the item attributes as they appeared\n before they were deleted. For DeleteItem, the valid values are:

\n
    \n
  • \n

    \n NONE - If ReturnValues is not specified, or if its\n value is NONE, then nothing is returned. (This setting is the\n default for ReturnValues.)

    \n
  • \n
  • \n

    \n ALL_OLD - The content of the old item is returned.

    \n
  • \n
\n

There is no additional cost associated with requesting a return value aside from the\n small network and processing overhead of receiving a larger response. No read capacity\n units are consumed.

\n \n

The ReturnValues parameter is used by several DynamoDB operations;\n however, DeleteItem does not recognize any values other than\n NONE or ALL_OLD.

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

Use ReturnValues if you want to get the item attributes as they appeared\n before they were deleted. For DeleteItem, the valid values are:

\n
    \n
  • \n

    \n NONE - If ReturnValues is not specified, or if its\n value is NONE, then nothing is returned. (This setting is the\n default for ReturnValues.)

    \n
  • \n
  • \n

    \n ALL_OLD - The content of the old item is returned.

    \n
  • \n
\n

There is no additional cost associated with requesting a return value aside from the\n small network and processing overhead of receiving a larger response. No read capacity\n units are consumed.

\n \n

The ReturnValues parameter is used by several DynamoDB operations;\n however, DeleteItem does not recognize any values other than\n NONE or ALL_OLD.

\n
" } }, "ReturnConsumedCapacity": { @@ -2285,24 +2321,25 @@ "ConditionExpression": { "target": "com.amazonaws.dynamodb#ConditionExpression", "traits": { - "smithy.api#documentation": "

A condition that must be satisfied in order for a conditional DeleteItem\n to succeed.

\n

An expression can contain any of the following:

\n
    \n
  • \n

    Functions: attribute_exists | attribute_not_exists | attribute_type |\n contains | begins_with | size\n

    \n

    These function names are case-sensitive.

    \n
  • \n
  • \n

    Comparison operators: = | <> |\n < | > | <= | >= |\n BETWEEN | IN \n

    \n
  • \n
  • \n

    Logical operators: AND | OR | NOT\n

    \n
  • \n
\n

For more information about condition expressions, see Condition Expressions in the Amazon DynamoDB Developer\n Guide.

" + "smithy.api#documentation": "

A condition that must be satisfied in order for a conditional DeleteItem\n to succeed.

\n

An expression can contain any of the following:

\n
    \n
  • \n

    Functions: attribute_exists | attribute_not_exists | attribute_type |\n contains | begins_with | size\n

    \n

    These function names are case-sensitive.

    \n
  • \n
  • \n

    Comparison operators: = | <> |\n < | > | <= | >= |\n BETWEEN | IN \n

    \n
  • \n
  • \n

    Logical operators: AND | OR | NOT\n

    \n
  • \n
\n

For more information about condition expressions, see Condition Expressions in the Amazon DynamoDB Developer\n Guide.

" } }, "ExpressionAttributeNames": { "target": "com.amazonaws.dynamodb#ExpressionAttributeNameMap", "traits": { - "smithy.api#documentation": "

One or more substitution tokens for attribute names in an expression. The following\n are some use cases for using ExpressionAttributeNames:

\n
    \n
  • \n

    To access an attribute whose name conflicts with a DynamoDB reserved\n word.

    \n
  • \n
  • \n

    To create a placeholder for repeating occurrences of an attribute name in an\n expression.

    \n
  • \n
  • \n

    To prevent special characters in an attribute name from being misinterpreted\n in an expression.

    \n
  • \n
\n

Use the # character in an expression to dereference\n an attribute name. For example, consider the following attribute name:

\n
    \n
  • \n

    \n Percentile\n

    \n
  • \n
\n

The name of this attribute conflicts with a reserved word, so it cannot be used\n directly in an expression. (For the complete list of reserved words, see Reserved Words in the Amazon DynamoDB Developer\n Guide). To work around this, you could specify the following for\n ExpressionAttributeNames:

\n
    \n
  • \n

    \n {\"#P\":\"Percentile\"}\n

    \n
  • \n
\n

You could then use this substitution in an expression, as in this example:

\n
    \n
  • \n

    \n #P = :val\n

    \n
  • \n
\n \n

Tokens that begin with the : character are\n expression attribute values, which are placeholders for the\n actual value at runtime.

\n
\n

For more information on expression attribute names, see Specifying Item Attributes in the Amazon DynamoDB Developer\n Guide.

" + "smithy.api#documentation": "

One or more substitution tokens for attribute names in an expression. The following\n are some use cases for using ExpressionAttributeNames:

\n
    \n
  • \n

    To access an attribute whose name conflicts with a DynamoDB reserved\n word.

    \n
  • \n
  • \n

    To create a placeholder for repeating occurrences of an attribute name in an\n expression.

    \n
  • \n
  • \n

    To prevent special characters in an attribute name from being misinterpreted\n in an expression.

    \n
  • \n
\n

Use the # character in an expression to dereference\n an attribute name. For example, consider the following attribute name:

\n
    \n
  • \n

    \n Percentile\n

    \n
  • \n
\n

The name of this attribute conflicts with a reserved word, so it cannot be used\n directly in an expression. (For the complete list of reserved words, see Reserved Words in the Amazon DynamoDB Developer\n Guide). To work around this, you could specify the following for\n ExpressionAttributeNames:

\n
    \n
  • \n

    \n {\"#P\":\"Percentile\"}\n

    \n
  • \n
\n

You could then use this substitution in an expression, as in this example:

\n
    \n
  • \n

    \n #P = :val\n

    \n
  • \n
\n \n

Tokens that begin with the : character are\n expression attribute values, which are placeholders for the\n actual value at runtime.

\n
\n

For more information on expression attribute names, see Specifying Item Attributes in the Amazon DynamoDB Developer\n Guide.

" } }, "ExpressionAttributeValues": { "target": "com.amazonaws.dynamodb#ExpressionAttributeValueMap", "traits": { - "smithy.api#documentation": "

One or more values that can be substituted in an expression.

\n

Use the : (colon) character in an expression to\n dereference an attribute value. For example, suppose that you wanted to check whether\n the value of the ProductStatus attribute was one of the following:

\n

\n Available | Backordered | Discontinued\n

\n

You would first need to specify ExpressionAttributeValues as\n follows:

\n

\n { \":avail\":{\"S\":\"Available\"}, \":back\":{\"S\":\"Backordered\"},\n \":disc\":{\"S\":\"Discontinued\"} }\n

\n

You could then use these values in an expression, such as this:

\n

\n ProductStatus IN (:avail, :back, :disc)\n

\n

For more information on expression attribute values, see Condition Expressions in the Amazon DynamoDB Developer\n Guide.

" + "smithy.api#documentation": "

One or more values that can be substituted in an expression.

\n

Use the : (colon) character in an expression to\n dereference an attribute value. For example, suppose that you wanted to check whether\n the value of the ProductStatus attribute was one of the following:

\n

\n Available | Backordered | Discontinued\n

\n

You would first need to specify ExpressionAttributeValues as\n follows:

\n

\n { \":avail\":{\"S\":\"Available\"}, \":back\":{\"S\":\"Backordered\"},\n \":disc\":{\"S\":\"Discontinued\"} }\n

\n

You could then use these values in an expression, such as this:

\n

\n ProductStatus IN (:avail, :back, :disc)\n

\n

For more information on expression attribute values, see Condition Expressions in the Amazon DynamoDB Developer\n Guide.

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

Represents the input of a DeleteItem operation.

" + "smithy.api#documentation": "

Represents the input of a DeleteItem operation.

", + "smithy.api#input": {} } }, "com.amazonaws.dynamodb#DeleteItemOutput": { @@ -2317,18 +2354,19 @@ "ConsumedCapacity": { "target": "com.amazonaws.dynamodb#ConsumedCapacity", "traits": { - "smithy.api#documentation": "

The capacity units consumed by the DeleteItem operation. The data\n returned includes the total provisioned throughput consumed, along with statistics for\n the table and any indexes involved in the operation. ConsumedCapacity is\n only returned if the ReturnConsumedCapacity parameter was specified. For\n more information, see Provisioned Mode in the Amazon DynamoDB Developer\n Guide.

" + "smithy.api#documentation": "

The capacity units consumed by the DeleteItem operation. The data\n returned includes the total provisioned throughput consumed, along with statistics for\n the table and any indexes involved in the operation. ConsumedCapacity is\n only returned if the ReturnConsumedCapacity parameter was specified. For\n more information, see Provisioned Throughput in the Amazon DynamoDB Developer\n Guide.

" } }, "ItemCollectionMetrics": { "target": "com.amazonaws.dynamodb#ItemCollectionMetrics", "traits": { - "smithy.api#documentation": "

Information about item collections, if any, that were affected by the\n DeleteItem operation. ItemCollectionMetrics is only\n returned if the ReturnItemCollectionMetrics parameter was specified. If the\n table does not have any local secondary indexes, this information is not returned in the\n response.

\n

Each ItemCollectionMetrics element consists of:

\n
    \n
  • \n

    \n ItemCollectionKey - The partition key value of the item collection.\n This is the same as the partition key value of the item itself.

    \n
  • \n
  • \n

    \n SizeEstimateRangeGB - An estimate of item collection size, in\n gigabytes. This value is a two-element array containing a lower bound and an\n upper bound for the estimate. The estimate includes the size of all the items in\n the table, plus the size of all attributes projected into all of the local\n secondary indexes on that table. Use this estimate to measure whether a local\n secondary index is approaching its size limit.

    \n

    The estimate is subject to change over time; therefore, do not rely on the\n precision or accuracy of the estimate.

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

Information about item collections, if any, that were affected by the\n DeleteItem operation. ItemCollectionMetrics is only\n returned if the ReturnItemCollectionMetrics parameter was specified. If the\n table does not have any local secondary indexes, this information is not returned in the\n response.

\n

Each ItemCollectionMetrics element consists of:

\n
    \n
  • \n

    \n ItemCollectionKey - The partition key value of the item collection.\n This is the same as the partition key value of the item itself.

    \n
  • \n
  • \n

    \n SizeEstimateRangeGB - An estimate of item collection size, in\n gigabytes. This value is a two-element array containing a lower bound and an\n upper bound for the estimate. The estimate includes the size of all the items in\n the table, plus the size of all attributes projected into all of the local\n secondary indexes on that table. Use this estimate to measure whether a local\n secondary index is approaching its size limit.

    \n

    The estimate is subject to change over time; therefore, do not rely on the\n precision or accuracy of the estimate.

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

Represents the output of a DeleteItem operation.

" + "smithy.api#documentation": "

Represents the output of a DeleteItem operation.

", + "smithy.api#output": {} } }, "com.amazonaws.dynamodb#DeleteReplicaAction": { @@ -2405,7 +2443,7 @@ "aws.api#clientDiscoveredEndpoint": { "required": false }, - "smithy.api#documentation": "

The DeleteTable operation deletes a table and all of its items. After a\n DeleteTable request, the specified table is in the\n DELETING state until DynamoDB completes the deletion. If the table is\n in the ACTIVE state, you can delete it. If a table is in\n CREATING or UPDATING states, then DynamoDB returns a\n ResourceInUseException. If the specified table does not exist, DynamoDB\n returns a ResourceNotFoundException. If table is already in the\n DELETING state, no error is returned.

\n \n

DynamoDB might continue to accept data read and write operations, such as\n GetItem and PutItem, on a table in the\n DELETING state until the table deletion is complete.

\n
\n

When you delete a table, any indexes on that table are also deleted.

\n

If you have DynamoDB Streams enabled on the table, then the corresponding stream on\n that table goes into the DISABLED state, and the stream is automatically\n deleted after 24 hours.

\n\n

Use the DescribeTable action to check the status of the table.

" + "smithy.api#documentation": "

The DeleteTable operation deletes a table and all of its items. After a\n DeleteTable request, the specified table is in the\n DELETING state until DynamoDB completes the deletion. If the table is\n in the ACTIVE state, you can delete it. If a table is in\n CREATING or UPDATING states, then DynamoDB returns a\n ResourceInUseException. If the specified table does not exist, DynamoDB\n returns a ResourceNotFoundException. If table is already in the\n DELETING state, no error is returned.

\n \n

This operation only applies to Version 2019.11.21 (Current) \n of global tables.\n

\n
\n \n

DynamoDB might continue to accept data read and write operations, such as\n GetItem and PutItem, on a table in the\n DELETING state until the table deletion is complete.

\n
\n

When you delete a table, any indexes on that table are also deleted.

\n

If you have DynamoDB Streams enabled on the table, then the corresponding stream on\n that table goes into the DISABLED state, and the stream is automatically\n deleted after 24 hours.

\n

Use the DescribeTable action to check the status of the table.

" } }, "com.amazonaws.dynamodb#DeleteTableInput": { @@ -2420,7 +2458,8 @@ } }, "traits": { - "smithy.api#documentation": "

Represents the input of a DeleteTable operation.

" + "smithy.api#documentation": "

Represents the input of a DeleteTable operation.

", + "smithy.api#input": {} } }, "com.amazonaws.dynamodb#DeleteTableOutput": { @@ -2434,9 +2473,13 @@ } }, "traits": { - "smithy.api#documentation": "

Represents the output of a DeleteTable operation.

" + "smithy.api#documentation": "

Represents the output of a DeleteTable operation.

", + "smithy.api#output": {} } }, + "com.amazonaws.dynamodb#DeletionProtectionEnabled": { + "type": "boolean" + }, "com.amazonaws.dynamodb#DescribeBackup": { "type": "operation", "input": { @@ -2460,7 +2503,7 @@ "aws.api#clientDiscoveredEndpoint": { "required": false }, - "smithy.api#documentation": "

Describes an existing backup of a table.

\n

You can call DescribeBackup at a maximum rate of 10 times per\n second.

" + "smithy.api#documentation": "

Describes an existing backup of a table.

\n

You can call DescribeBackup at a maximum rate of 10 times per\n second.

" } }, "com.amazonaws.dynamodb#DescribeBackupInput": { @@ -2473,6 +2516,9 @@ "smithy.api#required": {} } } + }, + "traits": { + "smithy.api#input": {} } }, "com.amazonaws.dynamodb#DescribeBackupOutput": { @@ -2484,6 +2530,9 @@ "smithy.api#documentation": "

Contains the description of the backup created for the table.

" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.dynamodb#DescribeContinuousBackups": { @@ -2509,7 +2558,7 @@ "aws.api#clientDiscoveredEndpoint": { "required": false }, - "smithy.api#documentation": "

Checks the status of continuous backups and point in time recovery on the specified\n table. Continuous backups are ENABLED on all tables at table creation. If\n point in time recovery is enabled, PointInTimeRecoveryStatus will be set to\n ENABLED.

\n

After continuous backups and point in time recovery are enabled, you can restore to\n any point in time within EarliestRestorableDateTime and\n LatestRestorableDateTime.

\n

\n LatestRestorableDateTime is typically 5 minutes before the current time.\n You can restore your table to any point in time during the last 35 days.

\n

You can call DescribeContinuousBackups at a maximum rate of 10 times per\n second.

" + "smithy.api#documentation": "

Checks the status of continuous backups and point in time recovery on the specified\n table. Continuous backups are ENABLED on all tables at table creation. If\n point in time recovery is enabled, PointInTimeRecoveryStatus will be set to\n ENABLED.

\n

After continuous backups and point in time recovery are enabled, you can restore to\n any point in time within EarliestRestorableDateTime and\n LatestRestorableDateTime.

\n

\n LatestRestorableDateTime is typically 5 minutes before the current time.\n You can restore your table to any point in time during the last 35 days.

\n

You can call DescribeContinuousBackups at a maximum rate of 10 times per\n second.

" } }, "com.amazonaws.dynamodb#DescribeContinuousBackupsInput": { @@ -2522,6 +2571,9 @@ "smithy.api#required": {} } } + }, + "traits": { + "smithy.api#input": {} } }, "com.amazonaws.dynamodb#DescribeContinuousBackupsOutput": { @@ -2533,6 +2585,9 @@ "smithy.api#documentation": "

Represents the continuous backups and point in time recovery settings on the\n table.

" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.dynamodb#DescribeContributorInsights": { @@ -2552,7 +2607,7 @@ } ], "traits": { - "smithy.api#documentation": "

Returns information about contributor insights, for a given table or global secondary\n index.

" + "smithy.api#documentation": "

Returns information about contributor insights for a given table or global secondary\n index.

" } }, "com.amazonaws.dynamodb#DescribeContributorInsightsInput": { @@ -2571,6 +2626,9 @@ "smithy.api#documentation": "

The name of the global secondary index to describe, if applicable.

" } } + }, + "traits": { + "smithy.api#input": {} } }, "com.amazonaws.dynamodb#DescribeContributorInsightsOutput": { @@ -2609,9 +2667,12 @@ "FailureException": { "target": "com.amazonaws.dynamodb#FailureException", "traits": { - "smithy.api#documentation": "

Returns information about the last failure that was encountered.

\n

The most common exceptions for a FAILED status are:

\n
    \n
  • \n

    LimitExceededException - Per-account Amazon CloudWatch Contributor Insights\n rule limit reached. Please disable Contributor Insights for other tables/indexes\n OR disable Contributor Insights rules before retrying.

    \n
  • \n
  • \n

    AccessDeniedException - Amazon CloudWatch Contributor Insights rules cannot be\n modified due to insufficient permissions.

    \n
  • \n
  • \n

    AccessDeniedException - Failed to create service-linked role for Contributor\n Insights due to insufficient permissions.

    \n
  • \n
  • \n

    InternalServerError - Failed to create Amazon CloudWatch Contributor Insights\n rules. Please retry request.

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

Returns information about the last failure that was encountered.

\n

The most common exceptions for a FAILED status are:

\n
    \n
  • \n

    LimitExceededException - Per-account Amazon CloudWatch Contributor Insights\n rule limit reached. Please disable Contributor Insights for other tables/indexes\n OR disable Contributor Insights rules before retrying.

    \n
  • \n
  • \n

    AccessDeniedException - Amazon CloudWatch Contributor Insights rules cannot be\n modified due to insufficient permissions.

    \n
  • \n
  • \n

    AccessDeniedException - Failed to create service-linked role for Contributor\n Insights due to insufficient permissions.

    \n
  • \n
  • \n

    InternalServerError - Failed to create Amazon CloudWatch Contributor Insights\n rules. Please retry request.

    \n
  • \n
" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.dynamodb#DescribeEndpoints": { @@ -2623,12 +2684,15 @@ "target": "com.amazonaws.dynamodb#DescribeEndpointsResponse" }, "traits": { - "smithy.api#documentation": "

Returns the regional endpoint information.

" + "smithy.api#documentation": "

Returns the regional endpoint information. This action must be included in your VPC \n endpoint policies, or access to the DescribeEndpoints API will be denied. For more information \n on policy permissions, please see Internetwork traffic privacy.

" } }, "com.amazonaws.dynamodb#DescribeEndpointsRequest": { "type": "structure", - "members": {} + "members": {}, + "traits": { + "smithy.api#input": {} + } }, "com.amazonaws.dynamodb#DescribeEndpointsResponse": { "type": "structure", @@ -2640,6 +2704,9 @@ "smithy.api#required": {} } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.dynamodb#DescribeExport": { @@ -2675,6 +2742,9 @@ "smithy.api#required": {} } } + }, + "traits": { + "smithy.api#input": {} } }, "com.amazonaws.dynamodb#DescribeExportOutput": { @@ -2686,6 +2756,9 @@ "smithy.api#documentation": "

Represents the properties of the export.

" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.dynamodb#DescribeGlobalTable": { @@ -2711,7 +2784,7 @@ "aws.api#clientDiscoveredEndpoint": { "required": false }, - "smithy.api#documentation": "

Returns information about the specified global table.

\n \n

This operation only applies to Version\n 2017.11.29 of global tables. If you are using global tables Version\n 2019.11.21 you can use DescribeTable instead.

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

Returns information about the specified global table.

\n \n

This operation only applies to Version\n 2017.11.29 (Legacy) of global tables. We recommend using \n Version 2019.11.21 (Current)\n when creating new global tables, as it provides greater flexibility, higher efficiency and consumes less write capacity than \n 2017.11.29 (Legacy). To determine which version you are using, see \n Determining the version. \n To update existing global tables from version 2017.11.29 (Legacy) to version\n 2019.11.21 (Current), see \n Updating global tables.\n

\n
" } }, "com.amazonaws.dynamodb#DescribeGlobalTableInput": { @@ -2724,6 +2797,9 @@ "smithy.api#required": {} } } + }, + "traits": { + "smithy.api#input": {} } }, "com.amazonaws.dynamodb#DescribeGlobalTableOutput": { @@ -2735,6 +2811,9 @@ "smithy.api#documentation": "

Contains the details of the global table.

" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.dynamodb#DescribeGlobalTableSettings": { @@ -2760,7 +2839,7 @@ "aws.api#clientDiscoveredEndpoint": { "required": false }, - "smithy.api#documentation": "

Describes Region-specific settings for a global table.

\n \n

This operation only applies to Version\n 2017.11.29 of global tables.

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

Describes Region-specific settings for a global table.

\n \n

This operation only applies to Version\n 2017.11.29 (Legacy) of global tables. We recommend using\n Version 2019.11.21 (Current)\n when creating new global tables, as it provides greater flexibility, higher efficiency and consumes less write capacity than \n 2017.11.29 (Legacy). To determine which version you are using, see \n Determining the version. \n To update existing global tables from version 2017.11.29 (Legacy) to version\n 2019.11.21 (Current), see \n Updating global tables.\n

\n
" } }, "com.amazonaws.dynamodb#DescribeGlobalTableSettingsInput": { @@ -2773,6 +2852,9 @@ "smithy.api#required": {} } } + }, + "traits": { + "smithy.api#input": {} } }, "com.amazonaws.dynamodb#DescribeGlobalTableSettingsOutput": { @@ -2790,6 +2872,9 @@ "smithy.api#documentation": "

The Region-specific settings for the global table.

" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.dynamodb#DescribeImport": { @@ -2819,6 +2904,9 @@ "smithy.api#required": {} } } + }, + "traits": { + "smithy.api#input": {} } }, "com.amazonaws.dynamodb#DescribeImportOutput": { @@ -2831,6 +2919,9 @@ "smithy.api#required": {} } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.dynamodb#DescribeKinesisStreamingDestination": { @@ -2869,6 +2960,9 @@ "smithy.api#required": {} } } + }, + "traits": { + "smithy.api#input": {} } }, "com.amazonaws.dynamodb#DescribeKinesisStreamingDestinationOutput": { @@ -2886,6 +2980,9 @@ "smithy.api#documentation": "

The list of replica structures for the table being described.

" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.dynamodb#DescribeLimits": { @@ -2908,14 +3005,15 @@ "aws.api#clientDiscoveredEndpoint": { "required": false }, - "smithy.api#documentation": "

Returns the current provisioned-capacity quotas for your Amazon Web Services account in\n a Region, both for the Region as a whole and for any one DynamoDB table that you create\n there.

\n

When you establish an Amazon Web Services account, the account has initial quotas on\n the maximum read capacity units and write capacity units that you can provision across\n all of your DynamoDB tables in a given Region. Also, there are per-table\n quotas that apply when you create a table there. For more information, see Service,\n Account, and Table Quotas page in the Amazon DynamoDB\n Developer Guide.

\n\n

Although you can increase these quotas by filing a case at Amazon Web Services Support Center, obtaining the\n increase is not instantaneous. The DescribeLimits action lets you write\n code to compare the capacity you are currently using to those quotas imposed by your\n account so that you have enough time to apply for an increase before you hit a\n quota.

\n\n

For example, you could use one of the Amazon Web Services SDKs to do the\n following:

\n\n
    \n
  1. \n

    Call DescribeLimits for a particular Region to obtain your\n current account quotas on provisioned capacity there.

    \n
  2. \n
  3. \n

    Create a variable to hold the aggregate read capacity units provisioned for\n all your tables in that Region, and one to hold the aggregate write capacity\n units. Zero them both.

    \n
  4. \n
  5. \n

    Call ListTables to obtain a list of all your DynamoDB\n tables.

    \n
  6. \n
  7. \n

    For each table name listed by ListTables, do the\n following:

    \n
      \n
    • \n

      Call DescribeTable with the table name.

      \n
    • \n
    • \n

      Use the data returned by DescribeTable to add the read\n capacity units and write capacity units provisioned for the table itself\n to your variables.

      \n
    • \n
    • \n

      If the table has one or more global secondary indexes (GSIs), loop\n over these GSIs and add their provisioned capacity values to your\n variables as well.

      \n
    • \n
    \n
  8. \n
  9. \n

    Report the account quotas for that Region returned by\n DescribeLimits, along with the total current provisioned\n capacity levels you have calculated.

    \n
  10. \n
\n\n

This will let you see whether you are getting close to your account-level\n quotas.

\n

The per-table quotas apply only when you are creating a new table. They restrict the\n sum of the provisioned capacity of the new table itself and all its global secondary\n indexes.

\n

For existing tables and their GSIs, DynamoDB doesn't let you increase provisioned\n capacity extremely rapidly, but the only quota that applies is that the aggregate\n provisioned capacity over all your tables and GSIs cannot exceed either of the\n per-account quotas.

\n \n

\n DescribeLimits should only be called periodically. You can expect\n throttling errors if you call it more than once in a minute.

\n
\n

The DescribeLimits Request element has no content.

" + "smithy.api#documentation": "

Returns the current provisioned-capacity quotas for your Amazon Web Services account in\n a Region, both for the Region as a whole and for any one DynamoDB table that you create\n there.

\n

When you establish an Amazon Web Services account, the account has initial quotas on\n the maximum read capacity units and write capacity units that you can provision across\n all of your DynamoDB tables in a given Region. Also, there are per-table\n quotas that apply when you create a table there. For more information, see Service,\n Account, and Table Quotas page in the Amazon DynamoDB\n Developer Guide.

\n

Although you can increase these quotas by filing a case at Amazon Web Services Support Center, obtaining the\n increase is not instantaneous. The DescribeLimits action lets you write\n code to compare the capacity you are currently using to those quotas imposed by your\n account so that you have enough time to apply for an increase before you hit a\n quota.

\n

For example, you could use one of the Amazon Web Services SDKs to do the\n following:

\n
    \n
  1. \n

    Call DescribeLimits for a particular Region to obtain your\n current account quotas on provisioned capacity there.

    \n
  2. \n
  3. \n

    Create a variable to hold the aggregate read capacity units provisioned for\n all your tables in that Region, and one to hold the aggregate write capacity\n units. Zero them both.

    \n
  4. \n
  5. \n

    Call ListTables to obtain a list of all your DynamoDB\n tables.

    \n
  6. \n
  7. \n

    For each table name listed by ListTables, do the\n following:

    \n
      \n
    • \n

      Call DescribeTable with the table name.

      \n
    • \n
    • \n

      Use the data returned by DescribeTable to add the read\n capacity units and write capacity units provisioned for the table itself\n to your variables.

      \n
    • \n
    • \n

      If the table has one or more global secondary indexes (GSIs), loop\n over these GSIs and add their provisioned capacity values to your\n variables as well.

      \n
    • \n
    \n
  8. \n
  9. \n

    Report the account quotas for that Region returned by\n DescribeLimits, along with the total current provisioned\n capacity levels you have calculated.

    \n
  10. \n
\n

This will let you see whether you are getting close to your account-level\n quotas.

\n

The per-table quotas apply only when you are creating a new table. They restrict the\n sum of the provisioned capacity of the new table itself and all its global secondary\n indexes.

\n

For existing tables and their GSIs, DynamoDB doesn't let you increase provisioned\n capacity extremely rapidly, but the only quota that applies is that the aggregate\n provisioned capacity over all your tables and GSIs cannot exceed either of the\n per-account quotas.

\n \n

\n DescribeLimits should only be called periodically. You can expect\n throttling errors if you call it more than once in a minute.

\n
\n

The DescribeLimits Request element has no content.

" } }, "com.amazonaws.dynamodb#DescribeLimitsInput": { "type": "structure", "members": {}, "traits": { - "smithy.api#documentation": "

Represents the input of a DescribeLimits operation. Has no\n content.

" + "smithy.api#documentation": "

Represents the input of a DescribeLimits operation. Has no\n content.

", + "smithy.api#input": {} } }, "com.amazonaws.dynamodb#DescribeLimitsOutput": { @@ -2947,7 +3045,8 @@ } }, "traits": { - "smithy.api#documentation": "

Represents the output of a DescribeLimits operation.

" + "smithy.api#documentation": "

Represents the output of a DescribeLimits operation.

", + "smithy.api#output": {} } }, "com.amazonaws.dynamodb#DescribeTable": { @@ -2973,7 +3072,7 @@ "aws.api#clientDiscoveredEndpoint": { "required": false }, - "smithy.api#documentation": "

Returns information about the table, including the current status of the table, when\n it was created, the primary key schema, and any indexes on the table.

\n \n

If you issue a DescribeTable request immediately after a\n CreateTable request, DynamoDB might return a\n ResourceNotFoundException. This is because\n DescribeTable uses an eventually consistent query, and the metadata\n for your table might not be available at that moment. Wait for a few seconds, and\n then try the DescribeTable request again.

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

Returns information about the table, including the current status of the table, when\n it was created, the primary key schema, and any indexes on the table.

\n \n

This operation only applies to Version 2019.11.21 (Current) \n of global tables.\n

\n
\n \n

If you issue a DescribeTable request immediately after a\n CreateTable request, DynamoDB might return a\n ResourceNotFoundException. This is because\n DescribeTable uses an eventually consistent query, and the metadata\n for your table might not be available at that moment. Wait for a few seconds, and\n then try the DescribeTable request again.

\n
", "smithy.waiters#waitable": { "TableExists": { "acceptors": [ @@ -3022,7 +3121,8 @@ } }, "traits": { - "smithy.api#documentation": "

Represents the input of a DescribeTable operation.

" + "smithy.api#documentation": "

Represents the input of a DescribeTable operation.

", + "smithy.api#input": {} } }, "com.amazonaws.dynamodb#DescribeTableOutput": { @@ -3036,7 +3136,8 @@ } }, "traits": { - "smithy.api#documentation": "

Represents the output of a DescribeTable operation.

" + "smithy.api#documentation": "

Represents the output of a DescribeTable operation.

", + "smithy.api#output": {} } }, "com.amazonaws.dynamodb#DescribeTableReplicaAutoScaling": { @@ -3056,7 +3157,7 @@ } ], "traits": { - "smithy.api#documentation": "

Describes auto scaling settings across replicas of the global table at once.

\n \n

This operation only applies to Version\n 2019.11.21 of global tables.

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

Describes auto scaling settings across replicas of the global table at once.

\n \n

This operation only applies to Version 2019.11.21 (Current)\n of global tables.

\n
" } }, "com.amazonaws.dynamodb#DescribeTableReplicaAutoScalingInput": { @@ -3069,6 +3170,9 @@ "smithy.api#required": {} } } + }, + "traits": { + "smithy.api#input": {} } }, "com.amazonaws.dynamodb#DescribeTableReplicaAutoScalingOutput": { @@ -3080,6 +3184,9 @@ "smithy.api#documentation": "

Represents the auto scaling properties of the table.

" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.dynamodb#DescribeTimeToLive": { @@ -3118,6 +3225,9 @@ "smithy.api#required": {} } } + }, + "traits": { + "smithy.api#input": {} } }, "com.amazonaws.dynamodb#DescribeTimeToLiveOutput": { @@ -3129,6 +3239,9 @@ "smithy.api#documentation": "

" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.dynamodb#DestinationStatus": { @@ -3198,7 +3311,7 @@ "smithy.api#documentation": "

Stops replication from the DynamoDB table to the Kinesis data stream. This is done\n without deleting either of the resources.

" } }, - "com.amazonaws.dynamodb#Double": { + "com.amazonaws.dynamodb#DoubleObject": { "type": "double" }, "com.amazonaws.dynamodb#DuplicateItemException": { @@ -3393,7 +3506,7 @@ "name": "dynamodb" }, "aws.protocols#awsJson1_0": {}, - "smithy.api#documentation": "Amazon DynamoDB\n\n

Amazon DynamoDB is a fully managed NoSQL database service that provides fast\n and predictable performance with seamless scalability. DynamoDB lets you\n offload the administrative burdens of operating and scaling a distributed database, so\n that you don't have to worry about hardware provisioning, setup and configuration,\n replication, software patching, or cluster scaling.

\n\n

With DynamoDB, you can create database tables that can store and retrieve\n any amount of data, and serve any level of request traffic. You can scale up or scale\n down your tables' throughput capacity without downtime or performance degradation, and\n use the Amazon Web Services Management Console to monitor resource utilization and performance\n metrics.

\n\n

DynamoDB automatically spreads the data and traffic for your tables over\n a sufficient number of servers to handle your throughput and storage requirements, while\n maintaining consistent and fast performance. All of your data is stored on solid state\n disks (SSDs) and automatically replicated across multiple Availability Zones in an\n Amazon Web Services Region, providing built-in high availability and data\n durability.

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

Amazon DynamoDB is a fully managed NoSQL database service that provides fast\n and predictable performance with seamless scalability. DynamoDB lets you\n offload the administrative burdens of operating and scaling a distributed database, so\n that you don't have to worry about hardware provisioning, setup and configuration,\n replication, software patching, or cluster scaling.

\n

With DynamoDB, you can create database tables that can store and retrieve\n any amount of data, and serve any level of request traffic. You can scale up or scale\n down your tables' throughput capacity without downtime or performance degradation, and\n use the Amazon Web Services Management Console to monitor resource utilization and performance\n metrics.

\n

DynamoDB automatically spreads the data and traffic for your tables over\n a sufficient number of servers to handle your throughput and storage requirements, while\n maintaining consistent and fast performance. All of your data is stored on solid state\n disks (SSDs) and automatically replicated across multiple Availability Zones in an\n Amazon Web Services Region, providing built-in high availability and data\n durability.

", "smithy.api#title": "Amazon DynamoDB", "smithy.api#xmlNamespace": { "uri": "http://dynamodb.amazonaws.com/doc/2012-08-10/" @@ -3403,7 +3516,7 @@ "parameters": { "Region": { "builtIn": "AWS::Region", - "required": true, + "required": false, "documentation": "The AWS region used to dispatch the request.", "type": "String" }, @@ -3432,13 +3545,12 @@ { "conditions": [ { - "fn": "aws.partition", + "fn": "isSet", "argv": [ { - "ref": "Region" + "ref": "Endpoint" } - ], - "assign": "PartitionResult" + ] } ], "type": "tree", @@ -3446,14 +3558,20 @@ { "conditions": [ { - "fn": "isSet", + "fn": "booleanEquals", "argv": [ { - "ref": "Endpoint" - } + "ref": "UseFIPS" + }, + true ] } ], + "error": "Invalid Configuration: FIPS and custom endpoint are not supported", + "type": "error" + }, + { + "conditions": [], "type": "tree", "rules": [ { @@ -3462,67 +3580,42 @@ "fn": "booleanEquals", "argv": [ { - "ref": "UseFIPS" + "ref": "UseDualStack" }, true ] } ], - "error": "Invalid Configuration: FIPS and custom endpoint are not supported", + "error": "Invalid Configuration: Dualstack 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" + "endpoint": { + "url": { + "ref": "Endpoint" }, - { - "conditions": [], - "endpoint": { - "url": { - "ref": "Endpoint" - }, - "properties": {}, - "headers": {} - }, - "type": "endpoint" - } - ] + "properties": {}, + "headers": {} + }, + "type": "endpoint" } ] - }, + } + ] + }, + { + "conditions": [], + "type": "tree", + "rules": [ { "conditions": [ { - "fn": "booleanEquals", - "argv": [ - { - "ref": "UseFIPS" - }, - true - ] - }, - { - "fn": "booleanEquals", + "fn": "isSet", "argv": [ { - "ref": "UseDualStack" - }, - true + "ref": "Region" + } ] } ], @@ -3531,187 +3624,275 @@ { "conditions": [ { - "fn": "booleanEquals", + "fn": "aws.partition", "argv": [ - true, { - "fn": "getAttr", + "ref": "Region" + } + ], + "assign": "PartitionResult" + } + ], + "type": "tree", + "rules": [ + { + "conditions": [ + { + "fn": "booleanEquals", "argv": [ { - "ref": "PartitionResult" + "ref": "UseFIPS" }, - "supportsFIPS" + true ] - } - ] - }, - { - "fn": "booleanEquals", - "argv": [ - true, + }, { - "fn": "getAttr", + "fn": "booleanEquals", "argv": [ { - "ref": "PartitionResult" + "ref": "UseDualStack" }, - "supportsDualStack" + true ] } - ] - } - ], - "type": "tree", - "rules": [ - { - "conditions": [], + ], "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://dynamodb-fips.{Region}.{PartitionResult#dualStackDnsSuffix}", + "properties": {}, + "headers": {} + }, + "type": "endpoint" + } + ] + } + ] + }, { "conditions": [], - "endpoint": { - "url": "https://dynamodb-fips.{Region}.{PartitionResult#dualStackDnsSuffix}", - "properties": {}, - "headers": {} - }, - "type": "endpoint" + "error": "FIPS and DualStack are enabled, but this partition does not support one or both", + "type": "error" } ] - } - ] - }, - { - "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, + "conditions": [ { - "fn": "getAttr", + "fn": "booleanEquals", "argv": [ { - "ref": "PartitionResult" + "ref": "UseFIPS" }, - "supportsFIPS" + true ] } - ] - } - ], - "type": "tree", - "rules": [ - { - "conditions": [], + ], "type": "tree", "rules": [ { "conditions": [ { - "fn": "stringEquals", + "fn": "booleanEquals", "argv": [ - "aws-us-gov", + true, { "fn": "getAttr", "argv": [ { "ref": "PartitionResult" }, - "name" + "supportsFIPS" ] } ] } ], - "endpoint": { - "url": "https://dynamodb.{Region}.amazonaws.com", - "properties": {}, - "headers": {} - }, - "type": "endpoint" + "type": "tree", + "rules": [ + { + "conditions": [], + "type": "tree", + "rules": [ + { + "conditions": [ + { + "fn": "stringEquals", + "argv": [ + "aws-us-gov", + { + "fn": "getAttr", + "argv": [ + { + "ref": "PartitionResult" + }, + "name" + ] + } + ] + } + ], + "endpoint": { + "url": "https://dynamodb.{Region}.amazonaws.com", + "properties": {}, + "headers": {} + }, + "type": "endpoint" + }, + { + "conditions": [], + "endpoint": { + "url": "https://dynamodb-fips.{Region}.{PartitionResult#dnsSuffix}", + "properties": {}, + "headers": {} + }, + "type": "endpoint" + } + ] + } + ] }, { "conditions": [], - "endpoint": { - "url": "https://dynamodb-fips.{Region}.{PartitionResult#dnsSuffix}", - "properties": {}, - "headers": {} - }, - "type": "endpoint" + "error": "FIPS is enabled but this partition does not support FIPS", + "type": "error" } ] - } - ] - }, - { - "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, + "conditions": [ { - "fn": "getAttr", + "fn": "booleanEquals", "argv": [ { - "ref": "PartitionResult" + "ref": "UseDualStack" }, - "supportsDualStack" + 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://dynamodb.{Region}.{PartitionResult#dualStackDnsSuffix}", + "properties": {}, + "headers": {} + }, + "type": "endpoint" + } + ] + } ] + }, + { + "conditions": [], + "error": "DualStack is enabled but this partition does not support DualStack", + "type": "error" } ] - } - ], - "type": "tree", - "rules": [ + }, { "conditions": [], "type": "tree", "rules": [ + { + "conditions": [ + { + "fn": "stringEquals", + "argv": [ + { + "ref": "Region" + }, + "local" + ] + } + ], + "endpoint": { + "url": "http://localhost:8000", + "properties": { + "authSchemes": [ + { + "name": "sigv4", + "signingName": "dynamodb", + "signingRegion": "us-east-1" + } + ] + }, + "headers": {} + }, + "type": "endpoint" + }, { "conditions": [], "endpoint": { - "url": "https://dynamodb.{Region}.{PartitionResult#dualStackDnsSuffix}", + "url": "https://dynamodb.{Region}.{PartitionResult#dnsSuffix}", "properties": {}, "headers": {} }, @@ -3720,55 +3901,13 @@ ] } ] - }, - { - "conditions": [], - "error": "DualStack is enabled but this partition does not support DualStack", - "type": "error" } ] }, { "conditions": [], - "type": "tree", - "rules": [ - { - "conditions": [ - { - "fn": "stringEquals", - "argv": [ - { - "ref": "Region" - }, - "local" - ] - } - ], - "endpoint": { - "url": "http://localhost:8000", - "properties": { - "authSchemes": [ - { - "name": "sigv4", - "signingRegion": "us-east-1", - "signingName": "dynamodb" - } - ] - }, - "headers": {} - }, - "type": "endpoint" - }, - { - "conditions": [], - "endpoint": { - "url": "https://dynamodb.{Region}.{PartitionResult#dnsSuffix}", - "properties": {}, - "headers": {} - }, - "type": "endpoint" - } - ] + "error": "Invalid Configuration: Missing Region", + "type": "error" } ] } @@ -3777,55 +3916,68 @@ "smithy.rules#endpointTests": { "testCases": [ { - "documentation": "For region me-south-1 with FIPS disabled and DualStack disabled", + "documentation": "For region af-south-1 with FIPS disabled and DualStack disabled", "expect": { "endpoint": { - "url": "https://dynamodb.me-south-1.amazonaws.com" + "url": "https://dynamodb.af-south-1.amazonaws.com" } }, "params": { - "UseFIPS": false, + "Region": "af-south-1", "UseDualStack": false, - "Region": "me-south-1" + "UseFIPS": false } }, { - "documentation": "For region ca-central-1 with FIPS disabled and DualStack disabled", + "documentation": "For region ap-east-1 with FIPS disabled and DualStack disabled", "expect": { "endpoint": { - "url": "https://dynamodb.ca-central-1.amazonaws.com" + "url": "https://dynamodb.ap-east-1.amazonaws.com" } }, "params": { - "UseFIPS": false, + "Region": "ap-east-1", "UseDualStack": false, - "Region": "ca-central-1" + "UseFIPS": false } }, { - "documentation": "For region ca-central-1 with FIPS enabled and DualStack disabled", + "documentation": "For region ap-northeast-1 with FIPS disabled and DualStack disabled", "expect": { "endpoint": { - "url": "https://dynamodb-fips.ca-central-1.amazonaws.com" + "url": "https://dynamodb.ap-northeast-1.amazonaws.com" } }, "params": { - "UseFIPS": true, + "Region": "ap-northeast-1", "UseDualStack": false, - "Region": "ca-central-1" + "UseFIPS": false } }, { - "documentation": "For region ap-southeast-1 with FIPS disabled and DualStack disabled", + "documentation": "For region ap-northeast-2 with FIPS disabled and DualStack disabled", "expect": { "endpoint": { - "url": "https://dynamodb.ap-southeast-1.amazonaws.com" + "url": "https://dynamodb.ap-northeast-2.amazonaws.com" } }, "params": { - "UseFIPS": false, + "Region": "ap-northeast-2", "UseDualStack": false, - "Region": "ap-southeast-1" + "UseFIPS": false + } + }, + { + "documentation": "For region ap-northeast-3 with FIPS disabled and DualStack disabled", + "expect": { + "endpoint": { + "url": "https://dynamodb.ap-northeast-3.amazonaws.com" + } + }, + "params": { + "Region": "ap-northeast-3", + "UseDualStack": false, + "UseFIPS": false } }, { @@ -3836,22 +3988,22 @@ } }, "params": { - "UseFIPS": false, + "Region": "ap-south-1", "UseDualStack": false, - "Region": "ap-south-1" + "UseFIPS": false } }, { - "documentation": "For region eu-south-1 with FIPS disabled and DualStack disabled", + "documentation": "For region ap-southeast-1 with FIPS disabled and DualStack disabled", "expect": { "endpoint": { - "url": "https://dynamodb.eu-south-1.amazonaws.com" + "url": "https://dynamodb.ap-southeast-1.amazonaws.com" } }, "params": { - "UseFIPS": false, + "Region": "ap-southeast-1", "UseDualStack": false, - "Region": "eu-south-1" + "UseFIPS": false } }, { @@ -3862,174 +4014,174 @@ } }, "params": { - "UseFIPS": false, + "Region": "ap-southeast-2", "UseDualStack": false, - "Region": "ap-southeast-2" + "UseFIPS": false } }, { - "documentation": "For region ap-northeast-1 with FIPS disabled and DualStack disabled", + "documentation": "For region ap-southeast-3 with FIPS disabled and DualStack disabled", "expect": { "endpoint": { - "url": "https://dynamodb.ap-northeast-1.amazonaws.com" + "url": "https://dynamodb.ap-southeast-3.amazonaws.com" } }, "params": { - "UseFIPS": false, + "Region": "ap-southeast-3", "UseDualStack": false, - "Region": "ap-northeast-1" + "UseFIPS": false } }, { - "documentation": "For region ap-northeast-2 with FIPS disabled and DualStack disabled", + "documentation": "For region ca-central-1 with FIPS disabled and DualStack disabled", "expect": { "endpoint": { - "url": "https://dynamodb.ap-northeast-2.amazonaws.com" + "url": "https://dynamodb.ca-central-1.amazonaws.com" } }, "params": { - "UseFIPS": false, + "Region": "ca-central-1", "UseDualStack": false, - "Region": "ap-northeast-2" + "UseFIPS": false } }, { - "documentation": "For region ap-northeast-3 with FIPS disabled and DualStack disabled", + "documentation": "For region ca-central-1 with FIPS enabled and DualStack disabled", "expect": { "endpoint": { - "url": "https://dynamodb.ap-northeast-3.amazonaws.com" + "url": "https://dynamodb-fips.ca-central-1.amazonaws.com" } }, "params": { - "UseFIPS": false, + "Region": "ca-central-1", "UseDualStack": false, - "Region": "ap-northeast-3" + "UseFIPS": true } }, { - "documentation": "For region sa-east-1 with FIPS disabled and DualStack disabled", + "documentation": "For region eu-central-1 with FIPS disabled and DualStack disabled", "expect": { "endpoint": { - "url": "https://dynamodb.sa-east-1.amazonaws.com" + "url": "https://dynamodb.eu-central-1.amazonaws.com" } }, "params": { - "UseFIPS": false, + "Region": "eu-central-1", "UseDualStack": false, - "Region": "sa-east-1" + "UseFIPS": false } }, { - "documentation": "For region ap-southeast-3 with FIPS disabled and DualStack disabled", + "documentation": "For region eu-north-1 with FIPS disabled and DualStack disabled", "expect": { "endpoint": { - "url": "https://dynamodb.ap-southeast-3.amazonaws.com" + "url": "https://dynamodb.eu-north-1.amazonaws.com" } }, "params": { - "UseFIPS": false, + "Region": "eu-north-1", "UseDualStack": false, - "Region": "ap-southeast-3" + "UseFIPS": false } }, { - "documentation": "For region local with FIPS disabled and DualStack disabled", + "documentation": "For region eu-south-1 with FIPS disabled and DualStack disabled", "expect": { "endpoint": { - "properties": { - "authSchemes": [ - { - "signingName": "dynamodb", - "name": "sigv4", - "signingRegion": "us-east-1" - } - ] - }, - "url": "http://localhost:8000" + "url": "https://dynamodb.eu-south-1.amazonaws.com" } }, "params": { - "UseFIPS": false, + "Region": "eu-south-1", "UseDualStack": false, - "Region": "local" + "UseFIPS": false } }, { - "documentation": "For region af-south-1 with FIPS disabled and DualStack disabled", + "documentation": "For region eu-west-1 with FIPS disabled and DualStack disabled", "expect": { "endpoint": { - "url": "https://dynamodb.af-south-1.amazonaws.com" + "url": "https://dynamodb.eu-west-1.amazonaws.com" } }, "params": { - "UseFIPS": false, + "Region": "eu-west-1", "UseDualStack": false, - "Region": "af-south-1" + "UseFIPS": false } }, { - "documentation": "For region eu-north-1 with FIPS disabled and DualStack disabled", + "documentation": "For region eu-west-2 with FIPS disabled and DualStack disabled", "expect": { "endpoint": { - "url": "https://dynamodb.eu-north-1.amazonaws.com" + "url": "https://dynamodb.eu-west-2.amazonaws.com" } }, "params": { - "UseFIPS": false, + "Region": "eu-west-2", "UseDualStack": false, - "Region": "eu-north-1" + "UseFIPS": false } }, { - "documentation": "For region ap-east-1 with FIPS disabled and DualStack disabled", + "documentation": "For region eu-west-3 with FIPS disabled and DualStack disabled", "expect": { "endpoint": { - "url": "https://dynamodb.ap-east-1.amazonaws.com" + "url": "https://dynamodb.eu-west-3.amazonaws.com" } }, "params": { - "UseFIPS": false, + "Region": "eu-west-3", "UseDualStack": false, - "Region": "ap-east-1" + "UseFIPS": false } }, { - "documentation": "For region eu-west-1 with FIPS disabled and DualStack disabled", + "documentation": "For region local with FIPS disabled and DualStack disabled", "expect": { "endpoint": { - "url": "https://dynamodb.eu-west-1.amazonaws.com" + "properties": { + "authSchemes": [ + { + "name": "sigv4", + "signingName": "dynamodb", + "signingRegion": "us-east-1" + } + ] + }, + "url": "http://localhost:8000" } }, "params": { - "UseFIPS": false, + "Region": "local", "UseDualStack": false, - "Region": "eu-west-1" + "UseFIPS": false } }, { - "documentation": "For region eu-west-2 with FIPS disabled and DualStack disabled", + "documentation": "For region me-south-1 with FIPS disabled and DualStack disabled", "expect": { "endpoint": { - "url": "https://dynamodb.eu-west-2.amazonaws.com" + "url": "https://dynamodb.me-south-1.amazonaws.com" } }, "params": { - "UseFIPS": false, + "Region": "me-south-1", "UseDualStack": false, - "Region": "eu-west-2" + "UseFIPS": false } }, { - "documentation": "For region eu-west-3 with FIPS disabled and DualStack disabled", + "documentation": "For region sa-east-1 with FIPS disabled and DualStack disabled", "expect": { "endpoint": { - "url": "https://dynamodb.eu-west-3.amazonaws.com" + "url": "https://dynamodb.sa-east-1.amazonaws.com" } }, "params": { - "UseFIPS": false, + "Region": "sa-east-1", "UseDualStack": false, - "Region": "eu-west-3" + "UseFIPS": false } }, { @@ -4040,9 +4192,9 @@ } }, "params": { - "UseFIPS": false, + "Region": "us-east-1", "UseDualStack": false, - "Region": "us-east-1" + "UseFIPS": false } }, { @@ -4053,9 +4205,9 @@ } }, "params": { - "UseFIPS": true, + "Region": "us-east-1", "UseDualStack": false, - "Region": "us-east-1" + "UseFIPS": true } }, { @@ -4066,9 +4218,9 @@ } }, "params": { - "UseFIPS": false, + "Region": "us-east-2", "UseDualStack": false, - "Region": "us-east-2" + "UseFIPS": false } }, { @@ -4079,22 +4231,9 @@ } }, "params": { - "UseFIPS": true, - "UseDualStack": false, - "Region": "us-east-2" - } - }, - { - "documentation": "For region eu-central-1 with FIPS disabled and DualStack disabled", - "expect": { - "endpoint": { - "url": "https://dynamodb.eu-central-1.amazonaws.com" - } - }, - "params": { - "UseFIPS": false, + "Region": "us-east-2", "UseDualStack": false, - "Region": "eu-central-1" + "UseFIPS": true } }, { @@ -4105,9 +4244,9 @@ } }, "params": { - "UseFIPS": false, + "Region": "us-west-1", "UseDualStack": false, - "Region": "us-west-1" + "UseFIPS": false } }, { @@ -4118,9 +4257,9 @@ } }, "params": { - "UseFIPS": true, + "Region": "us-west-1", "UseDualStack": false, - "Region": "us-west-1" + "UseFIPS": true } }, { @@ -4131,9 +4270,9 @@ } }, "params": { - "UseFIPS": false, + "Region": "us-west-2", "UseDualStack": false, - "Region": "us-west-2" + "UseFIPS": false } }, { @@ -4144,9 +4283,9 @@ } }, "params": { - "UseFIPS": true, + "Region": "us-west-2", "UseDualStack": false, - "Region": "us-west-2" + "UseFIPS": true } }, { @@ -4157,9 +4296,9 @@ } }, "params": { - "UseFIPS": true, + "Region": "us-east-1", "UseDualStack": true, - "Region": "us-east-1" + "UseFIPS": true } }, { @@ -4170,230 +4309,243 @@ } }, "params": { - "UseFIPS": false, + "Region": "us-east-1", "UseDualStack": true, - "Region": "us-east-1" + "UseFIPS": false } }, { - "documentation": "For region us-gov-west-1 with FIPS disabled and DualStack disabled", + "documentation": "For region cn-north-1 with FIPS disabled and DualStack disabled", "expect": { "endpoint": { - "url": "https://dynamodb.us-gov-west-1.amazonaws.com" + "url": "https://dynamodb.cn-north-1.amazonaws.com.cn" } }, "params": { - "UseFIPS": false, + "Region": "cn-north-1", "UseDualStack": false, - "Region": "us-gov-west-1" + "UseFIPS": false } }, { - "documentation": "For region us-gov-west-1 with FIPS enabled and DualStack disabled", + "documentation": "For region cn-northwest-1 with FIPS disabled and DualStack disabled", "expect": { "endpoint": { - "url": "https://dynamodb.us-gov-west-1.amazonaws.com" + "url": "https://dynamodb.cn-northwest-1.amazonaws.com.cn" } }, "params": { - "UseFIPS": true, + "Region": "cn-northwest-1", "UseDualStack": false, - "Region": "us-gov-west-1" + "UseFIPS": false } }, { - "documentation": "For region us-gov-east-1 with FIPS disabled and DualStack disabled", + "documentation": "For region cn-north-1 with FIPS enabled and DualStack enabled", "expect": { "endpoint": { - "url": "https://dynamodb.us-gov-east-1.amazonaws.com" + "url": "https://dynamodb-fips.cn-north-1.api.amazonwebservices.com.cn" } }, "params": { - "UseFIPS": false, - "UseDualStack": false, - "Region": "us-gov-east-1" + "Region": "cn-north-1", + "UseDualStack": true, + "UseFIPS": true } }, { - "documentation": "For region us-gov-east-1 with FIPS enabled and DualStack disabled", + "documentation": "For region cn-north-1 with FIPS enabled and DualStack disabled", "expect": { "endpoint": { - "url": "https://dynamodb.us-gov-east-1.amazonaws.com" + "url": "https://dynamodb-fips.cn-north-1.amazonaws.com.cn" } }, "params": { - "UseFIPS": true, + "Region": "cn-north-1", "UseDualStack": false, - "Region": "us-gov-east-1" + "UseFIPS": true } }, { - "documentation": "For region us-gov-east-1 with FIPS enabled and DualStack enabled", + "documentation": "For region cn-north-1 with FIPS disabled and DualStack enabled", "expect": { "endpoint": { - "url": "https://dynamodb-fips.us-gov-east-1.api.aws" + "url": "https://dynamodb.cn-north-1.api.amazonwebservices.com.cn" } }, "params": { - "UseFIPS": true, + "Region": "cn-north-1", "UseDualStack": true, - "Region": "us-gov-east-1" + "UseFIPS": false } }, { - "documentation": "For region us-gov-east-1 with FIPS disabled and DualStack enabled", + "documentation": "For region us-gov-east-1 with FIPS disabled and DualStack disabled", "expect": { "endpoint": { - "url": "https://dynamodb.us-gov-east-1.api.aws" + "url": "https://dynamodb.us-gov-east-1.amazonaws.com" } }, "params": { - "UseFIPS": false, - "UseDualStack": true, - "Region": "us-gov-east-1" + "Region": "us-gov-east-1", + "UseDualStack": false, + "UseFIPS": false } }, { - "documentation": "For region us-isob-east-1 with FIPS disabled and DualStack disabled", + "documentation": "For region us-gov-east-1 with FIPS enabled and DualStack disabled", "expect": { "endpoint": { - "url": "https://dynamodb.us-isob-east-1.sc2s.sgov.gov" + "url": "https://dynamodb.us-gov-east-1.amazonaws.com" } }, "params": { - "UseFIPS": false, + "Region": "us-gov-east-1", "UseDualStack": false, - "Region": "us-isob-east-1" + "UseFIPS": true } }, { - "documentation": "For region us-isob-east-1 with FIPS enabled and DualStack disabled", + "documentation": "For region us-gov-west-1 with FIPS disabled and DualStack disabled", "expect": { "endpoint": { - "url": "https://dynamodb-fips.us-isob-east-1.sc2s.sgov.gov" + "url": "https://dynamodb.us-gov-west-1.amazonaws.com" } }, "params": { - "UseFIPS": true, + "Region": "us-gov-west-1", "UseDualStack": false, - "Region": "us-isob-east-1" + "UseFIPS": false } }, { - "documentation": "For region cn-northwest-1 with FIPS disabled and DualStack disabled", + "documentation": "For region us-gov-west-1 with FIPS enabled and DualStack disabled", "expect": { "endpoint": { - "url": "https://dynamodb.cn-northwest-1.amazonaws.com.cn" + "url": "https://dynamodb.us-gov-west-1.amazonaws.com" } }, "params": { - "UseFIPS": false, + "Region": "us-gov-west-1", "UseDualStack": false, - "Region": "cn-northwest-1" + "UseFIPS": true } }, { - "documentation": "For region cn-north-1 with FIPS disabled and DualStack disabled", + "documentation": "For region us-gov-east-1 with FIPS enabled and DualStack enabled", "expect": { "endpoint": { - "url": "https://dynamodb.cn-north-1.amazonaws.com.cn" + "url": "https://dynamodb-fips.us-gov-east-1.api.aws" } }, "params": { - "UseFIPS": false, - "UseDualStack": false, - "Region": "cn-north-1" + "Region": "us-gov-east-1", + "UseDualStack": true, + "UseFIPS": true } }, { - "documentation": "For region cn-north-1 with FIPS enabled and DualStack enabled", + "documentation": "For region us-gov-east-1 with FIPS disabled and DualStack enabled", "expect": { "endpoint": { - "url": "https://dynamodb-fips.cn-north-1.api.amazonwebservices.com.cn" + "url": "https://dynamodb.us-gov-east-1.api.aws" } }, "params": { - "UseFIPS": true, + "Region": "us-gov-east-1", "UseDualStack": true, - "Region": "cn-north-1" + "UseFIPS": false } }, { - "documentation": "For region cn-north-1 with FIPS enabled and DualStack disabled", + "documentation": "For region us-iso-east-1 with FIPS disabled and DualStack disabled", "expect": { "endpoint": { - "url": "https://dynamodb-fips.cn-north-1.amazonaws.com.cn" + "url": "https://dynamodb.us-iso-east-1.c2s.ic.gov" } }, "params": { - "UseFIPS": true, + "Region": "us-iso-east-1", "UseDualStack": false, - "Region": "cn-north-1" + "UseFIPS": false } }, { - "documentation": "For region cn-north-1 with FIPS disabled and DualStack enabled", + "documentation": "For region us-iso-west-1 with FIPS disabled and DualStack disabled", "expect": { "endpoint": { - "url": "https://dynamodb.cn-north-1.api.amazonwebservices.com.cn" + "url": "https://dynamodb.us-iso-west-1.c2s.ic.gov" } }, "params": { - "UseFIPS": false, - "UseDualStack": true, - "Region": "cn-north-1" + "Region": "us-iso-west-1", + "UseDualStack": false, + "UseFIPS": false } }, { - "documentation": "For region us-iso-west-1 with FIPS disabled and DualStack disabled", + "documentation": "For region us-iso-east-1 with FIPS enabled and DualStack disabled", "expect": { "endpoint": { - "url": "https://dynamodb.us-iso-west-1.c2s.ic.gov" + "url": "https://dynamodb-fips.us-iso-east-1.c2s.ic.gov" } }, "params": { - "UseFIPS": false, + "Region": "us-iso-east-1", "UseDualStack": false, - "Region": "us-iso-west-1" + "UseFIPS": true } }, { - "documentation": "For region us-iso-east-1 with FIPS disabled and DualStack disabled", + "documentation": "For region us-isob-east-1 with FIPS disabled and DualStack disabled", "expect": { "endpoint": { - "url": "https://dynamodb.us-iso-east-1.c2s.ic.gov" + "url": "https://dynamodb.us-isob-east-1.sc2s.sgov.gov" } }, "params": { - "UseFIPS": false, + "Region": "us-isob-east-1", "UseDualStack": false, - "Region": "us-iso-east-1" + "UseFIPS": false } }, { - "documentation": "For region us-iso-east-1 with FIPS enabled and DualStack disabled", + "documentation": "For region us-isob-east-1 with FIPS enabled and DualStack disabled", "expect": { "endpoint": { - "url": "https://dynamodb-fips.us-iso-east-1.c2s.ic.gov" + "url": "https://dynamodb-fips.us-isob-east-1.sc2s.sgov.gov" } }, "params": { - "UseFIPS": true, + "Region": "us-isob-east-1", "UseDualStack": false, - "Region": "us-iso-east-1" + "UseFIPS": true } }, { - "documentation": "For custom endpoint with fips disabled and dualstack disabled", + "documentation": "For custom endpoint with region set and fips disabled and dualstack disabled", "expect": { "endpoint": { "url": "https://example.com" } }, "params": { + "Region": "us-east-1", + "UseDualStack": false, "UseFIPS": 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": { "UseDualStack": false, - "Region": "us-east-1", + "UseFIPS": false, "Endpoint": "https://example.com" } }, @@ -4403,9 +4555,9 @@ "error": "Invalid Configuration: FIPS and custom endpoint are not supported" }, "params": { - "UseFIPS": true, - "UseDualStack": false, "Region": "us-east-1", + "UseDualStack": false, + "UseFIPS": true, "Endpoint": "https://example.com" } }, @@ -4415,9 +4567,9 @@ "error": "Invalid Configuration: Dualstack and custom endpoint are not supported" }, "params": { - "UseFIPS": false, - "UseDualStack": true, "Region": "us-east-1", + "UseDualStack": true, + "UseFIPS": false, "Endpoint": "https://example.com" } } @@ -4540,7 +4692,7 @@ } ], "traits": { - "smithy.api#documentation": "

This operation allows you to perform reads and singleton writes on data stored in\n DynamoDB, using PartiQL.

\n

For PartiQL reads (SELECT statement), if the total number of processed\n items exceeds the maximum dataset size limit of 1 MB, the read stops and results are\n returned to the user as a LastEvaluatedKey value to continue the read in a\n subsequent operation. If the filter criteria in WHERE clause does not match\n any data, the read will return an empty result set.

\n

A single SELECT statement response can return up to the maximum number of\n items (if using the Limit parameter) or a maximum of 1 MB of data (and then apply any\n filtering to the results using WHERE clause). If\n LastEvaluatedKey is present in the response, you need to paginate the\n result set.

" + "smithy.api#documentation": "

This operation allows you to perform reads and singleton writes on data stored in\n DynamoDB, using PartiQL.

\n

For PartiQL reads (SELECT statement), if the total number of processed\n items exceeds the maximum dataset size limit of 1 MB, the read stops and results are\n returned to the user as a LastEvaluatedKey value to continue the read in a\n subsequent operation. If the filter criteria in WHERE clause does not match\n any data, the read will return an empty result set.

\n

A single SELECT statement response can return up to the maximum number of\n items (if using the Limit parameter) or a maximum of 1 MB of data (and then apply any\n filtering to the results using WHERE clause). If\n LastEvaluatedKey is present in the response, you need to paginate the\n result set. If NextToken is present, you need to paginate the result set and include \n NextToken.

" } }, "com.amazonaws.dynamodb#ExecuteStatementInput": { @@ -4580,6 +4732,9 @@ "smithy.api#documentation": "

The maximum number of items to evaluate (not necessarily the number of matching\n items). If DynamoDB processes the number of items up to the limit while processing the\n results, it stops the operation and returns the matching values up to that point, along\n with a key in LastEvaluatedKey to apply in a subsequent operation so you\n can pick up where you left off. Also, if the processed dataset size exceeds 1 MB before\n DynamoDB reaches this limit, it stops the operation and returns the matching values up\n to the limit, and a key in LastEvaluatedKey to apply in a subsequent\n operation to continue the operation.

" } } + }, + "traits": { + "smithy.api#input": {} } }, "com.amazonaws.dynamodb#ExecuteStatementOutput": { @@ -4606,6 +4761,9 @@ "smithy.api#documentation": "

The primary key of the item where the operation stopped, inclusive of the previous\n result set. Use this value to start a new operation, excluding this value in the new\n request. If LastEvaluatedKey is empty, then the \"last page\" of results has\n been processed and there is no more data to be retrieved. If\n LastEvaluatedKey is not empty, it does not necessarily mean that there\n is more data in the result set. The only way to know when you have reached the end of\n the result set is when LastEvaluatedKey is empty.

" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.dynamodb#ExecuteTransaction": { @@ -4640,7 +4798,7 @@ } ], "traits": { - "smithy.api#documentation": "

This operation allows you to perform transactional reads or writes on data stored in\n DynamoDB, using PartiQL.

\n \n

The entire transaction must consist of either read statements or write statements,\n you cannot mix both in one transaction. The EXISTS function is an exception and can\n be used to check the condition of specific attributes of the item in a similar\n manner to ConditionCheck in the TransactWriteItems API.

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

This operation allows you to perform transactional reads or writes on data stored in\n DynamoDB, using PartiQL.

\n \n

The entire transaction must consist of either read statements or write statements,\n you cannot mix both in one transaction. The EXISTS function is an exception and can\n be used to check the condition of specific attributes of the item in a similar\n manner to ConditionCheck in the TransactWriteItems API.

\n
" } }, "com.amazonaws.dynamodb#ExecuteTransactionInput": { @@ -4666,6 +4824,9 @@ "smithy.api#documentation": "

Determines the level of detail about either provisioned or on-demand throughput\n consumption that is returned in the response. For more information, see TransactGetItems and TransactWriteItems.

" } } + }, + "traits": { + "smithy.api#input": {} } }, "com.amazonaws.dynamodb#ExecuteTransactionOutput": { @@ -4683,6 +4844,9 @@ "smithy.api#documentation": "

The capacity units consumed by the entire operation. The values of the list are\n ordered according to the ordering of the statements.

" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.dynamodb#ExpectedAttributeMap": { @@ -4700,30 +4864,30 @@ "Value": { "target": "com.amazonaws.dynamodb#AttributeValue", "traits": { - "smithy.api#documentation": "

Represents the data for the expected attribute.

\n

Each attribute value is described as a name-value pair. The name is the data type, and\n the value is the data itself.

\n

For more information, see Data Types in the Amazon DynamoDB Developer\n Guide.

" + "smithy.api#documentation": "

Represents the data for the expected attribute.

\n

Each attribute value is described as a name-value pair. The name is the data type, and\n the value is the data itself.

\n

For more information, see Data Types in the Amazon DynamoDB Developer\n Guide.

" } }, "Exists": { "target": "com.amazonaws.dynamodb#BooleanObject", "traits": { - "smithy.api#documentation": "

Causes DynamoDB to evaluate the value before attempting a conditional\n operation:

\n
    \n
  • \n

    If Exists is true, DynamoDB will check to\n see if that attribute value already exists in the table. If it is found, then\n the operation succeeds. If it is not found, the operation fails with a\n ConditionCheckFailedException.

    \n
  • \n
  • \n

    If Exists is false, DynamoDB assumes that\n the attribute value does not exist in the table. If in fact the value does not\n exist, then the assumption is valid and the operation succeeds. If the value is\n found, despite the assumption that it does not exist, the operation fails with a\n ConditionCheckFailedException.

    \n
  • \n
\n

The default setting for Exists is true. If you supply a\n Value all by itself, DynamoDB assumes the attribute exists:\n You don't have to set Exists to true, because it is\n implied.

\n

DynamoDB returns a ValidationException if:

\n
    \n
  • \n

    \n Exists is true but there is no Value to\n check. (You expect a value to exist, but don't specify what that value\n is.)

    \n
  • \n
  • \n

    \n Exists is false but you also provide a\n Value. (You cannot expect an attribute to have a value, while\n also expecting it not to exist.)

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

Causes DynamoDB to evaluate the value before attempting a conditional\n operation:

\n
    \n
  • \n

    If Exists is true, DynamoDB will check to\n see if that attribute value already exists in the table. If it is found, then\n the operation succeeds. If it is not found, the operation fails with a\n ConditionCheckFailedException.

    \n
  • \n
  • \n

    If Exists is false, DynamoDB assumes that\n the attribute value does not exist in the table. If in fact the value does not\n exist, then the assumption is valid and the operation succeeds. If the value is\n found, despite the assumption that it does not exist, the operation fails with a\n ConditionCheckFailedException.

    \n
  • \n
\n

The default setting for Exists is true. If you supply a\n Value all by itself, DynamoDB assumes the attribute exists:\n You don't have to set Exists to true, because it is\n implied.

\n

DynamoDB returns a ValidationException if:

\n
    \n
  • \n

    \n Exists is true but there is no Value to\n check. (You expect a value to exist, but don't specify what that value\n is.)

    \n
  • \n
  • \n

    \n Exists is false but you also provide a\n Value. (You cannot expect an attribute to have a value, while\n also expecting it not to exist.)

    \n
  • \n
" } }, "ComparisonOperator": { "target": "com.amazonaws.dynamodb#ComparisonOperator", "traits": { - "smithy.api#documentation": "

A comparator for evaluating attributes in the AttributeValueList. For\n example, equals, greater than, less than, etc.

\n

The following comparison operators are available:

\n

\n EQ | NE | LE | LT | GE | GT | NOT_NULL | NULL | CONTAINS | NOT_CONTAINS |\n BEGINS_WITH | IN | BETWEEN\n

\n

The following are descriptions of each comparison operator.

\n
    \n
  • \n

    \n EQ : Equal. EQ is supported for all data types,\n including lists and maps.

    \n

    \n AttributeValueList can contain only one AttributeValue\n element of type String, Number, Binary, String Set, Number Set, or Binary Set.\n If an item contains an AttributeValue element of a different type\n than the one provided in the request, the value does not match. For example,\n {\"S\":\"6\"} does not equal {\"N\":\"6\"}. Also,\n {\"N\":\"6\"} does not equal {\"NS\":[\"6\", \"2\",\n \"1\"]}.

    \n

    \n
  • \n
  • \n

    \n NE : Not equal. NE is supported for all data types,\n including lists and maps.

    \n

    \n AttributeValueList can contain only one AttributeValue\n of type String, Number, Binary, String Set, Number Set, or Binary Set. If an\n item contains an AttributeValue of a different type than the one\n provided in the request, the value does not match. For example,\n {\"S\":\"6\"} does not equal {\"N\":\"6\"}. Also,\n {\"N\":\"6\"} does not equal {\"NS\":[\"6\", \"2\",\n \"1\"]}.

    \n

    \n
  • \n
  • \n

    \n LE : Less than or equal.

    \n

    \n AttributeValueList can contain only one AttributeValue\n element of type String, Number, or Binary (not a set type). If an item contains\n an AttributeValue element of a different type than the one provided\n in the request, the value does not match. For example, {\"S\":\"6\"}\n does not equal {\"N\":\"6\"}. Also, {\"N\":\"6\"} does not\n compare to {\"NS\":[\"6\", \"2\", \"1\"]}.

    \n

    \n
  • \n
  • \n

    \n LT : Less than.

    \n

    \n AttributeValueList can contain only one AttributeValue\n of type String, Number, or Binary (not a set type). If an item contains an\n AttributeValue element of a different type than the one\n provided in the request, the value does not match. For example,\n {\"S\":\"6\"} does not equal {\"N\":\"6\"}. Also,\n {\"N\":\"6\"} does not compare to {\"NS\":[\"6\", \"2\",\n \"1\"]}.

    \n

    \n
  • \n
  • \n

    \n GE : Greater than or equal.

    \n

    \n AttributeValueList can contain only one AttributeValue\n element of type String, Number, or Binary (not a set type). If an item contains\n an AttributeValue element of a different type than the one provided\n in the request, the value does not match. For example, {\"S\":\"6\"}\n does not equal {\"N\":\"6\"}. Also, {\"N\":\"6\"} does not\n compare to {\"NS\":[\"6\", \"2\", \"1\"]}.

    \n

    \n
  • \n
  • \n

    \n GT : Greater than.

    \n

    \n AttributeValueList can contain only one AttributeValue\n element of type String, Number, or Binary (not a set type). If an item contains\n an AttributeValue element of a different type than the one provided\n in the request, the value does not match. For example, {\"S\":\"6\"}\n does not equal {\"N\":\"6\"}. Also, {\"N\":\"6\"} does not\n compare to {\"NS\":[\"6\", \"2\", \"1\"]}.

    \n

    \n
  • \n
  • \n

    \n NOT_NULL : The attribute exists. NOT_NULL is supported\n for all data types, including lists and maps.

    \n \n

    This operator tests for the existence of an attribute, not its data type.\n If the data type of attribute \"a\" is null, and you evaluate it\n using NOT_NULL, the result is a Boolean true. This\n result is because the attribute \"a\" exists; its data type is\n not relevant to the NOT_NULL comparison operator.

    \n
    \n
  • \n
  • \n

    \n NULL : The attribute does not exist. NULL is supported\n for all data types, including lists and maps.

    \n \n

    This operator tests for the nonexistence of an attribute, not its data\n type. If the data type of attribute \"a\" is null, and you\n evaluate it using NULL, the result is a Boolean\n false. This is because the attribute \"a\"\n exists; its data type is not relevant to the NULL comparison\n operator.

    \n
    \n
  • \n
  • \n

    \n CONTAINS : Checks for a subsequence, or value in a set.

    \n

    \n AttributeValueList can contain only one AttributeValue\n element of type String, Number, or Binary (not a set type). If the target\n attribute of the comparison is of type String, then the operator checks for a\n substring match. If the target attribute of the comparison is of type Binary,\n then the operator looks for a subsequence of the target that matches the input.\n If the target attribute of the comparison is a set (\"SS\",\n \"NS\", or \"BS\"), then the operator evaluates to\n true if it finds an exact match with any member of the set.

    \n

    CONTAINS is supported for lists: When evaluating \"a CONTAINS b\",\n \"a\" can be a list; however, \"b\" cannot be a set, a\n map, or a list.

    \n
  • \n
  • \n

    \n NOT_CONTAINS : Checks for absence of a subsequence, or absence of a\n value in a set.

    \n

    \n AttributeValueList can contain only one AttributeValue\n element of type String, Number, or Binary (not a set type). If the target\n attribute of the comparison is a String, then the operator checks for the\n absence of a substring match. If the target attribute of the comparison is\n Binary, then the operator checks for the absence of a subsequence of the target\n that matches the input. If the target attribute of the comparison is a set\n (\"SS\", \"NS\", or \"BS\"), then the\n operator evaluates to true if it does not find an exact\n match with any member of the set.

    \n

    NOT_CONTAINS is supported for lists: When evaluating \"a NOT CONTAINS\n b\", \"a\" can be a list; however, \"b\" cannot\n be a set, a map, or a list.

    \n
  • \n
  • \n

    \n BEGINS_WITH : Checks for a prefix.

    \n

    \n AttributeValueList can contain only one AttributeValue\n of type String or Binary (not a Number or a set type). The target attribute of\n the comparison must be of type String or Binary (not a Number or a set\n type).

    \n

    \n
  • \n
  • \n

    \n IN : Checks for matching elements in a list.

    \n

    \n AttributeValueList can contain one or more\n AttributeValue elements of type String, Number, or Binary.\n These attributes are compared against an existing attribute of an item. If any\n elements of the input are equal to the item attribute, the expression evaluates\n to true.

    \n
  • \n
  • \n

    \n BETWEEN : Greater than or equal to the first value, and less than\n or equal to the second value.

    \n

    \n AttributeValueList must contain two AttributeValue\n elements of the same type, either String, Number, or Binary (not a set type). A\n target attribute matches if the target value is greater than, or equal to, the\n first element and less than, or equal to, the second element. If an item\n contains an AttributeValue element of a different type than the one\n provided in the request, the value does not match. For example,\n {\"S\":\"6\"} does not compare to {\"N\":\"6\"}. Also,\n {\"N\":\"6\"} does not compare to {\"NS\":[\"6\", \"2\",\n \"1\"]}\n

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

A comparator for evaluating attributes in the AttributeValueList. For\n example, equals, greater than, less than, etc.

\n

The following comparison operators are available:

\n

\n EQ | NE | LE | LT | GE | GT | NOT_NULL | NULL | CONTAINS | NOT_CONTAINS |\n BEGINS_WITH | IN | BETWEEN\n

\n

The following are descriptions of each comparison operator.

\n
    \n
  • \n

    \n EQ : Equal. EQ is supported for all data types,\n including lists and maps.

    \n

    \n AttributeValueList can contain only one AttributeValue\n element of type String, Number, Binary, String Set, Number Set, or Binary Set.\n If an item contains an AttributeValue element of a different type\n than the one provided in the request, the value does not match. For example,\n {\"S\":\"6\"} does not equal {\"N\":\"6\"}. Also,\n {\"N\":\"6\"} does not equal {\"NS\":[\"6\", \"2\",\n \"1\"]}.

    \n

    \n
  • \n
  • \n

    \n NE : Not equal. NE is supported for all data types,\n including lists and maps.

    \n

    \n AttributeValueList can contain only one AttributeValue\n of type String, Number, Binary, String Set, Number Set, or Binary Set. If an\n item contains an AttributeValue of a different type than the one\n provided in the request, the value does not match. For example,\n {\"S\":\"6\"} does not equal {\"N\":\"6\"}. Also,\n {\"N\":\"6\"} does not equal {\"NS\":[\"6\", \"2\",\n \"1\"]}.

    \n

    \n
  • \n
  • \n

    \n LE : Less than or equal.

    \n

    \n AttributeValueList can contain only one AttributeValue\n element of type String, Number, or Binary (not a set type). If an item contains\n an AttributeValue element of a different type than the one provided\n in the request, the value does not match. For example, {\"S\":\"6\"}\n does not equal {\"N\":\"6\"}. Also, {\"N\":\"6\"} does not\n compare to {\"NS\":[\"6\", \"2\", \"1\"]}.

    \n

    \n
  • \n
  • \n

    \n LT : Less than.

    \n

    \n AttributeValueList can contain only one AttributeValue\n of type String, Number, or Binary (not a set type). If an item contains an\n AttributeValue element of a different type than the one\n provided in the request, the value does not match. For example,\n {\"S\":\"6\"} does not equal {\"N\":\"6\"}. Also,\n {\"N\":\"6\"} does not compare to {\"NS\":[\"6\", \"2\",\n \"1\"]}.

    \n

    \n
  • \n
  • \n

    \n GE : Greater than or equal.

    \n

    \n AttributeValueList can contain only one AttributeValue\n element of type String, Number, or Binary (not a set type). If an item contains\n an AttributeValue element of a different type than the one provided\n in the request, the value does not match. For example, {\"S\":\"6\"}\n does not equal {\"N\":\"6\"}. Also, {\"N\":\"6\"} does not\n compare to {\"NS\":[\"6\", \"2\", \"1\"]}.

    \n

    \n
  • \n
  • \n

    \n GT : Greater than.

    \n

    \n AttributeValueList can contain only one AttributeValue\n element of type String, Number, or Binary (not a set type). If an item contains\n an AttributeValue element of a different type than the one provided\n in the request, the value does not match. For example, {\"S\":\"6\"}\n does not equal {\"N\":\"6\"}. Also, {\"N\":\"6\"} does not\n compare to {\"NS\":[\"6\", \"2\", \"1\"]}.

    \n

    \n
  • \n
  • \n

    \n NOT_NULL : The attribute exists. NOT_NULL is supported\n for all data types, including lists and maps.

    \n \n

    This operator tests for the existence of an attribute, not its data type.\n If the data type of attribute \"a\" is null, and you evaluate it\n using NOT_NULL, the result is a Boolean true. This\n result is because the attribute \"a\" exists; its data type is\n not relevant to the NOT_NULL comparison operator.

    \n
    \n
  • \n
  • \n

    \n NULL : The attribute does not exist. NULL is supported\n for all data types, including lists and maps.

    \n \n

    This operator tests for the nonexistence of an attribute, not its data\n type. If the data type of attribute \"a\" is null, and you\n evaluate it using NULL, the result is a Boolean\n false. This is because the attribute \"a\"\n exists; its data type is not relevant to the NULL comparison\n operator.

    \n
    \n
  • \n
  • \n

    \n CONTAINS : Checks for a subsequence, or value in a set.

    \n

    \n AttributeValueList can contain only one AttributeValue\n element of type String, Number, or Binary (not a set type). If the target\n attribute of the comparison is of type String, then the operator checks for a\n substring match. If the target attribute of the comparison is of type Binary,\n then the operator looks for a subsequence of the target that matches the input.\n If the target attribute of the comparison is a set (\"SS\",\n \"NS\", or \"BS\"), then the operator evaluates to\n true if it finds an exact match with any member of the set.

    \n

    CONTAINS is supported for lists: When evaluating \"a CONTAINS b\",\n \"a\" can be a list; however, \"b\" cannot be a set, a\n map, or a list.

    \n
  • \n
  • \n

    \n NOT_CONTAINS : Checks for absence of a subsequence, or absence of a\n value in a set.

    \n

    \n AttributeValueList can contain only one AttributeValue\n element of type String, Number, or Binary (not a set type). If the target\n attribute of the comparison is a String, then the operator checks for the\n absence of a substring match. If the target attribute of the comparison is\n Binary, then the operator checks for the absence of a subsequence of the target\n that matches the input. If the target attribute of the comparison is a set\n (\"SS\", \"NS\", or \"BS\"), then the\n operator evaluates to true if it does not find an exact\n match with any member of the set.

    \n

    NOT_CONTAINS is supported for lists: When evaluating \"a NOT CONTAINS\n b\", \"a\" can be a list; however, \"b\" cannot\n be a set, a map, or a list.

    \n
  • \n
  • \n

    \n BEGINS_WITH : Checks for a prefix.

    \n

    \n AttributeValueList can contain only one AttributeValue\n of type String or Binary (not a Number or a set type). The target attribute of\n the comparison must be of type String or Binary (not a Number or a set\n type).

    \n

    \n
  • \n
  • \n

    \n IN : Checks for matching elements in a list.

    \n

    \n AttributeValueList can contain one or more\n AttributeValue elements of type String, Number, or Binary.\n These attributes are compared against an existing attribute of an item. If any\n elements of the input are equal to the item attribute, the expression evaluates\n to true.

    \n
  • \n
  • \n

    \n BETWEEN : Greater than or equal to the first value, and less than\n or equal to the second value.

    \n

    \n AttributeValueList must contain two AttributeValue\n elements of the same type, either String, Number, or Binary (not a set type). A\n target attribute matches if the target value is greater than, or equal to, the\n first element and less than, or equal to, the second element. If an item\n contains an AttributeValue element of a different type than the one\n provided in the request, the value does not match. For example,\n {\"S\":\"6\"} does not compare to {\"N\":\"6\"}. Also,\n {\"N\":\"6\"} does not compare to {\"NS\":[\"6\", \"2\",\n \"1\"]}\n

    \n
  • \n
" } }, "AttributeValueList": { "target": "com.amazonaws.dynamodb#AttributeValueList", "traits": { - "smithy.api#documentation": "

One or more values to evaluate against the supplied attribute. The number of values in\n the list depends on the ComparisonOperator being used.

\n

For type Number, value comparisons are numeric.

\n

String value comparisons for greater than, equals, or less than are based on ASCII\n character code values. For example, a is greater than A, and\n a is greater than B. For a list of code values, see http://en.wikipedia.org/wiki/ASCII#ASCII_printable_characters.

\n

For Binary, DynamoDB treats each byte of the binary data as unsigned when it\n compares binary values.

\n

For information on specifying data types in JSON, see JSON Data Format\n in the Amazon DynamoDB Developer Guide.

" + "smithy.api#documentation": "

One or more values to evaluate against the supplied attribute. The number of values in\n the list depends on the ComparisonOperator being used.

\n

For type Number, value comparisons are numeric.

\n

String value comparisons for greater than, equals, or less than are based on ASCII\n character code values. For example, a is greater than A, and\n a is greater than B. For a list of code values, see http://en.wikipedia.org/wiki/ASCII#ASCII_printable_characters.

\n

For Binary, DynamoDB treats each byte of the binary data as unsigned when it\n compares binary values.

\n

For information on specifying data types in JSON, see JSON Data Format\n in the Amazon DynamoDB Developer Guide.

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

Represents a condition to be compared with an attribute value. This condition can be\n used with DeleteItem, PutItem, or UpdateItem\n operations; if the comparison evaluates to true, the operation succeeds; if not, the\n operation fails. You can use ExpectedAttributeValue in one of two different\n ways:

\n
    \n
  • \n

    Use AttributeValueList to specify one or more values to compare\n against an attribute. Use ComparisonOperator to specify how you\n want to perform the comparison. If the comparison evaluates to true, then the\n conditional operation succeeds.

    \n
  • \n
  • \n

    Use Value to specify a value that DynamoDB will compare against\n an attribute. If the values match, then ExpectedAttributeValue\n evaluates to true and the conditional operation succeeds. Optionally, you can\n also set Exists to false, indicating that you do\n not expect to find the attribute value in the table. In this\n case, the conditional operation succeeds only if the comparison evaluates to\n false.

    \n
  • \n
\n

\n Value and Exists are incompatible with\n AttributeValueList and ComparisonOperator. Note that if\n you use both sets of parameters at once, DynamoDB will return a\n ValidationException exception.

" + "smithy.api#documentation": "

Represents a condition to be compared with an attribute value. This condition can be\n used with DeleteItem, PutItem, or UpdateItem\n operations; if the comparison evaluates to true, the operation succeeds; if not, the\n operation fails. You can use ExpectedAttributeValue in one of two different\n ways:

\n
    \n
  • \n

    Use AttributeValueList to specify one or more values to compare\n against an attribute. Use ComparisonOperator to specify how you\n want to perform the comparison. If the comparison evaluates to true, then the\n conditional operation succeeds.

    \n
  • \n
  • \n

    Use Value to specify a value that DynamoDB will compare against\n an attribute. If the values match, then ExpectedAttributeValue\n evaluates to true and the conditional operation succeeds. Optionally, you can\n also set Exists to false, indicating that you do\n not expect to find the attribute value in the table. In this\n case, the conditional operation succeeds only if the comparison evaluates to\n false.

    \n
  • \n
\n

\n Value and Exists are incompatible with\n AttributeValueList and ComparisonOperator. Note that if\n you use both sets of parameters at once, DynamoDB will return a\n ValidationException exception.

" } }, "com.amazonaws.dynamodb#ExportArn": { @@ -4825,7 +4989,7 @@ "S3SseAlgorithm": { "target": "com.amazonaws.dynamodb#S3SseAlgorithm", "traits": { - "smithy.api#documentation": "

Type of encryption used on the bucket where export data is stored. Valid values for\n S3SseAlgorithm are:

\n
    \n
  • \n

    \n AES256 - server-side encryption with Amazon S3 managed\n keys

    \n
  • \n
  • \n

    \n KMS - server-side encryption with KMS managed\n keys

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

Type of encryption used on the bucket where export data is stored. Valid values for\n S3SseAlgorithm are:

\n
    \n
  • \n

    \n AES256 - server-side encryption with Amazon S3 managed\n keys

    \n
  • \n
  • \n

    \n KMS - server-side encryption with KMS managed\n keys

    \n
  • \n
" } }, "S3SseKmsKeyId": { @@ -5010,7 +5174,7 @@ "ClientToken": { "target": "com.amazonaws.dynamodb#ClientToken", "traits": { - "smithy.api#documentation": "

Providing a ClientToken makes the call to\n ExportTableToPointInTimeInput idempotent, meaning that multiple\n identical calls have the same effect as one single call.

\n

A client token is valid for 8 hours after the first request that uses it is completed.\n After 8 hours, any request with the same client token is treated as a new request. Do\n not resubmit the same request with the same client token for more than 8 hours, or the\n result might not be idempotent.

\n

If you submit a request with the same client token but a change in other parameters\n within the 8-hour idempotency window, DynamoDB returns an\n ImportConflictException.

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

Providing a ClientToken makes the call to\n ExportTableToPointInTimeInput idempotent, meaning that multiple\n identical calls have the same effect as one single call.

\n

A client token is valid for 8 hours after the first request that uses it is completed.\n After 8 hours, any request with the same client token is treated as a new request. Do\n not resubmit the same request with the same client token for more than 8 hours, or the\n result might not be idempotent.

\n

If you submit a request with the same client token but a change in other parameters\n within the 8-hour idempotency window, DynamoDB returns an\n ImportConflictException.

", "smithy.api#idempotencyToken": {} } }, @@ -5036,7 +5200,7 @@ "S3SseAlgorithm": { "target": "com.amazonaws.dynamodb#S3SseAlgorithm", "traits": { - "smithy.api#documentation": "

Type of encryption used on the bucket where export data will be stored. Valid values\n for S3SseAlgorithm are:

\n
    \n
  • \n

    \n AES256 - server-side encryption with Amazon S3 managed\n keys

    \n
  • \n
  • \n

    \n KMS - server-side encryption with KMS managed\n keys

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

Type of encryption used on the bucket where export data will be stored. Valid values\n for S3SseAlgorithm are:

\n
    \n
  • \n

    \n AES256 - server-side encryption with Amazon S3 managed\n keys

    \n
  • \n
  • \n

    \n KMS - server-side encryption with KMS managed\n keys

    \n
  • \n
" } }, "S3SseKmsKeyId": { @@ -5051,6 +5215,9 @@ "smithy.api#documentation": "

The format for the exported data. Valid values for ExportFormat are\n DYNAMODB_JSON or ION.

" } } + }, + "traits": { + "smithy.api#input": {} } }, "com.amazonaws.dynamodb#ExportTableToPointInTimeOutput": { @@ -5062,6 +5229,9 @@ "smithy.api#documentation": "

Contains a description of the table export.

" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.dynamodb#ExportTime": { @@ -5189,7 +5359,7 @@ "aws.api#clientDiscoveredEndpoint": { "required": false }, - "smithy.api#documentation": "

The GetItem operation returns a set of attributes for the item with the\n given primary key. If there is no matching item, GetItem does not return\n any data and there will be no Item element in the response.

\n

\n GetItem provides an eventually consistent read by default. If your\n application requires a strongly consistent read, set ConsistentRead to\n true. Although a strongly consistent read might take more time than an\n eventually consistent read, it always returns the last updated value.

" + "smithy.api#documentation": "

The GetItem operation returns a set of attributes for the item with the\n given primary key. If there is no matching item, GetItem does not return\n any data and there will be no Item element in the response.

\n

\n GetItem provides an eventually consistent read by default. If your\n application requires a strongly consistent read, set ConsistentRead to\n true. Although a strongly consistent read might take more time than an\n eventually consistent read, it always returns the last updated value.

" } }, "com.amazonaws.dynamodb#GetItemInput": { @@ -5205,7 +5375,7 @@ "Key": { "target": "com.amazonaws.dynamodb#Key", "traits": { - "smithy.api#documentation": "

A map of attribute names to AttributeValue objects, representing the\n primary key of the item to retrieve.

\n

For the primary key, you must provide all of the attributes. For example, with a\n simple primary key, you only need to provide a value for the partition key. For a\n composite primary key, you must provide values for both the partition key and the sort\n key.

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

A map of attribute names to AttributeValue objects, representing the\n primary key of the item to retrieve.

\n

For the primary key, you must provide all of the attributes. For example, with a\n simple primary key, you only need to provide a value for the partition key. For a\n composite primary key, you must provide values for both the partition key and the sort\n key.

", "smithy.api#required": {} } }, @@ -5227,18 +5397,19 @@ "ProjectionExpression": { "target": "com.amazonaws.dynamodb#ProjectionExpression", "traits": { - "smithy.api#documentation": "

A string that identifies one or more attributes to retrieve from the table. These\n attributes can include scalars, sets, or elements of a JSON document. The attributes in\n the expression must be separated by commas.

\n

If no attribute names are specified, then all attributes are returned. If any of the\n requested attributes are not found, they do not appear in the result.

\n

For more information, see Specifying Item Attributes in the Amazon DynamoDB Developer\n Guide.

" + "smithy.api#documentation": "

A string that identifies one or more attributes to retrieve from the table. These\n attributes can include scalars, sets, or elements of a JSON document. The attributes in\n the expression must be separated by commas.

\n

If no attribute names are specified, then all attributes are returned. If any of the\n requested attributes are not found, they do not appear in the result.

\n

For more information, see Specifying Item Attributes in the Amazon DynamoDB Developer\n Guide.

" } }, "ExpressionAttributeNames": { "target": "com.amazonaws.dynamodb#ExpressionAttributeNameMap", "traits": { - "smithy.api#documentation": "

One or more substitution tokens for attribute names in an expression. The following\n are some use cases for using ExpressionAttributeNames:

\n
    \n
  • \n

    To access an attribute whose name conflicts with a DynamoDB reserved\n word.

    \n
  • \n
  • \n

    To create a placeholder for repeating occurrences of an attribute name in an\n expression.

    \n
  • \n
  • \n

    To prevent special characters in an attribute name from being misinterpreted\n in an expression.

    \n
  • \n
\n

Use the # character in an expression to dereference\n an attribute name. For example, consider the following attribute name:

\n
    \n
  • \n

    \n Percentile\n

    \n
  • \n
\n

The name of this attribute conflicts with a reserved word, so it cannot be used\n directly in an expression. (For the complete list of reserved words, see Reserved Words in the Amazon DynamoDB Developer\n Guide). To work around this, you could specify the following for\n ExpressionAttributeNames:

\n
    \n
  • \n

    \n {\"#P\":\"Percentile\"}\n

    \n
  • \n
\n

You could then use this substitution in an expression, as in this example:

\n
    \n
  • \n

    \n #P = :val\n

    \n
  • \n
\n \n

Tokens that begin with the : character are\n expression attribute values, which are placeholders for the\n actual value at runtime.

\n
\n

For more information on expression attribute names, see Specifying Item Attributes in the Amazon DynamoDB Developer\n Guide.

" + "smithy.api#documentation": "

One or more substitution tokens for attribute names in an expression. The following\n are some use cases for using ExpressionAttributeNames:

\n
    \n
  • \n

    To access an attribute whose name conflicts with a DynamoDB reserved\n word.

    \n
  • \n
  • \n

    To create a placeholder for repeating occurrences of an attribute name in an\n expression.

    \n
  • \n
  • \n

    To prevent special characters in an attribute name from being misinterpreted\n in an expression.

    \n
  • \n
\n

Use the # character in an expression to dereference\n an attribute name. For example, consider the following attribute name:

\n
    \n
  • \n

    \n Percentile\n

    \n
  • \n
\n

The name of this attribute conflicts with a reserved word, so it cannot be used\n directly in an expression. (For the complete list of reserved words, see Reserved Words in the Amazon DynamoDB Developer\n Guide). To work around this, you could specify the following for\n ExpressionAttributeNames:

\n
    \n
  • \n

    \n {\"#P\":\"Percentile\"}\n

    \n
  • \n
\n

You could then use this substitution in an expression, as in this example:

\n
    \n
  • \n

    \n #P = :val\n

    \n
  • \n
\n \n

Tokens that begin with the : character are\n expression attribute values, which are placeholders for the\n actual value at runtime.

\n
\n

For more information on expression attribute names, see Specifying Item Attributes in the Amazon DynamoDB Developer\n Guide.

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

Represents the input of a GetItem operation.

" + "smithy.api#documentation": "

Represents the input of a GetItem operation.

", + "smithy.api#input": {} } }, "com.amazonaws.dynamodb#GetItemOutput": { @@ -5253,12 +5424,13 @@ "ConsumedCapacity": { "target": "com.amazonaws.dynamodb#ConsumedCapacity", "traits": { - "smithy.api#documentation": "

The capacity units consumed by the GetItem operation. The data returned\n includes the total provisioned throughput consumed, along with statistics for the table\n and any indexes involved in the operation. ConsumedCapacity is only\n returned if the ReturnConsumedCapacity parameter was specified. For more\n information, see Read/Write Capacity Mode in the Amazon DynamoDB Developer\n Guide.

" + "smithy.api#documentation": "

The capacity units consumed by the GetItem operation. The data returned\n includes the total provisioned throughput consumed, along with statistics for the table\n and any indexes involved in the operation. ConsumedCapacity is only\n returned if the ReturnConsumedCapacity parameter was specified. For more\n information, see Provisioned Throughput in the Amazon DynamoDB Developer\n Guide.

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

Represents the output of a GetItem operation.

" + "smithy.api#documentation": "

Represents the output of a GetItem operation.

", + "smithy.api#output": {} } }, "com.amazonaws.dynamodb#GlobalSecondaryIndex": { @@ -5274,7 +5446,7 @@ "KeySchema": { "target": "com.amazonaws.dynamodb#KeySchema", "traits": { - "smithy.api#documentation": "

The complete key schema for a global secondary index, which consists of one or more\n pairs of attribute names and key types:

\n
    \n
  • \n

    \n HASH - partition key

    \n
  • \n
  • \n

    \n RANGE - sort key

    \n
  • \n
\n \n

The partition key of an item is also known as its hash\n attribute. The term \"hash attribute\" derives from DynamoDB's usage of\n an internal hash function to evenly distribute data items across partitions, based\n on their partition key values.

\n

The sort key of an item is also known as its range attribute.\n The term \"range attribute\" derives from the way DynamoDB stores items with the same\n partition key physically close together, in sorted order by the sort key\n value.

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

The complete key schema for a global secondary index, which consists of one or more\n pairs of attribute names and key types:

\n
    \n
  • \n

    \n HASH - partition key

    \n
  • \n
  • \n

    \n RANGE - sort key

    \n
  • \n
\n \n

The partition key of an item is also known as its hash\n attribute. The term \"hash attribute\" derives from DynamoDB's usage of\n an internal hash function to evenly distribute data items across partitions, based\n on their partition key values.

\n

The sort key of an item is also known as its range attribute.\n The term \"range attribute\" derives from the way DynamoDB stores items with the same\n partition key physically close together, in sorted order by the sort key\n value.

\n
", "smithy.api#required": {} } }, @@ -5288,7 +5460,7 @@ "ProvisionedThroughput": { "target": "com.amazonaws.dynamodb#ProvisionedThroughput", "traits": { - "smithy.api#documentation": "

Represents the provisioned throughput settings for the specified global secondary\n index.

\n

For current minimum and maximum provisioned throughput values, see Service,\n Account, and Table Quotas in the Amazon DynamoDB Developer\n Guide.

" + "smithy.api#documentation": "

Represents the provisioned throughput settings for the specified global secondary\n index.

\n

For current minimum and maximum provisioned throughput values, see Service,\n Account, and Table Quotas in the Amazon DynamoDB Developer\n Guide.

" } } }, @@ -5336,7 +5508,7 @@ "KeySchema": { "target": "com.amazonaws.dynamodb#KeySchema", "traits": { - "smithy.api#documentation": "

The complete key schema for a global secondary index, which consists of one or more\n pairs of attribute names and key types:

\n
    \n
  • \n

    \n HASH - partition key

    \n
  • \n
  • \n

    \n RANGE - sort key

    \n
  • \n
\n \n

The partition key of an item is also known as its hash\n attribute. The term \"hash attribute\" derives from DynamoDB's usage of an internal hash function to evenly distribute data items across\n partitions, based on their partition key values.

\n

The sort key of an item is also known as its range attribute.\n The term \"range attribute\" derives from the way DynamoDB stores items with\n the same partition key physically close together, in sorted order by the sort key\n value.

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

The complete key schema for a global secondary index, which consists of one or more\n pairs of attribute names and key types:

\n
    \n
  • \n

    \n HASH - partition key

    \n
  • \n
  • \n

    \n RANGE - sort key

    \n
  • \n
\n \n

The partition key of an item is also known as its hash\n attribute. The term \"hash attribute\" derives from DynamoDB's usage of an internal hash function to evenly distribute data items across\n partitions, based on their partition key values.

\n

The sort key of an item is also known as its range attribute.\n The term \"range attribute\" derives from the way DynamoDB stores items with\n the same partition key physically close together, in sorted order by the sort key\n value.

\n
" } }, "Projection": { @@ -5348,29 +5520,29 @@ "IndexStatus": { "target": "com.amazonaws.dynamodb#IndexStatus", "traits": { - "smithy.api#documentation": "

The current state of the global secondary index:

\n
    \n
  • \n

    \n CREATING - The index is being created.

    \n
  • \n
  • \n

    \n UPDATING - The index is being updated.

    \n
  • \n
  • \n

    \n DELETING - The index is being deleted.

    \n
  • \n
  • \n

    \n ACTIVE - The index is ready for use.

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

The current state of the global secondary index:

\n
    \n
  • \n

    \n CREATING - The index is being created.

    \n
  • \n
  • \n

    \n UPDATING - The index is being updated.

    \n
  • \n
  • \n

    \n DELETING - The index is being deleted.

    \n
  • \n
  • \n

    \n ACTIVE - The index is ready for use.

    \n
  • \n
" } }, "Backfilling": { "target": "com.amazonaws.dynamodb#Backfilling", "traits": { - "smithy.api#documentation": "

Indicates whether the index is currently backfilling. Backfilling\n is the process of reading items from the table and determining whether they can be added\n to the index. (Not all items will qualify: For example, a partition key cannot have any\n duplicate values.) If an item can be added to the index, DynamoDB will do so. After all\n items have been processed, the backfilling operation is complete and\n Backfilling is false.

\n

You can delete an index that is being created during the Backfilling\n phase when IndexStatus is set to CREATING and Backfilling is\n true. You can't delete the index that is being created when IndexStatus is\n set to CREATING and Backfilling is false.

\n \n

For indexes that were created during a CreateTable operation, the\n Backfilling attribute does not appear in the\n DescribeTable output.

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

Indicates whether the index is currently backfilling. Backfilling\n is the process of reading items from the table and determining whether they can be added\n to the index. (Not all items will qualify: For example, a partition key cannot have any\n duplicate values.) If an item can be added to the index, DynamoDB will do so. After all\n items have been processed, the backfilling operation is complete and\n Backfilling is false.

\n

You can delete an index that is being created during the Backfilling\n phase when IndexStatus is set to CREATING and Backfilling is\n true. You can't delete the index that is being created when IndexStatus is\n set to CREATING and Backfilling is false.

\n \n

For indexes that were created during a CreateTable operation, the\n Backfilling attribute does not appear in the\n DescribeTable output.

\n
" } }, "ProvisionedThroughput": { "target": "com.amazonaws.dynamodb#ProvisionedThroughputDescription", "traits": { - "smithy.api#documentation": "

Represents the provisioned throughput settings for the specified global secondary\n index.

\n

For current minimum and maximum provisioned throughput values, see Service,\n Account, and Table Quotas in the Amazon DynamoDB Developer\n Guide.

" + "smithy.api#documentation": "

Represents the provisioned throughput settings for the specified global secondary\n index.

\n

For current minimum and maximum provisioned throughput values, see Service,\n Account, and Table Quotas in the Amazon DynamoDB Developer\n Guide.

" } }, "IndexSizeBytes": { - "target": "com.amazonaws.dynamodb#Long", + "target": "com.amazonaws.dynamodb#LongObject", "traits": { "smithy.api#documentation": "

The total size of the specified index, in bytes. DynamoDB updates this value\n approximately every six hours. Recent changes might not be reflected in this\n value.

" } }, "ItemCount": { - "target": "com.amazonaws.dynamodb#Long", + "target": "com.amazonaws.dynamodb#LongObject", "traits": { "smithy.api#documentation": "

The number of items in the specified index. DynamoDB updates this value approximately\n every six hours. Recent changes might not be reflected in this value.

" } @@ -5404,7 +5576,7 @@ "KeySchema": { "target": "com.amazonaws.dynamodb#KeySchema", "traits": { - "smithy.api#documentation": "

The complete key schema for a global secondary index, which consists of one or more\n pairs of attribute names and key types:

\n
    \n
  • \n

    \n HASH - partition key

    \n
  • \n
  • \n

    \n RANGE - sort key

    \n
  • \n
\n \n

The partition key of an item is also known as its hash\n attribute. The term \"hash attribute\" derives from DynamoDB's usage of an internal hash function to evenly distribute data items across\n partitions, based on their partition key values.

\n

The sort key of an item is also known as its range attribute.\n The term \"range attribute\" derives from the way DynamoDB stores items with\n the same partition key physically close together, in sorted order by the sort key\n value.

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

The complete key schema for a global secondary index, which consists of one or more\n pairs of attribute names and key types:

\n
    \n
  • \n

    \n HASH - partition key

    \n
  • \n
  • \n

    \n RANGE - sort key

    \n
  • \n
\n \n

The partition key of an item is also known as its hash\n attribute. The term \"hash attribute\" derives from DynamoDB's usage of an internal hash function to evenly distribute data items across\n partitions, based on their partition key values.

\n

The sort key of an item is also known as its range attribute.\n The term \"range attribute\" derives from the way DynamoDB stores items with\n the same partition key physically close together, in sorted order by the sort key\n value.

\n
" } }, "Projection": { @@ -5442,7 +5614,7 @@ "Create": { "target": "com.amazonaws.dynamodb#CreateGlobalSecondaryIndexAction", "traits": { - "smithy.api#documentation": "

The parameters required for creating a global secondary index on an existing\n table:

\n
    \n
  • \n

    \n IndexName \n

    \n
  • \n
  • \n

    \n KeySchema \n

    \n
  • \n
  • \n

    \n AttributeDefinitions \n

    \n
  • \n
  • \n

    \n Projection \n

    \n
  • \n
  • \n

    \n ProvisionedThroughput \n

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

The parameters required for creating a global secondary index on an existing\n table:

\n
    \n
  • \n

    \n IndexName \n

    \n
  • \n
  • \n

    \n KeySchema \n

    \n
  • \n
  • \n

    \n AttributeDefinitions \n

    \n
  • \n
  • \n

    \n Projection \n

    \n
  • \n
  • \n

    \n ProvisionedThroughput \n

    \n
  • \n
" } }, "Delete": { @@ -5453,7 +5625,7 @@ } }, "traits": { - "smithy.api#documentation": "

Represents one of the following:

\n
    \n
  • \n

    A new global secondary index to be added to an existing table.

    \n
  • \n
  • \n

    New provisioned throughput parameters for an existing global secondary\n index.

    \n
  • \n
  • \n

    An existing global secondary index to be removed from an existing\n table.

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

Represents one of the following:

\n
    \n
  • \n

    A new global secondary index to be added to an existing table.

    \n
  • \n
  • \n

    New provisioned throughput parameters for an existing global secondary\n index.

    \n
  • \n
  • \n

    An existing global secondary index to be removed from an existing\n table.

    \n
  • \n
" } }, "com.amazonaws.dynamodb#GlobalSecondaryIndexUpdateList": { @@ -5527,7 +5699,7 @@ "GlobalTableStatus": { "target": "com.amazonaws.dynamodb#GlobalTableStatus", "traits": { - "smithy.api#documentation": "

The current state of the global table:

\n
    \n
  • \n

    \n CREATING - The global table is being created.

    \n
  • \n
  • \n

    \n UPDATING - The global table is being updated.

    \n
  • \n
  • \n

    \n DELETING - The global table is being deleted.

    \n
  • \n
  • \n

    \n ACTIVE - The global table is ready for use.

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

The current state of the global table:

\n
    \n
  • \n

    \n CREATING - The global table is being created.

    \n
  • \n
  • \n

    \n UPDATING - The global table is being updated.

    \n
  • \n
  • \n

    \n DELETING - The global table is being deleted.

    \n
  • \n
  • \n

    \n ACTIVE - The global table is ready for use.

    \n
  • \n
" } }, "GlobalTableName": { @@ -5897,7 +6069,7 @@ } }, "ProcessedSizeBytes": { - "target": "com.amazonaws.dynamodb#Long", + "target": "com.amazonaws.dynamodb#LongObject", "traits": { "smithy.api#documentation": "

The total size of data processed from the source file, in Bytes.

" } @@ -5939,7 +6111,7 @@ "ClientToken": { "target": "com.amazonaws.dynamodb#ClientToken", "traits": { - "smithy.api#documentation": "

Providing a ClientToken makes the call to ImportTableInput\n idempotent, meaning that multiple identical calls have the same effect as one single\n call.

\n

A client token is valid for 8 hours after the first request that uses it is completed.\n After 8 hours, any request with the same client token is treated as a new request. Do\n not resubmit the same request with the same client token for more than 8 hours, or the\n result might not be idempotent.

\n

If you submit a request with the same client token but a change in other parameters\n within the 8-hour idempotency window, DynamoDB returns an\n IdempotentParameterMismatch exception.

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

Providing a ClientToken makes the call to ImportTableInput\n idempotent, meaning that multiple identical calls have the same effect as one single\n call.

\n

A client token is valid for 8 hours after the first request that uses it is completed.\n After 8 hours, any request with the same client token is treated as a new request. Do\n not resubmit the same request with the same client token for more than 8 hours, or the\n result might not be idempotent.

\n

If you submit a request with the same client token but a change in other parameters\n within the 8-hour idempotency window, DynamoDB returns an\n IdempotentParameterMismatch exception.

", "smithy.api#idempotencyToken": {} } }, @@ -5976,6 +6148,9 @@ "smithy.api#required": {} } } + }, + "traits": { + "smithy.api#input": {} } }, "com.amazonaws.dynamodb#ImportTableOutput": { @@ -5988,6 +6163,9 @@ "smithy.api#required": {} } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.dynamodb#ImportedItemCount": { @@ -6191,7 +6369,7 @@ "SizeEstimateRangeGB": { "target": "com.amazonaws.dynamodb#ItemCollectionSizeEstimateRange", "traits": { - "smithy.api#documentation": "

An estimate of item collection size, in gigabytes. This value is a two-element array\n containing a lower bound and an upper bound for the estimate. The estimate includes the\n size of all the items in the table, plus the size of all attributes projected into all\n of the local secondary indexes on that table. Use this estimate to measure whether a\n local secondary index is approaching its size limit.

\n

The estimate is subject to change over time; therefore, do not rely on the precision\n or accuracy of the estimate.

" + "smithy.api#documentation": "

An estimate of item collection size, in gigabytes. This value is a two-element array\n containing a lower bound and an upper bound for the estimate. The estimate includes the\n size of all the items in the table, plus the size of all attributes projected into all\n of the local secondary indexes on that table. Use this estimate to measure whether a\n local secondary index is approaching its size limit.

\n

The estimate is subject to change over time; therefore, do not rely on the precision\n or accuracy of the estimate.

" } } }, @@ -6351,13 +6529,13 @@ "KeyType": { "target": "com.amazonaws.dynamodb#KeyType", "traits": { - "smithy.api#documentation": "

The role that this key attribute will assume:

\n
    \n
  • \n

    \n HASH - partition key

    \n
  • \n
  • \n

    \n RANGE - sort key

    \n
  • \n
\n \n

The partition key of an item is also known as its hash\n attribute. The term \"hash attribute\" derives from DynamoDB's usage of an internal hash function to evenly distribute data items across\n partitions, based on their partition key values.

\n

The sort key of an item is also known as its range attribute.\n The term \"range attribute\" derives from the way DynamoDB stores items with\n the same partition key physically close together, in sorted order by the sort key\n value.

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

The role that this key attribute will assume:

\n
    \n
  • \n

    \n HASH - partition key

    \n
  • \n
  • \n

    \n RANGE - sort key

    \n
  • \n
\n \n

The partition key of an item is also known as its hash\n attribute. The term \"hash attribute\" derives from DynamoDB's usage of an internal hash function to evenly distribute data items across\n partitions, based on their partition key values.

\n

The sort key of an item is also known as its range attribute.\n The term \"range attribute\" derives from the way DynamoDB stores items with\n the same partition key physically close together, in sorted order by the sort key\n value.

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

Represents a single element of a key schema. A key schema\n specifies the attributes that make up the primary key of a table, or the key attributes\n of an index.

\n

A KeySchemaElement represents exactly one attribute of the primary key.\n For example, a simple primary key would be represented by one\n KeySchemaElement (for the partition key). A composite primary key would\n require one KeySchemaElement for the partition key, and another\n KeySchemaElement for the sort key.

\n

A KeySchemaElement must be a scalar, top-level attribute (not a nested\n attribute). The data type must be one of String, Number, or Binary. The attribute cannot\n be nested within a List or a Map.

" + "smithy.api#documentation": "

Represents a single element of a key schema. A key schema\n specifies the attributes that make up the primary key of a table, or the key attributes\n of an index.

\n

A KeySchemaElement represents exactly one attribute of the primary key.\n For example, a simple primary key would be represented by one\n KeySchemaElement (for the partition key). A composite primary key would\n require one KeySchemaElement for the partition key, and another\n KeySchemaElement for the sort key.

\n

A KeySchemaElement must be a scalar, top-level attribute (not a nested\n attribute). The data type must be one of String, Number, or Binary. The attribute cannot\n be nested within a List or a Map.

" } }, "com.amazonaws.dynamodb#KeyType": { @@ -6402,18 +6580,18 @@ "ProjectionExpression": { "target": "com.amazonaws.dynamodb#ProjectionExpression", "traits": { - "smithy.api#documentation": "

A string that identifies one or more attributes to retrieve from the table. These\n attributes can include scalars, sets, or elements of a JSON document. The attributes in\n the ProjectionExpression must be separated by commas.

\n

If no attribute names are specified, then all attributes will be returned. If any of\n the requested attributes are not found, they will not appear in the result.

\n

For more information, see Accessing Item Attributes in the Amazon DynamoDB Developer\n Guide.

" + "smithy.api#documentation": "

A string that identifies one or more attributes to retrieve from the table. These\n attributes can include scalars, sets, or elements of a JSON document. The attributes in\n the ProjectionExpression must be separated by commas.

\n

If no attribute names are specified, then all attributes will be returned. If any of\n the requested attributes are not found, they will not appear in the result.

\n

For more information, see Accessing Item Attributes in the Amazon DynamoDB Developer\n Guide.

" } }, "ExpressionAttributeNames": { "target": "com.amazonaws.dynamodb#ExpressionAttributeNameMap", "traits": { - "smithy.api#documentation": "

One or more substitution tokens for attribute names in an expression. The following\n are some use cases for using ExpressionAttributeNames:

\n
    \n
  • \n

    To access an attribute whose name conflicts with a DynamoDB reserved\n word.

    \n
  • \n
  • \n

    To create a placeholder for repeating occurrences of an attribute name in an\n expression.

    \n
  • \n
  • \n

    To prevent special characters in an attribute name from being misinterpreted\n in an expression.

    \n
  • \n
\n

Use the # character in an expression to dereference\n an attribute name. For example, consider the following attribute name:

\n
    \n
  • \n

    \n Percentile\n

    \n
  • \n
\n

The name of this attribute conflicts with a reserved word, so it cannot be used\n directly in an expression. (For the complete list of reserved words, see Reserved Words in the Amazon DynamoDB Developer\n Guide). To work around this, you could specify the following for\n ExpressionAttributeNames:

\n
    \n
  • \n

    \n {\"#P\":\"Percentile\"}\n

    \n
  • \n
\n

You could then use this substitution in an expression, as in this example:

\n
    \n
  • \n

    \n #P = :val\n

    \n
  • \n
\n \n

Tokens that begin with the : character are\n expression attribute values, which are placeholders for the\n actual value at runtime.

\n
\n

For more information on expression attribute names, see Accessing Item Attributes in the Amazon DynamoDB Developer\n Guide.

" + "smithy.api#documentation": "

One or more substitution tokens for attribute names in an expression. The following\n are some use cases for using ExpressionAttributeNames:

\n
    \n
  • \n

    To access an attribute whose name conflicts with a DynamoDB reserved\n word.

    \n
  • \n
  • \n

    To create a placeholder for repeating occurrences of an attribute name in an\n expression.

    \n
  • \n
  • \n

    To prevent special characters in an attribute name from being misinterpreted\n in an expression.

    \n
  • \n
\n

Use the # character in an expression to dereference\n an attribute name. For example, consider the following attribute name:

\n
    \n
  • \n

    \n Percentile\n

    \n
  • \n
\n

The name of this attribute conflicts with a reserved word, so it cannot be used\n directly in an expression. (For the complete list of reserved words, see Reserved Words in the Amazon DynamoDB Developer\n Guide). To work around this, you could specify the following for\n ExpressionAttributeNames:

\n
    \n
  • \n

    \n {\"#P\":\"Percentile\"}\n

    \n
  • \n
\n

You could then use this substitution in an expression, as in this example:

\n
    \n
  • \n

    \n #P = :val\n

    \n
  • \n
\n \n

Tokens that begin with the : character are\n expression attribute values, which are placeholders for the\n actual value at runtime.

\n
\n

For more information on expression attribute names, see Accessing Item Attributes in the Amazon DynamoDB Developer\n Guide.

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

Represents a set of primary keys and, for each key, the attributes to retrieve from\n the table.

\n

For each primary key, you must provide all of the key attributes.\n For example, with a simple primary key, you only need to provide the partition key. For\n a composite primary key, you must provide both the partition key\n and the sort key.

" + "smithy.api#documentation": "

Represents a set of primary keys and, for each key, the attributes to retrieve from\n the table.

\n

For each primary key, you must provide all of the key attributes.\n For example, with a simple primary key, you only need to provide the partition key. For\n a composite primary key, you must provide both the partition key\n and the sort key.

" } }, "com.amazonaws.dynamodb#KinesisDataStreamDestination": { @@ -6504,7 +6682,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.

", "smithy.api#error": "client" } }, @@ -6534,7 +6712,7 @@ "aws.api#clientDiscoveredEndpoint": { "required": false }, - "smithy.api#documentation": "

List backups associated with an Amazon Web Services account. To list backups for a\n given table, specify TableName. ListBackups returns a\n paginated list of results with at most 1 MB worth of items in a page. You can also\n specify a maximum number of entries to be returned in a page.

\n

In the request, start time is inclusive, but end time is exclusive. Note that these\n boundaries are for the time at which the original backup was requested.

\n

You can call ListBackups a maximum of five times per second.

" + "smithy.api#documentation": "

List backups associated with an Amazon Web Services account. To list backups for a\n given table, specify TableName. ListBackups returns a\n paginated list of results with at most 1 MB worth of items in a page. You can also\n specify a maximum number of entries to be returned in a page.

\n

In the request, start time is inclusive, but end time is exclusive. Note that these\n boundaries are for the time at which the original backup was requested.

\n

You can call ListBackups a maximum of five times per second.

" } }, "com.amazonaws.dynamodb#ListBackupsInput": { @@ -6573,9 +6751,12 @@ "BackupType": { "target": "com.amazonaws.dynamodb#BackupTypeFilter", "traits": { - "smithy.api#documentation": "

The backups from the table specified by BackupType are listed.

\n

Where BackupType can be:

\n
    \n
  • \n

    \n USER - On-demand backup created by you. (The default setting if no\n other backup types are specified.)

    \n
  • \n
  • \n

    \n SYSTEM - On-demand backup automatically created by DynamoDB.

    \n
  • \n
  • \n

    \n ALL - All types of on-demand backups (USER and SYSTEM).

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

The backups from the table specified by BackupType are listed.

\n

Where BackupType can be:

\n
    \n
  • \n

    \n USER - On-demand backup created by you. (The default setting if no\n other backup types are specified.)

    \n
  • \n
  • \n

    \n SYSTEM - On-demand backup automatically created by DynamoDB.

    \n
  • \n
  • \n

    \n ALL - All types of on-demand backups (USER and SYSTEM).

    \n
  • \n
" } } + }, + "traits": { + "smithy.api#input": {} } }, "com.amazonaws.dynamodb#ListBackupsOutput": { @@ -6590,9 +6771,12 @@ "LastEvaluatedBackupArn": { "target": "com.amazonaws.dynamodb#BackupArn", "traits": { - "smithy.api#documentation": "

The ARN of the backup last evaluated when the current page of results was returned,\n inclusive of the current page of results. This value may be specified as the\n ExclusiveStartBackupArn of a new ListBackups operation in\n order to fetch the next page of results.

\n

If LastEvaluatedBackupArn is empty, then the last page of results has\n been processed and there are no more results to be retrieved.

\n

If LastEvaluatedBackupArn is not empty, this may or may not indicate\n that there is more data to be returned. All results are guaranteed to have been returned\n if and only if no value for LastEvaluatedBackupArn is returned.

" + "smithy.api#documentation": "

The ARN of the backup last evaluated when the current page of results was returned,\n inclusive of the current page of results. This value may be specified as the\n ExclusiveStartBackupArn of a new ListBackups operation in\n order to fetch the next page of results.

\n

If LastEvaluatedBackupArn is empty, then the last page of results has\n been processed and there are no more results to be retrieved.

\n

If LastEvaluatedBackupArn is not empty, this may or may not indicate\n that there is more data to be returned. All results are guaranteed to have been returned\n if and only if no value for LastEvaluatedBackupArn is returned.

" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.dynamodb#ListContributorInsights": { @@ -6642,6 +6826,9 @@ "smithy.api#documentation": "

Maximum number of results to return per page.

" } } + }, + "traits": { + "smithy.api#input": {} } }, "com.amazonaws.dynamodb#ListContributorInsightsLimit": { @@ -6668,6 +6855,9 @@ "smithy.api#documentation": "

A token to go to the next page if there is one.

" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.dynamodb#ListExports": { @@ -6716,6 +6906,9 @@ "smithy.api#documentation": "

An optional string that, if supplied, must be copied from the output of a previous\n call to ListExports. When provided in this manner, the API fetches the next\n page of results.

" } } + }, + "traits": { + "smithy.api#input": {} } }, "com.amazonaws.dynamodb#ListExportsMaxLimit": { @@ -6742,6 +6935,9 @@ "smithy.api#documentation": "

If this value is returned, there are additional results to be displayed. To retrieve\n them, call ListExports again, with NextToken set to this\n value.

" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.dynamodb#ListGlobalTables": { @@ -6764,7 +6960,7 @@ "aws.api#clientDiscoveredEndpoint": { "required": false }, - "smithy.api#documentation": "

Lists all global tables that have a replica in the specified Region.

\n \n

This operation only applies to Version\n 2017.11.29 of global tables.

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

Lists all global tables that have a replica in the specified Region.

\n \n

This operation only applies to Version\n 2017.11.29 (Legacy) of global tables. We recommend using\n Version 2019.11.21 (Current)\n when creating new global tables, as it provides greater flexibility, higher efficiency and consumes less write capacity than \n 2017.11.29 (Legacy). To determine which version you are using, see \n Determining the version. \n To update existing global tables from version 2017.11.29 (Legacy) to version\n 2019.11.21 (Current), see \n Updating global tables.\n

\n
" } }, "com.amazonaws.dynamodb#ListGlobalTablesInput": { @@ -6779,7 +6975,7 @@ "Limit": { "target": "com.amazonaws.dynamodb#PositiveIntegerObject", "traits": { - "smithy.api#documentation": "

The maximum number of table names to return, if the parameter is not specified\n DynamoDB defaults to 100.

\n

If the number of global tables DynamoDB finds reaches this limit, it stops the\n operation and returns the table names collected up to that point, with a table name in\n the LastEvaluatedGlobalTableName to apply in a subsequent operation to the\n ExclusiveStartGlobalTableName parameter.

" + "smithy.api#documentation": "

The maximum number of table names to return, if the parameter is not specified\n DynamoDB defaults to 100.

\n

If the number of global tables DynamoDB finds reaches this limit, it stops the\n operation and returns the table names collected up to that point, with a table name in\n the LastEvaluatedGlobalTableName to apply in a subsequent operation to the\n ExclusiveStartGlobalTableName parameter.

" } }, "RegionName": { @@ -6788,6 +6984,9 @@ "smithy.api#documentation": "

Lists the global tables in a specific Region.

" } } + }, + "traits": { + "smithy.api#input": {} } }, "com.amazonaws.dynamodb#ListGlobalTablesOutput": { @@ -6805,6 +7004,9 @@ "smithy.api#documentation": "

Last evaluated global table name.

" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.dynamodb#ListImports": { @@ -6850,6 +7052,9 @@ "smithy.api#documentation": "

An optional string that, if supplied, must be copied from the output of a previous\n call to ListImports. When provided in this manner, the API fetches the next\n page of results.

" } } + }, + "traits": { + "smithy.api#input": {} } }, "com.amazonaws.dynamodb#ListImportsMaxLimit": { @@ -6876,6 +7081,9 @@ "smithy.api#documentation": "

If this value is returned, there are additional results to be displayed. To retrieve\n them, call ListImports again, with NextToken set to this\n value.

" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.dynamodb#ListTables": { @@ -6924,7 +7132,8 @@ } }, "traits": { - "smithy.api#documentation": "

Represents the input of a ListTables operation.

" + "smithy.api#documentation": "

Represents the input of a ListTables operation.

", + "smithy.api#input": {} } }, "com.amazonaws.dynamodb#ListTablesInputLimit": { @@ -6942,18 +7151,19 @@ "TableNames": { "target": "com.amazonaws.dynamodb#TableNameList", "traits": { - "smithy.api#documentation": "

The names of the tables associated with the current account at the current endpoint.\n The maximum size of this array is 100.

\n

If LastEvaluatedTableName also appears in the output, you can use this\n value as the ExclusiveStartTableName parameter in a subsequent\n ListTables request and obtain the next page of results.

" + "smithy.api#documentation": "

The names of the tables associated with the current account at the current endpoint.\n The maximum size of this array is 100.

\n

If LastEvaluatedTableName also appears in the output, you can use this\n value as the ExclusiveStartTableName parameter in a subsequent\n ListTables request and obtain the next page of results.

" } }, "LastEvaluatedTableName": { "target": "com.amazonaws.dynamodb#TableName", "traits": { - "smithy.api#documentation": "

The name of the last table in the current page of results. Use this value as the\n ExclusiveStartTableName in a new request to obtain the next page of\n results, until all the table names are returned.

\n

If you do not receive a LastEvaluatedTableName value in the response,\n this means that there are no more table names to be retrieved.

" + "smithy.api#documentation": "

The name of the last table in the current page of results. Use this value as the\n ExclusiveStartTableName in a new request to obtain the next page of\n results, until all the table names are returned.

\n

If you do not receive a LastEvaluatedTableName value in the response,\n this means that there are no more table names to be retrieved.

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

Represents the output of a ListTables operation.

" + "smithy.api#documentation": "

Represents the output of a ListTables operation.

", + "smithy.api#output": {} } }, "com.amazonaws.dynamodb#ListTagsOfResource": { @@ -6979,7 +7189,7 @@ "aws.api#clientDiscoveredEndpoint": { "required": false }, - "smithy.api#documentation": "

List all tags on an Amazon DynamoDB resource. You can call ListTagsOfResource up to 10\n times per second, per account.

\n

For an overview on tagging DynamoDB resources, see Tagging for DynamoDB\n in the Amazon DynamoDB Developer Guide.

" + "smithy.api#documentation": "

List all tags on an Amazon DynamoDB resource. You can call ListTagsOfResource up to 10\n times per second, per account.

\n

For an overview on tagging DynamoDB resources, see Tagging for DynamoDB\n in the Amazon DynamoDB Developer Guide.

" } }, "com.amazonaws.dynamodb#ListTagsOfResourceInput": { @@ -6998,6 +7208,9 @@ "smithy.api#documentation": "

An optional string that, if supplied, must be copied from the output of a previous\n call to ListTagOfResource. When provided in this manner, this API fetches the next page\n of results.

" } } + }, + "traits": { + "smithy.api#input": {} } }, "com.amazonaws.dynamodb#ListTagsOfResourceOutput": { @@ -7015,6 +7228,9 @@ "smithy.api#documentation": "

If this value is returned, there are additional results to be displayed. To retrieve\n them, call ListTagsOfResource again, with NextToken set to this value.

" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.dynamodb#LocalSecondaryIndex": { @@ -7030,7 +7246,7 @@ "KeySchema": { "target": "com.amazonaws.dynamodb#KeySchema", "traits": { - "smithy.api#documentation": "

The complete key schema for the local secondary index, consisting of one or more pairs\n of attribute names and key types:

\n
    \n
  • \n

    \n HASH - partition key

    \n
  • \n
  • \n

    \n RANGE - sort key

    \n
  • \n
\n \n

The partition key of an item is also known as its hash\n attribute. The term \"hash attribute\" derives from DynamoDB's usage of\n an internal hash function to evenly distribute data items across partitions, based\n on their partition key values.

\n

The sort key of an item is also known as its range attribute.\n The term \"range attribute\" derives from the way DynamoDB stores items with the same\n partition key physically close together, in sorted order by the sort key\n value.

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

The complete key schema for the local secondary index, consisting of one or more pairs\n of attribute names and key types:

\n
    \n
  • \n

    \n HASH - partition key

    \n
  • \n
  • \n

    \n RANGE - sort key

    \n
  • \n
\n \n

The partition key of an item is also known as its hash\n attribute. The term \"hash attribute\" derives from DynamoDB's usage of\n an internal hash function to evenly distribute data items across partitions, based\n on their partition key values.

\n

The sort key of an item is also known as its range attribute.\n The term \"range attribute\" derives from the way DynamoDB stores items with the same\n partition key physically close together, in sorted order by the sort key\n value.

\n
", "smithy.api#required": {} } }, @@ -7058,7 +7274,7 @@ "KeySchema": { "target": "com.amazonaws.dynamodb#KeySchema", "traits": { - "smithy.api#documentation": "

The complete key schema for the local secondary index, consisting of one or more pairs\n of attribute names and key types:

\n
    \n
  • \n

    \n HASH - partition key

    \n
  • \n
  • \n

    \n RANGE - sort key

    \n
  • \n
\n \n

The partition key of an item is also known as its hash\n attribute. The term \"hash attribute\" derives from DynamoDB's usage of\n an internal hash function to evenly distribute data items across partitions, based\n on their partition key values.

\n

The sort key of an item is also known as its range attribute.\n The term \"range attribute\" derives from the way DynamoDB stores items with the same\n partition key physically close together, in sorted order by the sort key\n value.

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

The complete key schema for the local secondary index, consisting of one or more pairs\n of attribute names and key types:

\n
    \n
  • \n

    \n HASH - partition key

    \n
  • \n
  • \n

    \n RANGE - sort key

    \n
  • \n
\n \n

The partition key of an item is also known as its hash\n attribute. The term \"hash attribute\" derives from DynamoDB's usage of\n an internal hash function to evenly distribute data items across partitions, based\n on their partition key values.

\n

The sort key of an item is also known as its range attribute.\n The term \"range attribute\" derives from the way DynamoDB stores items with the same\n partition key physically close together, in sorted order by the sort key\n value.

\n
" } }, "Projection": { @@ -7068,13 +7284,13 @@ } }, "IndexSizeBytes": { - "target": "com.amazonaws.dynamodb#Long", + "target": "com.amazonaws.dynamodb#LongObject", "traits": { "smithy.api#documentation": "

The total size of the specified index, in bytes. DynamoDB updates this value\n approximately every six hours. Recent changes might not be reflected in this\n value.

" } }, "ItemCount": { - "target": "com.amazonaws.dynamodb#Long", + "target": "com.amazonaws.dynamodb#LongObject", "traits": { "smithy.api#documentation": "

The number of items in the specified index. DynamoDB updates this value\n approximately every six hours. Recent changes might not be reflected in this\n value.

" } @@ -7108,7 +7324,7 @@ "KeySchema": { "target": "com.amazonaws.dynamodb#KeySchema", "traits": { - "smithy.api#documentation": "

The complete key schema for a local secondary index, which consists of one or more\n pairs of attribute names and key types:

\n
    \n
  • \n

    \n HASH - partition key

    \n
  • \n
  • \n

    \n RANGE - sort key

    \n
  • \n
\n \n

The partition key of an item is also known as its hash\n attribute. The term \"hash attribute\" derives from DynamoDB's usage of\n an internal hash function to evenly distribute data items across partitions, based\n on their partition key values.

\n

The sort key of an item is also known as its range attribute.\n The term \"range attribute\" derives from the way DynamoDB stores items with the same\n partition key physically close together, in sorted order by the sort key\n value.

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

The complete key schema for a local secondary index, which consists of one or more\n pairs of attribute names and key types:

\n
    \n
  • \n

    \n HASH - partition key

    \n
  • \n
  • \n

    \n RANGE - sort key

    \n
  • \n
\n \n

The partition key of an item is also known as its hash\n attribute. The term \"hash attribute\" derives from DynamoDB's usage of\n an internal hash function to evenly distribute data items across partitions, based\n on their partition key values.

\n

The sort key of an item is also known as its range attribute.\n The term \"range attribute\" derives from the way DynamoDB stores items with the same\n partition key physically close together, in sorted order by the sort key\n value.

\n
" } }, "Projection": { @@ -7135,6 +7351,12 @@ } }, "com.amazonaws.dynamodb#Long": { + "type": "long", + "traits": { + "smithy.api#default": 0 + } + }, + "com.amazonaws.dynamodb#LongObject": { "type": "long" }, "com.amazonaws.dynamodb#MapAttributeValue": { @@ -7265,7 +7487,7 @@ "PointInTimeRecoveryStatus": { "target": "com.amazonaws.dynamodb#PointInTimeRecoveryStatus", "traits": { - "smithy.api#documentation": "

The current state of point in time recovery:

\n
    \n
  • \n

    \n ENABLED - Point in time recovery is enabled.

    \n
  • \n
  • \n

    \n DISABLED - Point in time recovery is disabled.

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

The current state of point in time recovery:

\n
    \n
  • \n

    \n ENABLED - Point in time recovery is enabled.

    \n
  • \n
  • \n

    \n DISABLED - Point in time recovery is disabled.

    \n
  • \n
" } }, "EarliestRestorableDateTime": { @@ -7371,13 +7593,13 @@ "ProjectionType": { "target": "com.amazonaws.dynamodb#ProjectionType", "traits": { - "smithy.api#documentation": "

The set of attributes that are projected into the index:

\n
    \n
  • \n

    \n KEYS_ONLY - Only the index and primary keys are projected into the\n index.

    \n
  • \n
  • \n

    \n INCLUDE - In addition to the attributes described in\n KEYS_ONLY, the secondary index will include other non-key\n attributes that you specify.

    \n
  • \n
  • \n

    \n ALL - All of the table attributes are projected into the\n index.

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

The set of attributes that are projected into the index:

\n
    \n
  • \n

    \n KEYS_ONLY - Only the index and primary keys are projected into the\n index.

    \n
  • \n
  • \n

    \n INCLUDE - In addition to the attributes described in\n KEYS_ONLY, the secondary index will include other non-key\n attributes that you specify.

    \n
  • \n
  • \n

    \n ALL - All of the table attributes are projected into the\n index.

    \n
  • \n
" } }, "NonKeyAttributes": { "target": "com.amazonaws.dynamodb#NonKeyAttributeNameList", "traits": { - "smithy.api#documentation": "

Represents the non-key attribute names which will be projected into the index.

\n

For local secondary indexes, the total count of NonKeyAttributes summed\n across all of the local secondary indexes, must not exceed 100. If you project the same\n attribute into two different indexes, this counts as two distinct attributes when\n determining the total.

" + "smithy.api#documentation": "

Represents the non-key attribute names which will be projected into the index.

\n

For local secondary indexes, the total count of NonKeyAttributes summed\n across all of the local secondary indexes, must not exceed 100. If you project the same\n attribute into two different indexes, this counts as two distinct attributes when\n determining the total.

" } } }, @@ -7417,20 +7639,20 @@ "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": {} } } }, "traits": { - "smithy.api#documentation": "

Represents the provisioned throughput settings for a specified table or index. The\n settings can be modified using the UpdateTable operation.

\n

For current minimum and maximum provisioned throughput values, see Service,\n Account, and Table Quotas in the Amazon DynamoDB Developer\n Guide.

" + "smithy.api#documentation": "

Represents the provisioned throughput settings for a specified table or index. The\n settings can be modified using the UpdateTable operation.

\n

For current minimum and maximum provisioned throughput values, see Service,\n Account, and Table Quotas in the Amazon DynamoDB Developer\n Guide.

" } }, "com.amazonaws.dynamodb#ProvisionedThroughputDescription": { @@ -7584,7 +7806,7 @@ "aws.api#clientDiscoveredEndpoint": { "required": false }, - "smithy.api#documentation": "

Creates a new item, or replaces an old item with a new item. If an item that has the\n same primary key as the new item already exists in the specified table, the new item\n completely replaces the existing item. You can perform a conditional put operation (add\n a new item if one with the specified primary key doesn't exist), or replace an existing\n item if it has certain attribute values. You can return the item's attribute values in\n the same operation, using the ReturnValues parameter.

\n\n

When you add an item, the primary key attributes are the only required attributes.\n

\n

Empty String and Binary attribute values are allowed. Attribute values of type String\n and Binary must have a length greater than zero if the attribute is used as a key\n attribute for a table or index. Set type attributes cannot be empty.

\n

Invalid Requests with empty values will be rejected with a\n ValidationException exception.

\n \n

To prevent a new item from replacing an existing item, use a conditional\n expression that contains the attribute_not_exists function with the\n name of the attribute being used as the partition key for the table. Since every\n record must contain that attribute, the attribute_not_exists function\n will only succeed if no matching item exists.

\n
\n

For more information about PutItem, see Working with\n Items in the Amazon DynamoDB Developer Guide.

" + "smithy.api#documentation": "

Creates a new item, or replaces an old item with a new item. If an item that has the\n same primary key as the new item already exists in the specified table, the new item\n completely replaces the existing item. You can perform a conditional put operation (add\n a new item if one with the specified primary key doesn't exist), or replace an existing\n item if it has certain attribute values. You can return the item's attribute values in\n the same operation, using the ReturnValues parameter.

\n

When you add an item, the primary key attributes are the only required attributes.\n

\n

Empty String and Binary attribute values are allowed. Attribute values of type String\n and Binary must have a length greater than zero if the attribute is used as a key\n attribute for a table or index. Set type attributes cannot be empty.

\n

Invalid Requests with empty values will be rejected with a\n ValidationException exception.

\n \n

To prevent a new item from replacing an existing item, use a conditional\n expression that contains the attribute_not_exists function with the\n name of the attribute being used as the partition key for the table. Since every\n record must contain that attribute, the attribute_not_exists function\n will only succeed if no matching item exists.

\n
\n

For more information about PutItem, see Working with\n Items in the Amazon DynamoDB Developer Guide.

" } }, "com.amazonaws.dynamodb#PutItemInput": { @@ -7600,7 +7822,7 @@ "Item": { "target": "com.amazonaws.dynamodb#PutItemInputAttributeMap", "traits": { - "smithy.api#documentation": "

A map of attribute name/value pairs, one for each attribute. Only the primary key\n attributes are required; you can optionally provide other attribute name-value pairs for\n the item.

\n

You must provide all of the attributes for the primary key. For example, with a simple\n primary key, you only need to provide a value for the partition key. For a composite\n primary key, you must provide both values for both the partition key and the sort\n key.

\n

If you specify any attributes that are part of an index key, then the data types for\n those attributes must match those of the schema in the table's attribute\n definition.

\n

Empty String and Binary attribute values are allowed. Attribute values of type String\n and Binary must have a length greater than zero if the attribute is used as a key\n attribute for a table or index.

\n\n

For more information about primary keys, see Primary Key in the Amazon DynamoDB Developer\n Guide.

\n

Each element in the Item map is an AttributeValue\n object.

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

A map of attribute name/value pairs, one for each attribute. Only the primary key\n attributes are required; you can optionally provide other attribute name-value pairs for\n the item.

\n

You must provide all of the attributes for the primary key. For example, with a simple\n primary key, you only need to provide a value for the partition key. For a composite\n primary key, you must provide both values for both the partition key and the sort\n key.

\n

If you specify any attributes that are part of an index key, then the data types for\n those attributes must match those of the schema in the table's attribute\n definition.

\n

Empty String and Binary attribute values are allowed. Attribute values of type String\n and Binary must have a length greater than zero if the attribute is used as a key\n attribute for a table or index.

\n

For more information about primary keys, see Primary Key in the Amazon DynamoDB Developer\n Guide.

\n

Each element in the Item map is an AttributeValue\n object.

", "smithy.api#required": {} } }, @@ -7613,7 +7835,7 @@ "ReturnValues": { "target": "com.amazonaws.dynamodb#ReturnValue", "traits": { - "smithy.api#documentation": "

Use ReturnValues if you want to get the item attributes as they appeared\n before they were updated with the PutItem request. For\n PutItem, the valid values are:

\n
    \n
  • \n

    \n NONE - If ReturnValues is not specified, or if its\n value is NONE, then nothing is returned. (This setting is the\n default for ReturnValues.)

    \n
  • \n
  • \n

    \n ALL_OLD - If PutItem overwrote an attribute name-value\n pair, then the content of the old item is returned.

    \n
  • \n
\n

The values returned are strongly consistent.

\n

There is no additional cost associated with requesting a return value aside from the\n small network and processing overhead of receiving a larger response. No read capacity\n units are consumed.

\n \n

The ReturnValues parameter is used by several DynamoDB operations;\n however, PutItem does not recognize any values other than\n NONE or ALL_OLD.

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

Use ReturnValues if you want to get the item attributes as they appeared\n before they were updated with the PutItem request. For\n PutItem, the valid values are:

\n
    \n
  • \n

    \n NONE - If ReturnValues is not specified, or if its\n value is NONE, then nothing is returned. (This setting is the\n default for ReturnValues.)

    \n
  • \n
  • \n

    \n ALL_OLD - If PutItem overwrote an attribute name-value\n pair, then the content of the old item is returned.

    \n
  • \n
\n

The values returned are strongly consistent.

\n

There is no additional cost associated with requesting a return value aside from the\n small network and processing overhead of receiving a larger response. No read capacity\n units are consumed.

\n \n

The ReturnValues parameter is used by several DynamoDB operations;\n however, PutItem does not recognize any values other than\n NONE or ALL_OLD.

\n
" } }, "ReturnConsumedCapacity": { @@ -7634,24 +7856,25 @@ "ConditionExpression": { "target": "com.amazonaws.dynamodb#ConditionExpression", "traits": { - "smithy.api#documentation": "

A condition that must be satisfied in order for a conditional PutItem\n operation to succeed.

\n

An expression can contain any of the following:

\n
    \n
  • \n

    Functions: attribute_exists | attribute_not_exists | attribute_type |\n contains | begins_with | size\n

    \n

    These function names are case-sensitive.

    \n
  • \n
  • \n

    Comparison operators: = | <> |\n < | > | <= | >= |\n BETWEEN | IN \n

    \n
  • \n
  • \n

    Logical operators: AND | OR | NOT\n

    \n
  • \n
\n

For more information on condition expressions, see Condition Expressions in the Amazon DynamoDB Developer\n Guide.

" + "smithy.api#documentation": "

A condition that must be satisfied in order for a conditional PutItem\n operation to succeed.

\n

An expression can contain any of the following:

\n
    \n
  • \n

    Functions: attribute_exists | attribute_not_exists | attribute_type |\n contains | begins_with | size\n

    \n

    These function names are case-sensitive.

    \n
  • \n
  • \n

    Comparison operators: = | <> |\n < | > | <= | >= |\n BETWEEN | IN \n

    \n
  • \n
  • \n

    Logical operators: AND | OR | NOT\n

    \n
  • \n
\n

For more information on condition expressions, see Condition Expressions in the Amazon DynamoDB Developer\n Guide.

" } }, "ExpressionAttributeNames": { "target": "com.amazonaws.dynamodb#ExpressionAttributeNameMap", "traits": { - "smithy.api#documentation": "

One or more substitution tokens for attribute names in an expression. The following\n are some use cases for using ExpressionAttributeNames:

\n
    \n
  • \n

    To access an attribute whose name conflicts with a DynamoDB reserved\n word.

    \n
  • \n
  • \n

    To create a placeholder for repeating occurrences of an attribute name in an\n expression.

    \n
  • \n
  • \n

    To prevent special characters in an attribute name from being misinterpreted\n in an expression.

    \n
  • \n
\n

Use the # character in an expression to dereference\n an attribute name. For example, consider the following attribute name:

\n
    \n
  • \n

    \n Percentile\n

    \n
  • \n
\n

The name of this attribute conflicts with a reserved word, so it cannot be used\n directly in an expression. (For the complete list of reserved words, see Reserved Words in the Amazon DynamoDB Developer\n Guide). To work around this, you could specify the following for\n ExpressionAttributeNames:

\n
    \n
  • \n

    \n {\"#P\":\"Percentile\"}\n

    \n
  • \n
\n

You could then use this substitution in an expression, as in this example:

\n
    \n
  • \n

    \n #P = :val\n

    \n
  • \n
\n \n

Tokens that begin with the : character are\n expression attribute values, which are placeholders for the\n actual value at runtime.

\n
\n

For more information on expression attribute names, see Specifying Item Attributes in the Amazon DynamoDB Developer\n Guide.

" + "smithy.api#documentation": "

One or more substitution tokens for attribute names in an expression. The following\n are some use cases for using ExpressionAttributeNames:

\n
    \n
  • \n

    To access an attribute whose name conflicts with a DynamoDB reserved\n word.

    \n
  • \n
  • \n

    To create a placeholder for repeating occurrences of an attribute name in an\n expression.

    \n
  • \n
  • \n

    To prevent special characters in an attribute name from being misinterpreted\n in an expression.

    \n
  • \n
\n

Use the # character in an expression to dereference\n an attribute name. For example, consider the following attribute name:

\n
    \n
  • \n

    \n Percentile\n

    \n
  • \n
\n

The name of this attribute conflicts with a reserved word, so it cannot be used\n directly in an expression. (For the complete list of reserved words, see Reserved Words in the Amazon DynamoDB Developer\n Guide). To work around this, you could specify the following for\n ExpressionAttributeNames:

\n
    \n
  • \n

    \n {\"#P\":\"Percentile\"}\n

    \n
  • \n
\n

You could then use this substitution in an expression, as in this example:

\n
    \n
  • \n

    \n #P = :val\n

    \n
  • \n
\n \n

Tokens that begin with the : character are\n expression attribute values, which are placeholders for the\n actual value at runtime.

\n
\n

For more information on expression attribute names, see Specifying Item Attributes in the Amazon DynamoDB Developer\n Guide.

" } }, "ExpressionAttributeValues": { "target": "com.amazonaws.dynamodb#ExpressionAttributeValueMap", "traits": { - "smithy.api#documentation": "

One or more values that can be substituted in an expression.

\n

Use the : (colon) character in an expression to\n dereference an attribute value. For example, suppose that you wanted to check whether\n the value of the ProductStatus attribute was one of the following:

\n

\n Available | Backordered | Discontinued\n

\n

You would first need to specify ExpressionAttributeValues as\n follows:

\n

\n { \":avail\":{\"S\":\"Available\"}, \":back\":{\"S\":\"Backordered\"},\n \":disc\":{\"S\":\"Discontinued\"} }\n

\n

You could then use these values in an expression, such as this:

\n

\n ProductStatus IN (:avail, :back, :disc)\n

\n

For more information on expression attribute values, see Condition Expressions in the Amazon DynamoDB Developer\n Guide.

" + "smithy.api#documentation": "

One or more values that can be substituted in an expression.

\n

Use the : (colon) character in an expression to\n dereference an attribute value. For example, suppose that you wanted to check whether\n the value of the ProductStatus attribute was one of the following:

\n

\n Available | Backordered | Discontinued\n

\n

You would first need to specify ExpressionAttributeValues as\n follows:

\n

\n { \":avail\":{\"S\":\"Available\"}, \":back\":{\"S\":\"Backordered\"},\n \":disc\":{\"S\":\"Discontinued\"} }\n

\n

You could then use these values in an expression, such as this:

\n

\n ProductStatus IN (:avail, :back, :disc)\n

\n

For more information on expression attribute values, see Condition Expressions in the Amazon DynamoDB Developer\n Guide.

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

Represents the input of a PutItem operation.

" + "smithy.api#documentation": "

Represents the input of a PutItem operation.

", + "smithy.api#input": {} } }, "com.amazonaws.dynamodb#PutItemInputAttributeMap": { @@ -7675,18 +7898,19 @@ "ConsumedCapacity": { "target": "com.amazonaws.dynamodb#ConsumedCapacity", "traits": { - "smithy.api#documentation": "

The capacity units consumed by the PutItem operation. The data returned\n includes the total provisioned throughput consumed, along with statistics for the table\n and any indexes involved in the operation. ConsumedCapacity is only\n returned if the ReturnConsumedCapacity parameter was specified. For more\n information, see Read/Write Capacity Mode in the Amazon DynamoDB Developer\n Guide.

" + "smithy.api#documentation": "

The capacity units consumed by the PutItem operation. The data returned\n includes the total provisioned throughput consumed, along with statistics for the table\n and any indexes involved in the operation. ConsumedCapacity is only\n returned if the ReturnConsumedCapacity parameter was specified. For more\n information, see Provisioned Throughput in the Amazon DynamoDB Developer\n Guide.

" } }, "ItemCollectionMetrics": { "target": "com.amazonaws.dynamodb#ItemCollectionMetrics", "traits": { - "smithy.api#documentation": "

Information about item collections, if any, that were affected by the\n PutItem operation. ItemCollectionMetrics is only returned\n if the ReturnItemCollectionMetrics parameter was specified. If the table\n does not have any local secondary indexes, this information is not returned in the\n response.

\n

Each ItemCollectionMetrics element consists of:

\n
    \n
  • \n

    \n ItemCollectionKey - The partition key value of the item collection.\n This is the same as the partition key value of the item itself.

    \n
  • \n
  • \n

    \n SizeEstimateRangeGB - An estimate of item collection size, in\n gigabytes. This value is a two-element array containing a lower bound and an\n upper bound for the estimate. The estimate includes the size of all the items in\n the table, plus the size of all attributes projected into all of the local\n secondary indexes on that table. Use this estimate to measure whether a local\n secondary index is approaching its size limit.

    \n

    The estimate is subject to change over time; therefore, do not rely on the\n precision or accuracy of the estimate.

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

Information about item collections, if any, that were affected by the\n PutItem operation. ItemCollectionMetrics is only returned\n if the ReturnItemCollectionMetrics parameter was specified. If the table\n does not have any local secondary indexes, this information is not returned in the\n response.

\n

Each ItemCollectionMetrics element consists of:

\n
    \n
  • \n

    \n ItemCollectionKey - The partition key value of the item collection.\n This is the same as the partition key value of the item itself.

    \n
  • \n
  • \n

    \n SizeEstimateRangeGB - An estimate of item collection size, in\n gigabytes. This value is a two-element array containing a lower bound and an\n upper bound for the estimate. The estimate includes the size of all the items in\n the table, plus the size of all attributes projected into all of the local\n secondary indexes on that table. Use this estimate to measure whether a local\n secondary index is approaching its size limit.

    \n

    The estimate is subject to change over time; therefore, do not rely on the\n precision or accuracy of the estimate.

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

Represents the output of a PutItem operation.

" + "smithy.api#documentation": "

Represents the output of a PutItem operation.

", + "smithy.api#output": {} } }, "com.amazonaws.dynamodb#PutRequest": { @@ -7733,7 +7957,7 @@ "aws.api#clientDiscoveredEndpoint": { "required": false }, - "smithy.api#documentation": "

You must provide the name of the partition key attribute and a single value for that\n attribute. Query returns all items with that partition key value.\n Optionally, you can provide a sort key attribute and use a comparison operator to refine\n the search results.

\n\n

Use the KeyConditionExpression parameter to provide a specific value for\n the partition key. The Query operation will return all of the items from\n the table or index with that partition key value. You can optionally narrow the scope of\n the Query operation by specifying a sort key value and a comparison\n operator in KeyConditionExpression. To further refine the\n Query results, you can optionally provide a\n FilterExpression. A FilterExpression determines which\n items within the results should be returned to you. All of the other results are\n discarded.

\n

A Query operation always returns a result set. If no matching items are\n found, the result set will be empty. Queries that do not return results consume the\n minimum number of read capacity units for that type of read operation.

\n \n

DynamoDB calculates the number of read capacity units consumed based on item\n size, not on the amount of data that is returned to an application. The number of\n capacity units consumed will be the same whether you request all of the attributes\n (the default behavior) or just some of them (using a projection expression). The\n number will also be the same whether or not you use a FilterExpression.\n

\n
\n

\n Query results are always sorted by the sort key value. If the data type of\n the sort key is Number, the results are returned in numeric order; otherwise, the\n results are returned in order of UTF-8 bytes. By default, the sort order is ascending.\n To reverse the order, set the ScanIndexForward parameter to false.

\n

A single Query operation will read up to the maximum number of items set\n (if using the Limit parameter) or a maximum of 1 MB of data and then apply\n any filtering to the results using FilterExpression. If\n LastEvaluatedKey is present in the response, you will need to paginate\n the result set. For more information, see Paginating\n the Results in the Amazon DynamoDB Developer Guide.

\n

\n FilterExpression is applied after a Query finishes, but before\n the results are returned. A FilterExpression cannot contain partition key\n or sort key attributes. You need to specify those attributes in the\n KeyConditionExpression.

\n \n

A Query operation can return an empty result set and a\n LastEvaluatedKey if all the items read for the page of results are\n filtered out.

\n
\n

You can query a table, a local secondary index, or a global secondary index. For a\n query on a table or on a local secondary index, you can set the\n ConsistentRead parameter to true and obtain a strongly\n consistent result. Global secondary indexes support eventually consistent reads only, so\n do not specify ConsistentRead when querying a global secondary\n index.

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

You must provide the name of the partition key attribute and a single value for that\n attribute. Query returns all items with that partition key value.\n Optionally, you can provide a sort key attribute and use a comparison operator to refine\n the search results.

\n

Use the KeyConditionExpression parameter to provide a specific value for\n the partition key. The Query operation will return all of the items from\n the table or index with that partition key value. You can optionally narrow the scope of\n the Query operation by specifying a sort key value and a comparison\n operator in KeyConditionExpression. To further refine the\n Query results, you can optionally provide a\n FilterExpression. A FilterExpression determines which\n items within the results should be returned to you. All of the other results are\n discarded.

\n

A Query operation always returns a result set. If no matching items are\n found, the result set will be empty. Queries that do not return results consume the\n minimum number of read capacity units for that type of read operation.

\n \n

DynamoDB calculates the number of read capacity units consumed based on item\n size, not on the amount of data that is returned to an application. The number of\n capacity units consumed will be the same whether you request all of the attributes\n (the default behavior) or just some of them (using a projection expression). The\n number will also be the same whether or not you use a FilterExpression.\n

\n
\n

\n Query results are always sorted by the sort key value. If the data type of\n the sort key is Number, the results are returned in numeric order; otherwise, the\n results are returned in order of UTF-8 bytes. By default, the sort order is ascending.\n To reverse the order, set the ScanIndexForward parameter to false.

\n

A single Query operation will read up to the maximum number of items set\n (if using the Limit parameter) or a maximum of 1 MB of data and then apply\n any filtering to the results using FilterExpression. If\n LastEvaluatedKey is present in the response, you will need to paginate\n the result set. For more information, see Paginating\n the Results in the Amazon DynamoDB Developer Guide.

\n

\n FilterExpression is applied after a Query finishes, but before\n the results are returned. A FilterExpression cannot contain partition key\n or sort key attributes. You need to specify those attributes in the\n KeyConditionExpression.

\n \n

A Query operation can return an empty result set and a\n LastEvaluatedKey if all the items read for the page of results are\n filtered out.

\n
\n

You can query a table, a local secondary index, or a global secondary index. For a\n query on a table or on a local secondary index, you can set the\n ConsistentRead parameter to true and obtain a strongly\n consistent result. Global secondary indexes support eventually consistent reads only, so\n do not specify ConsistentRead when querying a global secondary\n index.

", "smithy.api#paginated": { "inputToken": "ExclusiveStartKey", "outputToken": "LastEvaluatedKey", @@ -7758,13 +7982,13 @@ "IndexName": { "target": "com.amazonaws.dynamodb#IndexName", "traits": { - "smithy.api#documentation": "

The name of an index to query. This index can be any local secondary index or global\n secondary index on the table. Note that if you use the IndexName parameter,\n you must also provide TableName.\n

" + "smithy.api#documentation": "

The name of an index to query. This index can be any local secondary index or global\n secondary index on the table. Note that if you use the IndexName parameter,\n you must also provide TableName.\n

" } }, "Select": { "target": "com.amazonaws.dynamodb#Select", "traits": { - "smithy.api#documentation": "

The attributes to be returned in the result. You can retrieve all item attributes,\n specific item attributes, the count of matching items, or in the case of an index, some\n or all of the attributes projected into the index.

\n
    \n
  • \n

    \n ALL_ATTRIBUTES - Returns all of the item attributes from the\n specified table or index. If you query a local secondary index, then for each\n matching item in the index, DynamoDB fetches the entire item from the parent\n table. If the index is configured to project all item attributes, then all of\n the data can be obtained from the local secondary index, and no fetching is\n required.

    \n
  • \n
  • \n

    \n ALL_PROJECTED_ATTRIBUTES - Allowed only when querying an index.\n Retrieves all attributes that have been projected into the index. If the index\n is configured to project all attributes, this return value is equivalent to\n specifying ALL_ATTRIBUTES.

    \n
  • \n
  • \n

    \n COUNT - Returns the number of matching items, rather than the\n matching items themselves.

    \n
  • \n
  • \n

    \n SPECIFIC_ATTRIBUTES - Returns only the attributes listed in\n ProjectionExpression. This return value is equivalent to\n specifying ProjectionExpression without specifying any value for\n Select.

    \n

    If you query or scan a local secondary index and request only attributes that\n are projected into that index, the operation will read only the index and not\n the table. If any of the requested attributes are not projected into the local\n secondary index, DynamoDB fetches each of these attributes from the parent\n table. This extra fetching incurs additional throughput cost and latency.

    \n

    If you query or scan a global secondary index, you can only request attributes\n that are projected into the index. Global secondary index queries cannot fetch\n attributes from the parent table.

    \n
  • \n
\n

If neither Select nor ProjectionExpression are specified,\n DynamoDB defaults to ALL_ATTRIBUTES when accessing a table, and\n ALL_PROJECTED_ATTRIBUTES when accessing an index. You cannot use both\n Select and ProjectionExpression together in a single\n request, unless the value for Select is SPECIFIC_ATTRIBUTES.\n (This usage is equivalent to specifying ProjectionExpression without any\n value for Select.)

\n \n

If you use the ProjectionExpression parameter, then the value for\n Select can only be SPECIFIC_ATTRIBUTES. Any other\n value for Select will return an error.

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

The attributes to be returned in the result. You can retrieve all item attributes,\n specific item attributes, the count of matching items, or in the case of an index, some\n or all of the attributes projected into the index.

\n
    \n
  • \n

    \n ALL_ATTRIBUTES - Returns all of the item attributes from the\n specified table or index. If you query a local secondary index, then for each\n matching item in the index, DynamoDB fetches the entire item from the parent\n table. If the index is configured to project all item attributes, then all of\n the data can be obtained from the local secondary index, and no fetching is\n required.

    \n
  • \n
  • \n

    \n ALL_PROJECTED_ATTRIBUTES - Allowed only when querying an index.\n Retrieves all attributes that have been projected into the index. If the index\n is configured to project all attributes, this return value is equivalent to\n specifying ALL_ATTRIBUTES.

    \n
  • \n
  • \n

    \n COUNT - Returns the number of matching items, rather than the\n matching items themselves. Note that this uses the same quantity of read capacity units \n as getting the items, and is subject to the same item size calculations.

    \n
  • \n
  • \n

    \n SPECIFIC_ATTRIBUTES - Returns only the attributes listed in\n ProjectionExpression. This return value is equivalent to\n specifying ProjectionExpression without specifying any value for\n Select.

    \n

    If you query or scan a local secondary index and request only attributes that\n are projected into that index, the operation will read only the index and not\n the table. If any of the requested attributes are not projected into the local\n secondary index, DynamoDB fetches each of these attributes from the parent\n table. This extra fetching incurs additional throughput cost and latency.

    \n

    If you query or scan a global secondary index, you can only request attributes\n that are projected into the index. Global secondary index queries cannot fetch\n attributes from the parent table.

    \n
  • \n
\n

If neither Select nor ProjectionExpression are specified,\n DynamoDB defaults to ALL_ATTRIBUTES when accessing a table, and\n ALL_PROJECTED_ATTRIBUTES when accessing an index. You cannot use both\n Select and ProjectionExpression together in a single\n request, unless the value for Select is SPECIFIC_ATTRIBUTES.\n (This usage is equivalent to specifying ProjectionExpression without any\n value for Select.)

\n \n

If you use the ProjectionExpression parameter, then the value for\n Select can only be SPECIFIC_ATTRIBUTES. Any other\n value for Select will return an error.

\n
" } }, "AttributesToGet": { @@ -7782,7 +8006,7 @@ "ConsistentRead": { "target": "com.amazonaws.dynamodb#ConsistentRead", "traits": { - "smithy.api#documentation": "

Determines the read consistency model: If set to true, then the operation\n uses strongly consistent reads; otherwise, the operation uses eventually consistent\n reads.

\n

Strongly consistent reads are not supported on global secondary indexes. If you query\n a global secondary index with ConsistentRead set to true, you\n will receive a ValidationException.

" + "smithy.api#documentation": "

Determines the read consistency model: If set to true, then the operation\n uses strongly consistent reads; otherwise, the operation uses eventually consistent\n reads.

\n

Strongly consistent reads are not supported on global secondary indexes. If you query\n a global secondary index with ConsistentRead set to true, you\n will receive a ValidationException.

" } }, "KeyConditions": { @@ -7806,13 +8030,13 @@ "ScanIndexForward": { "target": "com.amazonaws.dynamodb#BooleanObject", "traits": { - "smithy.api#documentation": "

Specifies the order for index traversal: If true (default), the traversal\n is performed in ascending order; if false, the traversal is performed in\n descending order.

\n

Items with the same partition key value are stored in sorted order by sort key. If the\n sort key data type is Number, the results are stored in numeric order. For type String,\n the results are stored in order of UTF-8 bytes. For type Binary, DynamoDB treats each\n byte of the binary data as unsigned.

\n

If ScanIndexForward is true, DynamoDB returns the results in\n the order in which they are stored (by sort key value). This is the default behavior. If\n ScanIndexForward is false, DynamoDB reads the results in\n reverse order by sort key value, and then returns the results to the client.

" + "smithy.api#documentation": "

Specifies the order for index traversal: If true (default), the traversal\n is performed in ascending order; if false, the traversal is performed in\n descending order.

\n

Items with the same partition key value are stored in sorted order by sort key. If the\n sort key data type is Number, the results are stored in numeric order. For type String,\n the results are stored in order of UTF-8 bytes. For type Binary, DynamoDB treats each\n byte of the binary data as unsigned.

\n

If ScanIndexForward is true, DynamoDB returns the results in\n the order in which they are stored (by sort key value). This is the default behavior. If\n ScanIndexForward is false, DynamoDB reads the results in\n reverse order by sort key value, and then returns the results to the client.

" } }, "ExclusiveStartKey": { "target": "com.amazonaws.dynamodb#Key", "traits": { - "smithy.api#documentation": "

The primary key of the first item that this operation will evaluate. Use the value\n that was returned for LastEvaluatedKey in the previous operation.

\n

The data type for ExclusiveStartKey must be String, Number, or Binary. No\n set data types are allowed.

" + "smithy.api#documentation": "

The primary key of the first item that this operation will evaluate. Use the value\n that was returned for LastEvaluatedKey in the previous operation.

\n

The data type for ExclusiveStartKey must be String, Number, or Binary. No\n set data types are allowed.

" } }, "ReturnConsumedCapacity": { @@ -7821,36 +8045,37 @@ "ProjectionExpression": { "target": "com.amazonaws.dynamodb#ProjectionExpression", "traits": { - "smithy.api#documentation": "

A string that identifies one or more attributes to retrieve from the table. These\n attributes can include scalars, sets, or elements of a JSON document. The attributes in\n the expression must be separated by commas.

\n

If no attribute names are specified, then all attributes will be returned. If any of\n the requested attributes are not found, they will not appear in the result.

\n

For more information, see Accessing Item Attributes in the Amazon DynamoDB Developer\n Guide.

" + "smithy.api#documentation": "

A string that identifies one or more attributes to retrieve from the table. These\n attributes can include scalars, sets, or elements of a JSON document. The attributes in\n the expression must be separated by commas.

\n

If no attribute names are specified, then all attributes will be returned. If any of\n the requested attributes are not found, they will not appear in the result.

\n

For more information, see Accessing Item Attributes in the Amazon DynamoDB Developer\n Guide.

" } }, "FilterExpression": { "target": "com.amazonaws.dynamodb#ConditionExpression", "traits": { - "smithy.api#documentation": "

A string that contains conditions that DynamoDB applies after the Query\n operation, but before the data is returned to you. Items that do not satisfy the\n FilterExpression criteria are not returned.

\n

A FilterExpression does not allow key attributes. You cannot define a\n filter expression based on a partition key or a sort key.

\n \n

A FilterExpression is applied after the items have already been read;\n the process of filtering does not consume any additional read capacity units.

\n
\n

For more information, see Filter Expressions in the Amazon DynamoDB Developer\n Guide.

" + "smithy.api#documentation": "

A string that contains conditions that DynamoDB applies after the Query\n operation, but before the data is returned to you. Items that do not satisfy the\n FilterExpression criteria are not returned.

\n

A FilterExpression does not allow key attributes. You cannot define a\n filter expression based on a partition key or a sort key.

\n \n

A FilterExpression is applied after the items have already been read;\n the process of filtering does not consume any additional read capacity units.

\n
\n

For more information, see Filter Expressions in the Amazon DynamoDB Developer\n Guide.

" } }, "KeyConditionExpression": { "target": "com.amazonaws.dynamodb#KeyExpression", "traits": { - "smithy.api#documentation": "

The condition that specifies the key values for items to be retrieved by the\n Query action.

\n\n

The condition must perform an equality test on a single partition key value.

\n

The condition can optionally perform one of several comparison tests on a single sort\n key value. This allows Query to retrieve one item with a given partition\n key value and sort key value, or several items that have the same partition key value\n but different sort key values.

\n\n

The partition key equality test is required, and must be specified in the following\n format:

\n\n

\n partitionKeyName\n =\n :partitionkeyval\n

\n\n

If you also want to provide a condition for the sort key, it must be combined using\n AND with the condition for the sort key. Following is an example, using\n the = comparison operator for the sort key:

\n\n

\n partitionKeyName\n =\n :partitionkeyval\n AND\n sortKeyName\n =\n :sortkeyval\n

\n

Valid comparisons for the sort key condition are as follows:

\n
    \n
  • \n

    \n sortKeyName\n =\n :sortkeyval - true if the sort key value is equal to\n :sortkeyval.

    \n
  • \n
  • \n

    \n sortKeyName\n <\n :sortkeyval - true if the sort key value is less than\n :sortkeyval.

    \n
  • \n
  • \n

    \n sortKeyName\n <=\n :sortkeyval - true if the sort key value is less than or equal to\n :sortkeyval.

    \n
  • \n
  • \n

    \n sortKeyName\n >\n :sortkeyval - true if the sort key value is greater than\n :sortkeyval.

    \n
  • \n
  • \n

    \n sortKeyName\n >= \n :sortkeyval - true if the sort key value is greater than or equal\n to :sortkeyval.

    \n
  • \n
  • \n

    \n sortKeyName\n BETWEEN\n :sortkeyval1\n AND\n :sortkeyval2 - true if the sort key value is greater than or equal\n to :sortkeyval1, and less than or equal to\n :sortkeyval2.

    \n
  • \n
  • \n

    \n begins_with (\n sortKeyName, :sortkeyval\n ) - true if the sort key value begins with a particular operand.\n (You cannot use this function with a sort key that is of type Number.) Note that\n the function name begins_with is case-sensitive.

    \n\n
  • \n
\n\n

Use the ExpressionAttributeValues parameter to replace tokens such as\n :partitionval and :sortval with actual values at\n runtime.

\n\n

You can optionally use the ExpressionAttributeNames parameter to replace\n the names of the partition key and sort key with placeholder tokens. This option might\n be necessary if an attribute name conflicts with a DynamoDB reserved word. For example,\n the following KeyConditionExpression parameter causes an error because\n Size is a reserved word:

\n
    \n
  • \n

    \n Size = :myval\n

    \n
  • \n
\n

To work around this, define a placeholder (such a #S) to represent the\n attribute name Size. KeyConditionExpression then is as\n follows:

\n
    \n
  • \n

    \n #S = :myval\n

    \n
  • \n
\n

For a list of reserved words, see Reserved Words\n in the Amazon DynamoDB Developer Guide.

\n\n

For more information on ExpressionAttributeNames and\n ExpressionAttributeValues, see Using\n Placeholders for Attribute Names and Values in the Amazon DynamoDB\n Developer Guide.

" + "smithy.api#documentation": "

The condition that specifies the key values for items to be retrieved by the\n Query action.

\n

The condition must perform an equality test on a single partition key value.

\n

The condition can optionally perform one of several comparison tests on a single sort\n key value. This allows Query to retrieve one item with a given partition\n key value and sort key value, or several items that have the same partition key value\n but different sort key values.

\n

The partition key equality test is required, and must be specified in the following\n format:

\n

\n partitionKeyName\n =\n :partitionkeyval\n

\n

If you also want to provide a condition for the sort key, it must be combined using\n AND with the condition for the sort key. Following is an example, using\n the = comparison operator for the sort key:

\n

\n partitionKeyName\n =\n :partitionkeyval\n AND\n sortKeyName\n =\n :sortkeyval\n

\n

Valid comparisons for the sort key condition are as follows:

\n
    \n
  • \n

    \n sortKeyName\n =\n :sortkeyval - true if the sort key value is equal to\n :sortkeyval.

    \n
  • \n
  • \n

    \n sortKeyName\n <\n :sortkeyval - true if the sort key value is less than\n :sortkeyval.

    \n
  • \n
  • \n

    \n sortKeyName\n <=\n :sortkeyval - true if the sort key value is less than or equal to\n :sortkeyval.

    \n
  • \n
  • \n

    \n sortKeyName\n >\n :sortkeyval - true if the sort key value is greater than\n :sortkeyval.

    \n
  • \n
  • \n

    \n sortKeyName\n >= \n :sortkeyval - true if the sort key value is greater than or equal\n to :sortkeyval.

    \n
  • \n
  • \n

    \n sortKeyName\n BETWEEN\n :sortkeyval1\n AND\n :sortkeyval2 - true if the sort key value is greater than or equal\n to :sortkeyval1, and less than or equal to\n :sortkeyval2.

    \n
  • \n
  • \n

    \n begins_with (\n sortKeyName, :sortkeyval\n ) - true if the sort key value begins with a particular operand.\n (You cannot use this function with a sort key that is of type Number.) Note that\n the function name begins_with is case-sensitive.

    \n
  • \n
\n

Use the ExpressionAttributeValues parameter to replace tokens such as\n :partitionval and :sortval with actual values at\n runtime.

\n

You can optionally use the ExpressionAttributeNames parameter to replace\n the names of the partition key and sort key with placeholder tokens. This option might\n be necessary if an attribute name conflicts with a DynamoDB reserved word. For example,\n the following KeyConditionExpression parameter causes an error because\n Size is a reserved word:

\n
    \n
  • \n

    \n Size = :myval\n

    \n
  • \n
\n

To work around this, define a placeholder (such a #S) to represent the\n attribute name Size. KeyConditionExpression then is as\n follows:

\n
    \n
  • \n

    \n #S = :myval\n

    \n
  • \n
\n

For a list of reserved words, see Reserved Words\n in the Amazon DynamoDB Developer Guide.

\n

For more information on ExpressionAttributeNames and\n ExpressionAttributeValues, see Using\n Placeholders for Attribute Names and Values in the Amazon DynamoDB\n Developer Guide.

" } }, "ExpressionAttributeNames": { "target": "com.amazonaws.dynamodb#ExpressionAttributeNameMap", "traits": { - "smithy.api#documentation": "

One or more substitution tokens for attribute names in an expression. The following\n are some use cases for using ExpressionAttributeNames:

\n
    \n
  • \n

    To access an attribute whose name conflicts with a DynamoDB reserved\n word.

    \n
  • \n
  • \n

    To create a placeholder for repeating occurrences of an attribute name in an\n expression.

    \n
  • \n
  • \n

    To prevent special characters in an attribute name from being misinterpreted\n in an expression.

    \n
  • \n
\n

Use the # character in an expression to dereference\n an attribute name. For example, consider the following attribute name:

\n
    \n
  • \n

    \n Percentile\n

    \n
  • \n
\n

The name of this attribute conflicts with a reserved word, so it cannot be used\n directly in an expression. (For the complete list of reserved words, see Reserved Words in the Amazon DynamoDB Developer\n Guide). To work around this, you could specify the following for\n ExpressionAttributeNames:

\n
    \n
  • \n

    \n {\"#P\":\"Percentile\"}\n

    \n
  • \n
\n

You could then use this substitution in an expression, as in this example:

\n
    \n
  • \n

    \n #P = :val\n

    \n
  • \n
\n \n

Tokens that begin with the : character are\n expression attribute values, which are placeholders for the\n actual value at runtime.

\n
\n

For more information on expression attribute names, see Specifying Item Attributes in the Amazon DynamoDB Developer\n Guide.

" + "smithy.api#documentation": "

One or more substitution tokens for attribute names in an expression. The following\n are some use cases for using ExpressionAttributeNames:

\n
    \n
  • \n

    To access an attribute whose name conflicts with a DynamoDB reserved\n word.

    \n
  • \n
  • \n

    To create a placeholder for repeating occurrences of an attribute name in an\n expression.

    \n
  • \n
  • \n

    To prevent special characters in an attribute name from being misinterpreted\n in an expression.

    \n
  • \n
\n

Use the # character in an expression to dereference\n an attribute name. For example, consider the following attribute name:

\n
    \n
  • \n

    \n Percentile\n

    \n
  • \n
\n

The name of this attribute conflicts with a reserved word, so it cannot be used\n directly in an expression. (For the complete list of reserved words, see Reserved Words in the Amazon DynamoDB Developer\n Guide). To work around this, you could specify the following for\n ExpressionAttributeNames:

\n
    \n
  • \n

    \n {\"#P\":\"Percentile\"}\n

    \n
  • \n
\n

You could then use this substitution in an expression, as in this example:

\n
    \n
  • \n

    \n #P = :val\n

    \n
  • \n
\n \n

Tokens that begin with the : character are\n expression attribute values, which are placeholders for the\n actual value at runtime.

\n
\n

For more information on expression attribute names, see Specifying Item Attributes in the Amazon DynamoDB Developer\n Guide.

" } }, "ExpressionAttributeValues": { "target": "com.amazonaws.dynamodb#ExpressionAttributeValueMap", "traits": { - "smithy.api#documentation": "

One or more values that can be substituted in an expression.

\n

Use the : (colon) character in an expression to\n dereference an attribute value. For example, suppose that you wanted to check whether\n the value of the ProductStatus attribute was one of the following:

\n

\n Available | Backordered | Discontinued\n

\n

You would first need to specify ExpressionAttributeValues as\n follows:

\n

\n { \":avail\":{\"S\":\"Available\"}, \":back\":{\"S\":\"Backordered\"},\n \":disc\":{\"S\":\"Discontinued\"} }\n

\n

You could then use these values in an expression, such as this:

\n

\n ProductStatus IN (:avail, :back, :disc)\n

\n

For more information on expression attribute values, see Specifying Conditions in the Amazon DynamoDB Developer\n Guide.

" + "smithy.api#documentation": "

One or more values that can be substituted in an expression.

\n

Use the : (colon) character in an expression to\n dereference an attribute value. For example, suppose that you wanted to check whether\n the value of the ProductStatus attribute was one of the following:

\n

\n Available | Backordered | Discontinued\n

\n

You would first need to specify ExpressionAttributeValues as\n follows:

\n

\n { \":avail\":{\"S\":\"Available\"}, \":back\":{\"S\":\"Backordered\"},\n \":disc\":{\"S\":\"Discontinued\"} }\n

\n

You could then use these values in an expression, such as this:

\n

\n ProductStatus IN (:avail, :back, :disc)\n

\n

For more information on expression attribute values, see Specifying Conditions in the Amazon DynamoDB Developer\n Guide.

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

Represents the input of a Query operation.

" + "smithy.api#documentation": "

Represents the input of a Query operation.

", + "smithy.api#input": {} } }, "com.amazonaws.dynamodb#QueryOutput": { @@ -7866,20 +8091,20 @@ "target": "com.amazonaws.dynamodb#Integer", "traits": { "smithy.api#default": 0, - "smithy.api#documentation": "

The number of items in the response.

\n

If you used a QueryFilter in the request, then Count is the\n number of items returned after the filter was applied, and ScannedCount is\n the number of matching items before the filter was applied.

\n

If you did not use a filter in the request, then Count and\n ScannedCount are the same.

" + "smithy.api#documentation": "

The number of items in the response.

\n

If you used a QueryFilter in the request, then Count is the\n number of items returned after the filter was applied, and ScannedCount is\n the number of matching items before the filter was applied.

\n

If you did not use a filter in the request, then Count and\n ScannedCount are the same.

" } }, "ScannedCount": { "target": "com.amazonaws.dynamodb#Integer", "traits": { "smithy.api#default": 0, - "smithy.api#documentation": "

The number of items evaluated, before any QueryFilter is applied. A high\n ScannedCount value with few, or no, Count results\n indicates an inefficient Query operation. For more information, see Count and\n ScannedCount in the Amazon DynamoDB Developer\n Guide.

\n

If you did not use a filter in the request, then ScannedCount is the same\n as Count.

" + "smithy.api#documentation": "

The number of items evaluated, before any QueryFilter is applied. A high\n ScannedCount value with few, or no, Count results\n indicates an inefficient Query operation. For more information, see Count and\n ScannedCount in the Amazon DynamoDB Developer\n Guide.

\n

If you did not use a filter in the request, then ScannedCount is the same\n as Count.

" } }, "LastEvaluatedKey": { "target": "com.amazonaws.dynamodb#Key", "traits": { - "smithy.api#documentation": "

The primary key of the item where the operation stopped, inclusive of the previous\n result set. Use this value to start a new operation, excluding this value in the new\n request.

\n

If LastEvaluatedKey is empty, then the \"last page\" of results has been\n processed and there is no more data to be retrieved.

\n

If LastEvaluatedKey is not empty, it does not necessarily mean that there\n is more data in the result set. The only way to know when you have reached the end of\n the result set is when LastEvaluatedKey is empty.

" + "smithy.api#documentation": "

The primary key of the item where the operation stopped, inclusive of the previous\n result set. Use this value to start a new operation, excluding this value in the new\n request.

\n

If LastEvaluatedKey is empty, then the \"last page\" of results has been\n processed and there is no more data to be retrieved.

\n

If LastEvaluatedKey is not empty, it does not necessarily mean that there\n is more data in the result set. The only way to know when you have reached the end of\n the result set is when LastEvaluatedKey is empty.

" } }, "ConsumedCapacity": { @@ -7890,7 +8115,8 @@ } }, "traits": { - "smithy.api#documentation": "

Represents the output of a Query operation.

" + "smithy.api#documentation": "

Represents the output of a Query operation.

", + "smithy.api#output": {} } }, "com.amazonaws.dynamodb#RegionName": { @@ -7946,7 +8172,7 @@ "ReplicaStatus": { "target": "com.amazonaws.dynamodb#ReplicaStatus", "traits": { - "smithy.api#documentation": "

The current state of the replica:

\n
    \n
  • \n

    \n CREATING - The replica is being created.

    \n
  • \n
  • \n

    \n UPDATING - The replica is being updated.

    \n
  • \n
  • \n

    \n DELETING - The replica is being deleted.

    \n
  • \n
  • \n

    \n ACTIVE - The replica is ready for use.

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

The current state of the replica:

\n
    \n
  • \n

    \n CREATING - The replica is being created.

    \n
  • \n
  • \n

    \n UPDATING - The replica is being updated.

    \n
  • \n
  • \n

    \n DELETING - The replica is being deleted.

    \n
  • \n
  • \n

    \n ACTIVE - The replica is ready for use.

    \n
  • \n
" } } }, @@ -8007,7 +8233,7 @@ "ReplicaStatus": { "target": "com.amazonaws.dynamodb#ReplicaStatus", "traits": { - "smithy.api#documentation": "

The current state of the replica:

\n
    \n
  • \n

    \n CREATING - The replica is being created.

    \n
  • \n
  • \n

    \n UPDATING - The replica is being updated.

    \n
  • \n
  • \n

    \n DELETING - The replica is being deleted.

    \n
  • \n
  • \n

    \n ACTIVE - The replica is ready for use.

    \n
  • \n
  • \n

    \n REGION_DISABLED - The replica is inaccessible because the Amazon Web Services Region has been disabled.

    \n \n

    If the Amazon Web Services Region remains inaccessible for more than 20\n hours, DynamoDB will remove this replica from the replication\n group. The replica will not be deleted and replication will stop from and to\n this region.

    \n
    \n
  • \n
  • \n

    \n INACCESSIBLE_ENCRYPTION_CREDENTIALS - The KMS key\n used to encrypt the table is inaccessible.

    \n \n

    If the KMS key remains inaccessible for more than 20 hours,\n DynamoDB will remove this replica from the replication group.\n The replica will not be deleted and replication will stop from and to this\n region.

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

The current state of the replica:

\n
    \n
  • \n

    \n CREATING - The replica is being created.

    \n
  • \n
  • \n

    \n UPDATING - The replica is being updated.

    \n
  • \n
  • \n

    \n DELETING - The replica is being deleted.

    \n
  • \n
  • \n

    \n ACTIVE - The replica is ready for use.

    \n
  • \n
  • \n

    \n REGION_DISABLED - The replica is inaccessible because the Amazon Web Services Region has been disabled.

    \n \n

    If the Amazon Web Services Region remains inaccessible for more than 20\n hours, DynamoDB will remove this replica from the replication\n group. The replica will not be deleted and replication will stop from and to\n this region.

    \n
    \n
  • \n
  • \n

    \n INACCESSIBLE_ENCRYPTION_CREDENTIALS - The KMS key\n used to encrypt the table is inaccessible.

    \n \n

    If the KMS key remains inaccessible for more than 20 hours,\n DynamoDB will remove this replica from the replication group.\n The replica will not be deleted and replication will stop from and to this\n region.

    \n
    \n
  • \n
" } }, "ReplicaStatusDescription": { @@ -8093,7 +8319,7 @@ "IndexStatus": { "target": "com.amazonaws.dynamodb#IndexStatus", "traits": { - "smithy.api#documentation": "

The current state of the replica global secondary index:

\n
    \n
  • \n

    \n CREATING - The index is being created.

    \n
  • \n
  • \n

    \n UPDATING - The table/index configuration is being updated. The\n table/index remains available for data operations when\n UPDATING\n

    \n\n
  • \n
  • \n

    \n DELETING - The index is being deleted.

    \n
  • \n
  • \n

    \n ACTIVE - The index is ready for use.

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

The current state of the replica global secondary index:

\n
    \n
  • \n

    \n CREATING - The index is being created.

    \n
  • \n
  • \n

    \n UPDATING - The table/index configuration is being updated. The\n table/index remains available for data operations when\n UPDATING\n

    \n
  • \n
  • \n

    \n DELETING - The index is being deleted.

    \n
  • \n
  • \n

    \n ACTIVE - The index is ready for use.

    \n
  • \n
" } }, "ProvisionedReadCapacityAutoScalingSettings": { @@ -8186,7 +8412,7 @@ "IndexStatus": { "target": "com.amazonaws.dynamodb#IndexStatus", "traits": { - "smithy.api#documentation": "

The current status of the global secondary index:

\n
    \n
  • \n

    \n CREATING - The global secondary index is being created.

    \n
  • \n
  • \n

    \n UPDATING - The global secondary index is being updated.

    \n
  • \n
  • \n

    \n DELETING - The global secondary index is being deleted.

    \n
  • \n
  • \n

    \n ACTIVE - The global secondary index is ready for use.

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

The current status of the global secondary index:

\n
    \n
  • \n

    \n CREATING - The global secondary index is being created.

    \n
  • \n
  • \n

    \n UPDATING - The global secondary index is being updated.

    \n
  • \n
  • \n

    \n DELETING - The global secondary index is being deleted.

    \n
  • \n
  • \n

    \n ACTIVE - The global secondary index is ready for use.

    \n
  • \n
" } }, "ProvisionedReadCapacityUnits": { @@ -8294,7 +8520,7 @@ "ReplicaStatus": { "target": "com.amazonaws.dynamodb#ReplicaStatus", "traits": { - "smithy.api#documentation": "

The current state of the Region:

\n
    \n
  • \n

    \n CREATING - The Region is being created.

    \n
  • \n
  • \n

    \n UPDATING - The Region is being updated.

    \n
  • \n
  • \n

    \n DELETING - The Region is being deleted.

    \n
  • \n
  • \n

    \n ACTIVE - The Region is ready for use.

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

The current state of the Region:

\n
    \n
  • \n

    \n CREATING - The Region is being created.

    \n
  • \n
  • \n

    \n UPDATING - The Region is being updated.

    \n
  • \n
  • \n

    \n DELETING - The Region is being deleted.

    \n
  • \n
  • \n

    \n ACTIVE - The Region is ready for use.

    \n
  • \n
" } }, "ReplicaBillingModeSummary": { @@ -8468,7 +8694,7 @@ } }, "traits": { - "smithy.api#documentation": "

Represents one of the following:

\n
    \n
  • \n

    A new replica to be added to an existing global table.

    \n
  • \n
  • \n

    New parameters for an existing replica.

    \n
  • \n
  • \n

    An existing replica to be removed from an existing global table.

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

Represents one of the following:

\n
    \n
  • \n

    A new replica to be added to an existing global table.

    \n
  • \n
  • \n

    New parameters for an existing replica.

    \n
  • \n
  • \n

    An existing replica to be removed from an existing global table.

    \n
  • \n
" } }, "com.amazonaws.dynamodb#ReplicaUpdateList": { @@ -8500,7 +8726,7 @@ } }, "traits": { - "smithy.api#documentation": "

Represents one of the following:

\n
    \n
  • \n

    A new replica to be added to an existing regional table or global table. This\n request invokes the CreateTableReplica action in the destination\n Region.

    \n
  • \n
  • \n

    New parameters for an existing replica. This request invokes the\n UpdateTable action in the destination Region.

    \n
  • \n
  • \n

    An existing replica to be deleted. The request invokes the\n DeleteTableReplica action in the destination Region, deleting\n the replica and all if its items in the destination Region.

    \n
  • \n
\n \n

When you manually remove a table or global table replica, you do not automatically\n remove any associated scalable targets, scaling policies, or CloudWatch\n alarms.

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

Represents one of the following:

\n
    \n
  • \n

    A new replica to be added to an existing regional table or global table. This\n request invokes the CreateTableReplica action in the destination\n Region.

    \n
  • \n
  • \n

    New parameters for an existing replica. This request invokes the\n UpdateTable action in the destination Region.

    \n
  • \n
  • \n

    An existing replica to be deleted. The request invokes the\n DeleteTableReplica action in the destination Region, deleting\n the replica and all if its items in the destination Region.

    \n
  • \n
\n \n

When you manually remove a table or global table replica, you do not automatically\n remove any associated scalable targets, scaling policies, or CloudWatch\n alarms.

\n
" } }, "com.amazonaws.dynamodb#ReplicationGroupUpdateList": { @@ -8637,7 +8863,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 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
" } }, "com.amazonaws.dynamodb#RestoreTableFromBackupInput": { @@ -8687,6 +8913,9 @@ "smithy.api#documentation": "

The new server-side encryption settings for the restored table.

" } } + }, + "traits": { + "smithy.api#input": {} } }, "com.amazonaws.dynamodb#RestoreTableFromBackupOutput": { @@ -8698,6 +8927,9 @@ "smithy.api#documentation": "

The description of the table created from an existing backup.

" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.dynamodb#RestoreTableToPointInTime": { @@ -8738,7 +8970,7 @@ "aws.api#clientDiscoveredEndpoint": { "required": false }, - "smithy.api#documentation": "

Restores the specified table to the specified point in time within\n EarliestRestorableDateTime and LatestRestorableDateTime.\n You can restore your table to any point in time during the last 35 days. Any number of\n users can execute up to 4 concurrent restores (any type of restore) in a given account.

\n

When you restore using point in time recovery, DynamoDB restores your table data to\n the state based on the selected date and time (day:hour:minute:second) to a new table.

\n

Along with data, the following are also included on the new restored table using\n point in time recovery:

\n
    \n
  • \n

    Global secondary indexes (GSIs)

    \n
  • \n
  • \n

    Local secondary indexes (LSIs)

    \n
  • \n
  • \n

    Provisioned read and write capacity

    \n
  • \n
  • \n

    Encryption settings

    \n \n

    All these settings come from the current settings of the source table at\n the time of restore.

    \n
    \n
  • \n
\n\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
  • \n

    Point in time recovery settings

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

Restores the specified table to the specified point in time within\n EarliestRestorableDateTime and LatestRestorableDateTime.\n You can restore your table to any point in time during the last 35 days. Any number of\n users can execute up to 4 concurrent restores (any type of restore) in a given account.

\n

When you restore using point in time recovery, DynamoDB restores your table data to\n the state based on the selected date and time (day:hour:minute:second) to a new table.

\n

Along with data, the following are also included on the new restored table using\n point in time recovery:

\n
    \n
  • \n

    Global secondary indexes (GSIs)

    \n
  • \n
  • \n

    Local secondary indexes (LSIs)

    \n
  • \n
  • \n

    Provisioned read and write capacity

    \n
  • \n
  • \n

    Encryption settings

    \n \n

    All these settings come from the current settings of the source table at\n the time of restore.

    \n
    \n
  • \n
\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
  • \n

    Point in time recovery settings

    \n
  • \n
" } }, "com.amazonaws.dynamodb#RestoreTableToPointInTimeInput": { @@ -8805,6 +9037,9 @@ "smithy.api#documentation": "

The new server-side encryption settings for the restored table.

" } } + }, + "traits": { + "smithy.api#input": {} } }, "com.amazonaws.dynamodb#RestoreTableToPointInTimeOutput": { @@ -8816,6 +9051,9 @@ "smithy.api#documentation": "

Represents the properties of a table.

" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.dynamodb#ReturnConsumedCapacity": { @@ -8841,7 +9079,7 @@ } }, "traits": { - "smithy.api#documentation": "

Determines the level of detail about either provisioned or on-demand throughput\n consumption that is returned in the response:

\n
    \n
  • \n

    \n INDEXES - The response includes the aggregate\n ConsumedCapacity for the operation, together with\n ConsumedCapacity for each table and secondary index that was\n accessed.

    \n

    Note that some operations, such as GetItem and\n BatchGetItem, do not access any indexes at all. In these cases,\n specifying INDEXES will only return ConsumedCapacity\n information for table(s).

    \n
  • \n
  • \n

    \n TOTAL - The response includes only the aggregate\n ConsumedCapacity for the operation.

    \n
  • \n
  • \n

    \n NONE - No ConsumedCapacity details are included in the\n response.

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

Determines the level of detail about either provisioned or on-demand throughput\n consumption that is returned in the response:

\n
    \n
  • \n

    \n INDEXES - The response includes the aggregate\n ConsumedCapacity for the operation, together with\n ConsumedCapacity for each table and secondary index that was\n accessed.

    \n

    Note that some operations, such as GetItem and\n BatchGetItem, do not access any indexes at all. In these cases,\n specifying INDEXES will only return ConsumedCapacity\n information for table(s).

    \n
  • \n
  • \n

    \n TOTAL - The response includes only the aggregate\n ConsumedCapacity for the operation.

    \n
  • \n
  • \n

    \n NONE - No ConsumedCapacity details are included in the\n response.

    \n
  • \n
" } }, "com.amazonaws.dynamodb#ReturnItemCollectionMetrics": { @@ -8997,13 +9235,13 @@ "Status": { "target": "com.amazonaws.dynamodb#SSEStatus", "traits": { - "smithy.api#documentation": "

Represents the current state of server-side encryption. The only supported values\n are:

\n
    \n
  • \n

    \n ENABLED - Server-side encryption is enabled.

    \n
  • \n
  • \n

    \n UPDATING - Server-side encryption is being updated.

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

Represents the current state of server-side encryption. The only supported values\n are:

\n
    \n
  • \n

    \n ENABLED - Server-side encryption is enabled.

    \n
  • \n
  • \n

    \n UPDATING - Server-side encryption is being updated.

    \n
  • \n
" } }, "SSEType": { "target": "com.amazonaws.dynamodb#SSEType", "traits": { - "smithy.api#documentation": "

Server-side encryption type. The only supported value is:

\n
    \n
  • \n

    \n KMS - Server-side encryption that uses Key Management Service. The\n key is stored in your account and is managed by KMS (KMS charges apply).

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

Server-side encryption type. The only supported value is:

\n
    \n
  • \n

    \n KMS - Server-side encryption that uses Key Management Service. The\n key is stored in your account and is managed by KMS (KMS charges apply).

    \n
  • \n
" } }, "KMSMasterKeyArn": { @@ -9038,7 +9276,7 @@ "SSEType": { "target": "com.amazonaws.dynamodb#SSEType", "traits": { - "smithy.api#documentation": "

Server-side encryption type. The only supported value is:

\n
    \n
  • \n

    \n KMS - Server-side encryption that uses Key Management Service. The\n key is stored in your account and is managed by KMS (KMS charges apply).

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

Server-side encryption type. The only supported value is:

\n
    \n
  • \n

    \n KMS - Server-side encryption that uses Key Management Service. The\n key is stored in your account and is managed by KMS (KMS charges apply).

    \n
  • \n
" } }, "KMSMasterKeyId": { @@ -9156,7 +9394,7 @@ "aws.api#clientDiscoveredEndpoint": { "required": false }, - "smithy.api#documentation": "

The Scan operation returns one or more items and item attributes by\n accessing every item in a table or a secondary index. To have DynamoDB return fewer\n items, you can provide a FilterExpression operation.

\n

If the total number of scanned items exceeds the maximum dataset size limit of 1 MB,\n the scan stops and results are returned to the user as a LastEvaluatedKey\n value to continue the scan in a subsequent operation. The results also include the\n number of items exceeding the limit. A scan can result in no table data meeting the\n filter criteria.

\n

A single Scan operation reads up to the maximum number of items set (if\n using the Limit parameter) or a maximum of 1 MB of data and then apply any\n filtering to the results using FilterExpression. If\n LastEvaluatedKey is present in the response, you need to paginate the\n result set. For more information, see Paginating the\n Results in the Amazon DynamoDB Developer Guide.

\n

\n Scan operations proceed sequentially; however, for faster performance on\n a large table or secondary index, applications can request a parallel Scan\n operation by providing the Segment and TotalSegments\n parameters. For more information, see Parallel\n Scan in the Amazon DynamoDB Developer Guide.

\n

\n Scan uses eventually consistent reads when accessing the data in a table;\n therefore, the result set might not include the changes to data in the table immediately\n before the operation began. If you need a consistent copy of the data, as of the time\n that the Scan begins, you can set the ConsistentRead parameter\n to true.

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

The Scan operation returns one or more items and item attributes by\n accessing every item in a table or a secondary index. To have DynamoDB return fewer\n items, you can provide a FilterExpression operation.

\n

If the total number of scanned items exceeds the maximum dataset size limit of 1 MB,\n the scan stops and results are returned to the user as a LastEvaluatedKey\n value to continue the scan in a subsequent operation. The results also include the\n number of items exceeding the limit. A scan can result in no table data meeting the\n filter criteria.

\n

A single Scan operation reads up to the maximum number of items set (if\n using the Limit parameter) or a maximum of 1 MB of data and then apply any\n filtering to the results using FilterExpression. If\n LastEvaluatedKey is present in the response, you need to paginate the\n result set. For more information, see Paginating the\n Results in the Amazon DynamoDB Developer Guide.

\n

\n Scan operations proceed sequentially; however, for faster performance on\n a large table or secondary index, applications can request a parallel Scan\n operation by providing the Segment and TotalSegments\n parameters. For more information, see Parallel\n Scan in the Amazon DynamoDB Developer Guide.

\n

\n Scan uses eventually consistent reads when accessing the data in a table;\n therefore, the result set might not include the changes to data in the table immediately\n before the operation began. If you need a consistent copy of the data, as of the time\n that the Scan begins, you can set the ConsistentRead parameter\n to true.

", "smithy.api#paginated": { "inputToken": "ExclusiveStartKey", "outputToken": "LastEvaluatedKey", @@ -9199,7 +9437,7 @@ "Select": { "target": "com.amazonaws.dynamodb#Select", "traits": { - "smithy.api#documentation": "

The attributes to be returned in the result. You can retrieve all item attributes,\n specific item attributes, the count of matching items, or in the case of an index, some\n or all of the attributes projected into the index.

\n
    \n
  • \n

    \n ALL_ATTRIBUTES - Returns all of the item attributes from the\n specified table or index. If you query a local secondary index, then for each\n matching item in the index, DynamoDB fetches the entire item from the parent\n table. If the index is configured to project all item attributes, then all of\n the data can be obtained from the local secondary index, and no fetching is\n required.

    \n
  • \n
  • \n

    \n ALL_PROJECTED_ATTRIBUTES - Allowed only when querying an index.\n Retrieves all attributes that have been projected into the index. If the index\n is configured to project all attributes, this return value is equivalent to\n specifying ALL_ATTRIBUTES.

    \n
  • \n
  • \n

    \n COUNT - Returns the number of matching items, rather than the\n matching items themselves.

    \n
  • \n
  • \n

    \n SPECIFIC_ATTRIBUTES - Returns only the attributes listed in\n ProjectionExpression. This return value is equivalent to\n specifying ProjectionExpression without specifying any value for\n Select.

    \n

    If you query or scan a local secondary index and request only attributes that\n are projected into that index, the operation reads only the index and not the\n table. If any of the requested attributes are not projected into the local\n secondary index, DynamoDB fetches each of these attributes from the parent\n table. This extra fetching incurs additional throughput cost and latency.

    \n

    If you query or scan a global secondary index, you can only request attributes\n that are projected into the index. Global secondary index queries cannot fetch\n attributes from the parent table.

    \n
  • \n
\n

If neither Select nor ProjectionExpression are specified,\n DynamoDB defaults to ALL_ATTRIBUTES when accessing a table, and\n ALL_PROJECTED_ATTRIBUTES when accessing an index. You cannot use both\n Select and ProjectionExpression together in a single\n request, unless the value for Select is SPECIFIC_ATTRIBUTES.\n (This usage is equivalent to specifying ProjectionExpression without any\n value for Select.)

\n \n

If you use the ProjectionExpression parameter, then the value for\n Select can only be SPECIFIC_ATTRIBUTES. Any other\n value for Select will return an error.

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

The attributes to be returned in the result. You can retrieve all item attributes,\n specific item attributes, the count of matching items, or in the case of an index, some\n or all of the attributes projected into the index.

\n
    \n
  • \n

    \n ALL_ATTRIBUTES - Returns all of the item attributes from the\n specified table or index. If you query a local secondary index, then for each\n matching item in the index, DynamoDB fetches the entire item from the parent\n table. If the index is configured to project all item attributes, then all of\n the data can be obtained from the local secondary index, and no fetching is\n required.

    \n
  • \n
  • \n

    \n ALL_PROJECTED_ATTRIBUTES - Allowed only when querying an index.\n Retrieves all attributes that have been projected into the index. If the index\n is configured to project all attributes, this return value is equivalent to\n specifying ALL_ATTRIBUTES.

    \n
  • \n
  • \n

    \n COUNT - Returns the number of matching items, rather than the\n matching items themselves. Note that this uses the same quantity of read capacity units \n as getting the items, and is subject to the same item size calculations.

    \n
  • \n
  • \n

    \n SPECIFIC_ATTRIBUTES - Returns only the attributes listed in\n ProjectionExpression. This return value is equivalent to\n specifying ProjectionExpression without specifying any value for\n Select.

    \n

    If you query or scan a local secondary index and request only attributes that\n are projected into that index, the operation reads only the index and not the\n table. If any of the requested attributes are not projected into the local\n secondary index, DynamoDB fetches each of these attributes from the parent\n table. This extra fetching incurs additional throughput cost and latency.

    \n

    If you query or scan a global secondary index, you can only request attributes\n that are projected into the index. Global secondary index queries cannot fetch\n attributes from the parent table.

    \n
  • \n
\n

If neither Select nor ProjectionExpression are specified,\n DynamoDB defaults to ALL_ATTRIBUTES when accessing a table, and\n ALL_PROJECTED_ATTRIBUTES when accessing an index. You cannot use both\n Select and ProjectionExpression together in a single\n request, unless the value for Select is SPECIFIC_ATTRIBUTES.\n (This usage is equivalent to specifying ProjectionExpression without any\n value for Select.)

\n \n

If you use the ProjectionExpression parameter, then the value for\n Select can only be SPECIFIC_ATTRIBUTES. Any other\n value for Select will return an error.

\n
" } }, "ScanFilter": { @@ -9217,7 +9455,7 @@ "ExclusiveStartKey": { "target": "com.amazonaws.dynamodb#Key", "traits": { - "smithy.api#documentation": "

The primary key of the first item that this operation will evaluate. Use the value\n that was returned for LastEvaluatedKey in the previous operation.

\n

The data type for ExclusiveStartKey must be String, Number or Binary. No\n set data types are allowed.

\n

In a parallel scan, a Scan request that includes\n ExclusiveStartKey must specify the same segment whose previous\n Scan returned the corresponding value of\n LastEvaluatedKey.

" + "smithy.api#documentation": "

The primary key of the first item that this operation will evaluate. Use the value\n that was returned for LastEvaluatedKey in the previous operation.

\n

The data type for ExclusiveStartKey must be String, Number or Binary. No\n set data types are allowed.

\n

In a parallel scan, a Scan request that includes\n ExclusiveStartKey must specify the same segment whose previous\n Scan returned the corresponding value of\n LastEvaluatedKey.

" } }, "ReturnConsumedCapacity": { @@ -9226,48 +9464,49 @@ "TotalSegments": { "target": "com.amazonaws.dynamodb#ScanTotalSegments", "traits": { - "smithy.api#documentation": "

For a parallel Scan request, TotalSegments represents the\n total number of segments into which the Scan operation will be divided. The\n value of TotalSegments corresponds to the number of application workers\n that will perform the parallel scan. For example, if you want to use four application\n threads to scan a table or an index, specify a TotalSegments value of\n 4.

\n

The value for TotalSegments must be greater than or equal to 1, and less\n than or equal to 1000000. If you specify a TotalSegments value of 1, the\n Scan operation will be sequential rather than parallel.

\n

If you specify TotalSegments, you must also specify\n Segment.

" + "smithy.api#documentation": "

For a parallel Scan request, TotalSegments represents the\n total number of segments into which the Scan operation will be divided. The\n value of TotalSegments corresponds to the number of application workers\n that will perform the parallel scan. For example, if you want to use four application\n threads to scan a table or an index, specify a TotalSegments value of\n 4.

\n

The value for TotalSegments must be greater than or equal to 1, and less\n than or equal to 1000000. If you specify a TotalSegments value of 1, the\n Scan operation will be sequential rather than parallel.

\n

If you specify TotalSegments, you must also specify\n Segment.

" } }, "Segment": { "target": "com.amazonaws.dynamodb#ScanSegment", "traits": { - "smithy.api#documentation": "

For a parallel Scan request, Segment identifies an\n individual segment to be scanned by an application worker.

\n

Segment IDs are zero-based, so the first segment is always 0. For example, if you want\n to use four application threads to scan a table or an index, then the first thread\n specifies a Segment value of 0, the second thread specifies 1, and so\n on.

\n

The value of LastEvaluatedKey returned from a parallel Scan\n request must be used as ExclusiveStartKey with the same segment ID in a\n subsequent Scan operation.

\n

The value for Segment must be greater than or equal to 0, and less than\n the value provided for TotalSegments.

\n

If you provide Segment, you must also provide\n TotalSegments.

" + "smithy.api#documentation": "

For a parallel Scan request, Segment identifies an\n individual segment to be scanned by an application worker.

\n

Segment IDs are zero-based, so the first segment is always 0. For example, if you want\n to use four application threads to scan a table or an index, then the first thread\n specifies a Segment value of 0, the second thread specifies 1, and so\n on.

\n

The value of LastEvaluatedKey returned from a parallel Scan\n request must be used as ExclusiveStartKey with the same segment ID in a\n subsequent Scan operation.

\n

The value for Segment must be greater than or equal to 0, and less than\n the value provided for TotalSegments.

\n

If you provide Segment, you must also provide\n TotalSegments.

" } }, "ProjectionExpression": { "target": "com.amazonaws.dynamodb#ProjectionExpression", "traits": { - "smithy.api#documentation": "

A string that identifies one or more attributes to retrieve from the specified table\n or index. These attributes can include scalars, sets, or elements of a JSON document.\n The attributes in the expression must be separated by commas.

\n

If no attribute names are specified, then all attributes will be returned. If any of\n the requested attributes are not found, they will not appear in the result.

\n

For more information, see Specifying Item Attributes in the Amazon DynamoDB Developer\n Guide.

" + "smithy.api#documentation": "

A string that identifies one or more attributes to retrieve from the specified table\n or index. These attributes can include scalars, sets, or elements of a JSON document.\n The attributes in the expression must be separated by commas.

\n

If no attribute names are specified, then all attributes will be returned. If any of\n the requested attributes are not found, they will not appear in the result.

\n

For more information, see Specifying Item Attributes in the Amazon DynamoDB Developer\n Guide.

" } }, "FilterExpression": { "target": "com.amazonaws.dynamodb#ConditionExpression", "traits": { - "smithy.api#documentation": "

A string that contains conditions that DynamoDB applies after the Scan\n operation, but before the data is returned to you. Items that do not satisfy the\n FilterExpression criteria are not returned.

\n \n

A FilterExpression is applied after the items have already been read;\n the process of filtering does not consume any additional read capacity units.

\n
\n

For more information, see Filter Expressions in the Amazon DynamoDB Developer\n Guide.

" + "smithy.api#documentation": "

A string that contains conditions that DynamoDB applies after the Scan\n operation, but before the data is returned to you. Items that do not satisfy the\n FilterExpression criteria are not returned.

\n \n

A FilterExpression is applied after the items have already been read;\n the process of filtering does not consume any additional read capacity units.

\n
\n

For more information, see Filter Expressions in the Amazon DynamoDB Developer\n Guide.

" } }, "ExpressionAttributeNames": { "target": "com.amazonaws.dynamodb#ExpressionAttributeNameMap", "traits": { - "smithy.api#documentation": "

One or more substitution tokens for attribute names in an expression. The following\n are some use cases for using ExpressionAttributeNames:

\n
    \n
  • \n

    To access an attribute whose name conflicts with a DynamoDB reserved\n word.

    \n
  • \n
  • \n

    To create a placeholder for repeating occurrences of an attribute name in an\n expression.

    \n
  • \n
  • \n

    To prevent special characters in an attribute name from being misinterpreted\n in an expression.

    \n
  • \n
\n

Use the # character in an expression to dereference\n an attribute name. For example, consider the following attribute name:

\n
    \n
  • \n

    \n Percentile\n

    \n
  • \n
\n

The name of this attribute conflicts with a reserved word, so it cannot be used\n directly in an expression. (For the complete list of reserved words, see Reserved Words in the Amazon DynamoDB Developer\n Guide). To work around this, you could specify the following for\n ExpressionAttributeNames:

\n
    \n
  • \n

    \n {\"#P\":\"Percentile\"}\n

    \n
  • \n
\n

You could then use this substitution in an expression, as in this example:

\n
    \n
  • \n

    \n #P = :val\n

    \n
  • \n
\n \n

Tokens that begin with the : character are\n expression attribute values, which are placeholders for the\n actual value at runtime.

\n
\n

For more information on expression attribute names, see Specifying Item Attributes in the Amazon DynamoDB Developer\n Guide.

" + "smithy.api#documentation": "

One or more substitution tokens for attribute names in an expression. The following\n are some use cases for using ExpressionAttributeNames:

\n
    \n
  • \n

    To access an attribute whose name conflicts with a DynamoDB reserved\n word.

    \n
  • \n
  • \n

    To create a placeholder for repeating occurrences of an attribute name in an\n expression.

    \n
  • \n
  • \n

    To prevent special characters in an attribute name from being misinterpreted\n in an expression.

    \n
  • \n
\n

Use the # character in an expression to dereference\n an attribute name. For example, consider the following attribute name:

\n
    \n
  • \n

    \n Percentile\n

    \n
  • \n
\n

The name of this attribute conflicts with a reserved word, so it cannot be used\n directly in an expression. (For the complete list of reserved words, see Reserved Words in the Amazon DynamoDB Developer\n Guide). To work around this, you could specify the following for\n ExpressionAttributeNames:

\n
    \n
  • \n

    \n {\"#P\":\"Percentile\"}\n

    \n
  • \n
\n

You could then use this substitution in an expression, as in this example:

\n
    \n
  • \n

    \n #P = :val\n

    \n
  • \n
\n \n

Tokens that begin with the : character are\n expression attribute values, which are placeholders for the\n actual value at runtime.

\n
\n

For more information on expression attribute names, see Specifying Item Attributes in the Amazon DynamoDB Developer\n Guide.

" } }, "ExpressionAttributeValues": { "target": "com.amazonaws.dynamodb#ExpressionAttributeValueMap", "traits": { - "smithy.api#documentation": "

One or more values that can be substituted in an expression.

\n

Use the : (colon) character in an expression to\n dereference an attribute value. For example, suppose that you wanted to check whether\n the value of the ProductStatus attribute was one of the following:

\n

\n Available | Backordered | Discontinued\n

\n

You would first need to specify ExpressionAttributeValues as\n follows:

\n

\n { \":avail\":{\"S\":\"Available\"}, \":back\":{\"S\":\"Backordered\"},\n \":disc\":{\"S\":\"Discontinued\"} }\n

\n

You could then use these values in an expression, such as this:

\n

\n ProductStatus IN (:avail, :back, :disc)\n

\n

For more information on expression attribute values, see Condition Expressions in the Amazon DynamoDB Developer\n Guide.

" + "smithy.api#documentation": "

One or more values that can be substituted in an expression.

\n

Use the : (colon) character in an expression to\n dereference an attribute value. For example, suppose that you wanted to check whether\n the value of the ProductStatus attribute was one of the following:

\n

\n Available | Backordered | Discontinued\n

\n

You would first need to specify ExpressionAttributeValues as\n follows:

\n

\n { \":avail\":{\"S\":\"Available\"}, \":back\":{\"S\":\"Backordered\"},\n \":disc\":{\"S\":\"Discontinued\"} }\n

\n

You could then use these values in an expression, such as this:

\n

\n ProductStatus IN (:avail, :back, :disc)\n

\n

For more information on expression attribute values, see Condition Expressions in the Amazon DynamoDB Developer\n Guide.

" } }, "ConsistentRead": { "target": "com.amazonaws.dynamodb#ConsistentRead", "traits": { - "smithy.api#documentation": "

A Boolean value that determines the read consistency model during the scan:

\n
    \n
  • \n

    If ConsistentRead is false, then the data returned\n from Scan might not contain the results from other recently\n completed write operations (PutItem, UpdateItem, or\n DeleteItem).

    \n
  • \n
  • \n

    If ConsistentRead is true, then all of the write\n operations that completed before the Scan began are guaranteed to\n be contained in the Scan response.

    \n
  • \n
\n

The default setting for ConsistentRead is false.

\n

The ConsistentRead parameter is not supported on global secondary\n indexes. If you scan a global secondary index with ConsistentRead set to\n true, you will receive a ValidationException.

" + "smithy.api#documentation": "

A Boolean value that determines the read consistency model during the scan:

\n
    \n
  • \n

    If ConsistentRead is false, then the data returned\n from Scan might not contain the results from other recently\n completed write operations (PutItem, UpdateItem, or\n DeleteItem).

    \n
  • \n
  • \n

    If ConsistentRead is true, then all of the write\n operations that completed before the Scan began are guaranteed to\n be contained in the Scan response.

    \n
  • \n
\n

The default setting for ConsistentRead is false.

\n

The ConsistentRead parameter is not supported on global secondary\n indexes. If you scan a global secondary index with ConsistentRead set to\n true, you will receive a ValidationException.

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

Represents the input of a Scan operation.

" + "smithy.api#documentation": "

Represents the input of a Scan operation.

", + "smithy.api#input": {} } }, "com.amazonaws.dynamodb#ScanOutput": { @@ -9283,31 +9522,32 @@ "target": "com.amazonaws.dynamodb#Integer", "traits": { "smithy.api#default": 0, - "smithy.api#documentation": "

The number of items in the response.

\n

If you set ScanFilter in the request, then Count is the\n number of items returned after the filter was applied, and ScannedCount is\n the number of matching items before the filter was applied.

\n

If you did not use a filter in the request, then Count is the same as\n ScannedCount.

" + "smithy.api#documentation": "

The number of items in the response.

\n

If you set ScanFilter in the request, then Count is the\n number of items returned after the filter was applied, and ScannedCount is\n the number of matching items before the filter was applied.

\n

If you did not use a filter in the request, then Count is the same as\n ScannedCount.

" } }, "ScannedCount": { "target": "com.amazonaws.dynamodb#Integer", "traits": { "smithy.api#default": 0, - "smithy.api#documentation": "

The number of items evaluated, before any ScanFilter is applied. A high\n ScannedCount value with few, or no, Count results\n indicates an inefficient Scan operation. For more information, see Count and\n ScannedCount in the Amazon DynamoDB Developer\n Guide.

\n

If you did not use a filter in the request, then ScannedCount is the same\n as Count.

" + "smithy.api#documentation": "

The number of items evaluated, before any ScanFilter is applied. A high\n ScannedCount value with few, or no, Count results\n indicates an inefficient Scan operation. For more information, see Count and\n ScannedCount in the Amazon DynamoDB Developer\n Guide.

\n

If you did not use a filter in the request, then ScannedCount is the same\n as Count.

" } }, "LastEvaluatedKey": { "target": "com.amazonaws.dynamodb#Key", "traits": { - "smithy.api#documentation": "

The primary key of the item where the operation stopped, inclusive of the previous\n result set. Use this value to start a new operation, excluding this value in the new\n request.

\n

If LastEvaluatedKey is empty, then the \"last page\" of results has been\n processed and there is no more data to be retrieved.

\n

If LastEvaluatedKey is not empty, it does not necessarily mean that there\n is more data in the result set. The only way to know when you have reached the end of\n the result set is when LastEvaluatedKey is empty.

" + "smithy.api#documentation": "

The primary key of the item where the operation stopped, inclusive of the previous\n result set. Use this value to start a new operation, excluding this value in the new\n request.

\n

If LastEvaluatedKey is empty, then the \"last page\" of results has been\n processed and there is no more data to be retrieved.

\n

If LastEvaluatedKey is not empty, it does not necessarily mean that there\n is more data in the result set. The only way to know when you have reached the end of\n the result set is when LastEvaluatedKey is empty.

" } }, "ConsumedCapacity": { "target": "com.amazonaws.dynamodb#ConsumedCapacity", "traits": { - "smithy.api#documentation": "

The capacity units consumed by the Scan operation. The data returned\n includes the total provisioned throughput consumed, along with statistics for the table\n and any indexes involved in the operation. ConsumedCapacity is only\n returned if the ReturnConsumedCapacity parameter was specified. For more\n information, see Provisioned Throughput in the Amazon DynamoDB Developer\n Guide.

" + "smithy.api#documentation": "

The capacity units consumed by the Scan operation. The data returned\n includes the total provisioned throughput consumed, along with statistics for the table\n and any indexes involved in the operation. ConsumedCapacity is only\n returned if the ReturnConsumedCapacity parameter was specified. For more\n information, see \n Provisioned Throughput \n in the Amazon DynamoDB Developer Guide.

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

Represents the output of a Scan operation.

" + "smithy.api#documentation": "

Represents the output of a Scan operation.

", + "smithy.api#output": {} } }, "com.amazonaws.dynamodb#ScanSegment": { @@ -9390,7 +9630,7 @@ } }, "TableSizeBytes": { - "target": "com.amazonaws.dynamodb#Long", + "target": "com.amazonaws.dynamodb#LongObject", "traits": { "smithy.api#documentation": "

Size of the table in bytes. Note that this is an approximate value.

" } @@ -9425,7 +9665,7 @@ "BillingMode": { "target": "com.amazonaws.dynamodb#BillingMode", "traits": { - "smithy.api#documentation": "

Controls how you are charged for read and write throughput and how you manage\n capacity. This setting can be changed later.

\n
    \n
  • \n

    \n PROVISIONED - Sets the read/write capacity mode to\n PROVISIONED. We recommend using PROVISIONED for\n predictable workloads.

    \n
  • \n
  • \n

    \n PAY_PER_REQUEST - Sets the read/write capacity mode to\n PAY_PER_REQUEST. We recommend using\n PAY_PER_REQUEST for unpredictable workloads.

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

Controls how you are charged for read and write throughput and how you manage\n capacity. This setting can be changed later.

\n
    \n
  • \n

    \n PROVISIONED - Sets the read/write capacity mode to\n PROVISIONED. We recommend using PROVISIONED for\n predictable workloads.

    \n
  • \n
  • \n

    \n PAY_PER_REQUEST - Sets the read/write capacity mode to\n PAY_PER_REQUEST. We recommend using\n PAY_PER_REQUEST for unpredictable workloads.

    \n
  • \n
" } } }, @@ -9496,7 +9736,7 @@ "StreamViewType": { "target": "com.amazonaws.dynamodb#StreamViewType", "traits": { - "smithy.api#documentation": "

When an item in the table is modified, StreamViewType determines what\n information is written to the stream for this table. Valid values for\n StreamViewType are:

\n
    \n
  • \n

    \n KEYS_ONLY - Only the key attributes of the modified item are\n written to the stream.

    \n
  • \n
  • \n

    \n NEW_IMAGE - The entire item, as it appears after it was modified,\n is written to the stream.

    \n
  • \n
  • \n

    \n OLD_IMAGE - The entire item, as it appeared before it was modified,\n is written to the stream.

    \n
  • \n
  • \n

    \n NEW_AND_OLD_IMAGES - Both the new and the old item images of the\n item are written to the stream.

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

When an item in the table is modified, StreamViewType determines what\n information is written to the stream for this table. Valid values for\n StreamViewType are:

\n
    \n
  • \n

    \n KEYS_ONLY - Only the key attributes of the modified item are\n written to the stream.

    \n
  • \n
  • \n

    \n NEW_IMAGE - The entire item, as it appears after it was modified,\n is written to the stream.

    \n
  • \n
  • \n

    \n OLD_IMAGE - The entire item, as it appeared before it was modified,\n is written to the stream.

    \n
  • \n
  • \n

    \n NEW_AND_OLD_IMAGES - Both the new and the old item images of the\n item are written to the stream.

    \n
  • \n
" } } }, @@ -9572,7 +9812,7 @@ "TableStatus": { "target": "com.amazonaws.dynamodb#TableStatus", "traits": { - "smithy.api#documentation": "

The current state of the table:

\n
    \n
  • \n

    \n CREATING - The table is being created.

    \n
  • \n
  • \n

    \n UPDATING - The table is being updated.

    \n
  • \n
  • \n

    \n DELETING - The table is being deleted.

    \n
  • \n
  • \n

    \n ACTIVE - The table is ready for use.

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

The current state of the table:

\n
    \n
  • \n

    \n CREATING - The table is being created.

    \n
  • \n
  • \n

    \n UPDATING - The table is being updated.

    \n
  • \n
  • \n

    \n DELETING - The table is being deleted.

    \n
  • \n
  • \n

    \n ACTIVE - The table is ready for use.

    \n
  • \n
" } }, "Replicas": { @@ -9679,7 +9919,7 @@ "AttributeDefinitions": { "target": "com.amazonaws.dynamodb#AttributeDefinitions", "traits": { - "smithy.api#documentation": "

An array of AttributeDefinition objects. Each of these objects describes\n one attribute in the table and index key schema.

\n

Each AttributeDefinition object in this array is composed of:

\n
    \n
  • \n

    \n AttributeName - The name of the attribute.

    \n
  • \n
  • \n

    \n AttributeType - The data type for the attribute.

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

An array of AttributeDefinition objects. Each of these objects describes\n one attribute in the table and index key schema.

\n

Each AttributeDefinition object in this array is composed of:

\n
    \n
  • \n

    \n AttributeName - The name of the attribute.

    \n
  • \n
  • \n

    \n AttributeType - The data type for the attribute.

    \n
  • \n
" } }, "TableName": { @@ -9691,13 +9931,13 @@ "KeySchema": { "target": "com.amazonaws.dynamodb#KeySchema", "traits": { - "smithy.api#documentation": "

The primary key structure for the table. Each KeySchemaElement consists\n of:

\n
    \n
  • \n

    \n AttributeName - The name of the attribute.

    \n
  • \n
  • \n

    \n KeyType - The role of the attribute:

    \n
      \n
    • \n

      \n HASH - partition key

      \n
    • \n
    • \n

      \n RANGE - sort key

      \n
    • \n
    \n \n

    The partition key of an item is also known as its hash\n attribute. The term \"hash attribute\" derives from DynamoDB's\n usage of an internal hash function to evenly distribute data items across\n partitions, based on their partition key values.

    \n

    The sort key of an item is also known as its range\n attribute. The term \"range attribute\" derives from the way\n DynamoDB stores items with the same partition key physically close together,\n in sorted order by the sort key value.

    \n
    \n\n
  • \n
\n

For more information about primary keys, see Primary Key in the Amazon DynamoDB Developer\n Guide.

" + "smithy.api#documentation": "

The primary key structure for the table. Each KeySchemaElement consists\n of:

\n
    \n
  • \n

    \n AttributeName - The name of the attribute.

    \n
  • \n
  • \n

    \n KeyType - The role of the attribute:

    \n
      \n
    • \n

      \n HASH - partition key

      \n
    • \n
    • \n

      \n RANGE - sort key

      \n
    • \n
    \n \n

    The partition key of an item is also known as its hash\n attribute. The term \"hash attribute\" derives from DynamoDB's\n usage of an internal hash function to evenly distribute data items across\n partitions, based on their partition key values.

    \n

    The sort key of an item is also known as its range\n attribute. The term \"range attribute\" derives from the way\n DynamoDB stores items with the same partition key physically close together,\n in sorted order by the sort key value.

    \n
    \n
  • \n
\n

For more information about primary keys, see Primary Key in the Amazon DynamoDB Developer\n Guide.

" } }, "TableStatus": { "target": "com.amazonaws.dynamodb#TableStatus", "traits": { - "smithy.api#documentation": "

The current state of the table:

\n
    \n
  • \n

    \n CREATING - The table is being created.

    \n
  • \n
  • \n

    \n UPDATING - The table/index configuration is being updated. The\n table/index remains available for data operations when\n UPDATING.

    \n
  • \n
  • \n

    \n DELETING - The table is being deleted.

    \n
  • \n
  • \n

    \n ACTIVE - The table is ready for use.

    \n
  • \n
  • \n

    \n INACCESSIBLE_ENCRYPTION_CREDENTIALS - The KMS key\n used to encrypt the table in inaccessible. Table operations may fail due to\n failure to use the KMS key. DynamoDB will initiate the\n table archival process when a table's KMS key remains\n inaccessible for more than seven days.

    \n
  • \n
  • \n

    \n ARCHIVING - The table is being archived. Operations are not allowed\n until archival is complete.

    \n
  • \n
  • \n

    \n ARCHIVED - The table has been archived. See the ArchivalReason for\n more information.

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

The current state of the table:

\n
    \n
  • \n

    \n CREATING - The table is being created.

    \n
  • \n
  • \n

    \n UPDATING - The table/index configuration is being updated. The\n table/index remains available for data operations when\n UPDATING.

    \n
  • \n
  • \n

    \n DELETING - The table is being deleted.

    \n
  • \n
  • \n

    \n ACTIVE - The table is ready for use.

    \n
  • \n
  • \n

    \n INACCESSIBLE_ENCRYPTION_CREDENTIALS - The KMS key\n used to encrypt the table in inaccessible. Table operations may fail due to\n failure to use the KMS key. DynamoDB will initiate the\n table archival process when a table's KMS key remains\n inaccessible for more than seven days.

    \n
  • \n
  • \n

    \n ARCHIVING - The table is being archived. Operations are not allowed\n until archival is complete.

    \n
  • \n
  • \n

    \n ARCHIVED - The table has been archived. See the ArchivalReason for\n more information.

    \n
  • \n
" } }, "CreationDateTime": { @@ -9713,13 +9953,13 @@ } }, "TableSizeBytes": { - "target": "com.amazonaws.dynamodb#Long", + "target": "com.amazonaws.dynamodb#LongObject", "traits": { "smithy.api#documentation": "

The total size of the specified table, in bytes. DynamoDB updates this value\n approximately every six hours. Recent changes might not be reflected in this\n value.

" } }, "ItemCount": { - "target": "com.amazonaws.dynamodb#Long", + "target": "com.amazonaws.dynamodb#LongObject", "traits": { "smithy.api#documentation": "

The number of items in the specified table. DynamoDB updates this value approximately\n every six hours. Recent changes might not be reflected in this value.

" } @@ -9745,13 +9985,13 @@ "LocalSecondaryIndexes": { "target": "com.amazonaws.dynamodb#LocalSecondaryIndexDescriptionList", "traits": { - "smithy.api#documentation": "

Represents one or more local secondary indexes on the table. Each index is scoped to a\n given partition key value. Tables with one or more local secondary indexes are subject\n to an item collection size limit, where the amount of data within a given item\n collection cannot exceed 10 GB. Each element is composed of:

\n
    \n
  • \n

    \n IndexName - The name of the local secondary index.

    \n
  • \n
  • \n

    \n KeySchema - Specifies the complete index key schema. The attribute\n names in the key schema must be between 1 and 255 characters (inclusive). The\n key schema must begin with the same partition key as the table.

    \n
  • \n
  • \n

    \n Projection - Specifies attributes that are copied (projected) from\n the table into the index. These are in addition to the primary key attributes\n and index key attributes, which are automatically projected. Each attribute\n specification is composed of:

    \n
      \n
    • \n

      \n ProjectionType - One of the following:

      \n
        \n
      • \n

        \n KEYS_ONLY - Only the index and primary keys are\n projected into the index.

        \n
      • \n
      • \n

        \n INCLUDE - Only the specified table attributes are\n projected into the index. The list of projected attributes is in\n NonKeyAttributes.

        \n
      • \n
      • \n

        \n ALL - All of the table attributes are projected\n into the index.

        \n
      • \n
      \n
    • \n
    • \n

      \n NonKeyAttributes - A list of one or more non-key attribute\n names that are projected into the secondary index. The total count of\n attributes provided in NonKeyAttributes, summed across all\n of the secondary indexes, must not exceed 100. If you project the same\n attribute into two different indexes, this counts as two distinct\n attributes when determining the total.

      \n
    • \n
    \n
  • \n
  • \n

    \n IndexSizeBytes - Represents the total size of the index, in bytes.\n DynamoDB updates this value approximately every six hours. Recent changes might\n not be reflected in this value.

    \n
  • \n
  • \n

    \n ItemCount - Represents the number of items in the index. DynamoDB\n updates this value approximately every six hours. Recent changes might not be\n reflected in this value.

    \n
  • \n
\n

If the table is in the DELETING state, no information about indexes will\n be returned.

" + "smithy.api#documentation": "

Represents one or more local secondary indexes on the table. Each index is scoped to a\n given partition key value. Tables with one or more local secondary indexes are subject\n to an item collection size limit, where the amount of data within a given item\n collection cannot exceed 10 GB. Each element is composed of:

\n
    \n
  • \n

    \n IndexName - The name of the local secondary index.

    \n
  • \n
  • \n

    \n KeySchema - Specifies the complete index key schema. The attribute\n names in the key schema must be between 1 and 255 characters (inclusive). The\n key schema must begin with the same partition key as the table.

    \n
  • \n
  • \n

    \n Projection - Specifies attributes that are copied (projected) from\n the table into the index. These are in addition to the primary key attributes\n and index key attributes, which are automatically projected. Each attribute\n specification is composed of:

    \n
      \n
    • \n

      \n ProjectionType - One of the following:

      \n
        \n
      • \n

        \n KEYS_ONLY - Only the index and primary keys are\n projected into the index.

        \n
      • \n
      • \n

        \n INCLUDE - Only the specified table attributes are\n projected into the index. The list of projected attributes is in\n NonKeyAttributes.

        \n
      • \n
      • \n

        \n ALL - All of the table attributes are projected\n into the index.

        \n
      • \n
      \n
    • \n
    • \n

      \n NonKeyAttributes - A list of one or more non-key attribute\n names that are projected into the secondary index. The total count of\n attributes provided in NonKeyAttributes, summed across all\n of the secondary indexes, must not exceed 100. If you project the same\n attribute into two different indexes, this counts as two distinct\n attributes when determining the total.

      \n
    • \n
    \n
  • \n
  • \n

    \n IndexSizeBytes - Represents the total size of the index, in bytes.\n DynamoDB updates this value approximately every six hours. Recent changes might\n not be reflected in this value.

    \n
  • \n
  • \n

    \n ItemCount - Represents the number of items in the index. DynamoDB\n updates this value approximately every six hours. Recent changes might not be\n reflected in this value.

    \n
  • \n
\n

If the table is in the DELETING state, no information about indexes will\n be returned.

" } }, "GlobalSecondaryIndexes": { "target": "com.amazonaws.dynamodb#GlobalSecondaryIndexDescriptionList", "traits": { - "smithy.api#documentation": "

The global secondary indexes, if any, on the table. Each index is scoped to a given\n partition key value. Each element is composed of:

\n
    \n
  • \n

    \n Backfilling - If true, then the index is currently in the\n backfilling phase. Backfilling occurs only when a new global secondary index is\n added to the table. It is the process by which DynamoDB populates the new index\n with data from the table. (This attribute does not appear for indexes that were\n created during a CreateTable operation.)

    \n

    You can delete an index that is being created during the\n Backfilling phase when IndexStatus is set to\n CREATING and Backfilling is true. You can't delete the index that\n is being created when IndexStatus is set to CREATING and\n Backfilling is false. (This attribute does not appear for\n indexes that were created during a CreateTable operation.)

    \n
  • \n
  • \n

    \n IndexName - The name of the global secondary index.

    \n
  • \n
  • \n

    \n IndexSizeBytes - The total size of the global secondary index, in\n bytes. DynamoDB updates this value approximately every six hours. Recent changes\n might not be reflected in this value.

    \n
  • \n
  • \n

    \n IndexStatus - The current status of the global secondary\n index:

    \n
      \n
    • \n

      \n CREATING - The index is being created.

      \n
    • \n
    • \n

      \n UPDATING - The index is being updated.

      \n
    • \n
    • \n

      \n DELETING - The index is being deleted.

      \n
    • \n
    • \n

      \n ACTIVE - The index is ready for use.

      \n
    • \n
    \n
  • \n
  • \n

    \n ItemCount - The number of items in the global secondary index.\n DynamoDB updates this value approximately every six hours. Recent changes might\n not be reflected in this value.

    \n
  • \n
  • \n

    \n KeySchema - Specifies the complete index key schema. The attribute\n names in the key schema must be between 1 and 255 characters (inclusive). The\n key schema must begin with the same partition key as the table.

    \n
  • \n
  • \n

    \n Projection - Specifies attributes that are copied (projected) from\n the table into the index. These are in addition to the primary key attributes\n and index key attributes, which are automatically projected. Each attribute\n specification is composed of:

    \n
      \n
    • \n

      \n ProjectionType - One of the following:

      \n
        \n
      • \n

        \n KEYS_ONLY - Only the index and primary keys are\n projected into the index.

        \n
      • \n
      • \n

        \n INCLUDE - In addition to the attributes described\n in KEYS_ONLY, the secondary index will include\n other non-key attributes that you specify.

        \n
      • \n
      • \n

        \n ALL - All of the table attributes are projected\n into the index.

        \n
      • \n
      \n
    • \n
    • \n

      \n NonKeyAttributes - A list of one or more non-key attribute\n names that are projected into the secondary index. The total count of\n attributes provided in NonKeyAttributes, summed across all\n of the secondary indexes, must not exceed 100. If you project the same\n attribute into two different indexes, this counts as two distinct\n attributes when determining the total.

      \n
    • \n
    \n
  • \n
  • \n

    \n ProvisionedThroughput - The provisioned throughput settings for the\n global secondary index, consisting of read and write capacity units, along with\n data about increases and decreases.

    \n
  • \n
\n

If the table is in the DELETING state, no information about indexes will\n be returned.

" + "smithy.api#documentation": "

The global secondary indexes, if any, on the table. Each index is scoped to a given\n partition key value. Each element is composed of:

\n
    \n
  • \n

    \n Backfilling - If true, then the index is currently in the\n backfilling phase. Backfilling occurs only when a new global secondary index is\n added to the table. It is the process by which DynamoDB populates the new index\n with data from the table. (This attribute does not appear for indexes that were\n created during a CreateTable operation.)

    \n

    You can delete an index that is being created during the\n Backfilling phase when IndexStatus is set to\n CREATING and Backfilling is true. You can't delete the index that\n is being created when IndexStatus is set to CREATING and\n Backfilling is false. (This attribute does not appear for\n indexes that were created during a CreateTable operation.)

    \n
  • \n
  • \n

    \n IndexName - The name of the global secondary index.

    \n
  • \n
  • \n

    \n IndexSizeBytes - The total size of the global secondary index, in\n bytes. DynamoDB updates this value approximately every six hours. Recent changes\n might not be reflected in this value.

    \n
  • \n
  • \n

    \n IndexStatus - The current status of the global secondary\n index:

    \n
      \n
    • \n

      \n CREATING - The index is being created.

      \n
    • \n
    • \n

      \n UPDATING - The index is being updated.

      \n
    • \n
    • \n

      \n DELETING - The index is being deleted.

      \n
    • \n
    • \n

      \n ACTIVE - The index is ready for use.

      \n
    • \n
    \n
  • \n
  • \n

    \n ItemCount - The number of items in the global secondary index.\n DynamoDB updates this value approximately every six hours. Recent changes might\n not be reflected in this value.

    \n
  • \n
  • \n

    \n KeySchema - Specifies the complete index key schema. The attribute\n names in the key schema must be between 1 and 255 characters (inclusive). The\n key schema must begin with the same partition key as the table.

    \n
  • \n
  • \n

    \n Projection - Specifies attributes that are copied (projected) from\n the table into the index. These are in addition to the primary key attributes\n and index key attributes, which are automatically projected. Each attribute\n specification is composed of:

    \n
      \n
    • \n

      \n ProjectionType - One of the following:

      \n
        \n
      • \n

        \n KEYS_ONLY - Only the index and primary keys are\n projected into the index.

        \n
      • \n
      • \n

        \n INCLUDE - In addition to the attributes described\n in KEYS_ONLY, the secondary index will include\n other non-key attributes that you specify.

        \n
      • \n
      • \n

        \n ALL - All of the table attributes are projected\n into the index.

        \n
      • \n
      \n
    • \n
    • \n

      \n NonKeyAttributes - A list of one or more non-key attribute\n names that are projected into the secondary index. The total count of\n attributes provided in NonKeyAttributes, summed across all\n of the secondary indexes, must not exceed 100. If you project the same\n attribute into two different indexes, this counts as two distinct\n attributes when determining the total.

      \n
    • \n
    \n
  • \n
  • \n

    \n ProvisionedThroughput - The provisioned throughput settings for the\n global secondary index, consisting of read and write capacity units, along with\n data about increases and decreases.

    \n
  • \n
\n

If the table is in the DELETING state, no information about indexes will\n be returned.

" } }, "StreamSpecification": { @@ -9763,7 +10003,7 @@ "LatestStreamLabel": { "target": "com.amazonaws.dynamodb#String", "traits": { - "smithy.api#documentation": "

A timestamp, in ISO 8601 format, for this stream.

\n\n

Note that LatestStreamLabel is not a unique identifier for the stream,\n because it is possible that a stream from another table might have the same timestamp.\n However, the combination of the following three elements is guaranteed to be\n unique:

\n
    \n
  • \n

    Amazon Web Services customer ID

    \n
  • \n
  • \n

    Table name

    \n
  • \n
  • \n

    \n StreamLabel\n

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

A timestamp, in ISO 8601 format, for this stream.

\n

Note that LatestStreamLabel is not a unique identifier for the stream,\n because it is possible that a stream from another table might have the same timestamp.\n However, the combination of the following three elements is guaranteed to be\n unique:

\n
    \n
  • \n

    Amazon Web Services customer ID

    \n
  • \n
  • \n

    Table name

    \n
  • \n
  • \n

    \n StreamLabel\n

    \n
  • \n
" } }, "LatestStreamArn": { @@ -9807,6 +10047,12 @@ "traits": { "smithy.api#documentation": "

Contains details of the table class.

" } + }, + "DeletionProtectionEnabled": { + "target": "com.amazonaws.dynamodb#DeletionProtectionEnabled", + "traits": { + "smithy.api#documentation": "

Indicates whether deletion protection is enabled (true) or disabled (false) on the table.

" + } } }, "traits": { @@ -9925,7 +10171,7 @@ } }, "traits": { - "smithy.api#documentation": "

Describes a tag. A tag is a key-value pair. You can add up to 50 tags to a single\n DynamoDB table.

\n

Amazon Web Services-assigned tag names and values are automatically assigned the\n aws: prefix, which the user cannot assign. Amazon Web Services-assigned\n tag names do not count towards the tag limit of 50. User-assigned tag names have the\n prefix user: in the Cost Allocation Report. You cannot backdate the\n application of a tag.

\n

For an overview on tagging DynamoDB resources, see Tagging\n for DynamoDB in the Amazon DynamoDB Developer\n Guide.

" + "smithy.api#documentation": "

Describes a tag. A tag is a key-value pair. You can add up to 50 tags to a single\n DynamoDB table.

\n

Amazon Web Services-assigned tag names and values are automatically assigned the\n aws: prefix, which the user cannot assign. Amazon Web Services-assigned\n tag names do not count towards the tag limit of 50. User-assigned tag names have the\n prefix user: in the Cost Allocation Report. You cannot backdate the\n application of a tag.

\n

For an overview on tagging DynamoDB resources, see Tagging\n for DynamoDB in the Amazon DynamoDB Developer\n Guide.

" } }, "com.amazonaws.dynamodb#TagKeyList": { @@ -9978,7 +10224,7 @@ "aws.api#clientDiscoveredEndpoint": { "required": false }, - "smithy.api#documentation": "

Associate a set of tags with an Amazon DynamoDB 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. You can call TagResource up to five times per second, per\n account.

\n

For an overview on tagging DynamoDB resources, see Tagging for DynamoDB\n in the Amazon DynamoDB Developer Guide.

" + "smithy.api#documentation": "

Associate a set of tags with an Amazon DynamoDB 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. You can call TagResource up to five times per second, per\n account.

\n

For an overview on tagging DynamoDB resources, see Tagging for DynamoDB\n in the Amazon DynamoDB Developer Guide.

" } }, "com.amazonaws.dynamodb#TagResourceInput": { @@ -9998,6 +10244,9 @@ "smithy.api#required": {} } } + }, + "traits": { + "smithy.api#input": {} } }, "com.amazonaws.dynamodb#TagValueString": { @@ -10157,7 +10406,7 @@ "aws.api#clientDiscoveredEndpoint": { "required": false }, - "smithy.api#documentation": "

\n TransactGetItems is a synchronous operation that atomically retrieves\n multiple items from one or more tables (but not from indexes) in a single account and\n Region. A TransactGetItems call can contain up to 100\n TransactGetItem objects, each of which contains a Get\n structure that specifies an item to retrieve from a table in the account and Region. A\n call to TransactGetItems cannot retrieve items from tables in more than one\n Amazon Web Services account or Region. The aggregate size of the items in the\n transaction cannot exceed 4 MB.

\n

DynamoDB rejects the entire TransactGetItems request if any of\n the following is true:

\n
    \n
  • \n

    A conflicting operation is in the process of updating an item to be\n read.

    \n
  • \n
  • \n

    There is insufficient provisioned capacity for the transaction to be\n completed.

    \n
  • \n
  • \n

    There is a user error, such as an invalid data format.

    \n
  • \n
  • \n

    The aggregate size of the items in the transaction cannot exceed 4 MB.

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

\n TransactGetItems is a synchronous operation that atomically retrieves\n multiple items from one or more tables (but not from indexes) in a single account and\n Region. A TransactGetItems call can contain up to 100\n TransactGetItem objects, each of which contains a Get\n structure that specifies an item to retrieve from a table in the account and Region. A\n call to TransactGetItems cannot retrieve items from tables in more than one\n Amazon Web Services account or Region. The aggregate size of the items in the\n transaction cannot exceed 4 MB.

\n

DynamoDB rejects the entire TransactGetItems request if any of\n the following is true:

\n
    \n
  • \n

    A conflicting operation is in the process of updating an item to be\n read.

    \n
  • \n
  • \n

    There is insufficient provisioned capacity for the transaction to be\n completed.

    \n
  • \n
  • \n

    There is a user error, such as an invalid data format.

    \n
  • \n
  • \n

    The aggregate size of the items in the transaction exceeded 4 MB.

    \n
  • \n
" } }, "com.amazonaws.dynamodb#TransactGetItemsInput": { @@ -10176,6 +10425,9 @@ "smithy.api#documentation": "

A value of TOTAL causes consumed capacity information to be returned, and\n a value of NONE prevents that information from being returned. No other\n value is valid.

" } } + }, + "traits": { + "smithy.api#input": {} } }, "com.amazonaws.dynamodb#TransactGetItemsOutput": { @@ -10190,9 +10442,12 @@ "Responses": { "target": "com.amazonaws.dynamodb#ItemResponseList", "traits": { - "smithy.api#documentation": "

An ordered array of up to 100 ItemResponse objects, each of which\n corresponds to the TransactGetItem object in the same position in the\n TransactItems array. Each ItemResponse object\n contains a Map of the name-value pairs that are the projected attributes of the\n requested item.

\n

If a requested item could not be retrieved, the corresponding\n ItemResponse object is Null, or if the requested item has no projected\n attributes, the corresponding ItemResponse object is an empty Map.

" + "smithy.api#documentation": "

An ordered array of up to 100 ItemResponse objects, each of which\n corresponds to the TransactGetItem object in the same position in the\n TransactItems array. Each ItemResponse object\n contains a Map of the name-value pairs that are the projected attributes of the\n requested item.

\n

If a requested item could not be retrieved, the corresponding\n ItemResponse object is Null, or if the requested item has no projected\n attributes, the corresponding ItemResponse object is an empty Map.

" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.dynamodb#TransactWriteItem": { @@ -10277,7 +10532,7 @@ "aws.api#clientDiscoveredEndpoint": { "required": false }, - "smithy.api#documentation": "

\n TransactWriteItems is a synchronous write operation that groups up to 100\n action requests. These actions can target items in different tables, but not in\n different Amazon Web Services accounts or Regions, and no two actions can target the same\n item. For example, you cannot both ConditionCheck and Update\n the same item. The aggregate size of the items in the transaction cannot exceed 4\n MB.

\n\n

The actions are completed atomically so that either all of them succeed, or all of\n them fail. They are defined by the following objects:

\n\n
    \n
  • \n

    \n Put  —   Initiates a PutItem\n operation to write a new item. This structure specifies the primary key of the\n item to be written, the name of the table to write it in, an optional condition\n expression that must be satisfied for the write to succeed, a list of the item's\n attributes, and a field indicating whether to retrieve the item's attributes if\n the condition is not met.

    \n
  • \n
  • \n

    \n Update  —   Initiates an UpdateItem\n operation to update an existing item. This structure specifies the primary key\n of the item to be updated, the name of the table where it resides, an optional\n condition expression that must be satisfied for the update to succeed, an\n expression that defines one or more attributes to be updated, and a field\n indicating whether to retrieve the item's attributes if the condition is not\n met.

    \n
  • \n
  • \n

    \n Delete  —   Initiates a DeleteItem\n operation to delete an existing item. This structure specifies the primary key\n of the item to be deleted, the name of the table where it resides, an optional\n condition expression that must be satisfied for the deletion to succeed, and a\n field indicating whether to retrieve the item's attributes if the condition is\n not met.

    \n
  • \n
  • \n

    \n ConditionCheck  —   Applies a condition to an item\n that is not being modified by the transaction. This structure specifies the\n primary key of the item to be checked, the name of the table where it resides, a\n condition expression that must be satisfied for the transaction to succeed, and\n a field indicating whether to retrieve the item's attributes if the condition is\n not met.

    \n
  • \n
\n\n

DynamoDB rejects the entire TransactWriteItems request if any of the\n following is true:

\n
    \n
  • \n

    A condition in one of the condition expressions is not met.

    \n
  • \n
  • \n

    An ongoing operation is in the process of updating the same item.

    \n
  • \n
  • \n

    There is insufficient provisioned capacity for the transaction to be\n completed.

    \n
  • \n
  • \n

    An item size becomes too large (bigger than 400 KB), a local secondary index\n (LSI) becomes too large, or a similar validation error occurs because of changes\n made by the transaction.

    \n
  • \n
  • \n

    The aggregate size of the items in the transaction exceeds 4 MB.

    \n
  • \n
  • \n

    There is a user error, such as an invalid data format.

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

\n TransactWriteItems is a synchronous write operation that groups up to 100\n action requests. These actions can target items in different tables, but not in\n different Amazon Web Services accounts or Regions, and no two actions can target the same\n item. For example, you cannot both ConditionCheck and Update\n the same item. The aggregate size of the items in the transaction cannot exceed 4\n MB.

\n

The actions are completed atomically so that either all of them succeed, or all of\n them fail. They are defined by the following objects:

\n
    \n
  • \n

    \n Put  —   Initiates a PutItem\n operation to write a new item. This structure specifies the primary key of the\n item to be written, the name of the table to write it in, an optional condition\n expression that must be satisfied for the write to succeed, a list of the item's\n attributes, and a field indicating whether to retrieve the item's attributes if\n the condition is not met.

    \n
  • \n
  • \n

    \n Update  —   Initiates an UpdateItem\n operation to update an existing item. This structure specifies the primary key\n of the item to be updated, the name of the table where it resides, an optional\n condition expression that must be satisfied for the update to succeed, an\n expression that defines one or more attributes to be updated, and a field\n indicating whether to retrieve the item's attributes if the condition is not\n met.

    \n
  • \n
  • \n

    \n Delete  —   Initiates a DeleteItem\n operation to delete an existing item. This structure specifies the primary key\n of the item to be deleted, the name of the table where it resides, an optional\n condition expression that must be satisfied for the deletion to succeed, and a\n field indicating whether to retrieve the item's attributes if the condition is\n not met.

    \n
  • \n
  • \n

    \n ConditionCheck  —   Applies a condition to an item\n that is not being modified by the transaction. This structure specifies the\n primary key of the item to be checked, the name of the table where it resides, a\n condition expression that must be satisfied for the transaction to succeed, and\n a field indicating whether to retrieve the item's attributes if the condition is\n not met.

    \n
  • \n
\n

DynamoDB rejects the entire TransactWriteItems request if any of the\n following is true:

\n
    \n
  • \n

    A condition in one of the condition expressions is not met.

    \n
  • \n
  • \n

    An ongoing operation is in the process of updating the same item.

    \n
  • \n
  • \n

    There is insufficient provisioned capacity for the transaction to be\n completed.

    \n
  • \n
  • \n

    An item size becomes too large (bigger than 400 KB), a local secondary index\n (LSI) becomes too large, or a similar validation error occurs because of changes\n made by the transaction.

    \n
  • \n
  • \n

    The aggregate size of the items in the transaction exceeds 4 MB.

    \n
  • \n
  • \n

    There is a user error, such as an invalid data format.

    \n
  • \n
" } }, "com.amazonaws.dynamodb#TransactWriteItemsInput": { @@ -10302,10 +10557,13 @@ "ClientRequestToken": { "target": "com.amazonaws.dynamodb#ClientRequestToken", "traits": { - "smithy.api#documentation": "

Providing a ClientRequestToken makes the call to\n TransactWriteItems idempotent, meaning that multiple identical calls\n have the same effect as one single call.

\n

Although multiple identical calls using the same client request token produce the same\n result on the server (no side effects), the responses to the calls might not be the\n same. If the ReturnConsumedCapacity> parameter is set, then the initial\n TransactWriteItems call returns the amount of write capacity units\n consumed in making the changes. Subsequent TransactWriteItems calls with\n the same client token return the number of read capacity units consumed in reading the\n item.

\n

A client request token is valid for 10 minutes after the first request that uses it is\n completed. After 10 minutes, any request with the same client token is treated as a new\n request. Do not resubmit the same request with the same client token for more than 10\n minutes, or the result might not be idempotent.

\n

If you submit a request with the same client token but a change in other parameters\n within the 10-minute idempotency window, DynamoDB returns an\n IdempotentParameterMismatch exception.

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

Providing a ClientRequestToken makes the call to\n TransactWriteItems idempotent, meaning that multiple identical calls\n have the same effect as one single call.

\n

Although multiple identical calls using the same client request token produce the same\n result on the server (no side effects), the responses to the calls might not be the\n same. If the ReturnConsumedCapacity parameter is set, then the initial\n TransactWriteItems call returns the amount of write capacity units\n consumed in making the changes. Subsequent TransactWriteItems calls with\n the same client token return the number of read capacity units consumed in reading the\n item.

\n

A client request token is valid for 10 minutes after the first request that uses it is\n completed. After 10 minutes, any request with the same client token is treated as a new\n request. Do not resubmit the same request with the same client token for more than 10\n minutes, or the result might not be idempotent.

\n

If you submit a request with the same client token but a change in other parameters\n within the 10-minute idempotency window, DynamoDB returns an\n IdempotentParameterMismatch exception.

", "smithy.api#idempotencyToken": {} } } + }, + "traits": { + "smithy.api#input": {} } }, "com.amazonaws.dynamodb#TransactWriteItemsOutput": { @@ -10323,6 +10581,9 @@ "smithy.api#documentation": "

A list of tables that were processed by TransactWriteItems and, for each\n table, information about any item collections that were affected by individual\n UpdateItem, PutItem, or DeleteItem\n operations.

" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.dynamodb#TransactionCanceledException": { @@ -10339,7 +10600,7 @@ } }, "traits": { - "smithy.api#documentation": "

The entire transaction request was canceled.

\n

DynamoDB cancels a TransactWriteItems request under the following\n circumstances:

\n
    \n
  • \n

    A condition in one of the condition expressions is not met.

    \n
  • \n
  • \n

    A table in the TransactWriteItems request is in a different\n account or region.

    \n
  • \n
  • \n

    More than one action in the TransactWriteItems operation\n targets the same item.

    \n
  • \n
  • \n

    There is insufficient provisioned capacity for the transaction to be\n completed.

    \n
  • \n
  • \n

    An item size becomes too large (larger than 400 KB), or a local secondary\n index (LSI) becomes too large, or a similar validation error occurs because of\n changes made by the transaction.

    \n
  • \n
  • \n

    There is a user error, such as an invalid data format.

    \n
  • \n
\n\n

DynamoDB cancels a TransactGetItems request under the\n following circumstances:

\n
    \n
  • \n

    There is an ongoing TransactGetItems operation that conflicts\n with a concurrent PutItem, UpdateItem,\n DeleteItem or TransactWriteItems request. In this\n case the TransactGetItems operation fails with a\n TransactionCanceledException.

    \n
  • \n
  • \n

    A table in the TransactGetItems request is in a different\n account or region.

    \n
  • \n
  • \n

    There is insufficient provisioned capacity for the transaction to be\n completed.

    \n
  • \n
  • \n

    There is a user error, such as an invalid data format.

    \n
  • \n
\n\n \n

If using Java, DynamoDB lists the cancellation reasons on the\n CancellationReasons property. This property is not set for other\n languages. Transaction cancellation reasons are ordered in the order of requested\n items, if an item has no error it will have None code and\n Null message.

\n
\n

Cancellation reason codes and possible error messages:

\n
    \n
  • \n

    No Errors:

    \n
      \n
    • \n

      Code: None\n

      \n
    • \n
    • \n

      Message: null\n

      \n
    • \n
    \n
  • \n
  • \n

    Conditional Check Failed:

    \n
      \n
    • \n

      Code: ConditionalCheckFailed\n

      \n
    • \n
    • \n

      Message: The conditional request failed.

      \n
    • \n
    \n
  • \n
  • \n

    Item Collection Size Limit Exceeded:

    \n
      \n
    • \n

      Code: ItemCollectionSizeLimitExceeded\n

      \n
    • \n
    • \n

      Message: Collection size exceeded.

      \n
    • \n
    \n
  • \n
  • \n

    Transaction Conflict:

    \n
      \n
    • \n

      Code: TransactionConflict\n

      \n
    • \n
    • \n

      Message: Transaction is ongoing for the item.

      \n
    • \n
    \n
  • \n
  • \n

    Provisioned Throughput Exceeded:

    \n
      \n
    • \n

      Code: ProvisionedThroughputExceeded\n

      \n
    • \n
    • \n

      Messages:

      \n
        \n
      • \n

        The level of configured provisioned throughput for the\n table was exceeded. Consider increasing your provisioning level\n with the UpdateTable API.

        \n \n

        This Message is received when provisioned throughput is\n exceeded is on a provisioned DynamoDB\n table.

        \n
        \n
      • \n
      • \n

        The level of configured provisioned throughput for one or\n more global secondary indexes of the table was exceeded.\n Consider increasing your provisioning level for the\n under-provisioned global secondary indexes with the UpdateTable\n API.

        \n \n

        This message is returned when provisioned throughput is\n exceeded is on a provisioned GSI.

        \n
        \n
      • \n
      \n\n
    • \n
    \n
  • \n
  • \n

    Throttling Error:

    \n
      \n
    • \n

      Code: ThrottlingError\n

      \n
    • \n
    • \n

      Messages:

      \n
        \n
      • \n

        Throughput exceeds the current capacity of your table or\n index. DynamoDB is automatically scaling your table or\n index so please try again shortly. If exceptions persist, check\n if you have a hot key:\n https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/bp-partition-key-design.html.

        \n \n

        This message is returned when writes get throttled on an\n On-Demand table as DynamoDB is automatically\n scaling the table.

        \n
        \n
      • \n
      • \n

        Throughput exceeds the current capacity for one or more\n global secondary indexes. DynamoDB is automatically\n scaling your index so please try again shortly.

        \n \n

        This message is returned when when writes get throttled on\n an On-Demand GSI as DynamoDB is automatically\n scaling the GSI.

        \n
        \n
      • \n
      \n\n
    • \n
    \n
  • \n
  • \n

    Validation Error:

    \n
      \n
    • \n

      Code: ValidationError\n

      \n
    • \n
    • \n

      Messages:

      \n
        \n
      • \n

        One or more parameter values were invalid.

        \n
      • \n
      • \n

        The update expression attempted to update the secondary\n index key beyond allowed size limits.

        \n
      • \n
      • \n

        The update expression attempted to update the secondary\n index key to unsupported type.

        \n
      • \n
      • \n

        An operand in the update expression has an incorrect data\n type.

        \n
      • \n
      • \n

        Item size to update has exceeded the maximum allowed\n size.

        \n
      • \n
      • \n

        Number overflow. Attempting to store a number with\n magnitude larger than supported range.

        \n
      • \n
      • \n

        Type mismatch for attribute to update.

        \n
      • \n
      • \n

        Nesting Levels have exceeded supported limits.

        \n
      • \n
      • \n

        The document path provided in the update expression is\n invalid for update.

        \n
      • \n
      • \n

        The provided expression refers to an attribute that does\n not exist in the item.

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

The entire transaction request was canceled.

\n

DynamoDB cancels a TransactWriteItems request under the following\n circumstances:

\n
    \n
  • \n

    A condition in one of the condition expressions is not met.

    \n
  • \n
  • \n

    A table in the TransactWriteItems request is in a different\n account or region.

    \n
  • \n
  • \n

    More than one action in the TransactWriteItems operation\n targets the same item.

    \n
  • \n
  • \n

    There is insufficient provisioned capacity for the transaction to be\n completed.

    \n
  • \n
  • \n

    An item size becomes too large (larger than 400 KB), or a local secondary\n index (LSI) becomes too large, or a similar validation error occurs because of\n changes made by the transaction.

    \n
  • \n
  • \n

    There is a user error, such as an invalid data format.

    \n
  • \n
\n

DynamoDB cancels a TransactGetItems request under the\n following circumstances:

\n
    \n
  • \n

    There is an ongoing TransactGetItems operation that conflicts\n with a concurrent PutItem, UpdateItem,\n DeleteItem or TransactWriteItems request. In this\n case the TransactGetItems operation fails with a\n TransactionCanceledException.

    \n
  • \n
  • \n

    A table in the TransactGetItems request is in a different\n account or region.

    \n
  • \n
  • \n

    There is insufficient provisioned capacity for the transaction to be\n completed.

    \n
  • \n
  • \n

    There is a user error, such as an invalid data format.

    \n
  • \n
\n \n

If using Java, DynamoDB lists the cancellation reasons on the\n CancellationReasons property. This property is not set for other\n languages. Transaction cancellation reasons are ordered in the order of requested\n items, if an item has no error it will have None code and\n Null message.

\n
\n

Cancellation reason codes and possible error messages:

\n
    \n
  • \n

    No Errors:

    \n
      \n
    • \n

      Code: None\n

      \n
    • \n
    • \n

      Message: null\n

      \n
    • \n
    \n
  • \n
  • \n

    Conditional Check Failed:

    \n
      \n
    • \n

      Code: ConditionalCheckFailed\n

      \n
    • \n
    • \n

      Message: The conditional request failed.

      \n
    • \n
    \n
  • \n
  • \n

    Item Collection Size Limit Exceeded:

    \n
      \n
    • \n

      Code: ItemCollectionSizeLimitExceeded\n

      \n
    • \n
    • \n

      Message: Collection size exceeded.

      \n
    • \n
    \n
  • \n
  • \n

    Transaction Conflict:

    \n
      \n
    • \n

      Code: TransactionConflict\n

      \n
    • \n
    • \n

      Message: Transaction is ongoing for the item.

      \n
    • \n
    \n
  • \n
  • \n

    Provisioned Throughput Exceeded:

    \n
      \n
    • \n

      Code: ProvisionedThroughputExceeded\n

      \n
    • \n
    • \n

      Messages:

      \n
        \n
      • \n

        The level of configured provisioned throughput for the\n table was exceeded. Consider increasing your provisioning level\n with the UpdateTable API.

        \n \n

        This Message is received when provisioned throughput is\n exceeded is on a provisioned DynamoDB\n table.

        \n
        \n
      • \n
      • \n

        The level of configured provisioned throughput for one or\n more global secondary indexes of the table was exceeded.\n Consider increasing your provisioning level for the\n under-provisioned global secondary indexes with the UpdateTable\n API.

        \n \n

        This message is returned when provisioned throughput is\n exceeded is on a provisioned GSI.

        \n
        \n
      • \n
      \n
    • \n
    \n
  • \n
  • \n

    Throttling Error:

    \n
      \n
    • \n

      Code: ThrottlingError\n

      \n
    • \n
    • \n

      Messages:

      \n
        \n
      • \n

        Throughput exceeds the current capacity of your table or\n index. DynamoDB is automatically scaling your table or\n index so please try again shortly. If exceptions persist, check\n if you have a hot key:\n https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/bp-partition-key-design.html.

        \n \n

        This message is returned when writes get throttled on an\n On-Demand table as DynamoDB is automatically\n scaling the table.

        \n
        \n
      • \n
      • \n

        Throughput exceeds the current capacity for one or more\n global secondary indexes. DynamoDB is automatically\n scaling your index so please try again shortly.

        \n \n

        This message is returned when writes get throttled on\n an On-Demand GSI as DynamoDB is automatically\n scaling the GSI.

        \n
        \n
      • \n
      \n
    • \n
    \n
  • \n
  • \n

    Validation Error:

    \n
      \n
    • \n

      Code: ValidationError\n

      \n
    • \n
    • \n

      Messages:

      \n
        \n
      • \n

        One or more parameter values were invalid.

        \n
      • \n
      • \n

        The update expression attempted to update the secondary\n index key beyond allowed size limits.

        \n
      • \n
      • \n

        The update expression attempted to update the secondary\n index key to unsupported type.

        \n
      • \n
      • \n

        An operand in the update expression has an incorrect data\n type.

        \n
      • \n
      • \n

        Item size to update has exceeded the maximum allowed\n size.

        \n
      • \n
      • \n

        Number overflow. Attempting to store a number with\n magnitude larger than supported range.

        \n
      • \n
      • \n

        Type mismatch for attribute to update.

        \n
      • \n
      • \n

        Nesting Levels have exceeded supported limits.

        \n
      • \n
      • \n

        The document path provided in the update expression is\n invalid for update.

        \n
      • \n
      • \n

        The provided expression refers to an attribute that does\n not exist in the item.

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

The transaction with the given request token is already in progress.

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

The transaction with the given request token is already in progress.

\n

\n Recommended Settings \n

\n \n

\n This is a general recommendation for handling the TransactionInProgressException. These settings help \n ensure that the client retries will trigger completion of the ongoing TransactWriteItems request.\n

\n
\n
    \n
  • \n

    \n Set clientExecutionTimeout to a value that allows at least one retry to be processed after 5 \n seconds have elapsed since the first attempt for the TransactWriteItems operation.\n

    \n
  • \n
  • \n

    \n Set socketTimeout to a value a little lower than the requestTimeout setting.\n

    \n
  • \n
  • \n

    \n requestTimeout should be set based on the time taken for the individual retries of a single \n HTTP request for your use case, but setting it to 1 second or higher should work well to reduce chances of \n retries and TransactionInProgressException errors.\n

    \n
  • \n
  • \n

    \n Use exponential backoff when retrying and tune backoff if needed.\n

    \n
  • \n
\n

\n Assuming default retry policy, \n example timeout settings based on the guidelines above are as follows: \n

\n

Example timeline:

\n
    \n
  • \n

    0-1000 first attempt

    \n
  • \n
  • \n

    1000-1500 first sleep/delay (default retry policy uses 500 ms as base delay for 4xx errors)

    \n
  • \n
  • \n

    1500-2500 second attempt

    \n
  • \n
  • \n

    2500-3500 second sleep/delay (500 * 2, exponential backoff)

    \n
  • \n
  • \n

    3500-4500 third attempt

    \n
  • \n
  • \n

    4500-6500 third sleep/delay (500 * 2^2)

    \n
  • \n
  • \n

    6500-7500 fourth attempt (this can trigger inline recovery since 5 seconds have elapsed since the first attempt reached TC)

    \n
  • \n
", "smithy.api#error": "client" } }, @@ -10396,7 +10657,7 @@ "aws.api#clientDiscoveredEndpoint": { "required": false }, - "smithy.api#documentation": "

Removes the association of tags from an Amazon DynamoDB resource. You can call\n UntagResource up to five times per second, per account.

\n

For an overview on tagging DynamoDB resources, see Tagging for DynamoDB\n in the Amazon DynamoDB Developer Guide.

" + "smithy.api#documentation": "

Removes the association of tags from an Amazon DynamoDB resource. You can call\n UntagResource up to five times per second, per account.

\n

For an overview on tagging DynamoDB resources, see Tagging for DynamoDB\n in the Amazon DynamoDB Developer Guide.

" } }, "com.amazonaws.dynamodb#UntagResourceInput": { @@ -10416,6 +10677,9 @@ "smithy.api#required": {} } } + }, + "traits": { + "smithy.api#input": {} } }, "com.amazonaws.dynamodb#Update": { @@ -10497,7 +10761,7 @@ "aws.api#clientDiscoveredEndpoint": { "required": false }, - "smithy.api#documentation": "

\n UpdateContinuousBackups enables or disables point in time recovery for\n the specified table. A successful UpdateContinuousBackups call returns the\n current ContinuousBackupsDescription. Continuous backups are\n ENABLED on all tables at table creation. If point in time recovery is\n enabled, PointInTimeRecoveryStatus will be set to ENABLED.

\n

Once continuous backups and point in time recovery are enabled, you can restore to\n any point in time within EarliestRestorableDateTime and\n LatestRestorableDateTime.

\n

\n LatestRestorableDateTime is typically 5 minutes before the current time.\n You can restore your table to any point in time during the last 35 days.

" + "smithy.api#documentation": "

\n UpdateContinuousBackups enables or disables point in time recovery for\n the specified table. A successful UpdateContinuousBackups call returns the\n current ContinuousBackupsDescription. Continuous backups are\n ENABLED on all tables at table creation. If point in time recovery is\n enabled, PointInTimeRecoveryStatus will be set to ENABLED.

\n

Once continuous backups and point in time recovery are enabled, you can restore to\n any point in time within EarliestRestorableDateTime and\n LatestRestorableDateTime.

\n

\n LatestRestorableDateTime is typically 5 minutes before the current time.\n You can restore your table to any point in time during the last 35 days.

" } }, "com.amazonaws.dynamodb#UpdateContinuousBackupsInput": { @@ -10517,6 +10781,9 @@ "smithy.api#required": {} } } + }, + "traits": { + "smithy.api#input": {} } }, "com.amazonaws.dynamodb#UpdateContinuousBackupsOutput": { @@ -10528,6 +10795,9 @@ "smithy.api#documentation": "

Represents the continuous backups and point in time recovery settings on the\n table.

" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.dynamodb#UpdateContributorInsights": { @@ -10573,6 +10843,9 @@ "smithy.api#required": {} } } + }, + "traits": { + "smithy.api#input": {} } }, "com.amazonaws.dynamodb#UpdateContributorInsightsOutput": { @@ -10596,6 +10869,9 @@ "smithy.api#documentation": "

The status of contributor insights

" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.dynamodb#UpdateExpression": { @@ -10614,7 +10890,7 @@ "ProvisionedThroughput": { "target": "com.amazonaws.dynamodb#ProvisionedThroughput", "traits": { - "smithy.api#documentation": "

Represents the provisioned throughput settings for the specified global secondary\n index.

\n

For current minimum and maximum provisioned throughput values, see Service,\n Account, and Table Quotas in the Amazon DynamoDB Developer\n Guide.

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

Represents the provisioned throughput settings for the specified global secondary\n index.

\n

For current minimum and maximum provisioned throughput values, see Service,\n Account, and Table Quotas in the Amazon DynamoDB Developer\n Guide.

", "smithy.api#required": {} } } @@ -10655,7 +10931,7 @@ "aws.api#clientDiscoveredEndpoint": { "required": false }, - "smithy.api#documentation": "

Adds or removes replicas in the specified global table. The global table must already\n exist to be able to use this operation. Any replica to be added must be empty, have the\n same name as the global table, have the same key schema, have DynamoDB Streams enabled,\n and have the same provisioned and maximum write capacity units.

\n \n

Although you can use UpdateGlobalTable to add replicas and remove\n replicas in a single request, for simplicity we recommend that you issue separate\n requests for adding or removing replicas.

\n
\n

If global secondary indexes are specified, then the following conditions must also be\n met:

\n
    \n
  • \n

    The global secondary indexes must have the same name.

    \n
  • \n
  • \n

    The global secondary indexes must have the same hash key and sort key (if\n present).

    \n
  • \n
  • \n

    The global secondary indexes must have the same provisioned and maximum write\n capacity units.

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

Adds or removes replicas in the specified global table. The global table must already\n exist to be able to use this operation. Any replica to be added must be empty, have the\n same name as the global table, have the same key schema, have DynamoDB Streams enabled,\n and have the same provisioned and maximum write capacity units.

\n \n

This operation only applies to Version\n 2017.11.29 (Legacy) of global tables. We recommend using\n Version 2019.11.21 (Current)\n when creating new global tables, as it provides greater flexibility, higher efficiency and consumes less write capacity than \n 2017.11.29 (Legacy). To determine which version you are using, see \n Determining the version. \n To update existing global tables from version 2017.11.29 (Legacy) to version\n 2019.11.21 (Current), see \n Updating global tables.\n

\n
\n \n

\n This operation only applies to Version\n 2017.11.29 of global tables. If you are using global tables Version\n 2019.11.21 you can use DescribeTable instead.\n

\n

\n Although you can use UpdateGlobalTable to add replicas and remove\n replicas in a single request, for simplicity we recommend that you issue separate\n requests for adding or removing replicas.\n

\n
\n

If global secondary indexes are specified, then the following conditions must also be\n met:

\n
    \n
  • \n

    The global secondary indexes must have the same name.

    \n
  • \n
  • \n

    The global secondary indexes must have the same hash key and sort key (if\n present).

    \n
  • \n
  • \n

    The global secondary indexes must have the same provisioned and maximum write\n capacity units.

    \n
  • \n
" } }, "com.amazonaws.dynamodb#UpdateGlobalTableInput": { @@ -10675,6 +10951,9 @@ "smithy.api#required": {} } } + }, + "traits": { + "smithy.api#input": {} } }, "com.amazonaws.dynamodb#UpdateGlobalTableOutput": { @@ -10686,6 +10965,9 @@ "smithy.api#documentation": "

Contains the details of the global table.

" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.dynamodb#UpdateGlobalTableSettings": { @@ -10723,7 +11005,7 @@ "aws.api#clientDiscoveredEndpoint": { "required": false }, - "smithy.api#documentation": "

Updates settings for a global table.

" + "smithy.api#documentation": "

Updates settings for a global table.

\n \n

This operation only applies to Version\n 2017.11.29 (Legacy) of global tables. We recommend using\n Version 2019.11.21 (Current)\n when creating new global tables, as it provides greater flexibility, higher efficiency and consumes less write capacity than \n 2017.11.29 (Legacy). To determine which version you are using, see \n Determining the version. \n To update existing global tables from version 2017.11.29 (Legacy) to version\n 2019.11.21 (Current), see \n Updating global tables.\n

\n
" } }, "com.amazonaws.dynamodb#UpdateGlobalTableSettingsInput": { @@ -10739,7 +11021,7 @@ "GlobalTableBillingMode": { "target": "com.amazonaws.dynamodb#BillingMode", "traits": { - "smithy.api#documentation": "

The billing mode of the global table. If GlobalTableBillingMode is not\n specified, the global table defaults to PROVISIONED capacity billing\n mode.

\n
    \n
  • \n

    \n PROVISIONED - We recommend using PROVISIONED for\n predictable workloads. PROVISIONED sets the billing mode to Provisioned Mode.

    \n
  • \n
  • \n

    \n PAY_PER_REQUEST - We recommend using PAY_PER_REQUEST\n for unpredictable workloads. PAY_PER_REQUEST sets the billing mode\n to On-Demand Mode.

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

The billing mode of the global table. If GlobalTableBillingMode is not\n specified, the global table defaults to PROVISIONED capacity billing\n mode.

\n
    \n
  • \n

    \n PROVISIONED - We recommend using PROVISIONED for\n predictable workloads. PROVISIONED sets the billing mode to Provisioned Mode.

    \n
  • \n
  • \n

    \n PAY_PER_REQUEST - We recommend using PAY_PER_REQUEST\n for unpredictable workloads. PAY_PER_REQUEST sets the billing mode\n to On-Demand Mode.

    \n
  • \n
" } }, "GlobalTableProvisionedWriteCapacityUnits": { @@ -10766,6 +11048,9 @@ "smithy.api#documentation": "

Represents the settings for a global table in a Region that will be modified.

" } } + }, + "traits": { + "smithy.api#input": {} } }, "com.amazonaws.dynamodb#UpdateGlobalTableSettingsOutput": { @@ -10783,6 +11068,9 @@ "smithy.api#documentation": "

The Region-specific settings for the global table.

" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.dynamodb#UpdateItem": { @@ -10823,7 +11111,7 @@ "aws.api#clientDiscoveredEndpoint": { "required": false }, - "smithy.api#documentation": "

Edits an existing item's attributes, or adds a new item to the table if it does not\n already exist. You can put, delete, or add attribute values. You can also perform a\n conditional update on an existing item (insert a new attribute name-value pair if it\n doesn't exist, or replace an existing name-value pair if it has certain expected\n attribute values).

\n

You can also return the item's attribute values in the same UpdateItem\n operation using the ReturnValues parameter.

" + "smithy.api#documentation": "

Edits an existing item's attributes, or adds a new item to the table if it does not\n already exist. You can put, delete, or add attribute values. You can also perform a\n conditional update on an existing item (insert a new attribute name-value pair if it\n doesn't exist, or replace an existing name-value pair if it has certain expected\n attribute values).

\n

You can also return the item's attribute values in the same UpdateItem\n operation using the ReturnValues parameter.

" } }, "com.amazonaws.dynamodb#UpdateItemInput": { @@ -10839,7 +11127,7 @@ "Key": { "target": "com.amazonaws.dynamodb#Key", "traits": { - "smithy.api#documentation": "

The primary key of the item to be updated. Each element consists of an attribute name\n and a value for that attribute.

\n

For the primary key, you must provide all of the attributes. For example, with a\n simple primary key, you only need to provide a value for the partition key. For a\n composite primary key, you must provide values for both the partition key and the sort\n key.

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

The primary key of the item to be updated. Each element consists of an attribute name\n and a value for that attribute.

\n

For the primary key, you must provide all of the attributes. For example, with a\n simple primary key, you only need to provide a value for the partition key. For a\n composite primary key, you must provide values for both the partition key and the sort\n key.

", "smithy.api#required": {} } }, @@ -10864,7 +11152,7 @@ "ReturnValues": { "target": "com.amazonaws.dynamodb#ReturnValue", "traits": { - "smithy.api#documentation": "

Use ReturnValues if you want to get the item attributes as they appear\n before or after they are updated. For UpdateItem, the valid values\n are:

\n
    \n
  • \n

    \n NONE - If ReturnValues is not specified, or if its\n value is NONE, then nothing is returned. (This setting is the\n default for ReturnValues.)

    \n
  • \n
  • \n

    \n ALL_OLD - Returns all of the attributes of the item, as they\n appeared before the UpdateItem operation.

    \n
  • \n
  • \n

    \n UPDATED_OLD - Returns only the updated attributes, as they appeared\n before the UpdateItem operation.

    \n
  • \n
  • \n

    \n ALL_NEW - Returns all of the attributes of the item, as they appear\n after the UpdateItem operation.

    \n
  • \n
  • \n

    \n UPDATED_NEW - Returns only the updated attributes, as they appear\n after the UpdateItem operation.

    \n
  • \n
\n

There is no additional cost associated with requesting a return value aside from the\n small network and processing overhead of receiving a larger response. No read capacity\n units are consumed.

\n

The values returned are strongly consistent.

" + "smithy.api#documentation": "

Use ReturnValues if you want to get the item attributes as they appear\n before or after they are successfully updated. For UpdateItem, the valid values\n are:

\n
    \n
  • \n

    \n NONE - If ReturnValues is not specified, or if its\n value is NONE, then nothing is returned. (This setting is the\n default for ReturnValues.)

    \n
  • \n
  • \n

    \n ALL_OLD - Returns all of the attributes of the item, as they\n appeared before the UpdateItem operation.

    \n
  • \n
  • \n

    \n UPDATED_OLD - Returns only the updated attributes, as they appeared\n before the UpdateItem operation.

    \n
  • \n
  • \n

    \n ALL_NEW - Returns all of the attributes of the item, as they appear\n after the UpdateItem operation.

    \n
  • \n
  • \n

    \n UPDATED_NEW - Returns only the updated attributes, as they appear\n after the UpdateItem operation.

    \n
  • \n
\n

There is no additional cost associated with requesting a return value aside from the\n small network and processing overhead of receiving a larger response. No read capacity\n units are consumed.

\n

The values returned are strongly consistent.

" } }, "ReturnConsumedCapacity": { @@ -10879,30 +11167,31 @@ "UpdateExpression": { "target": "com.amazonaws.dynamodb#UpdateExpression", "traits": { - "smithy.api#documentation": "

An expression that defines one or more attributes to be updated, the action to be\n performed on them, and new values for them.

\n

The following action values are available for UpdateExpression.

\n
    \n
  • \n

    \n SET - Adds one or more attributes and values to an item. If any of\n these attributes already exist, they are replaced by the new values. You can\n also use SET to add or subtract from an attribute that is of type\n Number. For example: SET myNum = myNum + :val\n

    \n

    \n SET supports the following functions:

    \n
      \n
    • \n

      \n if_not_exists (path, operand) - if the item does not\n contain an attribute at the specified path, then\n if_not_exists evaluates to operand; otherwise, it\n evaluates to path. You can use this function to avoid overwriting an\n attribute that may already be present in the item.

      \n
    • \n
    • \n

      \n list_append (operand, operand) - evaluates to a list with a\n new element added to it. You can append the new element to the start or\n the end of the list by reversing the order of the operands.

      \n
    • \n
    \n

    These function names are case-sensitive.

    \n
  • \n
  • \n

    \n REMOVE - Removes one or more attributes from an item.

    \n
  • \n
  • \n

    \n ADD - Adds the specified value to the item, if the attribute does\n not already exist. If the attribute does exist, then the behavior of\n ADD depends on the data type of the attribute:

    \n
      \n
    • \n

      If the existing attribute is a number, and if Value is\n also a number, then Value is mathematically added to the\n existing attribute. If Value is a negative number, then it\n is subtracted from the existing attribute.

      \n \n

      If you use ADD to increment or decrement a number\n value for an item that doesn't exist before the update, DynamoDB\n uses 0 as the initial value.

      \n

      Similarly, if you use ADD for an existing item to\n increment or decrement an attribute value that doesn't exist before\n the update, DynamoDB uses 0 as the initial value. For\n example, suppose that the item you want to update doesn't have an\n attribute named itemcount, but you decide to\n ADD the number 3 to this attribute\n anyway. DynamoDB will create the itemcount attribute,\n set its initial value to 0, and finally add\n 3 to it. The result will be a new\n itemcount attribute in the item, with a value of\n 3.

      \n
      \n
    • \n
    • \n

      If the existing data type is a set and if Value is also a\n set, then Value is added to the existing set. For example,\n if the attribute value is the set [1,2], and the\n ADD action specified [3], then the final\n attribute value is [1,2,3]. An error occurs if an\n ADD action is specified for a set attribute and the\n attribute type specified does not match the existing set type.

      \n

      Both sets must have the same primitive data type. For example, if the\n existing data type is a set of strings, the Value must also\n be a set of strings.

      \n
    • \n
    \n \n

    The ADD action only supports Number and set data types. In\n addition, ADD can only be used on top-level attributes, not\n nested attributes.

    \n
    \n
  • \n
  • \n

    \n DELETE - Deletes an element from a set.

    \n

    If a set of values is specified, then those values are subtracted from the old\n set. For example, if the attribute value was the set [a,b,c] and\n the DELETE action specifies [a,c], then the final\n attribute value is [b]. Specifying an empty set is an error.

    \n \n

    The DELETE action only supports set data types. In addition,\n DELETE can only be used on top-level attributes, not nested\n attributes.

    \n
    \n\n
  • \n
\n

You can have many actions in a single expression, such as the following: SET\n a=:value1, b=:value2 DELETE :value3, :value4, :value5\n

\n

For more information on update expressions, see Modifying\n Items and Attributes in the Amazon DynamoDB Developer\n Guide.

" + "smithy.api#documentation": "

An expression that defines one or more attributes to be updated, the action to be\n performed on them, and new values for them.

\n

The following action values are available for UpdateExpression.

\n
    \n
  • \n

    \n SET - Adds one or more attributes and values to an item. If any of\n these attributes already exist, they are replaced by the new values. You can\n also use SET to add or subtract from an attribute that is of type\n Number. For example: SET myNum = myNum + :val\n

    \n

    \n SET supports the following functions:

    \n
      \n
    • \n

      \n if_not_exists (path, operand) - if the item does not\n contain an attribute at the specified path, then\n if_not_exists evaluates to operand; otherwise, it\n evaluates to path. You can use this function to avoid overwriting an\n attribute that may already be present in the item.

      \n
    • \n
    • \n

      \n list_append (operand, operand) - evaluates to a list with a\n new element added to it. You can append the new element to the start or\n the end of the list by reversing the order of the operands.

      \n
    • \n
    \n

    These function names are case-sensitive.

    \n
  • \n
  • \n

    \n REMOVE - Removes one or more attributes from an item.

    \n
  • \n
  • \n

    \n ADD - Adds the specified value to the item, if the attribute does\n not already exist. If the attribute does exist, then the behavior of\n ADD depends on the data type of the attribute:

    \n
      \n
    • \n

      If the existing attribute is a number, and if Value is\n also a number, then Value is mathematically added to the\n existing attribute. If Value is a negative number, then it\n is subtracted from the existing attribute.

      \n \n

      If you use ADD to increment or decrement a number\n value for an item that doesn't exist before the update, DynamoDB\n uses 0 as the initial value.

      \n

      Similarly, if you use ADD for an existing item to\n increment or decrement an attribute value that doesn't exist before\n the update, DynamoDB uses 0 as the initial value. For\n example, suppose that the item you want to update doesn't have an\n attribute named itemcount, but you decide to\n ADD the number 3 to this attribute\n anyway. DynamoDB will create the itemcount attribute,\n set its initial value to 0, and finally add\n 3 to it. The result will be a new\n itemcount attribute in the item, with a value of\n 3.

      \n
      \n
    • \n
    • \n

      If the existing data type is a set and if Value is also a\n set, then Value is added to the existing set. For example,\n if the attribute value is the set [1,2], and the\n ADD action specified [3], then the final\n attribute value is [1,2,3]. An error occurs if an\n ADD action is specified for a set attribute and the\n attribute type specified does not match the existing set type.

      \n

      Both sets must have the same primitive data type. For example, if the\n existing data type is a set of strings, the Value must also\n be a set of strings.

      \n
    • \n
    \n \n

    The ADD action only supports Number and set data types. In\n addition, ADD can only be used on top-level attributes, not\n nested attributes.

    \n
    \n
  • \n
  • \n

    \n DELETE - Deletes an element from a set.

    \n

    If a set of values is specified, then those values are subtracted from the old\n set. For example, if the attribute value was the set [a,b,c] and\n the DELETE action specifies [a,c], then the final\n attribute value is [b]. Specifying an empty set is an error.

    \n \n

    The DELETE action only supports set data types. In addition,\n DELETE can only be used on top-level attributes, not nested\n attributes.

    \n
    \n
  • \n
\n

You can have many actions in a single expression, such as the following: SET\n a=:value1, b=:value2 DELETE :value3, :value4, :value5\n

\n

For more information on update expressions, see Modifying\n Items and Attributes in the Amazon DynamoDB Developer\n Guide.

" } }, "ConditionExpression": { "target": "com.amazonaws.dynamodb#ConditionExpression", "traits": { - "smithy.api#documentation": "

A condition that must be satisfied in order for a conditional update to\n succeed.

\n

An expression can contain any of the following:

\n
    \n
  • \n

    Functions: attribute_exists | attribute_not_exists | attribute_type |\n contains | begins_with | size\n

    \n

    These function names are case-sensitive.

    \n
  • \n
  • \n

    Comparison operators: = | <> |\n < | > | <= | >= |\n BETWEEN | IN \n

    \n
  • \n
  • \n

    Logical operators: AND | OR | NOT\n

    \n
  • \n
\n

For more information about condition expressions, see Specifying Conditions in the Amazon DynamoDB Developer\n Guide.

" + "smithy.api#documentation": "

A condition that must be satisfied in order for a conditional update to\n succeed.

\n

An expression can contain any of the following:

\n
    \n
  • \n

    Functions: attribute_exists | attribute_not_exists | attribute_type |\n contains | begins_with | size\n

    \n

    These function names are case-sensitive.

    \n
  • \n
  • \n

    Comparison operators: = | <> |\n < | > | <= | >= |\n BETWEEN | IN \n

    \n
  • \n
  • \n

    Logical operators: AND | OR | NOT\n

    \n
  • \n
\n

For more information about condition expressions, see Specifying Conditions in the Amazon DynamoDB Developer\n Guide.

" } }, "ExpressionAttributeNames": { "target": "com.amazonaws.dynamodb#ExpressionAttributeNameMap", "traits": { - "smithy.api#documentation": "

One or more substitution tokens for attribute names in an expression. The following\n are some use cases for using ExpressionAttributeNames:

\n
    \n
  • \n

    To access an attribute whose name conflicts with a DynamoDB reserved\n word.

    \n
  • \n
  • \n

    To create a placeholder for repeating occurrences of an attribute name in an\n expression.

    \n
  • \n
  • \n

    To prevent special characters in an attribute name from being misinterpreted\n in an expression.

    \n
  • \n
\n

Use the # character in an expression to dereference\n an attribute name. For example, consider the following attribute name:

\n
    \n
  • \n

    \n Percentile\n

    \n
  • \n
\n

The name of this attribute conflicts with a reserved word, so it cannot be used\n directly in an expression. (For the complete list of reserved words, see Reserved Words in the Amazon DynamoDB Developer\n Guide.) To work around this, you could specify the following for\n ExpressionAttributeNames:

\n
    \n
  • \n

    \n {\"#P\":\"Percentile\"}\n

    \n
  • \n
\n

You could then use this substitution in an expression, as in this example:

\n
    \n
  • \n

    \n #P = :val\n

    \n
  • \n
\n \n

Tokens that begin with the : character are\n expression attribute values, which are placeholders for the\n actual value at runtime.

\n
\n

For more information about expression attribute names, see Specifying Item Attributes in the Amazon DynamoDB Developer\n Guide.

" + "smithy.api#documentation": "

One or more substitution tokens for attribute names in an expression. The following\n are some use cases for using ExpressionAttributeNames:

\n
    \n
  • \n

    To access an attribute whose name conflicts with a DynamoDB reserved\n word.

    \n
  • \n
  • \n

    To create a placeholder for repeating occurrences of an attribute name in an\n expression.

    \n
  • \n
  • \n

    To prevent special characters in an attribute name from being misinterpreted\n in an expression.

    \n
  • \n
\n

Use the # character in an expression to dereference\n an attribute name. For example, consider the following attribute name:

\n
    \n
  • \n

    \n Percentile\n

    \n
  • \n
\n

The name of this attribute conflicts with a reserved word, so it cannot be used\n directly in an expression. (For the complete list of reserved words, see Reserved Words in the Amazon DynamoDB Developer\n Guide.) To work around this, you could specify the following for\n ExpressionAttributeNames:

\n
    \n
  • \n

    \n {\"#P\":\"Percentile\"}\n

    \n
  • \n
\n

You could then use this substitution in an expression, as in this example:

\n
    \n
  • \n

    \n #P = :val\n

    \n
  • \n
\n \n

Tokens that begin with the : character are\n expression attribute values, which are placeholders for the\n actual value at runtime.

\n
\n

For more information about expression attribute names, see Specifying Item Attributes in the Amazon DynamoDB Developer\n Guide.

" } }, "ExpressionAttributeValues": { "target": "com.amazonaws.dynamodb#ExpressionAttributeValueMap", "traits": { - "smithy.api#documentation": "

One or more values that can be substituted in an expression.

\n

Use the : (colon) character in an expression to\n dereference an attribute value. For example, suppose that you wanted to check whether\n the value of the ProductStatus attribute was one of the following:

\n

\n Available | Backordered | Discontinued\n

\n

You would first need to specify ExpressionAttributeValues as\n follows:

\n

\n { \":avail\":{\"S\":\"Available\"}, \":back\":{\"S\":\"Backordered\"},\n \":disc\":{\"S\":\"Discontinued\"} }\n

\n

You could then use these values in an expression, such as this:

\n

\n ProductStatus IN (:avail, :back, :disc)\n

\n

For more information on expression attribute values, see Condition Expressions in the Amazon DynamoDB Developer\n Guide.

" + "smithy.api#documentation": "

One or more values that can be substituted in an expression.

\n

Use the : (colon) character in an expression to\n dereference an attribute value. For example, suppose that you wanted to check whether\n the value of the ProductStatus attribute was one of the following:

\n

\n Available | Backordered | Discontinued\n

\n

You would first need to specify ExpressionAttributeValues as\n follows:

\n

\n { \":avail\":{\"S\":\"Available\"}, \":back\":{\"S\":\"Backordered\"},\n \":disc\":{\"S\":\"Discontinued\"} }\n

\n

You could then use these values in an expression, such as this:

\n

\n ProductStatus IN (:avail, :back, :disc)\n

\n

For more information on expression attribute values, see Condition Expressions in the Amazon DynamoDB Developer\n Guide.

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

Represents the input of an UpdateItem operation.

" + "smithy.api#documentation": "

Represents the input of an UpdateItem operation.

", + "smithy.api#input": {} } }, "com.amazonaws.dynamodb#UpdateItemOutput": { @@ -10911,24 +11200,25 @@ "Attributes": { "target": "com.amazonaws.dynamodb#AttributeMap", "traits": { - "smithy.api#documentation": "

A map of attribute values as they appear before or after the UpdateItem\n operation, as determined by the ReturnValues parameter.

\n

The Attributes map is only present if ReturnValues was\n specified as something other than NONE in the request. Each element\n represents one attribute.

" + "smithy.api#documentation": "

A map of attribute values as they appear before or after the UpdateItem\n operation, as determined by the ReturnValues parameter.

\n

The Attributes map is only present if the update was successful and ReturnValues was\n specified as something other than NONE in the request. Each element\n represents one attribute.

" } }, "ConsumedCapacity": { "target": "com.amazonaws.dynamodb#ConsumedCapacity", "traits": { - "smithy.api#documentation": "

The capacity units consumed by the UpdateItem operation. The data\n returned includes the total provisioned throughput consumed, along with statistics for\n the table and any indexes involved in the operation. ConsumedCapacity is\n only returned if the ReturnConsumedCapacity parameter was specified. For\n more information, see Provisioned Throughput in the Amazon DynamoDB Developer\n Guide.

" + "smithy.api#documentation": "

The capacity units consumed by the UpdateItem operation. The data\n returned includes the total provisioned throughput consumed, along with statistics for\n the table and any indexes involved in the operation. ConsumedCapacity is\n only returned if the ReturnConsumedCapacity parameter was specified. For\n more information, see Provisioned Throughput in the Amazon DynamoDB Developer\n Guide.

" } }, "ItemCollectionMetrics": { "target": "com.amazonaws.dynamodb#ItemCollectionMetrics", "traits": { - "smithy.api#documentation": "

Information about item collections, if any, that were affected by the\n UpdateItem operation. ItemCollectionMetrics is only\n returned if the ReturnItemCollectionMetrics parameter was specified. If the\n table does not have any local secondary indexes, this information is not returned in the\n response.

\n

Each ItemCollectionMetrics element consists of:

\n
    \n
  • \n

    \n ItemCollectionKey - The partition key value of the item collection.\n This is the same as the partition key value of the item itself.

    \n
  • \n
  • \n

    \n SizeEstimateRangeGB - An estimate of item collection size, in\n gigabytes. This value is a two-element array containing a lower bound and an\n upper bound for the estimate. The estimate includes the size of all the items in\n the table, plus the size of all attributes projected into all of the local\n secondary indexes on that table. Use this estimate to measure whether a local\n secondary index is approaching its size limit.

    \n

    The estimate is subject to change over time; therefore, do not rely on the\n precision or accuracy of the estimate.

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

Information about item collections, if any, that were affected by the\n UpdateItem operation. ItemCollectionMetrics is only\n returned if the ReturnItemCollectionMetrics parameter was specified. If the\n table does not have any local secondary indexes, this information is not returned in the\n response.

\n

Each ItemCollectionMetrics element consists of:

\n
    \n
  • \n

    \n ItemCollectionKey - The partition key value of the item collection.\n This is the same as the partition key value of the item itself.

    \n
  • \n
  • \n

    \n SizeEstimateRangeGB - An estimate of item collection size, in\n gigabytes. This value is a two-element array containing a lower bound and an\n upper bound for the estimate. The estimate includes the size of all the items in\n the table, plus the size of all attributes projected into all of the local\n secondary indexes on that table. Use this estimate to measure whether a local\n secondary index is approaching its size limit.

    \n

    The estimate is subject to change over time; therefore, do not rely on the\n precision or accuracy of the estimate.

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

Represents the output of an UpdateItem operation.

" + "smithy.api#documentation": "

Represents the output of an UpdateItem operation.

", + "smithy.api#output": {} } }, "com.amazonaws.dynamodb#UpdateReplicationGroupMemberAction": { @@ -10999,7 +11289,7 @@ "aws.api#clientDiscoveredEndpoint": { "required": false }, - "smithy.api#documentation": "

Modifies the provisioned throughput settings, global secondary indexes, or DynamoDB\n Streams settings for a given table.

\n

You can only perform one of the following operations at once:

\n
    \n
  • \n

    Modify the provisioned throughput settings of the table.

    \n
  • \n
  • \n

    Remove a global secondary index from the table.

    \n
  • \n
  • \n

    Create a new global secondary index on the table. After the index begins\n backfilling, you can use UpdateTable to perform other\n operations.

    \n
  • \n
\n

\n UpdateTable is an asynchronous operation; while it is executing, the table\n status changes from ACTIVE to UPDATING. While it is\n UPDATING, you cannot issue another UpdateTable request.\n When the table returns to the ACTIVE state, the UpdateTable\n operation is complete.

" + "smithy.api#documentation": "

Modifies the provisioned throughput settings, global secondary indexes, or DynamoDB\n Streams settings for a given table.

\n \n

This operation only applies to Version 2019.11.21 (Current) \n of global tables.\n

\n
\n

You can only perform one of the following operations at once:

\n
    \n
  • \n

    Modify the provisioned throughput settings of the table.

    \n
  • \n
  • \n

    Remove a global secondary index from the table.

    \n
  • \n
  • \n

    Create a new global secondary index on the table. After the index begins\n backfilling, you can use UpdateTable to perform other\n operations.

    \n
  • \n
\n

\n UpdateTable is an asynchronous operation; while it is executing, the table\n status changes from ACTIVE to UPDATING. While it is\n UPDATING, you cannot issue another UpdateTable request.\n When the table returns to the ACTIVE state, the UpdateTable\n operation is complete.

" } }, "com.amazonaws.dynamodb#UpdateTableInput": { @@ -11021,7 +11311,7 @@ "BillingMode": { "target": "com.amazonaws.dynamodb#BillingMode", "traits": { - "smithy.api#documentation": "

Controls how you are charged for read and write throughput and how you manage\n capacity. When switching from pay-per-request to provisioned capacity, initial\n provisioned capacity values must be set. The initial provisioned capacity values are\n estimated based on the consumed read and write capacity of your table and global\n secondary indexes over the past 30 minutes.

\n
    \n
  • \n

    \n PROVISIONED - We recommend using PROVISIONED for\n predictable workloads. PROVISIONED sets the billing mode to Provisioned Mode.

    \n
  • \n
  • \n

    \n PAY_PER_REQUEST - We recommend using PAY_PER_REQUEST\n for unpredictable workloads. PAY_PER_REQUEST sets the billing mode\n to On-Demand Mode.

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

Controls how you are charged for read and write throughput and how you manage\n capacity. When switching from pay-per-request to provisioned capacity, initial\n provisioned capacity values must be set. The initial provisioned capacity values are\n estimated based on the consumed read and write capacity of your table and global\n secondary indexes over the past 30 minutes.

\n
    \n
  • \n

    \n PROVISIONED - We recommend using PROVISIONED for\n predictable workloads. PROVISIONED sets the billing mode to Provisioned Mode.

    \n
  • \n
  • \n

    \n PAY_PER_REQUEST - We recommend using PAY_PER_REQUEST\n for unpredictable workloads. PAY_PER_REQUEST sets the billing mode\n to On-Demand Mode.

    \n
  • \n
" } }, "ProvisionedThroughput": { @@ -11033,13 +11323,13 @@ "GlobalSecondaryIndexUpdates": { "target": "com.amazonaws.dynamodb#GlobalSecondaryIndexUpdateList", "traits": { - "smithy.api#documentation": "

An array of one or more global secondary indexes for the table. For each index in the\n array, you can request one action:

\n
    \n
  • \n

    \n Create - add a new global secondary index to the table.

    \n
  • \n
  • \n

    \n Update - modify the provisioned throughput settings of an existing\n global secondary index.

    \n
  • \n
  • \n

    \n Delete - remove a global secondary index from the table.

    \n
  • \n
\n

You can create or delete only one global secondary index per UpdateTable\n operation.

\n

For more information, see Managing Global\n Secondary Indexes in the Amazon DynamoDB Developer\n Guide.

" + "smithy.api#documentation": "

An array of one or more global secondary indexes for the table. For each index in the\n array, you can request one action:

\n
    \n
  • \n

    \n Create - add a new global secondary index to the table.

    \n
  • \n
  • \n

    \n Update - modify the provisioned throughput settings of an existing\n global secondary index.

    \n
  • \n
  • \n

    \n Delete - remove a global secondary index from the table.

    \n
  • \n
\n

You can create or delete only one global secondary index per UpdateTable\n operation.

\n

For more information, see Managing Global\n Secondary Indexes in the Amazon DynamoDB Developer\n Guide.

" } }, "StreamSpecification": { "target": "com.amazonaws.dynamodb#StreamSpecification", "traits": { - "smithy.api#documentation": "

Represents the DynamoDB Streams configuration for the table.

\n \n

You receive a ResourceInUseException if you try to enable a stream on\n a table that already has a stream, or if you try to disable a stream on a table that\n doesn't have a stream.

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

Represents the DynamoDB Streams configuration for the table.

\n \n

You receive a ResourceInUseException if you try to enable a stream on\n a table that already has a stream, or if you try to disable a stream on a table that\n doesn't have a stream.

\n
" } }, "SSESpecification": { @@ -11051,7 +11341,7 @@ "ReplicaUpdates": { "target": "com.amazonaws.dynamodb#ReplicationGroupUpdateList", "traits": { - "smithy.api#documentation": "

A list of replica update actions (create, delete, or update) for the table.

\n \n

This property only applies to Version\n 2019.11.21 of global tables.

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

A list of replica update actions (create, delete, or update) for the table.

\n \n

This property only applies to Version 2019.11.21 (Current)\n of global tables.\n

\n
" } }, "TableClass": { @@ -11059,10 +11349,17 @@ "traits": { "smithy.api#documentation": "

The table class of the table to be updated. Valid values are STANDARD and\n STANDARD_INFREQUENT_ACCESS.

" } + }, + "DeletionProtectionEnabled": { + "target": "com.amazonaws.dynamodb#DeletionProtectionEnabled", + "traits": { + "smithy.api#documentation": "

Indicates whether deletion protection is to be enabled (true) or disabled (false) on the table.

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

Represents the input of an UpdateTable operation.

" + "smithy.api#documentation": "

Represents the input of an UpdateTable operation.

", + "smithy.api#input": {} } }, "com.amazonaws.dynamodb#UpdateTableOutput": { @@ -11076,7 +11373,8 @@ } }, "traits": { - "smithy.api#documentation": "

Represents the output of an UpdateTable operation.

" + "smithy.api#documentation": "

Represents the output of an UpdateTable operation.

", + "smithy.api#output": {} } }, "com.amazonaws.dynamodb#UpdateTableReplicaAutoScaling": { @@ -11102,7 +11400,7 @@ } ], "traits": { - "smithy.api#documentation": "

Updates auto scaling settings on your global tables at once.

\n \n

This operation only applies to Version\n 2019.11.21 of global tables.

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

Updates auto scaling settings on your global tables at once.

\n \n

This operation only applies to Version 2019.11.21 (Current) \n of global tables.\n

\n
" } }, "com.amazonaws.dynamodb#UpdateTableReplicaAutoScalingInput": { @@ -11130,6 +11428,9 @@ "smithy.api#documentation": "

Represents the auto scaling settings of replicas of the table that will be\n modified.

" } } + }, + "traits": { + "smithy.api#input": {} } }, "com.amazonaws.dynamodb#UpdateTableReplicaAutoScalingOutput": { @@ -11141,6 +11442,9 @@ "smithy.api#documentation": "

Returns information about the auto scaling settings of a table with replicas.

" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.dynamodb#UpdateTimeToLive": { @@ -11172,7 +11476,7 @@ "aws.api#clientDiscoveredEndpoint": { "required": false }, - "smithy.api#documentation": "

The UpdateTimeToLive method enables or disables Time to Live (TTL) for\n the specified table. A successful UpdateTimeToLive call returns the current\n TimeToLiveSpecification. It can take up to one hour for the change to\n fully process. Any additional UpdateTimeToLive calls for the same table\n during this one hour duration result in a ValidationException.

\n

TTL compares the current time in epoch time format to the time stored in the TTL\n attribute of an item. If the epoch time value stored in the attribute is less than the\n current time, the item is marked as expired and subsequently deleted.

\n \n

The epoch time format is the number of seconds elapsed since 12:00:00 AM January\n 1, 1970 UTC.

\n
\n

DynamoDB deletes expired items on a best-effort basis to ensure availability of\n throughput for other data operations.

\n \n

DynamoDB typically deletes expired items within two days of expiration. The exact\n duration within which an item gets deleted after expiration is specific to the\n nature of the workload. Items that have expired and not been deleted will still show\n up in reads, queries, and scans.

\n
\n

As items are deleted, they are removed from any local secondary index and global\n secondary index immediately in the same eventually consistent way as a standard delete\n operation.

\n

For more information, see Time To Live in the\n Amazon DynamoDB Developer Guide.

" + "smithy.api#documentation": "

The UpdateTimeToLive method enables or disables Time to Live (TTL) for\n the specified table. A successful UpdateTimeToLive call returns the current\n TimeToLiveSpecification. It can take up to one hour for the change to\n fully process. Any additional UpdateTimeToLive calls for the same table\n during this one hour duration result in a ValidationException.

\n

TTL compares the current time in epoch time format to the time stored in the TTL\n attribute of an item. If the epoch time value stored in the attribute is less than the\n current time, the item is marked as expired and subsequently deleted.

\n \n

The epoch time format is the number of seconds elapsed since 12:00:00 AM January\n 1, 1970 UTC.

\n
\n

DynamoDB deletes expired items on a best-effort basis to ensure availability of\n throughput for other data operations.

\n \n

DynamoDB typically deletes expired items within two days of expiration. The exact\n duration within which an item gets deleted after expiration is specific to the\n nature of the workload. Items that have expired and not been deleted will still show\n up in reads, queries, and scans.

\n
\n

As items are deleted, they are removed from any local secondary index and global\n secondary index immediately in the same eventually consistent way as a standard delete\n operation.

\n

For more information, see Time To Live in the\n Amazon DynamoDB Developer Guide.

" } }, "com.amazonaws.dynamodb#UpdateTimeToLiveInput": { @@ -11194,7 +11498,8 @@ } }, "traits": { - "smithy.api#documentation": "

Represents the input of an UpdateTimeToLive operation.

" + "smithy.api#documentation": "

Represents the input of an UpdateTimeToLive operation.

", + "smithy.api#input": {} } }, "com.amazonaws.dynamodb#UpdateTimeToLiveOutput": { @@ -11206,6 +11511,9 @@ "smithy.api#documentation": "

Represents the output of an UpdateTimeToLive operation.

" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.dynamodb#WriteRequest": { diff --git a/aws/sdk/aws-models/ec2.json b/aws/sdk/aws-models/ec2.json index ee225e1375..b159cff5a5 100644 --- a/aws/sdk/aws-models/ec2.json +++ b/aws/sdk/aws-models/ec2.json @@ -632,7 +632,9 @@ "target": "com.amazonaws.ec2#VpcPeeringConnectionIdWithResolver", "traits": { "aws.protocols#ec2QueryName": "VpcPeeringConnectionId", + "smithy.api#clientOptional": {}, "smithy.api#documentation": "

The ID of the VPC peering connection. You must specify this parameter in the\n\t\t\trequest.

", + "smithy.api#required": {}, "smithy.api#xmlName": "vpcPeeringConnectionId" } } @@ -1674,6 +1676,12 @@ "traits": { "smithy.api#documentation": "

The Amazon Resource Name (ARN) of the Amazon Web Services Outpost on which to allocate\n the Dedicated Host.

" } + }, + "HostMaintenance": { + "target": "com.amazonaws.ec2#HostMaintenance", + "traits": { + "smithy.api#documentation": "

Indicates whether to enable or disable host maintenance for the Dedicated Host. For\n more information, see Host\n maintenance in the Amazon EC2 User Guide.

" + } } }, "traits": { @@ -2073,6 +2081,9 @@ { "target": "com.amazonaws.ec2#AssignPrivateIpAddresses" }, + { + "target": "com.amazonaws.ec2#AssignPrivateNatGatewayAddress" + }, { "target": "com.amazonaws.ec2#AssociateAddress" }, @@ -2094,6 +2105,9 @@ { "target": "com.amazonaws.ec2#AssociateIpamResourceDiscovery" }, + { + "target": "com.amazonaws.ec2#AssociateNatGatewayAddress" + }, { "target": "com.amazonaws.ec2#AssociateRouteTable" }, @@ -3174,6 +3188,9 @@ { "target": "com.amazonaws.ec2#DisassociateIpamResourceDiscovery" }, + { + "target": "com.amazonaws.ec2#DisassociateNatGatewayAddress" + }, { "target": "com.amazonaws.ec2#DisassociateRouteTable" }, @@ -3774,6 +3791,9 @@ { "target": "com.amazonaws.ec2#UnassignPrivateIpAddresses" }, + { + "target": "com.amazonaws.ec2#UnassignPrivateNatGatewayAddress" + }, { "target": "com.amazonaws.ec2#UnmonitorInstances" }, @@ -3809,7 +3829,7 @@ "parameters": { "Region": { "builtIn": "AWS::Region", - "required": true, + "required": false, "documentation": "The AWS region used to dispatch the request.", "type": "String" }, @@ -3838,13 +3858,12 @@ { "conditions": [ { - "fn": "aws.partition", + "fn": "isSet", "argv": [ { - "ref": "Region" + "ref": "Endpoint" } - ], - "assign": "PartitionResult" + ] } ], "type": "tree", @@ -3852,14 +3871,20 @@ { "conditions": [ { - "fn": "isSet", + "fn": "booleanEquals", "argv": [ { - "ref": "Endpoint" - } + "ref": "UseFIPS" + }, + true ] } ], + "error": "Invalid Configuration: FIPS and custom endpoint are not supported", + "type": "error" + }, + { + "conditions": [], "type": "tree", "rules": [ { @@ -3868,67 +3893,42 @@ "fn": "booleanEquals", "argv": [ { - "ref": "UseFIPS" + "ref": "UseDualStack" }, true ] } ], - "error": "Invalid Configuration: FIPS and custom endpoint are not supported", + "error": "Invalid Configuration: Dualstack 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" + "endpoint": { + "url": { + "ref": "Endpoint" }, - { - "conditions": [], - "endpoint": { - "url": { - "ref": "Endpoint" - }, - "properties": {}, - "headers": {} - }, - "type": "endpoint" - } - ] + "properties": {}, + "headers": {} + }, + "type": "endpoint" } ] - }, + } + ] + }, + { + "conditions": [], + "type": "tree", + "rules": [ { "conditions": [ { - "fn": "booleanEquals", - "argv": [ - { - "ref": "UseFIPS" - }, - true - ] - }, - { - "fn": "booleanEquals", + "fn": "isSet", "argv": [ { - "ref": "UseDualStack" - }, - true + "ref": "Region" + } ] } ], @@ -3937,187 +3937,286 @@ { "conditions": [ { - "fn": "booleanEquals", + "fn": "aws.partition", "argv": [ - true, { - "fn": "getAttr", + "ref": "Region" + } + ], + "assign": "PartitionResult" + } + ], + "type": "tree", + "rules": [ + { + "conditions": [ + { + "fn": "booleanEquals", "argv": [ { - "ref": "PartitionResult" + "ref": "UseFIPS" }, - "supportsFIPS" + true ] - } - ] - }, - { - "fn": "booleanEquals", - "argv": [ - true, + }, { - "fn": "getAttr", + "fn": "booleanEquals", "argv": [ { - "ref": "PartitionResult" + "ref": "UseDualStack" }, - "supportsDualStack" + true ] } - ] - } - ], - "type": "tree", - "rules": [ - { - "conditions": [], + ], "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://ec2-fips.{Region}.{PartitionResult#dualStackDnsSuffix}", + "properties": {}, + "headers": {} + }, + "type": "endpoint" + } + ] + } + ] + }, { "conditions": [], - "endpoint": { - "url": "https://ec2-fips.{Region}.{PartitionResult#dualStackDnsSuffix}", - "properties": {}, - "headers": {} - }, - "type": "endpoint" + "error": "FIPS and DualStack are enabled, but this partition does not support one or both", + "type": "error" } ] - } - ] - }, - { - "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, + "conditions": [ { - "fn": "getAttr", + "fn": "booleanEquals", "argv": [ { - "ref": "PartitionResult" + "ref": "UseFIPS" }, - "supportsFIPS" + true ] } - ] - } - ], - "type": "tree", - "rules": [ - { - "conditions": [], + ], "type": "tree", "rules": [ { "conditions": [ { - "fn": "stringEquals", + "fn": "booleanEquals", "argv": [ - "aws-us-gov", + true, { "fn": "getAttr", "argv": [ { "ref": "PartitionResult" }, - "name" + "supportsFIPS" ] } ] } ], - "endpoint": { - "url": "https://ec2.{Region}.amazonaws.com", - "properties": {}, - "headers": {} - }, - "type": "endpoint" + "type": "tree", + "rules": [ + { + "conditions": [], + "type": "tree", + "rules": [ + { + "conditions": [ + { + "fn": "stringEquals", + "argv": [ + "aws-us-gov", + { + "fn": "getAttr", + "argv": [ + { + "ref": "PartitionResult" + }, + "name" + ] + } + ] + } + ], + "endpoint": { + "url": "https://ec2.{Region}.amazonaws.com", + "properties": {}, + "headers": {} + }, + "type": "endpoint" + }, + { + "conditions": [], + "endpoint": { + "url": "https://ec2-fips.{Region}.{PartitionResult#dnsSuffix}", + "properties": {}, + "headers": {} + }, + "type": "endpoint" + } + ] + } + ] }, { "conditions": [], - "endpoint": { - "url": "https://ec2-fips.{Region}.{PartitionResult#dnsSuffix}", - "properties": {}, - "headers": {} - }, - "type": "endpoint" + "error": "FIPS is enabled but this partition does not support FIPS", + "type": "error" } ] - } - ] - }, - { - "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, + "conditions": [ { - "fn": "getAttr", + "fn": "booleanEquals", "argv": [ { - "ref": "PartitionResult" + "ref": "UseDualStack" }, - "supportsDualStack" + 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://ec2.{Region}.{PartitionResult#dualStackDnsSuffix}", + "properties": {}, + "headers": {} + }, + "type": "endpoint" + } + ] + } ] + }, + { + "conditions": [], + "error": "DualStack is enabled but this partition does not support DualStack", + "type": "error" } ] - } - ], - "type": "tree", - "rules": [ + }, { "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": { - "url": "https://ec2.{Region}.{PartitionResult#dualStackDnsSuffix}", + "url": "https://ec2.{Region}.{PartitionResult#dnsSuffix}", "properties": {}, "headers": {} }, @@ -4126,66 +4225,13 @@ ] } ] - }, - { - "conditions": [], - "error": "DualStack is enabled but this partition does not support DualStack", - "type": "error" } ] }, { "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": { - "url": "https://ec2.{Region}.{PartitionResult#dnsSuffix}", - "properties": {}, - "headers": {} - }, - "type": "endpoint" - } - ] + "error": "Invalid Configuration: Missing Region", + "type": "error" } ] } @@ -4194,653 +4240,666 @@ "smithy.rules#endpointTests": { "testCases": [ { - "documentation": "For region us-iso-west-1 with FIPS disabled and DualStack disabled", + "documentation": "For region af-south-1 with FIPS disabled and DualStack disabled", "expect": { "endpoint": { - "url": "https://ec2.us-iso-west-1.c2s.ic.gov" + "url": "https://ec2.af-south-1.amazonaws.com" } }, "params": { - "UseFIPS": false, + "Region": "af-south-1", "UseDualStack": false, - "Region": "us-iso-west-1" + "UseFIPS": false } }, { - "documentation": "For region us-iso-east-1 with FIPS disabled and DualStack disabled", + "documentation": "For region ap-east-1 with FIPS disabled and DualStack disabled", "expect": { "endpoint": { - "url": "https://ec2.us-iso-east-1.c2s.ic.gov" + "url": "https://ec2.ap-east-1.amazonaws.com" } }, "params": { - "UseFIPS": false, + "Region": "ap-east-1", "UseDualStack": false, - "Region": "us-iso-east-1" + "UseFIPS": false } }, { - "documentation": "For region us-iso-east-1 with FIPS enabled and DualStack disabled", + "documentation": "For region ap-northeast-1 with FIPS disabled and DualStack disabled", "expect": { "endpoint": { - "url": "https://ec2-fips.us-iso-east-1.c2s.ic.gov" + "url": "https://ec2.ap-northeast-1.amazonaws.com" } }, "params": { - "UseFIPS": true, + "Region": "ap-northeast-1", "UseDualStack": false, - "Region": "us-iso-east-1" + "UseFIPS": false } }, { - "documentation": "For region cn-north-1 with FIPS disabled and DualStack disabled", + "documentation": "For region ap-northeast-2 with FIPS disabled and DualStack disabled", "expect": { "endpoint": { - "url": "https://ec2.cn-north-1.amazonaws.com.cn" + "url": "https://ec2.ap-northeast-2.amazonaws.com" } }, "params": { - "UseFIPS": false, + "Region": "ap-northeast-2", "UseDualStack": false, - "Region": "cn-north-1" + "UseFIPS": false } }, { - "documentation": "For region cn-northwest-1 with FIPS disabled and DualStack disabled", + "documentation": "For region ap-northeast-3 with FIPS disabled and DualStack disabled", "expect": { "endpoint": { - "url": "https://ec2.cn-northwest-1.amazonaws.com.cn" + "url": "https://ec2.ap-northeast-3.amazonaws.com" } }, "params": { - "UseFIPS": false, + "Region": "ap-northeast-3", "UseDualStack": false, - "Region": "cn-northwest-1" + "UseFIPS": false } }, { - "documentation": "For region cn-north-1 with FIPS enabled and DualStack enabled", + "documentation": "For region ap-south-1 with FIPS disabled and DualStack disabled", "expect": { "endpoint": { - "url": "https://ec2-fips.cn-north-1.api.amazonwebservices.com.cn" + "url": "https://ec2.ap-south-1.amazonaws.com" } }, "params": { - "UseFIPS": true, - "UseDualStack": true, - "Region": "cn-north-1" + "Region": "ap-south-1", + "UseDualStack": false, + "UseFIPS": false } }, { - "documentation": "For region cn-north-1 with FIPS enabled and DualStack disabled", + "documentation": "For region ap-south-1 with FIPS disabled and DualStack enabled", "expect": { "endpoint": { - "url": "https://ec2-fips.cn-north-1.amazonaws.com.cn" + "url": "https://ec2.ap-south-1.api.aws" } }, "params": { - "UseFIPS": true, - "UseDualStack": false, - "Region": "cn-north-1" + "Region": "ap-south-1", + "UseDualStack": true, + "UseFIPS": false } }, { - "documentation": "For region cn-north-1 with FIPS disabled and DualStack enabled", + "documentation": "For region ap-southeast-1 with FIPS disabled and DualStack disabled", "expect": { "endpoint": { - "url": "https://ec2.cn-north-1.api.amazonwebservices.com.cn" + "url": "https://ec2.ap-southeast-1.amazonaws.com" } }, "params": { - "UseFIPS": false, - "UseDualStack": true, - "Region": "cn-north-1" + "Region": "ap-southeast-1", + "UseDualStack": false, + "UseFIPS": false } }, { - "documentation": "For region us-isob-east-1 with FIPS disabled and DualStack disabled", + "documentation": "For region ap-southeast-2 with FIPS disabled and DualStack disabled", "expect": { "endpoint": { - "url": "https://ec2.us-isob-east-1.sc2s.sgov.gov" + "url": "https://ec2.ap-southeast-2.amazonaws.com" } }, "params": { - "UseFIPS": false, + "Region": "ap-southeast-2", "UseDualStack": false, - "Region": "us-isob-east-1" + "UseFIPS": false } }, { - "documentation": "For region us-isob-east-1 with FIPS enabled and DualStack disabled", + "documentation": "For region ap-southeast-3 with FIPS disabled and DualStack disabled", "expect": { "endpoint": { - "url": "https://ec2-fips.us-isob-east-1.sc2s.sgov.gov" + "url": "https://ec2.ap-southeast-3.amazonaws.com" } }, "params": { - "UseFIPS": true, + "Region": "ap-southeast-3", "UseDualStack": false, - "Region": "us-isob-east-1" + "UseFIPS": false } }, { - "documentation": "For region us-gov-east-1 with FIPS disabled and DualStack disabled", + "documentation": "For region ca-central-1 with FIPS disabled and DualStack disabled", "expect": { "endpoint": { - "url": "https://ec2.us-gov-east-1.amazonaws.com" + "url": "https://ec2.ca-central-1.amazonaws.com" } }, "params": { - "UseFIPS": false, + "Region": "ca-central-1", "UseDualStack": false, - "Region": "us-gov-east-1" + "UseFIPS": false } }, { - "documentation": "For region us-gov-west-1 with FIPS disabled and DualStack disabled", + "documentation": "For region ca-central-1 with FIPS enabled and DualStack disabled", "expect": { "endpoint": { - "url": "https://ec2.us-gov-west-1.amazonaws.com" + "url": "https://ec2-fips.ca-central-1.amazonaws.com" } }, "params": { - "UseFIPS": false, + "Region": "ca-central-1", "UseDualStack": false, - "Region": "us-gov-west-1" + "UseFIPS": true } }, { - "documentation": "For region us-gov-east-1 with FIPS enabled and DualStack enabled", + "documentation": "For region eu-central-1 with FIPS disabled and DualStack disabled", "expect": { "endpoint": { - "url": "https://ec2-fips.us-gov-east-1.api.aws" + "url": "https://ec2.eu-central-1.amazonaws.com" } }, "params": { - "UseFIPS": true, - "UseDualStack": true, - "Region": "us-gov-east-1" + "Region": "eu-central-1", + "UseDualStack": false, + "UseFIPS": false } }, { - "documentation": "For region us-gov-east-1 with FIPS enabled and DualStack disabled", + "documentation": "For region eu-north-1 with FIPS disabled and DualStack disabled", "expect": { "endpoint": { - "url": "https://ec2.us-gov-east-1.amazonaws.com" + "url": "https://ec2.eu-north-1.amazonaws.com" } }, "params": { - "UseFIPS": true, + "Region": "eu-north-1", "UseDualStack": false, - "Region": "us-gov-east-1" + "UseFIPS": false } }, { - "documentation": "For region us-gov-east-1 with FIPS disabled and DualStack enabled", + "documentation": "For region eu-south-1 with FIPS disabled and DualStack disabled", "expect": { "endpoint": { - "url": "https://ec2.us-gov-east-1.api.aws" + "url": "https://ec2.eu-south-1.amazonaws.com" } }, "params": { - "UseFIPS": false, - "UseDualStack": true, - "Region": "us-gov-east-1" + "Region": "eu-south-1", + "UseDualStack": false, + "UseFIPS": false } }, { - "documentation": "For region sa-east-1 with FIPS disabled and DualStack disabled", + "documentation": "For region eu-west-1 with FIPS disabled and DualStack disabled", "expect": { "endpoint": { - "url": "https://ec2.sa-east-1.amazonaws.com" + "url": "https://ec2.eu-west-1.amazonaws.com" } }, "params": { - "UseFIPS": false, + "Region": "eu-west-1", "UseDualStack": false, - "Region": "sa-east-1" + "UseFIPS": false } }, { - "documentation": "For region sa-east-1 with FIPS disabled and DualStack enabled", + "documentation": "For region eu-west-1 with FIPS disabled and DualStack enabled", "expect": { "endpoint": { - "url": "https://ec2.sa-east-1.api.aws" + "url": "https://ec2.eu-west-1.api.aws" } }, "params": { - "UseFIPS": false, + "Region": "eu-west-1", "UseDualStack": true, - "Region": "sa-east-1" + "UseFIPS": false } }, { - "documentation": "For region us-east-2 with FIPS disabled and DualStack disabled", + "documentation": "For region eu-west-2 with FIPS disabled and DualStack disabled", "expect": { "endpoint": { - "url": "https://ec2.us-east-2.amazonaws.com" + "url": "https://ec2.eu-west-2.amazonaws.com" } }, "params": { - "UseFIPS": false, + "Region": "eu-west-2", "UseDualStack": false, - "Region": "us-east-2" + "UseFIPS": false } }, { - "documentation": "For region us-east-2 with FIPS enabled and DualStack disabled", + "documentation": "For region eu-west-3 with FIPS disabled and DualStack disabled", "expect": { "endpoint": { - "url": "https://ec2-fips.us-east-2.amazonaws.com" + "url": "https://ec2.eu-west-3.amazonaws.com" } }, "params": { - "UseFIPS": true, + "Region": "eu-west-3", "UseDualStack": false, - "Region": "us-east-2" + "UseFIPS": false } }, { - "documentation": "For region us-east-2 with FIPS disabled and DualStack enabled", + "documentation": "For region me-south-1 with FIPS disabled and DualStack disabled", "expect": { "endpoint": { - "url": "https://ec2.us-east-2.api.aws" + "url": "https://ec2.me-south-1.amazonaws.com" } }, "params": { - "UseFIPS": false, - "UseDualStack": true, - "Region": "us-east-2" + "Region": "me-south-1", + "UseDualStack": false, + "UseFIPS": false } }, { - "documentation": "For region eu-north-1 with FIPS disabled and DualStack disabled", + "documentation": "For region sa-east-1 with FIPS disabled and DualStack disabled", "expect": { "endpoint": { - "url": "https://ec2.eu-north-1.amazonaws.com" + "url": "https://ec2.sa-east-1.amazonaws.com" } }, "params": { - "UseFIPS": false, + "Region": "sa-east-1", "UseDualStack": false, - "Region": "eu-north-1" + "UseFIPS": false } }, { - "documentation": "For region me-south-1 with FIPS disabled and DualStack disabled", + "documentation": "For region sa-east-1 with FIPS disabled and DualStack enabled", "expect": { "endpoint": { - "url": "https://ec2.me-south-1.amazonaws.com" + "url": "https://ec2.sa-east-1.api.aws" } }, "params": { - "UseFIPS": false, - "UseDualStack": false, - "Region": "me-south-1" + "Region": "sa-east-1", + "UseDualStack": true, + "UseFIPS": false } }, { - "documentation": "For region eu-west-3 with FIPS disabled and DualStack disabled", + "documentation": "For region us-east-1 with FIPS disabled and DualStack disabled", "expect": { "endpoint": { - "url": "https://ec2.eu-west-3.amazonaws.com" + "url": "https://ec2.us-east-1.amazonaws.com" } }, "params": { - "UseFIPS": false, + "Region": "us-east-1", "UseDualStack": false, - "Region": "eu-west-3" + "UseFIPS": false } }, { - "documentation": "For region eu-west-2 with FIPS disabled and DualStack disabled", + "documentation": "For region us-east-1 with FIPS enabled and DualStack disabled", "expect": { "endpoint": { - "url": "https://ec2.eu-west-2.amazonaws.com" + "url": "https://ec2-fips.us-east-1.amazonaws.com" } }, "params": { - "UseFIPS": false, + "Region": "us-east-1", "UseDualStack": false, - "Region": "eu-west-2" + "UseFIPS": true } }, { - "documentation": "For region eu-west-1 with FIPS disabled and DualStack disabled", + "documentation": "For region us-east-1 with FIPS disabled and DualStack enabled", "expect": { "endpoint": { - "url": "https://ec2.eu-west-1.amazonaws.com" + "url": "https://ec2.us-east-1.api.aws" } }, "params": { - "UseFIPS": false, - "UseDualStack": false, - "Region": "eu-west-1" + "Region": "us-east-1", + "UseDualStack": true, + "UseFIPS": false } }, { - "documentation": "For region eu-west-1 with FIPS disabled and DualStack enabled", + "documentation": "For region us-east-2 with FIPS disabled and DualStack disabled", "expect": { "endpoint": { - "url": "https://ec2.eu-west-1.api.aws" + "url": "https://ec2.us-east-2.amazonaws.com" } }, "params": { - "UseFIPS": false, - "UseDualStack": true, - "Region": "eu-west-1" + "Region": "us-east-2", + "UseDualStack": false, + "UseFIPS": false } }, { - "documentation": "For region us-east-1 with FIPS disabled and DualStack disabled", + "documentation": "For region us-east-2 with FIPS enabled and DualStack disabled", "expect": { "endpoint": { - "url": "https://ec2.us-east-1.amazonaws.com" + "url": "https://ec2-fips.us-east-2.amazonaws.com" } }, "params": { - "UseFIPS": false, + "Region": "us-east-2", "UseDualStack": false, - "Region": "us-east-1" + "UseFIPS": true } }, { - "documentation": "For region us-east-1 with FIPS enabled and DualStack disabled", + "documentation": "For region us-east-2 with FIPS disabled and DualStack enabled", "expect": { "endpoint": { - "url": "https://ec2-fips.us-east-1.amazonaws.com" + "url": "https://ec2.us-east-2.api.aws" } }, "params": { - "UseFIPS": true, - "UseDualStack": false, - "Region": "us-east-1" + "Region": "us-east-2", + "UseDualStack": true, + "UseFIPS": false } }, { - "documentation": "For region us-east-1 with FIPS disabled and DualStack enabled", + "documentation": "For region us-west-1 with FIPS disabled and DualStack disabled", "expect": { "endpoint": { - "url": "https://ec2.us-east-1.api.aws" + "url": "https://ec2.us-west-1.amazonaws.com" } }, "params": { - "UseFIPS": false, - "UseDualStack": true, - "Region": "us-east-1" + "Region": "us-west-1", + "UseDualStack": false, + "UseFIPS": false } }, { - "documentation": "For region ap-northeast-3 with FIPS disabled and DualStack disabled", + "documentation": "For region us-west-1 with FIPS enabled and DualStack disabled", "expect": { "endpoint": { - "url": "https://ec2.ap-northeast-3.amazonaws.com" + "url": "https://ec2-fips.us-west-1.amazonaws.com" } }, "params": { - "UseFIPS": false, + "Region": "us-west-1", "UseDualStack": false, - "Region": "ap-northeast-3" + "UseFIPS": true } }, { - "documentation": "For region ap-northeast-2 with FIPS disabled and DualStack disabled", + "documentation": "For region us-west-2 with FIPS disabled and DualStack disabled", "expect": { "endpoint": { - "url": "https://ec2.ap-northeast-2.amazonaws.com" + "url": "https://ec2.us-west-2.amazonaws.com" } }, "params": { - "UseFIPS": false, + "Region": "us-west-2", "UseDualStack": false, - "Region": "ap-northeast-2" + "UseFIPS": false } }, { - "documentation": "For region ap-northeast-1 with FIPS disabled and DualStack disabled", + "documentation": "For region us-west-2 with FIPS enabled and DualStack disabled", "expect": { "endpoint": { - "url": "https://ec2.ap-northeast-1.amazonaws.com" + "url": "https://ec2-fips.us-west-2.amazonaws.com" } }, "params": { - "UseFIPS": false, + "Region": "us-west-2", "UseDualStack": false, - "Region": "ap-northeast-1" + "UseFIPS": true } }, { - "documentation": "For region ap-south-1 with FIPS disabled and DualStack disabled", + "documentation": "For region us-west-2 with FIPS disabled and DualStack enabled", "expect": { "endpoint": { - "url": "https://ec2.ap-south-1.amazonaws.com" + "url": "https://ec2.us-west-2.api.aws" } }, "params": { - "UseFIPS": false, - "UseDualStack": false, - "Region": "ap-south-1" + "Region": "us-west-2", + "UseDualStack": true, + "UseFIPS": false } }, { - "documentation": "For region ap-south-1 with FIPS disabled and DualStack enabled", + "documentation": "For region us-east-1 with FIPS enabled and DualStack enabled", "expect": { "endpoint": { - "url": "https://ec2.ap-south-1.api.aws" + "url": "https://ec2-fips.us-east-1.api.aws" } }, "params": { - "UseFIPS": false, + "Region": "us-east-1", "UseDualStack": true, - "Region": "ap-south-1" + "UseFIPS": true } }, { - "documentation": "For region af-south-1 with FIPS disabled and DualStack disabled", + "documentation": "For region cn-north-1 with FIPS disabled and DualStack disabled", "expect": { "endpoint": { - "url": "https://ec2.af-south-1.amazonaws.com" + "url": "https://ec2.cn-north-1.amazonaws.com.cn" } }, "params": { - "UseFIPS": false, + "Region": "cn-north-1", "UseDualStack": false, - "Region": "af-south-1" + "UseFIPS": false } }, { - "documentation": "For region us-west-2 with FIPS disabled and DualStack disabled", + "documentation": "For region cn-northwest-1 with FIPS disabled and DualStack disabled", "expect": { "endpoint": { - "url": "https://ec2.us-west-2.amazonaws.com" + "url": "https://ec2.cn-northwest-1.amazonaws.com.cn" } }, "params": { - "UseFIPS": false, + "Region": "cn-northwest-1", "UseDualStack": false, - "Region": "us-west-2" + "UseFIPS": false } }, { - "documentation": "For region us-west-2 with FIPS enabled and DualStack disabled", + "documentation": "For region cn-north-1 with FIPS enabled and DualStack enabled", "expect": { "endpoint": { - "url": "https://ec2-fips.us-west-2.amazonaws.com" + "url": "https://ec2-fips.cn-north-1.api.amazonwebservices.com.cn" } }, "params": { - "UseFIPS": true, + "Region": "cn-north-1", + "UseDualStack": true, + "UseFIPS": true + } + }, + { + "documentation": "For region cn-north-1 with FIPS enabled and DualStack disabled", + "expect": { + "endpoint": { + "url": "https://ec2-fips.cn-north-1.amazonaws.com.cn" + } + }, + "params": { + "Region": "cn-north-1", "UseDualStack": false, - "Region": "us-west-2" + "UseFIPS": true } }, { - "documentation": "For region us-west-2 with FIPS disabled and DualStack enabled", + "documentation": "For region cn-north-1 with FIPS disabled and DualStack enabled", "expect": { "endpoint": { - "url": "https://ec2.us-west-2.api.aws" + "url": "https://ec2.cn-north-1.api.amazonwebservices.com.cn" } }, "params": { - "UseFIPS": false, + "Region": "cn-north-1", "UseDualStack": true, - "Region": "us-west-2" + "UseFIPS": false } }, { - "documentation": "For region us-west-1 with FIPS disabled and DualStack disabled", + "documentation": "For region us-gov-east-1 with FIPS disabled and DualStack disabled", "expect": { "endpoint": { - "url": "https://ec2.us-west-1.amazonaws.com" + "url": "https://ec2.us-gov-east-1.amazonaws.com" } }, "params": { - "UseFIPS": false, + "Region": "us-gov-east-1", "UseDualStack": false, - "Region": "us-west-1" + "UseFIPS": false } }, { - "documentation": "For region us-west-1 with FIPS enabled and DualStack disabled", + "documentation": "For region us-gov-west-1 with FIPS disabled and DualStack disabled", "expect": { "endpoint": { - "url": "https://ec2-fips.us-west-1.amazonaws.com" + "url": "https://ec2.us-gov-west-1.amazonaws.com" } }, "params": { - "UseFIPS": true, + "Region": "us-gov-west-1", "UseDualStack": false, - "Region": "us-west-1" + "UseFIPS": false } }, { - "documentation": "For region ca-central-1 with FIPS disabled and DualStack disabled", + "documentation": "For region us-gov-east-1 with FIPS enabled and DualStack enabled", "expect": { "endpoint": { - "url": "https://ec2.ca-central-1.amazonaws.com" + "url": "https://ec2-fips.us-gov-east-1.api.aws" } }, "params": { - "UseFIPS": false, - "UseDualStack": false, - "Region": "ca-central-1" + "Region": "us-gov-east-1", + "UseDualStack": true, + "UseFIPS": true } }, { - "documentation": "For region ca-central-1 with FIPS enabled and DualStack disabled", + "documentation": "For region us-gov-east-1 with FIPS enabled and DualStack disabled", "expect": { "endpoint": { - "url": "https://ec2-fips.ca-central-1.amazonaws.com" + "url": "https://ec2.us-gov-east-1.amazonaws.com" } }, "params": { - "UseFIPS": true, + "Region": "us-gov-east-1", "UseDualStack": false, - "Region": "ca-central-1" + "UseFIPS": true } }, { - "documentation": "For region ap-southeast-3 with FIPS disabled and DualStack disabled", + "documentation": "For region us-gov-east-1 with FIPS disabled and DualStack enabled", "expect": { "endpoint": { - "url": "https://ec2.ap-southeast-3.amazonaws.com" + "url": "https://ec2.us-gov-east-1.api.aws" } }, "params": { - "UseFIPS": false, - "UseDualStack": false, - "Region": "ap-southeast-3" + "Region": "us-gov-east-1", + "UseDualStack": true, + "UseFIPS": false } }, { - "documentation": "For region ap-southeast-2 with FIPS disabled and DualStack disabled", + "documentation": "For region us-iso-east-1 with FIPS disabled and DualStack disabled", "expect": { "endpoint": { - "url": "https://ec2.ap-southeast-2.amazonaws.com" + "url": "https://ec2.us-iso-east-1.c2s.ic.gov" } }, "params": { - "UseFIPS": false, + "Region": "us-iso-east-1", "UseDualStack": false, - "Region": "ap-southeast-2" + "UseFIPS": false } }, { - "documentation": "For region ap-southeast-1 with FIPS disabled and DualStack disabled", + "documentation": "For region us-iso-west-1 with FIPS disabled and DualStack disabled", "expect": { "endpoint": { - "url": "https://ec2.ap-southeast-1.amazonaws.com" + "url": "https://ec2.us-iso-west-1.c2s.ic.gov" } }, "params": { - "UseFIPS": false, + "Region": "us-iso-west-1", "UseDualStack": false, - "Region": "ap-southeast-1" + "UseFIPS": false } }, { - "documentation": "For region eu-central-1 with FIPS disabled and DualStack disabled", + "documentation": "For region us-iso-east-1 with FIPS enabled and DualStack disabled", "expect": { "endpoint": { - "url": "https://ec2.eu-central-1.amazonaws.com" + "url": "https://ec2-fips.us-iso-east-1.c2s.ic.gov" } }, "params": { - "UseFIPS": false, + "Region": "us-iso-east-1", "UseDualStack": false, - "Region": "eu-central-1" + "UseFIPS": true } }, { - "documentation": "For region eu-south-1 with FIPS disabled and DualStack disabled", + "documentation": "For region us-isob-east-1 with FIPS disabled and DualStack disabled", "expect": { "endpoint": { - "url": "https://ec2.eu-south-1.amazonaws.com" + "url": "https://ec2.us-isob-east-1.sc2s.sgov.gov" } }, "params": { - "UseFIPS": false, + "Region": "us-isob-east-1", "UseDualStack": false, - "Region": "eu-south-1" + "UseFIPS": false } }, { - "documentation": "For region ap-east-1 with FIPS disabled and DualStack disabled", + "documentation": "For region us-isob-east-1 with FIPS enabled and DualStack disabled", "expect": { "endpoint": { - "url": "https://ec2.ap-east-1.amazonaws.com" + "url": "https://ec2-fips.us-isob-east-1.sc2s.sgov.gov" } }, "params": { - "UseFIPS": false, + "Region": "us-isob-east-1", "UseDualStack": false, - "Region": "ap-east-1" + "UseFIPS": true } }, { - "documentation": "For region us-east-1 with FIPS enabled and DualStack enabled", + "documentation": "For custom endpoint with region set and fips disabled and dualstack disabled", "expect": { "endpoint": { - "url": "https://ec2-fips.us-east-1.api.aws" + "url": "https://example.com" } }, "params": { - "UseFIPS": true, - "UseDualStack": true, - "Region": "us-east-1" + "Region": "us-east-1", + "UseDualStack": false, + "UseFIPS": false, + "Endpoint": "https://example.com" } }, { - "documentation": "For custom endpoint with fips disabled and dualstack disabled", + "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, - "Region": "us-east-1", + "UseFIPS": false, "Endpoint": "https://example.com" } }, @@ -4850,9 +4909,9 @@ "error": "Invalid Configuration: FIPS and custom endpoint are not supported" }, "params": { - "UseFIPS": true, - "UseDualStack": false, "Region": "us-east-1", + "UseDualStack": false, + "UseFIPS": true, "Endpoint": "https://example.com" } }, @@ -4862,9 +4921,9 @@ "error": "Invalid Configuration: Dualstack and custom endpoint are not supported" }, "params": { - "UseFIPS": false, - "UseDualStack": true, "Region": "us-east-1", + "UseDualStack": true, + "UseFIPS": false, "Endpoint": "https://example.com" } } @@ -5648,6 +5707,78 @@ } } }, + "com.amazonaws.ec2#AssignPrivateNatGatewayAddress": { + "type": "operation", + "input": { + "target": "com.amazonaws.ec2#AssignPrivateNatGatewayAddressRequest" + }, + "output": { + "target": "com.amazonaws.ec2#AssignPrivateNatGatewayAddressResult" + }, + "traits": { + "smithy.api#documentation": "

Assigns one or more private IPv4 addresses to a private NAT gateway. For more information, see Work with NAT gateways in the Amazon Virtual Private Cloud User Guide.

" + } + }, + "com.amazonaws.ec2#AssignPrivateNatGatewayAddressRequest": { + "type": "structure", + "members": { + "NatGatewayId": { + "target": "com.amazonaws.ec2#NatGatewayId", + "traits": { + "smithy.api#clientOptional": {}, + "smithy.api#documentation": "

The NAT gateway ID.

", + "smithy.api#required": {} + } + }, + "PrivateIpAddresses": { + "target": "com.amazonaws.ec2#IpList", + "traits": { + "smithy.api#documentation": "

The private IPv4 addresses you want to assign to the private NAT gateway.

", + "smithy.api#xmlName": "PrivateIpAddress" + } + }, + "PrivateIpAddressCount": { + "target": "com.amazonaws.ec2#PrivateIpAddressCount", + "traits": { + "smithy.api#clientOptional": {}, + "smithy.api#default": 0, + "smithy.api#documentation": "

The number of private IP addresses to assign to the NAT gateway. You can't specify this parameter when also specifying private IP addresses.

" + } + }, + "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.

" + } + } + }, + "traits": { + "smithy.api#input": {} + } + }, + "com.amazonaws.ec2#AssignPrivateNatGatewayAddressResult": { + "type": "structure", + "members": { + "NatGatewayId": { + "target": "com.amazonaws.ec2#NatGatewayId", + "traits": { + "aws.protocols#ec2QueryName": "NatGatewayId", + "smithy.api#documentation": "

The NAT gateway ID.

", + "smithy.api#xmlName": "natGatewayId" + } + }, + "NatGatewayAddresses": { + "target": "com.amazonaws.ec2#NatGatewayAddressList", + "traits": { + "aws.protocols#ec2QueryName": "NatGatewayAddressSet", + "smithy.api#documentation": "

NAT gateway IP addresses.

", + "smithy.api#xmlName": "natGatewayAddressSet" + } + } + } + }, "com.amazonaws.ec2#AssignedPrivateIpAddress": { "type": "structure", "members": { @@ -5701,7 +5832,7 @@ } }, "PublicIp": { - "target": "com.amazonaws.ec2#String", + "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.

" } @@ -5894,15 +6025,19 @@ "type": "structure", "members": { "CertificateArn": { - "target": "com.amazonaws.ec2#ResourceArn", + "target": "com.amazonaws.ec2#CertificateId", "traits": { - "smithy.api#documentation": "

The ARN of the ACM certificate with which to associate the IAM role.

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

The ARN of the ACM certificate with which to associate the IAM role.

", + "smithy.api#required": {} } }, "RoleArn": { - "target": "com.amazonaws.ec2#ResourceArn", + "target": "com.amazonaws.ec2#RoleId", "traits": { - "smithy.api#documentation": "

The ARN of the IAM role to associate with the ACM certificate. You can associate up to 16 IAM roles with an ACM \n\t\t\tcertificate.

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

The ARN of the IAM role to associate with the ACM certificate. You can associate up to 16 IAM roles with an ACM \n\t\t\tcertificate.

", + "smithy.api#required": {} } }, "DryRun": { @@ -6062,7 +6197,7 @@ "target": "com.amazonaws.ec2#AssociateIpamResourceDiscoveryResult" }, "traits": { - "smithy.api#documentation": "

Associates an IPAM resource discovery with an Amazon VPC IPAM. A resource discovery is an IPAM component that enables IPAM Service to manage and monitor resources that belong to the owning account.

" + "smithy.api#documentation": "

Associates an IPAM resource discovery with an Amazon VPC IPAM. A resource discovery is an IPAM component that enables IPAM to manage and monitor resources that belong to the owning account.

" } }, "com.amazonaws.ec2#AssociateIpamResourceDiscoveryRequest": { @@ -6124,6 +6259,79 @@ } } }, + "com.amazonaws.ec2#AssociateNatGatewayAddress": { + "type": "operation", + "input": { + "target": "com.amazonaws.ec2#AssociateNatGatewayAddressRequest" + }, + "output": { + "target": "com.amazonaws.ec2#AssociateNatGatewayAddressResult" + }, + "traits": { + "smithy.api#documentation": "

Associates Elastic IP addresses (EIPs) and private IPv4 addresses with a public NAT gateway. For more information, see Work with NAT gateways in the Amazon Virtual Private Cloud User Guide.

\n

By default, you can associate up to 2 Elastic IP addresses per public NAT gateway. You can increase the limit by requesting a quota adjustment. For more information, see Elastic IP address quotas in the Amazon Virtual Private Cloud User Guide.

" + } + }, + "com.amazonaws.ec2#AssociateNatGatewayAddressRequest": { + "type": "structure", + "members": { + "NatGatewayId": { + "target": "com.amazonaws.ec2#NatGatewayId", + "traits": { + "smithy.api#clientOptional": {}, + "smithy.api#documentation": "

The NAT gateway ID.

", + "smithy.api#required": {} + } + }, + "AllocationIds": { + "target": "com.amazonaws.ec2#AllocationIdList", + "traits": { + "smithy.api#clientOptional": {}, + "smithy.api#documentation": "

The allocation IDs of EIPs that you want to associate with your NAT gateway.

", + "smithy.api#required": {}, + "smithy.api#xmlName": "AllocationId" + } + }, + "PrivateIpAddresses": { + "target": "com.amazonaws.ec2#IpList", + "traits": { + "smithy.api#documentation": "

The private IPv4 addresses that you want to assign to the NAT gateway.

", + "smithy.api#xmlName": "PrivateIpAddress" + } + }, + "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.

" + } + } + }, + "traits": { + "smithy.api#input": {} + } + }, + "com.amazonaws.ec2#AssociateNatGatewayAddressResult": { + "type": "structure", + "members": { + "NatGatewayId": { + "target": "com.amazonaws.ec2#NatGatewayId", + "traits": { + "aws.protocols#ec2QueryName": "NatGatewayId", + "smithy.api#documentation": "

The NAT gateway ID.

", + "smithy.api#xmlName": "natGatewayId" + } + }, + "NatGatewayAddresses": { + "target": "com.amazonaws.ec2#NatGatewayAddressList", + "traits": { + "aws.protocols#ec2QueryName": "NatGatewayAddressSet", + "smithy.api#documentation": "

The IP addresses.

", + "smithy.api#xmlName": "natGatewayAddressSet" + } + } + } + }, "com.amazonaws.ec2#AssociateRouteTable": { "type": "operation", "input": { @@ -6278,19 +6486,25 @@ "TransitGatewayMulticastDomainId": { "target": "com.amazonaws.ec2#TransitGatewayMulticastDomainId", "traits": { - "smithy.api#documentation": "

The ID of the transit gateway multicast domain.

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

The ID of the transit gateway multicast domain.

", + "smithy.api#required": {} } }, "TransitGatewayAttachmentId": { "target": "com.amazonaws.ec2#TransitGatewayAttachmentId", "traits": { - "smithy.api#documentation": "

The ID of the transit gateway attachment to associate with the transit gateway multicast domain.

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

The ID of the transit gateway attachment to associate with the transit gateway multicast domain.

", + "smithy.api#required": {} } }, "SubnetIds": { "target": "com.amazonaws.ec2#TransitGatewaySubnetIdList", "traits": { - "smithy.api#documentation": "

The IDs of the subnets to associate with the transit gateway multicast domain.

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

The IDs of the subnets to associate with the transit gateway multicast domain.

", + "smithy.api#required": {} } }, "DryRun": { @@ -8330,6 +8544,12 @@ "traits": { "smithy.api#enumValue": "uefi" } + }, + "uefi_preferred": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "uefi-preferred" + } } } }, @@ -9001,7 +9221,7 @@ "target": "com.amazonaws.ec2#CancelImageLaunchPermissionResult" }, "traits": { - "smithy.api#documentation": "

Removes your Amazon Web Services account from the launch permissions for the specified AMI. For more\n information, see Cancel having an AMI shared with your Amazon Web Services account \n in the Amazon EC2 User Guide.

" + "smithy.api#documentation": "

Removes your Amazon Web Services account from the launch permissions for the specified AMI. For more\n information, see \n Cancel having an AMI shared with your Amazon Web Services account in the \n Amazon EC2 User Guide.

" } }, "com.amazonaws.ec2#CancelImageLaunchPermissionRequest": { @@ -9168,7 +9388,7 @@ "target": "com.amazonaws.ec2#CancelSpotFleetRequestsResponse" }, "traits": { - "smithy.api#documentation": "

Cancels the specified Spot Fleet requests.

\n

After you cancel a Spot Fleet request, the Spot Fleet launches no new Spot Instances.\n You must specify whether the Spot Fleet should also terminate its Spot Instances. If you\n terminate the instances, the Spot Fleet request enters the\n cancelled_terminating state. Otherwise, the Spot Fleet request enters\n the cancelled_running state and the instances continue to run until they\n are interrupted or you terminate them manually.

" + "smithy.api#documentation": "

Cancels the specified Spot Fleet requests.

\n

After you cancel a Spot Fleet request, the Spot Fleet launches no new instances.

\n

You must also specify whether a canceled Spot Fleet request should terminate its instances. If you\n choose to terminate the instances, the Spot Fleet request enters the\n cancelled_terminating state. Otherwise, the Spot Fleet request enters\n the cancelled_running state and the instances continue to run until they\n are interrupted or you terminate them manually.

" } }, "com.amazonaws.ec2#CancelSpotFleetRequestsError": { @@ -9237,7 +9457,7 @@ "aws.protocols#ec2QueryName": "DryRun", "smithy.api#clientOptional": {}, "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.

", + "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.

", "smithy.api#xmlName": "dryRun" } }, @@ -9257,7 +9477,7 @@ "aws.protocols#ec2QueryName": "TerminateInstances", "smithy.api#clientOptional": {}, "smithy.api#default": false, - "smithy.api#documentation": "

Indicates whether to terminate instances for a Spot Fleet request if it is canceled\n successfully.

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

Indicates whether to terminate the associated instances when the Spot Fleet request is canceled. \n The default is to terminate the instances.

\n

To let the instances continue to run after the Spot Fleet request is canceled, specify\n no-terminate-instances.

", "smithy.api#required": {}, "smithy.api#xmlName": "terminateInstances" } @@ -10408,6 +10628,9 @@ "smithy.api#documentation": "

Information about the client certificate to be used for authentication.

" } }, + "com.amazonaws.ec2#CertificateId": { + "type": "string" + }, "com.amazonaws.ec2#CidrAuthorizationContext": { "type": "structure", "members": { @@ -12409,7 +12632,7 @@ } }, "PresignedUrl": { - "target": "com.amazonaws.ec2#String", + "target": "com.amazonaws.ec2#CopySnapshotRequestPSU", "traits": { "aws.protocols#ec2QueryName": "PresignedUrl", "smithy.api#documentation": "

When you copy an encrypted source snapshot using the Amazon EC2 Query API, you must supply a\n pre-signed URL. This parameter is optional for unencrypted snapshots. For more information,\n see Query\n requests.

\n

The PresignedUrl should use the snapshot source endpoint, the\n CopySnapshot action, and include the SourceRegion,\n SourceSnapshotId, and DestinationRegion parameters. The\n PresignedUrl must be signed using Amazon Web Services Signature Version 4. Because EBS\n snapshots are stored in Amazon S3, the signing algorithm for this parameter uses the same logic\n that is described in Authenticating Requests: Using Query\n Parameters (Amazon Web Services Signature Version 4) in the Amazon Simple Storage Service API Reference. An\n invalid or improperly signed PresignedUrl will cause the copy operation to fail\n asynchronously, and the snapshot will move to an error state.

", @@ -12454,6 +12677,12 @@ "smithy.api#input": {} } }, + "com.amazonaws.ec2#CopySnapshotRequestPSU": { + "type": "string", + "traits": { + "smithy.api#sensitive": {} + } + }, "com.amazonaws.ec2#CopySnapshotResult": { "type": "structure", "members": { @@ -13359,8 +13588,7 @@ "traits": { "smithy.api#clientOptional": {}, "smithy.api#default": 0, - "smithy.api#documentation": "

For devices that support BGP, the customer gateway's BGP ASN.

\n

Default: 65000

", - "smithy.api#required": {} + "smithy.api#documentation": "

For devices that support BGP, the customer gateway's BGP ASN.

\n

Default: 65000

" } }, "PublicIp": { @@ -13812,7 +14040,7 @@ "ExcessCapacityTerminationPolicy": { "target": "com.amazonaws.ec2#FleetExcessCapacityTerminationPolicy", "traits": { - "smithy.api#documentation": "

Indicates whether running instances should be terminated if the total target capacity of\n the EC2 Fleet is decreased below the current size of the EC2 Fleet.

" + "smithy.api#documentation": "

Indicates whether running instances should be terminated if the total target capacity of\n the EC2 Fleet is decreased below the current size of the EC2 Fleet.

\n

Supported only for fleets of type maintain.

" } }, "LaunchTemplateConfigs": { @@ -14646,7 +14874,7 @@ "target": "com.amazonaws.ec2#CreateIpamResourceDiscoveryResult" }, "traits": { - "smithy.api#documentation": "

Creates an IPAM resource discovery. A resource discovery is an IPAM component that enables IPAM Service to manage and monitor resources that belong to the owning account.

" + "smithy.api#documentation": "

Creates an IPAM resource discovery. A resource discovery is an IPAM component that enables IPAM to manage and monitor resources that belong to the owning account.

" } }, "com.amazonaws.ec2#CreateIpamResourceDiscoveryRequest": { @@ -15041,9 +15269,7 @@ "DestinationCidrBlock": { "target": "com.amazonaws.ec2#String", "traits": { - "smithy.api#clientOptional": {}, - "smithy.api#documentation": "

The CIDR range used for destination matches. Routing decisions are based on \n the most specific match.

", - "smithy.api#required": {} + "smithy.api#documentation": "

The CIDR range used for destination matches. Routing decisions are based on \n the most specific match.

" } }, "LocalGatewayRouteTableId": { @@ -15073,6 +15299,12 @@ "traits": { "smithy.api#documentation": "

The ID of the network interface.

" } + }, + "DestinationPrefixListId": { + "target": "com.amazonaws.ec2#PrefixListResourceId", + "traits": { + "smithy.api#documentation": "

\n The ID of the prefix list. Use a prefix list in place of DestinationCidrBlock. You \n cannot use DestinationPrefixListId and DestinationCidrBlock in the same request.\n

" + } } }, "traits": { @@ -15409,7 +15641,7 @@ "target": "com.amazonaws.ec2#SubnetId", "traits": { "smithy.api#clientOptional": {}, - "smithy.api#documentation": "

The subnet in which to create the NAT gateway.

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

The ID of the subnet in which to create the NAT gateway.

", "smithy.api#required": {} } }, @@ -15431,6 +15663,28 @@ "traits": { "smithy.api#documentation": "

The private IPv4 address to assign to the NAT gateway. If you don't provide an address, a private IPv4 address will be automatically assigned.

" } + }, + "SecondaryAllocationIds": { + "target": "com.amazonaws.ec2#AllocationIdList", + "traits": { + "smithy.api#documentation": "

Secondary EIP allocation IDs. For more information about secondary addresses, see Create a NAT gateway in the Amazon Virtual Private Cloud User Guide.

", + "smithy.api#xmlName": "SecondaryAllocationId" + } + }, + "SecondaryPrivateIpAddresses": { + "target": "com.amazonaws.ec2#IpList", + "traits": { + "smithy.api#documentation": "

Secondary private IPv4 addresses. For more information about secondary addresses, see Create a NAT gateway in the Amazon Virtual Private Cloud User Guide.

", + "smithy.api#xmlName": "SecondaryPrivateIpAddress" + } + }, + "SecondaryPrivateIpAddressCount": { + "target": "com.amazonaws.ec2#PrivateIpAddressCount", + "traits": { + "smithy.api#clientOptional": {}, + "smithy.api#default": 0, + "smithy.api#documentation": "

[Private NAT gateway only] The number of secondary private IPv4 addresses you want to assign to the NAT gateway. For more information about secondary addresses, see Create a NAT gateway in the Amazon Virtual Private Cloud User Guide.

" + } } }, "traits": { @@ -19057,7 +19311,7 @@ "VolumeType": { "target": "com.amazonaws.ec2#VolumeType", "traits": { - "smithy.api#documentation": "

The volume type. This parameter can be one of the following values:

\n
    \n
  • \n

    General Purpose SSD: gp2 | gp3\n

    \n
  • \n
  • \n

    Provisioned IOPS SSD: io1 | io2\n

    \n
  • \n
  • \n

    Throughput Optimized HDD: st1\n

    \n
  • \n
  • \n

    Cold HDD: sc1\n

    \n
  • \n
  • \n

    Magnetic: standard\n

    \n
  • \n
\n

For more information, see Amazon EBS volume types in the\n Amazon Elastic Compute Cloud User Guide.

\n

Default: gp2\n

" + "smithy.api#documentation": "

The volume type. This parameter can be one of the following values:

\n
    \n
  • \n

    General Purpose SSD: gp2 | gp3\n

    \n
  • \n
  • \n

    Provisioned IOPS SSD: io1 | io2\n

    \n
  • \n
  • \n

    Throughput Optimized HDD: st1\n

    \n
  • \n
  • \n

    Cold HDD: sc1\n

    \n
  • \n
  • \n

    Magnetic: standard\n

    \n
  • \n
\n \n

Throughput Optimized HDD (st1) and Cold HDD (sc1) volumes can't be used as boot volumes.

\n
\n

For more information, see Amazon EBS volume types in the\n Amazon Elastic Compute Cloud User Guide.

\n

Default: gp2\n

" } }, "DryRun": { @@ -19473,7 +19727,9 @@ "target": "com.amazonaws.ec2#VpcId", "traits": { "aws.protocols#ec2QueryName": "VpcId", + "smithy.api#clientOptional": {}, "smithy.api#documentation": "

The ID of the requester VPC. You must specify this parameter in the\n\t\t\trequest.

", + "smithy.api#required": {}, "smithy.api#xmlName": "vpcId" } }, @@ -20718,7 +20974,7 @@ "target": "com.amazonaws.ec2#DeleteFleetsResult" }, "traits": { - "smithy.api#documentation": "

Deletes the specified EC2 Fleet.

\n

After you delete an EC2 Fleet, it launches no new instances.

\n

You must specify whether a deleted EC2 Fleet should also terminate its instances. If you\n choose to terminate the instances, the EC2 Fleet enters the deleted_terminating\n state. Otherwise, the EC2 Fleet enters the deleted_running state, and the instances\n continue to run until they are interrupted or you terminate them manually.

\n

For instant fleets, EC2 Fleet must terminate the instances when the fleet is\n deleted. A deleted instant fleet with running instances is not\n supported.

\n

\n Restrictions\n

\n
    \n
  • \n

    You can delete up to 25 instant fleets in a single request. If you exceed this\n number, no instant fleets are deleted and an error is returned. There is no\n restriction on the number of fleets of type maintain or request that can be deleted\n in a single request.

    \n
  • \n
  • \n

    Up to 1000 instances can be terminated in a single request to delete\n instant fleets.

    \n
  • \n
\n

For more information, see Delete an EC2\n Fleet in the Amazon EC2 User Guide.

" + "smithy.api#documentation": "

Deletes the specified EC2 Fleets.

\n

After you delete an EC2 Fleet, it launches no new instances.

\n

You must also specify whether a deleted EC2 Fleet should terminate its instances. If you\n choose to terminate the instances, the EC2 Fleet enters the deleted_terminating\n state. Otherwise, the EC2 Fleet enters the deleted_running state, and the instances\n continue to run until they are interrupted or you terminate them manually.

\n

For instant fleets, EC2 Fleet must terminate the instances when the fleet is\n deleted. A deleted instant fleet with running instances is not\n supported.

\n

\n Restrictions\n

\n
    \n
  • \n

    You can delete up to 25 instant fleets in a single request. If you exceed this\n number, no instant fleets are deleted and an error is returned. There is no\n restriction on the number of fleets of type maintain or request that can be deleted\n in a single request.

    \n
  • \n
  • \n

    Up to 1000 instances can be terminated in a single request to delete\n instant fleets.

    \n
  • \n
\n

For more information, see Delete an EC2\n Fleet in the Amazon EC2 User Guide.

" } }, "com.amazonaws.ec2#DeleteFleetsRequest": { @@ -20746,7 +21002,7 @@ "traits": { "smithy.api#clientOptional": {}, "smithy.api#default": false, - "smithy.api#documentation": "

Indicates whether to terminate the instances when the EC2 Fleet is deleted. The default is to\n terminate the instances.

\n

To let the instances continue to run after the EC2 Fleet is deleted, specify\n NoTerminateInstances. Supported only for fleets of type\n maintain and request.

\n

For instant fleets, you cannot specify NoTerminateInstances. A\n deleted instant fleet with running instances is not supported.

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

Indicates whether to terminate the associated instances when the EC2 Fleet is deleted. The default is to\n terminate the instances.

\n

To let the instances continue to run after the EC2 Fleet is deleted, specify\n no-terminate-instances. Supported only for fleets of type\n maintain and request.

\n

For instant fleets, you cannot specify NoTerminateInstances. A\n deleted instant fleet with running instances is not supported.

", "smithy.api#required": {} } } @@ -21077,7 +21333,7 @@ "target": "com.amazonaws.ec2#DeleteIpamResourceDiscoveryResult" }, "traits": { - "smithy.api#documentation": "

Deletes an IPAM resource discovery. A resource discovery is an IPAM component that enables IPAM Service to manage and monitor resources that belong to the owning account.

" + "smithy.api#documentation": "

Deletes an IPAM resource discovery. A resource discovery is an IPAM component that enables IPAM to manage and monitor resources that belong to the owning account.

" } }, "com.amazonaws.ec2#DeleteIpamResourceDiscoveryRequest": { @@ -21456,9 +21712,7 @@ "DestinationCidrBlock": { "target": "com.amazonaws.ec2#String", "traits": { - "smithy.api#clientOptional": {}, - "smithy.api#documentation": "

The CIDR range for the route. This must match the CIDR for the route exactly.

", - "smithy.api#required": {} + "smithy.api#documentation": "

The CIDR range for the route. This must match the CIDR for the route exactly.

" } }, "LocalGatewayRouteTableId": { @@ -21476,6 +21730,12 @@ "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.

" } + }, + "DestinationPrefixListId": { + "target": "com.amazonaws.ec2#PrefixListResourceId", + "traits": { + "smithy.api#documentation": "

\n Use a prefix list in place of DestinationCidrBlock. You cannot use \n DestinationPrefixListId and DestinationCidrBlock in the same request.\n

" + } } }, "traits": { @@ -22767,7 +23027,7 @@ "type": "structure", "members": { "TrafficMirrorFilterRuleId": { - "target": "com.amazonaws.ec2#TrafficMirrorFilterRuleId", + "target": "com.amazonaws.ec2#TrafficMirrorFilterRuleIdWithResolver", "traits": { "smithy.api#clientOptional": {}, "smithy.api#documentation": "

The ID of the Traffic Mirror rule.

", @@ -24212,7 +24472,7 @@ "target": "com.amazonaws.ec2#String", "traits": { "smithy.api#clientOptional": {}, - "smithy.api#documentation": "

The CIDR you want to deprovision from the pool.

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

The CIDR you want to deprovision from the pool. Enter the CIDR you want to deprovision with a netmask of /32. You must rerun this command for each IP address in the CIDR range. If your CIDR is a /24, you will have to run this command to deprovision each of the 256 IP addresses in the /24 CIDR.

", "smithy.api#required": {} } } @@ -24830,7 +25090,7 @@ "Filters": { "target": "com.amazonaws.ec2#FilterList", "traits": { - "smithy.api#documentation": "

The filters.

\n
    \n
  • \n

    \n group-name - For Availability Zones, use the Region name. For Local\n Zones, use the name of the group associated with the Local Zone (for example,\n us-west-2-lax-1) For Wavelength Zones, use the name of the group associated\n with the Wavelength Zone (for example, us-east-1-wl1-bos-wlz-1).

    \n
  • \n
  • \n

    \n message - The Zone message.

    \n
  • \n
  • \n

    \n opt-in-status - The opt-in status (opted-in, and\n not-opted-in | opt-in-not-required).

    \n
  • \n
  • \n

    \n parent-zoneID - The ID of the zone that handles some of the Local Zone\n and Wavelength Zone control plane operations, such as API calls.

    \n
  • \n
  • \n

    \n parent-zoneName - The ID of the zone that handles some of the Local Zone\n and Wavelength Zone control plane operations, such as API calls.

    \n
  • \n
  • \n

    \n region-name - The name of the Region for the Zone (for example,\n us-east-1).

    \n
  • \n
  • \n

    \n state - The state of the Availability Zone, the Local Zone, or the\n Wavelength Zone (available).

    \n
  • \n
  • \n

    \n zone-id - The ID of the Availability Zone (for example,\n use1-az1), the Local Zone (for example, usw2-lax1-az1), or the\n Wavelength Zone (for example, us-east-1-wl1-bos-wlz-1).

    \n
  • \n
  • \n

    \n zone-type - The type of zone, for example, local-zone.

    \n
  • \n
  • \n

    \n zone-name - The name of the Availability Zone (for example,\n us-east-1a), the Local Zone (for example, us-west-2-lax-1a), or\n the Wavelength Zone (for example, us-east-1-wl1-bos-wlz-1).

    \n
  • \n
  • \n

    \n zone-type - The type of zone, for example, local-zone.

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

The filters.

\n
    \n
  • \n

    \n group-name - For Availability Zones, use the Region name. For Local\n Zones, use the name of the group associated with the Local Zone (for example,\n us-west-2-lax-1) For Wavelength Zones, use the name of the group associated\n with the Wavelength Zone (for example, us-east-1-wl1-bos-wlz-1).

    \n
  • \n
  • \n

    \n message - The Zone message.

    \n
  • \n
  • \n

    \n opt-in-status - The opt-in status (opted-in |\n not-opted-in | opt-in-not-required).

    \n
  • \n
  • \n

    \n parent-zoneID - The ID of the zone that handles some of the Local Zone\n and Wavelength Zone control plane operations, such as API calls.

    \n
  • \n
  • \n

    \n parent-zoneName - The ID of the zone that handles some of the Local Zone\n and Wavelength Zone control plane operations, such as API calls.

    \n
  • \n
  • \n

    \n region-name - The name of the Region for the Zone (for example,\n us-east-1).

    \n
  • \n
  • \n

    \n state - The state of the Availability Zone, the Local Zone, or the\n Wavelength Zone (available).

    \n
  • \n
  • \n

    \n zone-id - The ID of the Availability Zone (for example,\n use1-az1), the Local Zone (for example, usw2-lax1-az1), or the\n Wavelength Zone (for example, us-east-1-wl1-bos-wlz-1).

    \n
  • \n
  • \n

    \n zone-name - The name of the Availability Zone (for example,\n us-east-1a), the Local Zone (for example, us-west-2-lax-1a), or\n the Wavelength Zone (for example, us-east-1-wl1-bos-wlz-1).

    \n
  • \n
  • \n

    \n zone-type - The type of zone (availability-zone | \n local-zone | wavelength-zone).

    \n
  • \n
", "smithy.api#xmlName": "Filter" } }, @@ -25454,7 +25714,7 @@ "aws.protocols#ec2QueryName": "MaxResults", "smithy.api#clientOptional": {}, "smithy.api#default": 0, - "smithy.api#documentation": "

The maximum number of results to return with a single call.\n\tTo retrieve the remaining results, make another call with the returned nextToken value.

\n

Constraint: If the value is greater than 1000, we return only 1000 items.

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

The maximum number of items to return for this request.\n\tTo get the next page of items, make another request with the token returned in the output.\n\tFor more information, see Pagination.

\n

Constraint: If the value is greater than 1000, we return only 1000 items.

", "smithy.api#xmlName": "maxResults" } }, @@ -25462,7 +25722,7 @@ "target": "com.amazonaws.ec2#String", "traits": { "aws.protocols#ec2QueryName": "NextToken", - "smithy.api#documentation": "

The token for the next page of results.

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

The token returned from a previous paginated request. Pagination continues from the end of the items returned by the previous request.

", "smithy.api#xmlName": "nextToken" } } @@ -25486,7 +25746,7 @@ "target": "com.amazonaws.ec2#String", "traits": { "aws.protocols#ec2QueryName": "NextToken", - "smithy.api#documentation": "

The token to use to retrieve the next page of results. This value is null when there are no more results to return.

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

The token to include in another request to get the next page of items. This value is null when there are no more items to return.

", "smithy.api#xmlName": "nextToken" } } @@ -26333,7 +26593,7 @@ "NextToken": { "target": "com.amazonaws.ec2#String", "traits": { - "smithy.api#documentation": "

The token for the next page of results.

" + "smithy.api#documentation": "

The token returned from a previous paginated request. Pagination continues from the end of the items returned by the previous request.

" } }, "MaxResults": { @@ -26341,7 +26601,7 @@ "traits": { "smithy.api#clientOptional": {}, "smithy.api#default": 0, - "smithy.api#documentation": "

The maximum number of results to return with a single call.\n\tTo retrieve the remaining results, make another call with the returned nextToken value.

" + "smithy.api#documentation": "

The maximum number of items to return for this request.\n\tTo get the next page of items, make another request with the token returned in the output.\n\tFor more information, see Pagination.

" } } }, @@ -26364,7 +26624,7 @@ "target": "com.amazonaws.ec2#String", "traits": { "aws.protocols#ec2QueryName": "NextToken", - "smithy.api#documentation": "

The token to use to retrieve the next page of results. This value is null when there are no more results to return.

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

The token to include in another request to get the next page of items. This value is null when there are no more items to return.

", "smithy.api#xmlName": "nextToken" } } @@ -26421,13 +26681,13 @@ "traits": { "smithy.api#clientOptional": {}, "smithy.api#default": 0, - "smithy.api#documentation": "

The maximum number of results to return with a single call.\n\tTo retrieve the remaining results, make another call with the returned nextToken value.

" + "smithy.api#documentation": "

The maximum number of items to return for this request.\n\tTo get the next page of items, make another request with the token returned in the output.\n\tFor more information, see Pagination.

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

The token for the next page of results.

" + "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": { @@ -26457,7 +26717,7 @@ "target": "com.amazonaws.ec2#String", "traits": { "aws.protocols#ec2QueryName": "NextToken", - "smithy.api#documentation": "

The token to use to retrieve the next page of results. This value is null when there are no more results to return.

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

The token to include in another request to get the next page of items. This value is null when there are no more items to return.

", "smithy.api#xmlName": "nextToken" } } @@ -26771,13 +27031,13 @@ "MaxResults": { "target": "com.amazonaws.ec2#DescribeFastLaunchImagesRequestMaxResults", "traits": { - "smithy.api#documentation": "

The maximum number of results to return in a single call. To retrieve the remaining results, \n\t\t\tmake another request with the returned NextToken value. If this parameter is not specified, \n\t\t\tthen all results are returned.

" + "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 for the next set of results.

" + "smithy.api#documentation": "

The token returned from a previous paginated request. Pagination continues from the end of the items returned by the previous request.

" } }, "DryRun": { @@ -26817,7 +27077,7 @@ "target": "com.amazonaws.ec2#NextToken", "traits": { "aws.protocols#ec2QueryName": "NextToken", - "smithy.api#documentation": "

The token to use for the next set of results. This value is null when there are \n\t\t\tno more results to return.

", + "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" } } @@ -26864,7 +27124,7 @@ "aws.protocols#ec2QueryName": "MaxParallelLaunches", "smithy.api#clientOptional": {}, "smithy.api#default": 0, - "smithy.api#documentation": "

The maximum number of parallel instances that are launched for creating resources.

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

The maximum number of instances that Amazon EC2 can launch at the same time to create \n\t\t\tpre-provisioned snapshots for Windows faster launching.

", "smithy.api#xmlName": "maxParallelLaunches" } }, @@ -27059,13 +27319,13 @@ "MaxResults": { "target": "com.amazonaws.ec2#DescribeFastSnapshotRestoresMaxResults", "traits": { - "smithy.api#documentation": "

The maximum number of results to return with a single call.\n\tTo retrieve the remaining results, make another call with the returned nextToken value.

" + "smithy.api#documentation": "

The maximum number of items to return for this request.\n\tTo get the next page of items, make another request with the token returned in the output. \n\tFor more information, see Pagination.

" } }, "NextToken": { "target": "com.amazonaws.ec2#NextToken", "traits": { - "smithy.api#documentation": "

The token for the next page of results.

" + "smithy.api#documentation": "

The token returned from a previous paginated request.\n Pagination continues from the end of the items returned by the previous request.

" } }, "DryRun": { @@ -27096,7 +27356,7 @@ "target": "com.amazonaws.ec2#NextToken", "traits": { "aws.protocols#ec2QueryName": "NextToken", - "smithy.api#documentation": "

The token to use to retrieve the next page of results. This value is null when there are no more results to return.

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

The token to include in another request to get the next page of items. \n This value is null when there are no more items to return.

", "smithy.api#xmlName": "nextToken" } } @@ -27176,13 +27436,13 @@ "traits": { "smithy.api#clientOptional": {}, "smithy.api#default": 0, - "smithy.api#documentation": "

The maximum number of results to return in a single call. Specify a value between 1 and\n 1000. The default value is 1000. To retrieve the remaining results, make another call with\n the returned NextToken value.

" + "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#String", "traits": { - "smithy.api#documentation": "

The token for the next set of results.

" + "smithy.api#documentation": "

The token returned from a previous paginated request. Pagination continues from the end of the items returned by the previous request.

" } }, "FleetId": { @@ -27221,7 +27481,7 @@ "target": "com.amazonaws.ec2#DateTime", "traits": { "aws.protocols#ec2QueryName": "LastEvaluatedTime", - "smithy.api#documentation": "

The last date and time for the events, in UTC format (for example,\n YYYY-MM-DDTHH:MM:SSZ).\n All records up to this time were retrieved.

\n

If nextToken indicates that there are more results, this value is not\n present.

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

The last date and time for the events, in UTC format (for example,\n YYYY-MM-DDTHH:MM:SSZ).\n All records up to this time were retrieved.

\n

If nextToken indicates that there are more items, this value is not\n present.

", "smithy.api#xmlName": "lastEvaluatedTime" } }, @@ -27229,7 +27489,7 @@ "target": "com.amazonaws.ec2#String", "traits": { "aws.protocols#ec2QueryName": "NextToken", - "smithy.api#documentation": "

The token for the next set of results.

", + "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" } }, @@ -27279,13 +27539,13 @@ "traits": { "smithy.api#clientOptional": {}, "smithy.api#default": 0, - "smithy.api#documentation": "

The maximum number of results to return in a single call. Specify a value between 1 and\n 1000. The default value is 1000. To retrieve the remaining results, make another call with\n the returned NextToken value.

" + "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#String", "traits": { - "smithy.api#documentation": "

The token for the next set of results.

" + "smithy.api#documentation": "

The token returned from a previous paginated request. Pagination continues from the end of the items returned by the previous request.

" } }, "FleetId": { @@ -27323,7 +27583,7 @@ "target": "com.amazonaws.ec2#String", "traits": { "aws.protocols#ec2QueryName": "NextToken", - "smithy.api#documentation": "

The token for the next set of results.

", + "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" } }, @@ -27437,13 +27697,13 @@ "traits": { "smithy.api#clientOptional": {}, "smithy.api#default": 0, - "smithy.api#documentation": "

The maximum number of results to return in a single call. Specify a value between 1 and\n 1000. The default value is 1000. To retrieve the remaining results, make another call with\n the returned NextToken value.

" + "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#String", "traits": { - "smithy.api#documentation": "

The token for the next set of results.

" + "smithy.api#documentation": "

The token returned from a previous paginated request. Pagination continues from the end of the items returned by the previous request.

" } }, "FleetIds": { @@ -27472,7 +27732,7 @@ "target": "com.amazonaws.ec2#String", "traits": { "aws.protocols#ec2QueryName": "NextToken", - "smithy.api#documentation": "

The token for the next set of results.

", + "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" } }, @@ -27533,13 +27793,13 @@ "traits": { "smithy.api#clientOptional": {}, "smithy.api#default": 0, - "smithy.api#documentation": "

The maximum number of results to return with a single call.\n\tTo retrieve the remaining results, make another call with the returned nextToken value.

" + "smithy.api#documentation": "

The maximum number of items to return for this request.\n\tTo get the next page of items, make another request with the token returned in the output.\n\tFor more information, see Pagination.

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

The token for the next page of results.

" + "smithy.api#documentation": "

The token to request the next page of items. Pagination continues from the end of the items returned by the previous request.

" } } }, @@ -27562,7 +27822,7 @@ "target": "com.amazonaws.ec2#String", "traits": { "aws.protocols#ec2QueryName": "NextToken", - "smithy.api#documentation": "

The token to use to retrieve the next page of results. This value is null when there are no more results to return.

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

The token to request the next page of items. This value is null when there are no more items to return.

", "smithy.api#xmlName": "nextToken" } } @@ -28028,13 +28288,13 @@ "traits": { "smithy.api#clientOptional": {}, "smithy.api#default": 0, - "smithy.api#documentation": "

The maximum number of results to return in a single call. To retrieve the remaining\n results, make another call with the returned NextToken value.

" + "smithy.api#documentation": "

The maximum number of items to return for this request. To get the next page of\n items, make another request with the token returned in the output. For more information, \n see Pagination.

" } }, "NextToken": { "target": "com.amazonaws.ec2#NextToken", "traits": { - "smithy.api#documentation": "

The token to request the next page of results.

" + "smithy.api#documentation": "

The token returned from a previous paginated request.\n Pagination continues from the end of the items returned by the previous request.

" } } }, @@ -28057,7 +28317,7 @@ "target": "com.amazonaws.ec2#NextToken", "traits": { "aws.protocols#ec2QueryName": "NextToken", - "smithy.api#documentation": "

The token to use to retrieve the next page of results. This value is null when there are no more results to return.

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

The token to include in another request to get the next page of items. \n This value is null when there are no more items to return.

", "smithy.api#xmlName": "nextToken" } } @@ -28323,13 +28583,13 @@ "traits": { "smithy.api#clientOptional": {}, "smithy.api#default": 0, - "smithy.api#documentation": "

The maximum number of results to return with a single call.\n\tTo retrieve the remaining results, make another call with the returned nextToken value.

" + "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#String", "traits": { - "smithy.api#documentation": "

The token for the next page of results.

" + "smithy.api#documentation": "

The token returned from a previous paginated request. Pagination continues from the end of the items returned by the previous request.

" } } }, @@ -28352,7 +28612,7 @@ "target": "com.amazonaws.ec2#String", "traits": { "aws.protocols#ec2QueryName": "NextToken", - "smithy.api#documentation": "

The token to use to retrieve the next page of results. This value is null when there are no more results to return.

", + "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" } } @@ -28456,6 +28716,33 @@ "outputToken": "NextToken", "items": "ImportSnapshotTasks", "pageSize": "MaxResults" + }, + "smithy.waiters#waitable": { + "SnapshotImported": { + "acceptors": [ + { + "state": "success", + "matcher": { + "output": { + "path": "ImportSnapshotTasks[].SnapshotTaskDetail.Status", + "expected": "completed", + "comparator": "allStringEquals" + } + } + }, + { + "state": "failure", + "matcher": { + "output": { + "path": "ImportSnapshotTasks[].SnapshotTaskDetail.Status", + "expected": "error", + "comparator": "anyStringEquals" + } + } + } + ], + "minDelay": 15 + } } } }, @@ -28632,13 +28919,13 @@ "traits": { "smithy.api#clientOptional": {}, "smithy.api#default": 0, - "smithy.api#documentation": "

The maximum number of results to return in a single call. To retrieve the remaining\n results, make another call with the returned NextToken value. This value\n can be between 5 and 1000. You cannot specify this parameter and the instance IDs\n parameter in the same call.

" + "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.

\n

You cannot specify this parameter and the instance IDs\n parameter in the same call.

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

The token to retrieve the next page of results.

" + "smithy.api#documentation": "

The token returned from a previous paginated request. Pagination continues from the end of the items returned by the previous request.

" } } }, @@ -28661,7 +28948,7 @@ "target": "com.amazonaws.ec2#String", "traits": { "aws.protocols#ec2QueryName": "NextToken", - "smithy.api#documentation": "

The token to use to retrieve the next page of results. This value is null\n when there are no more results to return.

", + "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" } } @@ -28873,13 +29160,13 @@ "traits": { "smithy.api#clientOptional": {}, "smithy.api#default": 0, - "smithy.api#documentation": "

The maximum number of results to return in a single call. To retrieve the remaining\n results, make another call with the returned NextToken value. This value\n can be between 5 and 1000. You cannot specify this parameter and the instance IDs\n parameter in the same call.

" + "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.

\n

You cannot specify this parameter and the instance IDs parameter in the same request.

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

The token to retrieve the next page of results.

" + "smithy.api#documentation": "

The token returned from a previous paginated request. Pagination continues from the end of the items returned by the previous request.

" } }, "DryRun": { @@ -28922,7 +29209,7 @@ "target": "com.amazonaws.ec2#String", "traits": { "aws.protocols#ec2QueryName": "NextToken", - "smithy.api#documentation": "

The token to use to retrieve the next page of results. This value is null\n when there are no more results to return.

", + "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" } } @@ -28973,13 +29260,13 @@ "MaxResults": { "target": "com.amazonaws.ec2#DITOMaxResults", "traits": { - "smithy.api#documentation": "

The maximum number of results to return for the request in a single page. The remaining results\n can be seen by sending another request with the next token value.

" + "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 to retrieve the next page of results.

" + "smithy.api#documentation": "

The token returned from a previous paginated request. Pagination continues from the end of the items returned by the previous request.

" } } }, @@ -29002,7 +29289,7 @@ "target": "com.amazonaws.ec2#NextToken", "traits": { "aws.protocols#ec2QueryName": "NextToken", - "smithy.api#documentation": "

The token to use to retrieve the next page of results. This value is null when there\n are no more results to return.

", + "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" } } @@ -29054,13 +29341,13 @@ "MaxResults": { "target": "com.amazonaws.ec2#DITMaxResults", "traits": { - "smithy.api#documentation": "

The maximum number of results to return for the request in a single page. The remaining results\n can be seen by sending another request with the next token value.

" + "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 to retrieve the next page of results.

" + "smithy.api#documentation": "

The token returned from a previous paginated request. Pagination continues from the end of the items returned by the previous request.

" } } }, @@ -29083,7 +29370,7 @@ "target": "com.amazonaws.ec2#NextToken", "traits": { "aws.protocols#ec2QueryName": "NextToken", - "smithy.api#documentation": "

The token to use to retrieve the next page of results. This value is null when there\n are no more results to return.

", + "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" } } @@ -29287,7 +29574,7 @@ "aws.protocols#ec2QueryName": "MaxResults", "smithy.api#clientOptional": {}, "smithy.api#default": 0, - "smithy.api#documentation": "

The maximum number of results to return in a single call. To retrieve the remaining\n results, make another call with the returned NextToken value. This value\n can be between 5 and 1000. You cannot specify this parameter and the instance IDs\n parameter in the same call.

", + "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.

\n

You cannot specify this parameter and the instance IDs parameter in the same request.

", "smithy.api#xmlName": "maxResults" } }, @@ -29295,7 +29582,7 @@ "target": "com.amazonaws.ec2#String", "traits": { "aws.protocols#ec2QueryName": "NextToken", - "smithy.api#documentation": "

The token to request the next page of results.

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

The token returned from a previous paginated request. Pagination continues from the end of the items returned by the previous request.

", "smithy.api#xmlName": "nextToken" } } @@ -29319,7 +29606,7 @@ "target": "com.amazonaws.ec2#String", "traits": { "aws.protocols#ec2QueryName": "NextToken", - "smithy.api#documentation": "

The token to use to retrieve the next page of results. This value is null\n when there are no more results to return.

", + "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" } } @@ -29410,7 +29697,7 @@ "NextToken": { "target": "com.amazonaws.ec2#String", "traits": { - "smithy.api#documentation": "

The token for the next page of results.

" + "smithy.api#documentation": "

The token returned from a previous paginated request. Pagination continues from the end of the items returned by the previous request.

" } }, "MaxResults": { @@ -29418,7 +29705,7 @@ "traits": { "smithy.api#clientOptional": {}, "smithy.api#default": 0, - "smithy.api#documentation": "

The maximum number of results to return with a single call.\n\tTo retrieve the remaining results, make another call with the returned nextToken value.

" + "smithy.api#documentation": "

The maximum number of items to return for this request.\n\tTo get the next page of items, make another request with the token returned in the output.\n\tFor more information, see Pagination.

" } } }, @@ -29441,7 +29728,7 @@ "target": "com.amazonaws.ec2#String", "traits": { "aws.protocols#ec2QueryName": "NextToken", - "smithy.api#documentation": "

The token to use to retrieve the next page of results. This value is null when there are no more results to return.

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

The token to include in another request to get the next page of items. This value is null when there are no more items to return.

", "smithy.api#xmlName": "nextToken" } } @@ -29539,7 +29826,7 @@ "target": "com.amazonaws.ec2#DescribeIpamResourceDiscoveriesResult" }, "traits": { - "smithy.api#documentation": "

Describes IPAM resource discoveries. A resource discovery is an IPAM component that enables IPAM Service to manage and monitor resources that belong to the owning account.

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

Describes IPAM resource discoveries. A resource discovery is an IPAM component that enables IPAM to manage and monitor resources that belong to the owning account.

", "smithy.api#paginated": { "inputToken": "NextToken", "outputToken": "NextToken", @@ -31067,7 +31354,7 @@ "traits": { "smithy.api#clientOptional": {}, "smithy.api#default": 0, - "smithy.api#documentation": "

The maximum number of results to return with a single call.\n\tTo retrieve the remaining results, make another call with the returned nextToken value.

" + "smithy.api#documentation": "

The maximum number of items to return for this request.\n\tTo get the next page of items, make another request with the token returned in the output.\n\tFor more information, see Pagination.

" } }, "NatGatewayIds": { @@ -31080,7 +31367,7 @@ "NextToken": { "target": "com.amazonaws.ec2#String", "traits": { - "smithy.api#documentation": "

The token for the next page of results.

" + "smithy.api#documentation": "

The token returned from a previous paginated request. Pagination continues from the end of the items returned by the previous request.

" } } }, @@ -31103,7 +31390,7 @@ "target": "com.amazonaws.ec2#String", "traits": { "aws.protocols#ec2QueryName": "NextToken", - "smithy.api#documentation": "

The token to use to retrieve the next page of results. This value is null when there are no more results to return.

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

The token to include in another request to get the next page of items. This value is null when there are no more items to return.

", "smithy.api#xmlName": "nextToken" } } @@ -31167,7 +31454,7 @@ "NextToken": { "target": "com.amazonaws.ec2#String", "traits": { - "smithy.api#documentation": "

The token for the next page of results.

" + "smithy.api#documentation": "

The token returned from a previous paginated request. Pagination continues from the end of the items returned by the previous request.

" } }, "MaxResults": { @@ -31175,7 +31462,7 @@ "traits": { "smithy.api#clientOptional": {}, "smithy.api#default": 0, - "smithy.api#documentation": "

The maximum number of results to return with a single call.\n\tTo retrieve the remaining results, make another call with the returned nextToken value.

" + "smithy.api#documentation": "

The maximum number of items to return for this request.\n\tTo get the next page of items, make another request with the token returned in the output.\n\tFor more information, see Pagination.

" } } }, @@ -31198,7 +31485,7 @@ "target": "com.amazonaws.ec2#String", "traits": { "aws.protocols#ec2QueryName": "NextToken", - "smithy.api#documentation": "

The token to use to retrieve the next page of results. This value is null when there are no more results to return.

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

The token to include in another request to get the next page of items. This value is null when there are no more items to return.

", "smithy.api#xmlName": "nextToken" } } @@ -31717,7 +32004,7 @@ "NextToken": { "target": "com.amazonaws.ec2#String", "traits": { - "smithy.api#documentation": "

The token to request the next page of results.

" + "smithy.api#documentation": "

The token returned from a previous paginated request.\n\t\t Pagination continues from the end of the items returned by the previous request.

" } }, "MaxResults": { @@ -31725,7 +32012,7 @@ "traits": { "smithy.api#clientOptional": {}, "smithy.api#default": 0, - "smithy.api#documentation": "

The maximum number of results to return in a single call. To retrieve the remaining results,\n\t\t\tmake another call with the returned NextToken value. If this parameter is not specified, up to 50 results are returned by default.

" + "smithy.api#documentation": "

The maximum number of items to return for this request. To get the next page of items,\n\t\t\tmake another request with the token returned in the output. If this parameter is not specified, \n\t\t\tup to 50 results are returned by default. For more information, see\n\t\t\tPagination.

" } } }, @@ -31749,7 +32036,7 @@ "target": "com.amazonaws.ec2#String", "traits": { "aws.protocols#ec2QueryName": "NextToken", - "smithy.api#documentation": "

The token to use to retrieve the next page of results.

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

The token to include in another request to get the next page of items.\n\t\t This value is null when there are no more items to return.

", "smithy.api#xmlName": "nextToken" } } @@ -31843,7 +32130,7 @@ "NextToken": { "target": "com.amazonaws.ec2#String", "traits": { - "smithy.api#documentation": "

The token to retrieve the next page of results.

" + "smithy.api#documentation": "

The token returned from a previous paginated request.\n\t\t Pagination continues from the end of the items returned by the previous request.

" } }, "MaxResults": { @@ -31851,7 +32138,7 @@ "traits": { "smithy.api#clientOptional": {}, "smithy.api#default": 0, - "smithy.api#documentation": "

The maximum number of items to return for this request. The request returns a token that you\n can specify in a subsequent call to get the next set of results. You cannot specify this\n parameter and the network interface IDs parameter in the same request.

" + "smithy.api#documentation": "

The maximum number of items to return for this request. To get the next page of items,\n\t\t make another request with the token returned in the output. You cannot specify this\n\t\t parameter and the network interface IDs parameter in the same request. For more information, \n\t\t see Pagination.

" } } }, @@ -31875,13 +32162,10 @@ "target": "com.amazonaws.ec2#String", "traits": { "aws.protocols#ec2QueryName": "NextToken", - "smithy.api#documentation": "

The token to use to retrieve the next page of results. This value is null when there are no more results to return.

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

The token to include in another request to get the next page of items. \n\t\t This value is null when there are no more items to return.

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

Contains the output of DescribeNetworkInterfaces.

" } }, "com.amazonaws.ec2#DescribePlacementGroups": { @@ -32308,13 +32592,13 @@ "traits": { "smithy.api#clientOptional": {}, "smithy.api#default": 0, - "smithy.api#documentation": "

The maximum number of results to return with a single call.\n\tTo retrieve the remaining results, make another call with the returned nextToken value.

" + "smithy.api#documentation": "

The maximum number of items to return for this request.\n\tTo get the next page of items, make another request with the token returned in the output. \n\tFor more information, see Pagination.

" } }, "NextToken": { "target": "com.amazonaws.ec2#NextToken", "traits": { - "smithy.api#documentation": "

The token for the next page of results.

" + "smithy.api#documentation": "

The token returned from a previous paginated request.\n Pagination continues from the end of the items returned by the previous request.

" } }, "DryRun": { @@ -32345,7 +32629,7 @@ "target": "com.amazonaws.ec2#String", "traits": { "aws.protocols#ec2QueryName": "NextToken", - "smithy.api#documentation": "

The token to use to retrieve the next page of results. This value is null when there are no more results to return.

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

The token to include in another request to get the next page of items. \n This value is null when there are no more items to return.

", "smithy.api#xmlName": "nextToken" } } @@ -32781,7 +33065,7 @@ "NextToken": { "target": "com.amazonaws.ec2#String", "traits": { - "smithy.api#documentation": "

The token for the next page of results.

" + "smithy.api#documentation": "

The token returned from a previous paginated request. Pagination continues from the end of the items returned by the previous request.

" } }, "MaxResults": { @@ -32789,7 +33073,7 @@ "traits": { "smithy.api#clientOptional": {}, "smithy.api#default": 0, - "smithy.api#documentation": "

The maximum number of results to return with a single call.\n\tTo retrieve the remaining results, make another call with the returned nextToken value.

" + "smithy.api#documentation": "

The maximum number of items to return for this request.\n\tTo get the next page of items, make another request with the token returned in the output.\n\tFor more information, see Pagination.

" } } }, @@ -32812,7 +33096,7 @@ "target": "com.amazonaws.ec2#String", "traits": { "aws.protocols#ec2QueryName": "NextToken", - "smithy.api#documentation": "

The token to use to retrieve the next page of results. This value is null when there are no more results to return.

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

The token to include in another request to get the next page of items. This value is null when there are no more items to return.

", "smithy.api#xmlName": "nextToken" } } @@ -33141,7 +33425,7 @@ "NextToken": { "target": "com.amazonaws.ec2#String", "traits": { - "smithy.api#documentation": "

The token for the next page of results.

" + "smithy.api#documentation": "

The token returned from a previous paginated request.\n Pagination continues from the end of the items returned by the previous request.

" } }, "MaxResults": { @@ -33149,7 +33433,7 @@ "traits": { "smithy.api#clientOptional": {}, "smithy.api#default": 0, - "smithy.api#documentation": "

The maximum number of results to return in a single call. To retrieve the remaining\n results, make another request with the returned NextToken value. This value\n can be between 5 and 1000. If this parameter is not specified, then all results are\n returned.

" + "smithy.api#documentation": "

The maximum number of items to return for this request. To get the next page of \n items, make another request with the token returned in the output. This value\n can be between 5 and 1000. If this parameter is not specified, then all items are\n returned. For more information, see Pagination.

" } } }, @@ -33172,7 +33456,7 @@ "target": "com.amazonaws.ec2#String", "traits": { "aws.protocols#ec2QueryName": "NextToken", - "smithy.api#documentation": "

The token to use to retrieve the next page of results. This value is null when there are no more results to return.

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

The token to include in another request to get the next page of items. \n This value is null when there are no more items to return.

", "smithy.api#xmlName": "nextToken" } } @@ -33269,7 +33553,7 @@ "NextToken": { "target": "com.amazonaws.ec2#String", "traits": { - "smithy.api#documentation": "

The token to request the next page of results.

" + "smithy.api#documentation": "

The token returned from a previous paginated request.\n Pagination continues from the end of the items returned by the previous request.

" } }, "MaxResults": { @@ -33277,7 +33561,7 @@ "traits": { "smithy.api#clientOptional": {}, "smithy.api#default": 0, - "smithy.api#documentation": "

The maximum number of results to return in a single call. To retrieve the remaining\n results, make another request with the returned NextToken value. This value\n can be between 5 and 1000. If this parameter is not specified, then all results are\n returned.

" + "smithy.api#documentation": "

The maximum number of items to return for this request. To get the next page of items,\n make another request with the token returned in the output. This value can be between 5 and 1000. \n If this parameter is not specified, then all items are returned. For more information, see \n Pagination.

" } } }, @@ -33300,7 +33584,7 @@ "target": "com.amazonaws.ec2#String", "traits": { "aws.protocols#ec2QueryName": "NextToken", - "smithy.api#documentation": "

The token to use to retrieve the next page of results. This value is null when there are no more results to return.

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

The token to include in another request to get the next page of items. \n This value is null when there are no more items to return.

", "smithy.api#xmlName": "nextToken" } } @@ -33423,13 +33707,13 @@ "NextToken": { "target": "com.amazonaws.ec2#String", "traits": { - "smithy.api#documentation": "

The token for the next page of results.

" + "smithy.api#documentation": "

The token returned from a previous paginated request.\n Pagination continues from the end of the items returned by the previous request.

" } }, "MaxResults": { "target": "com.amazonaws.ec2#DescribeSnapshotTierStatusMaxResults", "traits": { - "smithy.api#documentation": "

The maximum number of results to return with a single call.\n\tTo retrieve the remaining results, make another call with the returned nextToken value.

" + "smithy.api#documentation": "

The maximum number of items to return for this request.\n\tTo get the next page of items, make another request with the token returned in the output. \n\tFor more information, see Pagination.

" } } }, @@ -33452,7 +33736,7 @@ "target": "com.amazonaws.ec2#String", "traits": { "aws.protocols#ec2QueryName": "NextToken", - "smithy.api#documentation": "

The token to use to retrieve the next page of results. This value is null when there are no more results to return.

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

The token to include in another request to get the next page of items. \n This value is null when there are no more items to return.

", "smithy.api#xmlName": "nextToken" } } @@ -33467,7 +33751,7 @@ "target": "com.amazonaws.ec2#DescribeSnapshotsResult" }, "traits": { - "smithy.api#documentation": "

Describes the specified EBS snapshots available to you or all of the EBS snapshots\n available to you.

\n

The snapshots available to you include public snapshots, private snapshots that you own,\n and private snapshots owned by other Amazon Web Services accounts for which you have explicit create volume\n permissions.

\n

The create volume permissions fall into the following categories:

\n
    \n
  • \n

    \n public: The owner of the snapshot granted create volume\n permissions for the snapshot to the all group. All Amazon Web Services accounts have create\n volume permissions for these snapshots.

    \n
  • \n
  • \n

    \n explicit: The owner of the snapshot granted create volume\n permissions to a specific Amazon Web Services account.

    \n
  • \n
  • \n

    \n implicit: An Amazon Web Services account has implicit create volume permissions\n for all snapshots it owns.

    \n
  • \n
\n

The list of snapshots returned can be filtered by specifying snapshot IDs, snapshot\n owners, or Amazon Web Services accounts with create volume permissions. If no options are specified, \n Amazon EC2 returns all snapshots for which you have create volume permissions.

\n

If you specify one or more snapshot IDs, only snapshots that have the specified IDs are\n returned. If you specify an invalid snapshot ID, an error is returned. If you specify a\n snapshot ID for which you do not have access, it is not included in the returned\n results.

\n

If you specify one or more snapshot owners using the OwnerIds option, only\n snapshots from the specified owners and for which you have access are returned. The results\n can include the Amazon Web Services account IDs of the specified owners, amazon for snapshots\n owned by Amazon, or self for snapshots that you own.

\n

If you specify a list of restorable users, only snapshots with create snapshot permissions\n for those users are returned. You can specify Amazon Web Services account IDs (if you own the snapshots),\n self for snapshots for which you own or have explicit permissions, or\n all for public snapshots.

\n

If you are describing a long list of snapshots, we recommend that you paginate the output to make the\n list more manageable. The MaxResults parameter sets the maximum number of results\n returned in a single page. If the list of results exceeds your MaxResults value,\n then that number of results is returned along with a NextToken value that can be\n passed to a subsequent DescribeSnapshots request to retrieve the remaining\n results.

\n

To get the state of fast snapshot restores for a snapshot, use DescribeFastSnapshotRestores.

\n

For more information about EBS snapshots, see Amazon EBS snapshots in the Amazon Elastic Compute Cloud User Guide.

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

Describes the specified EBS snapshots available to you or all of the EBS snapshots\n available to you.

\n

The snapshots available to you include public snapshots, private snapshots that you own,\n and private snapshots owned by other Amazon Web Services accounts for which you have explicit create volume\n permissions.

\n

The create volume permissions fall into the following categories:

\n
    \n
  • \n

    \n public: The owner of the snapshot granted create volume\n permissions for the snapshot to the all group. All Amazon Web Services accounts have create\n volume permissions for these snapshots.

    \n
  • \n
  • \n

    \n explicit: The owner of the snapshot granted create volume\n permissions to a specific Amazon Web Services account.

    \n
  • \n
  • \n

    \n implicit: An Amazon Web Services account has implicit create volume permissions\n for all snapshots it owns.

    \n
  • \n
\n

The list of snapshots returned can be filtered by specifying snapshot IDs, snapshot\n owners, or Amazon Web Services accounts with create volume permissions. If no options are specified, \n Amazon EC2 returns all snapshots for which you have create volume permissions.

\n

If you specify one or more snapshot IDs, only snapshots that have the specified IDs are\n returned. If you specify an invalid snapshot ID, an error is returned. If you specify a\n snapshot ID for which you do not have access, it is not included in the returned\n results.

\n

If you specify one or more snapshot owners using the OwnerIds option, only\n snapshots from the specified owners and for which you have access are returned. The results\n can include the Amazon Web Services account IDs of the specified owners, amazon for snapshots\n owned by Amazon, or self for snapshots that you own.

\n

If you specify a list of restorable users, only snapshots with create snapshot permissions\n for those users are returned. You can specify Amazon Web Services account IDs (if you own the snapshots),\n self for snapshots for which you own or have explicit permissions, or\n all for public snapshots.

\n

If you are describing a long list of snapshots, we recommend that you paginate the output to make the\n list more manageable. For more information, see Pagination.

\n

To get the state of fast snapshot restores for a snapshot, use DescribeFastSnapshotRestores.

\n

For more information about EBS snapshots, see Amazon EBS snapshots in the Amazon Elastic Compute Cloud User Guide.

", "smithy.api#paginated": { "inputToken": "NextToken", "outputToken": "NextToken", @@ -33518,13 +33802,13 @@ "traits": { "smithy.api#clientOptional": {}, "smithy.api#default": 0, - "smithy.api#documentation": "

The maximum number of snapshot results returned by DescribeSnapshots in\n paginated output. When this parameter is used, DescribeSnapshots only returns\n MaxResults results in a single page along with a NextToken\n response element. The remaining results of the initial request can be seen by sending another\n DescribeSnapshots request with the returned NextToken value. This\n value can be between 5 and 1,000; if MaxResults is given a value larger than 1,000,\n only 1,000 results are returned. If this parameter is not used, then\n DescribeSnapshots returns all results. You cannot specify this parameter and\n the snapshot IDs parameter in the same request.

" + "smithy.api#documentation": "

The maximum number of snapshots to return for this request.\n This value can be between 5 and 1,000; if this value is larger than 1,000, only 1,000 results are returned. \n If this parameter is not used, then the request returns all snapshots. \n You cannot specify this parameter and the snapshot IDs parameter in the same request. For more information, \n see Pagination.

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

The NextToken value returned from a previous paginated\n DescribeSnapshots request where MaxResults was used and the\n results exceeded the value of that parameter. Pagination continues from the end of the\n previous results that returned the NextToken value. This value is\n null when there are no more results to return.

" + "smithy.api#documentation": "

The token returned from a previous paginated request.\n Pagination continues from the end of the items returned by the previous request.

" } }, "OwnerIds": { @@ -33578,7 +33862,7 @@ "target": "com.amazonaws.ec2#String", "traits": { "aws.protocols#ec2QueryName": "NextToken", - "smithy.api#documentation": "

The NextToken value to include in a future DescribeSnapshots\n request. When the results of a DescribeSnapshots request exceed\n MaxResults, this value can be used to retrieve the next page of results. This\n value is null when there are no more results to return.

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

The token to include in another request to return the next page of snapshots. \n This value is null when there are no more snapshots to return.

", "smithy.api#xmlName": "nextToken" } } @@ -33672,7 +33956,7 @@ "aws.protocols#ec2QueryName": "MaxResults", "smithy.api#clientOptional": {}, "smithy.api#default": 0, - "smithy.api#documentation": "

The maximum number of results to return in a single call. Specify a value between 1\n and 1000. The default value is 1000. To retrieve the remaining results, make another\n call with the returned NextToken value.

", + "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.

", "smithy.api#xmlName": "maxResults" } }, @@ -33680,7 +33964,7 @@ "target": "com.amazonaws.ec2#String", "traits": { "aws.protocols#ec2QueryName": "NextToken", - "smithy.api#documentation": "

The token for the next set of results.

", + "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" } }, @@ -33715,7 +33999,7 @@ "target": "com.amazonaws.ec2#String", "traits": { "aws.protocols#ec2QueryName": "NextToken", - "smithy.api#documentation": "

The token required to retrieve the next set of results. This value is\n null when there are no more results to return.

", + "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" } }, @@ -33782,7 +34066,7 @@ "aws.protocols#ec2QueryName": "MaxResults", "smithy.api#clientOptional": {}, "smithy.api#default": 0, - "smithy.api#documentation": "

The maximum number of results to return in a single call. Specify a value between 1\n and 1000. The default value is 1000. To retrieve the remaining results, make another\n call with the returned NextToken value.

", + "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.

", "smithy.api#xmlName": "maxResults" } }, @@ -33790,7 +34074,7 @@ "target": "com.amazonaws.ec2#String", "traits": { "aws.protocols#ec2QueryName": "NextToken", - "smithy.api#documentation": "

The token for the next set of results.

", + "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" } }, @@ -33835,7 +34119,7 @@ "target": "com.amazonaws.ec2#DateTime", "traits": { "aws.protocols#ec2QueryName": "LastEvaluatedTime", - "smithy.api#documentation": "

The last date and time for the events, in UTC format (for example,\n YYYY-MM-DDTHH:MM:SSZ).\n All records up to this time were retrieved.

\n

If nextToken indicates that there are more results, this value is not\n present.

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

The last date and time for the events, in UTC format (for example,\n YYYY-MM-DDTHH:MM:SSZ).\n All records up to this time were retrieved.

\n

If nextToken indicates that there are more items, this value is not\n present.

", "smithy.api#xmlName": "lastEvaluatedTime" } }, @@ -33843,7 +34127,7 @@ "target": "com.amazonaws.ec2#String", "traits": { "aws.protocols#ec2QueryName": "NextToken", - "smithy.api#documentation": "

The token required to retrieve the next set of results. This value is\n null when there are no more results to return.

", + "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" } }, @@ -33906,7 +34190,7 @@ "aws.protocols#ec2QueryName": "MaxResults", "smithy.api#clientOptional": {}, "smithy.api#default": 0, - "smithy.api#documentation": "

The maximum number of results to return in a single call. Specify a value between 1\n and 1000. The default value is 1000. To retrieve the remaining results, make another\n call with the returned NextToken value.

", + "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.

", "smithy.api#xmlName": "maxResults" } }, @@ -33914,7 +34198,7 @@ "target": "com.amazonaws.ec2#String", "traits": { "aws.protocols#ec2QueryName": "NextToken", - "smithy.api#documentation": "

The token for the next set of results.

", + "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" } }, @@ -33939,7 +34223,7 @@ "target": "com.amazonaws.ec2#String", "traits": { "aws.protocols#ec2QueryName": "NextToken", - "smithy.api#documentation": "

The token required to retrieve the next set of results. This value is\n null when there are no more results to return.

", + "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" } }, @@ -33966,7 +34250,7 @@ "target": "com.amazonaws.ec2#DescribeSpotInstanceRequestsResult" }, "traits": { - "smithy.api#documentation": "

Describes the specified Spot Instance requests.

\n

You can use DescribeSpotInstanceRequests to find a running Spot Instance by\n examining the response. If the status of the Spot Instance is fulfilled, the\n instance ID appears in the response and contains the identifier of the instance.\n Alternatively, you can use DescribeInstances\n with a filter to look for instances where the instance lifecycle is\n spot.

\n

We recommend that you set MaxResults to a value between 5 and 1000 to\n limit the number of results returned. This paginates the output, which makes the list\n more manageable and returns the results faster. If the list of results exceeds your\n MaxResults value, then that number of results is returned along with a\n NextToken value that can be passed to a subsequent\n DescribeSpotInstanceRequests request to retrieve the remaining\n results.

\n

Spot Instance requests are deleted four hours after they are canceled and their instances are\n terminated.

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

Describes the specified Spot Instance requests.

\n

You can use DescribeSpotInstanceRequests to find a running Spot Instance by\n examining the response. If the status of the Spot Instance is fulfilled, the\n instance ID appears in the response and contains the identifier of the instance.\n Alternatively, you can use DescribeInstances\n with a filter to look for instances where the instance lifecycle is\n spot.

\n

We recommend that you set MaxResults to a value between 5 and 1000 to\n limit the number of items returned. This paginates the output, which makes the list\n more manageable and returns the items faster. If the list of items exceeds your\n MaxResults value, then that number of items is returned along with a\n NextToken value that can be passed to a subsequent\n DescribeSpotInstanceRequests request to retrieve the remaining\n items.

\n

Spot Instance requests are deleted four hours after they are canceled and their instances are\n terminated.

", "smithy.api#paginated": { "inputToken": "NextToken", "outputToken": "NextToken", @@ -34081,7 +34365,7 @@ "NextToken": { "target": "com.amazonaws.ec2#String", "traits": { - "smithy.api#documentation": "

The token to request the next set of results. This value is null when\n there are no more results to return.

" + "smithy.api#documentation": "

The token returned from a previous paginated request. Pagination continues from the end of the items returned by the previous request.

" } }, "MaxResults": { @@ -34089,7 +34373,7 @@ "traits": { "smithy.api#clientOptional": {}, "smithy.api#default": 0, - "smithy.api#documentation": "

The maximum number of results to return in a single call. Specify a value between 5\n and 1000. To retrieve the remaining results, make another call with the returned\n NextToken value.

" + "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.

" } } }, @@ -34113,7 +34397,7 @@ "target": "com.amazonaws.ec2#String", "traits": { "aws.protocols#ec2QueryName": "NextToken", - "smithy.api#documentation": "

The token to use to retrieve the next set of results. This value is null\n when there are no more results to return.

", + "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" } } @@ -34189,7 +34473,7 @@ "aws.protocols#ec2QueryName": "MaxResults", "smithy.api#clientOptional": {}, "smithy.api#default": 0, - "smithy.api#documentation": "

The maximum number of results to return in a single call. Specify a value between 1\n and 1000. The default value is 1000. To retrieve the remaining results, make another\n call with the returned NextToken value.

", + "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.

", "smithy.api#xmlName": "maxResults" } }, @@ -34197,7 +34481,7 @@ "target": "com.amazonaws.ec2#String", "traits": { "aws.protocols#ec2QueryName": "NextToken", - "smithy.api#documentation": "

The token for the next set of results.

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

The token returned from a previous paginated request. Pagination continues from the end of the items returned by the previous request.

", "smithy.api#xmlName": "nextToken" } }, @@ -34229,7 +34513,7 @@ "target": "com.amazonaws.ec2#String", "traits": { "aws.protocols#ec2QueryName": "NextToken", - "smithy.api#documentation": "

The token required to retrieve the next set of results. This value is null or an empty\n string when there are no more results to return.

", + "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" } }, @@ -34299,13 +34583,13 @@ "traits": { "smithy.api#clientOptional": {}, "smithy.api#default": 0, - "smithy.api#documentation": "

The maximum number of items to return for this request. The request returns a token that you can specify in a subsequent call to get the next set of results.

" + "smithy.api#documentation": "

The maximum number of items to return for this request. To get the next page of items,\n make another request with the token returned in the output. For more information, \n see Pagination.

" } }, "NextToken": { "target": "com.amazonaws.ec2#DescribeStaleSecurityGroupsNextToken", "traits": { - "smithy.api#documentation": "

The token for the next set of items to return. (You received this token from a prior call.)

" + "smithy.api#documentation": "

The token returned from a previous paginated request.\n Pagination continues from the end of the items returned by the previous request.

" } }, "VpcId": { @@ -34328,7 +34612,7 @@ "target": "com.amazonaws.ec2#String", "traits": { "aws.protocols#ec2QueryName": "NextToken", - "smithy.api#documentation": "

The token to use when requesting the next set of items. If there are no additional items to return, the string is empty.

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

The token to include in another request to get the next page of items. \n If there are no additional items to return, the string is empty.

", "smithy.api#xmlName": "nextToken" } }, @@ -34388,7 +34672,7 @@ "NextToken": { "target": "com.amazonaws.ec2#String", "traits": { - "smithy.api#documentation": "

The token for the next page of results.

" + "smithy.api#documentation": "

The token returned from a previous paginated request. Pagination continues from the end of the items returned by the previous request.

" } }, "MaxResults": { @@ -34396,7 +34680,7 @@ "traits": { "smithy.api#clientOptional": {}, "smithy.api#default": 0, - "smithy.api#documentation": "

The maximum number of results to return in a single call. To retrieve the remaining\n results, make another call with the returned NextToken value. This value can be\n between 1 and 200. You cannot specify this parameter and the ImageIDs parameter\n in the same call.

" + "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.

\n

You cannot specify this parameter and the ImageIDs parameter\n in the same call.

" } } }, @@ -34429,7 +34713,7 @@ "target": "com.amazonaws.ec2#String", "traits": { "aws.protocols#ec2QueryName": "NextToken", - "smithy.api#documentation": "

The token to use to retrieve the next page of results. This value is null\n when there are no more results to return.

", + "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" } } @@ -34510,7 +34794,7 @@ "NextToken": { "target": "com.amazonaws.ec2#String", "traits": { - "smithy.api#documentation": "

The token for the next page of results.

" + "smithy.api#documentation": "

The token returned from a previous paginated request. Pagination continues from the end of the items returned by the previous request.

" } }, "MaxResults": { @@ -34518,7 +34802,7 @@ "traits": { "smithy.api#clientOptional": {}, "smithy.api#default": 0, - "smithy.api#documentation": "

The maximum number of results to return with a single call.\n\tTo retrieve the remaining results, make another call with the returned nextToken value.

" + "smithy.api#documentation": "

The maximum number of items to return for this request.\n\tTo get the next page of items, make another request with the token returned in the output.\n\tFor more information, see Pagination.

" } } }, @@ -34541,7 +34825,7 @@ "target": "com.amazonaws.ec2#String", "traits": { "aws.protocols#ec2QueryName": "NextToken", - "smithy.api#documentation": "

The token to use to retrieve the next page of results. This value is null when there are no more results to return.

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

The token to include in another request to get the next page of items. This value is null when there are no more items to return.

", "smithy.api#xmlName": "nextToken" } } @@ -34591,7 +34875,7 @@ "aws.protocols#ec2QueryName": "MaxResults", "smithy.api#clientOptional": {}, "smithy.api#default": 0, - "smithy.api#documentation": "

The maximum number of results to return in a single call.\n This value can be between 5 and 1000. \n\t\t\tTo retrieve the remaining results, make another call with the returned NextToken value.

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

The maximum number of items to return for this request. This value can be between 5 and 1000. \n To get the next page of items, make another request with the token returned in the output.\n For more information, see Pagination.

", "smithy.api#xmlName": "maxResults" } }, @@ -34599,7 +34883,7 @@ "target": "com.amazonaws.ec2#String", "traits": { "aws.protocols#ec2QueryName": "NextToken", - "smithy.api#documentation": "

The token to retrieve the next page of results.

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

The token returned from a previous paginated request.\n Pagination continues from the end of the items returned by the previous request.

", "smithy.api#xmlName": "nextToken" } } @@ -34615,7 +34899,7 @@ "target": "com.amazonaws.ec2#String", "traits": { "aws.protocols#ec2QueryName": "NextToken", - "smithy.api#documentation": "

The token to use to retrieve the next page of results. This value is\n null when there are no more results to return.

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

The token to include in another request to get the next page of items. \n This value is null when there are no more items to return.

", "smithy.api#xmlName": "nextToken" } }, @@ -36382,13 +36666,13 @@ "traits": { "smithy.api#clientOptional": {}, "smithy.api#default": 0, - "smithy.api#documentation": "

The maximum number of volume results returned by DescribeVolumeStatus in\n paginated output. When this parameter is used, the request only returns\n MaxResults results in a single page along with a NextToken\n response element. The remaining results of the initial request can be seen by sending another\n request with the returned NextToken value. This value can be between 5 and 1,000;\n if MaxResults is given a value larger than 1,000, only 1,000 results are returned.\n If this parameter is not used, then DescribeVolumeStatus returns all results. You\n cannot specify this parameter and the volume IDs parameter in the same request.

" + "smithy.api#documentation": "

The maximum number of items to return for this request. To get the next page of items,\n make another request with the token returned in the output. This value can be between 5 and 1,000;\n if the value is larger than 1,000, only 1,000 results are returned. If this parameter is not used, \n then all items are returned. You cannot specify this parameter and the volume IDs parameter in the \n same request. For more information, see Pagination.

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

The NextToken value to include in a future DescribeVolumeStatus\n request. When the results of the request exceed MaxResults, this value can be\n used to retrieve the next page of results. This value is null when there are no\n more results to return.

" + "smithy.api#documentation": "

The token returned from a previous paginated request.\n Pagination continues from the end of the items returned by the previous request.

" } }, "VolumeIds": { @@ -36420,7 +36704,7 @@ "target": "com.amazonaws.ec2#String", "traits": { "aws.protocols#ec2QueryName": "NextToken", - "smithy.api#documentation": "

The token to use to retrieve the next page of results. This value is null\n when there are no more results to return.

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

The token to include in another request to get the next page of items. \n This value is null when there are no more items to return.

", "smithy.api#xmlName": "nextToken" } }, @@ -36443,7 +36727,7 @@ "target": "com.amazonaws.ec2#DescribeVolumesResult" }, "traits": { - "smithy.api#documentation": "

Describes the specified EBS volumes or all of your EBS volumes.

\n

If you are describing a long list of volumes, we recommend that you paginate the output to make the list\n more manageable. The MaxResults parameter sets the maximum number of results\n returned in a single page. If the list of results exceeds your MaxResults value,\n then that number of results is returned along with a NextToken value that can be\n passed to a subsequent DescribeVolumes request to retrieve the remaining\n results.

\n

For more information about EBS volumes, see Amazon EBS volumes in the Amazon Elastic Compute Cloud User Guide.

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

Describes the specified EBS volumes or all of your EBS volumes.

\n

If you are describing a long list of volumes, we recommend that you paginate the output to make the list\n more manageable. For more information, see Pagination.

\n

For more information about EBS volumes, see Amazon EBS volumes in the Amazon Elastic Compute Cloud User Guide.

", "smithy.api#paginated": { "inputToken": "NextToken", "outputToken": "NextToken", @@ -36574,7 +36858,7 @@ "NextToken": { "target": "com.amazonaws.ec2#String", "traits": { - "smithy.api#documentation": "

The nextToken value returned by a previous paginated request.

" + "smithy.api#documentation": "

The token returned by a previous paginated request.\n Pagination continues from the end of the items returned by the previous request.

" } }, "MaxResults": { @@ -36582,7 +36866,7 @@ "traits": { "smithy.api#clientOptional": {}, "smithy.api#default": 0, - "smithy.api#documentation": "

The maximum number of results (up to a limit of 500) to be returned in a paginated\n request.

" + "smithy.api#documentation": "

The maximum number of results (up to a limit of 500) to be returned in a paginated\n request. For more information, see Pagination.

" } } }, @@ -36605,7 +36889,7 @@ "target": "com.amazonaws.ec2#String", "traits": { "aws.protocols#ec2QueryName": "NextToken", - "smithy.api#documentation": "

Token for pagination, null if there are no more results

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

The token to include in another request to get the next page of items. \n This value is null if there are no more items to return.

", "smithy.api#xmlName": "nextToken" } } @@ -36644,7 +36928,7 @@ "aws.protocols#ec2QueryName": "MaxResults", "smithy.api#clientOptional": {}, "smithy.api#default": 0, - "smithy.api#documentation": "

The maximum number of volume results returned by DescribeVolumes in paginated\n output. When this parameter is used, DescribeVolumes only returns\n MaxResults results in a single page along with a NextToken\n response element. The remaining results of the initial request can be seen by sending another\n DescribeVolumes request with the returned NextToken value. This\n value can be between 5 and 500; if MaxResults is given a value larger than 500,\n only 500 results are returned. If this parameter is not used, then\n DescribeVolumes returns all results. You cannot specify this parameter and the\n volume IDs parameter in the same request.

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

The maximum number of volumes to return for this request. \n This value can be between 5 and 500; if you specify a value larger than 500, only 500 items are returned. \n If this parameter is not used, then all items are returned. You cannot specify this parameter and the\n volume IDs parameter in the same request. For more information, see Pagination.

", "smithy.api#xmlName": "maxResults" } }, @@ -36652,7 +36936,7 @@ "target": "com.amazonaws.ec2#String", "traits": { "aws.protocols#ec2QueryName": "NextToken", - "smithy.api#documentation": "

The NextToken value returned from a previous paginated\n DescribeVolumes request where MaxResults was used and the results\n exceeded the value of that parameter. Pagination continues from the end of the previous\n results that returned the NextToken value. This value is null when\n there are no more results to return.

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

The token returned from a previous paginated request. \n Pagination continues from the end of the items returned from the previous request.

", "smithy.api#xmlName": "nextToken" } } @@ -36676,7 +36960,7 @@ "target": "com.amazonaws.ec2#String", "traits": { "aws.protocols#ec2QueryName": "NextToken", - "smithy.api#documentation": "

The NextToken value to include in a future DescribeVolumes\n request. When the results of a DescribeVolumes request exceed\n MaxResults, this value can be used to retrieve the next page of results. This\n value is null when there are no more results to return.

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

The token to include in another request to get the next page of items. \n This value is null when there are no more items to return.

", "smithy.api#xmlName": "nextToken" } } @@ -36823,7 +37107,7 @@ "aws.protocols#ec2QueryName": "MaxResults", "smithy.api#clientOptional": {}, "smithy.api#default": 0, - "smithy.api#documentation": "

The maximum number of results to return with a single call.\n\tTo retrieve the remaining results, make another call with the returned nextToken value.

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

The maximum number of items to return for this request.\n\tTo get the next page of items, make another request with the token returned in the output.\n\tFor more information, see Pagination.

", "smithy.api#xmlName": "maxResults" } }, @@ -36831,7 +37115,7 @@ "target": "com.amazonaws.ec2#DescribeVpcClassicLinkDnsSupportNextToken", "traits": { "aws.protocols#ec2QueryName": "NextToken", - "smithy.api#documentation": "

The token for the next page of results.

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

The token returned from a previous paginated request. Pagination continues from the end of the items returned by the previous request.

", "smithy.api#xmlName": "nextToken" } }, @@ -36854,7 +37138,7 @@ "target": "com.amazonaws.ec2#DescribeVpcClassicLinkDnsSupportNextToken", "traits": { "aws.protocols#ec2QueryName": "NextToken", - "smithy.api#documentation": "

The token to use to retrieve the next page of results. This value is null when there are no more results to return.

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

The token to include in another request to get the next page of items. This value is null when there are no more items to return.

", "smithy.api#xmlName": "nextToken" } }, @@ -37507,7 +37791,7 @@ "NextToken": { "target": "com.amazonaws.ec2#String", "traits": { - "smithy.api#documentation": "

The token for the next page of results.

" + "smithy.api#documentation": "

The token returned from a previous paginated request. Pagination continues from the end of the items returned by the previous request.

" } }, "MaxResults": { @@ -37515,7 +37799,7 @@ "traits": { "smithy.api#clientOptional": {}, "smithy.api#default": 0, - "smithy.api#documentation": "

The maximum number of results to return with a single call.\n\tTo retrieve the remaining results, make another call with the returned nextToken value.

" + "smithy.api#documentation": "

The maximum number of items to return for this request.\n\tTo get the next page of items, make another request with the token returned in the output.\n\tFor more information, see Pagination.

" } } }, @@ -37538,7 +37822,7 @@ "target": "com.amazonaws.ec2#String", "traits": { "aws.protocols#ec2QueryName": "NextToken", - "smithy.api#documentation": "

The token to use to retrieve the next page of results. This value is null when there are no more results to return.

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

The token to include in another request to get the next page of items. This value is null when there are no more items to return.

", "smithy.api#xmlName": "nextToken" } } @@ -37639,7 +37923,7 @@ "NextToken": { "target": "com.amazonaws.ec2#String", "traits": { - "smithy.api#documentation": "

The token for the next page of results.

" + "smithy.api#documentation": "

The token returned from a previous paginated request. Pagination continues from the end of the items returned by the previous request.

" } }, "MaxResults": { @@ -37647,7 +37931,7 @@ "traits": { "smithy.api#clientOptional": {}, "smithy.api#default": 0, - "smithy.api#documentation": "

The maximum number of results to return with a single call.\n\tTo retrieve the remaining results, make another call with the returned nextToken value.

" + "smithy.api#documentation": "

The maximum number of items to return for this request.\n\tTo get the next page of items, make another request with the token returned in the output.\n\tFor more information, see Pagination.

" } } }, @@ -37670,7 +37954,7 @@ "target": "com.amazonaws.ec2#String", "traits": { "aws.protocols#ec2QueryName": "NextToken", - "smithy.api#documentation": "

The token to use to retrieve the next page of results. This value is null when there are no more results to return.

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

The token to include in another request to get the next page of items. This value is null when there are no more items to return.

", "smithy.api#xmlName": "nextToken" } } @@ -38713,7 +38997,7 @@ "aws.protocols#ec2QueryName": "MaxParallelLaunches", "smithy.api#clientOptional": {}, "smithy.api#default": 0, - "smithy.api#documentation": "

The maximum number of parallel instances to launch for creating resources.

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

The maximum number of instances that Amazon EC2 can launch at the same time to \n\t\t\tcreate pre-provisioned snapshots for Windows faster launching.

", "smithy.api#xmlName": "maxParallelLaunches" } }, @@ -39383,7 +39667,7 @@ } }, "PublicIp": { - "target": "com.amazonaws.ec2#String", + "target": "com.amazonaws.ec2#EipAllocationPublicIp", "traits": { "smithy.api#documentation": "

[EC2-Classic] The Elastic IP address. Required for EC2-Classic.

" } @@ -39484,15 +39768,19 @@ "type": "structure", "members": { "CertificateArn": { - "target": "com.amazonaws.ec2#ResourceArn", + "target": "com.amazonaws.ec2#CertificateId", "traits": { - "smithy.api#documentation": "

The ARN of the ACM certificate from which to disassociate the IAM role.

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

The ARN of the ACM certificate from which to disassociate the IAM role.

", + "smithy.api#required": {} } }, "RoleArn": { - "target": "com.amazonaws.ec2#ResourceArn", + "target": "com.amazonaws.ec2#RoleId", "traits": { - "smithy.api#documentation": "

The ARN of the IAM role to disassociate.

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

The ARN of the IAM role to disassociate.

", + "smithy.api#required": {} } }, "DryRun": { @@ -39630,7 +39918,7 @@ "target": "com.amazonaws.ec2#DisassociateIpamResourceDiscoveryResult" }, "traits": { - "smithy.api#documentation": "

Disassociates a resource discovery from an Amazon VPC IPAM. A resource discovery is an IPAM component that enables IPAM Service to manage and monitor resources that belong to the owning account.

" + "smithy.api#documentation": "

Disassociates a resource discovery from an Amazon VPC IPAM. A resource discovery is an IPAM component that enables IPAM to manage and monitor resources that belong to the owning account.

" } }, "com.amazonaws.ec2#DisassociateIpamResourceDiscoveryRequest": { @@ -39670,6 +39958,80 @@ } } }, + "com.amazonaws.ec2#DisassociateNatGatewayAddress": { + "type": "operation", + "input": { + "target": "com.amazonaws.ec2#DisassociateNatGatewayAddressRequest" + }, + "output": { + "target": "com.amazonaws.ec2#DisassociateNatGatewayAddressResult" + }, + "traits": { + "smithy.api#documentation": "

Disassociates secondary Elastic IP addresses (EIPs) from a public NAT gateway. You cannot disassociate your primary EIP. For more information, see Edit secondary IP address associations in the Amazon Virtual Private Cloud User Guide.

\n

While disassociating is in progress, you cannot associate/disassociate additional EIPs while the connections are being drained. You are, however, allowed to delete the NAT gateway.

\n

An EIP will only be released at the end of MaxDrainDurationSeconds. The EIPs stay\n associated and support the existing connections but do not support any new connections\n (new connections are distributed across the remaining associated EIPs). As the existing\n connections drain out, the EIPs (and the corresponding private IPs mapped to them) get\n released.

" + } + }, + "com.amazonaws.ec2#DisassociateNatGatewayAddressRequest": { + "type": "structure", + "members": { + "NatGatewayId": { + "target": "com.amazonaws.ec2#NatGatewayId", + "traits": { + "smithy.api#clientOptional": {}, + "smithy.api#documentation": "

The NAT gateway ID.

", + "smithy.api#required": {} + } + }, + "AssociationIds": { + "target": "com.amazonaws.ec2#EipAssociationIdList", + "traits": { + "smithy.api#clientOptional": {}, + "smithy.api#documentation": "

The association IDs of EIPs that have been associated with the NAT gateway.

", + "smithy.api#required": {}, + "smithy.api#xmlName": "AssociationId" + } + }, + "MaxDrainDurationSeconds": { + "target": "com.amazonaws.ec2#DrainSeconds", + "traits": { + "smithy.api#clientOptional": {}, + "smithy.api#default": 0, + "smithy.api#documentation": "

The maximum amount of time to wait (in seconds) before forcibly releasing the IP addresses if connections are still in progress. Default value is 350 seconds.

" + } + }, + "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.

" + } + } + }, + "traits": { + "smithy.api#input": {} + } + }, + "com.amazonaws.ec2#DisassociateNatGatewayAddressResult": { + "type": "structure", + "members": { + "NatGatewayId": { + "target": "com.amazonaws.ec2#NatGatewayId", + "traits": { + "aws.protocols#ec2QueryName": "NatGatewayId", + "smithy.api#documentation": "

The NAT gateway ID.

", + "smithy.api#xmlName": "natGatewayId" + } + }, + "NatGatewayAddresses": { + "target": "com.amazonaws.ec2#NatGatewayAddressList", + "traits": { + "aws.protocols#ec2QueryName": "NatGatewayAddressSet", + "smithy.api#documentation": "

Information about the NAT gateway IP addresses.

", + "smithy.api#xmlName": "natGatewayAddressSet" + } + } + } + }, "com.amazonaws.ec2#DisassociateRouteTable": { "type": "operation", "input": { @@ -39779,19 +40141,25 @@ "TransitGatewayMulticastDomainId": { "target": "com.amazonaws.ec2#TransitGatewayMulticastDomainId", "traits": { - "smithy.api#documentation": "

The ID of the transit gateway multicast domain.

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

The ID of the transit gateway multicast domain.

", + "smithy.api#required": {} } }, "TransitGatewayAttachmentId": { "target": "com.amazonaws.ec2#TransitGatewayAttachmentId", "traits": { - "smithy.api#documentation": "

The ID of the attachment.

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

The ID of the attachment.

", + "smithy.api#required": {} } }, "SubnetIds": { "target": "com.amazonaws.ec2#TransitGatewaySubnetIdList", "traits": { - "smithy.api#documentation": "

The IDs of the subnets;

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

The IDs of the subnets;

", + "smithy.api#required": {} } }, "DryRun": { @@ -40351,6 +40719,16 @@ "smithy.api#documentation": "

The DNS records created for the endpoint.

", "smithy.api#xmlName": "dnsRecordIpType" } + }, + "PrivateDnsOnlyForInboundResolverEndpoint": { + "target": "com.amazonaws.ec2#Boolean", + "traits": { + "aws.protocols#ec2QueryName": "PrivateDnsOnlyForInboundResolverEndpoint", + "smithy.api#clientOptional": {}, + "smithy.api#default": false, + "smithy.api#documentation": "

Indicates whether to enable private DNS only for inbound endpoints.

", + "smithy.api#xmlName": "privateDnsOnlyForInboundResolverEndpoint" + } } }, "traits": { @@ -40365,6 +40743,14 @@ "traits": { "smithy.api#documentation": "

The DNS records created for the endpoint.

" } + }, + "PrivateDnsOnlyForInboundResolverEndpoint": { + "target": "com.amazonaws.ec2#Boolean", + "traits": { + "smithy.api#clientOptional": {}, + "smithy.api#default": false, + "smithy.api#documentation": "

Indicates whether to enable private DNS only for inbound endpoints. This option is\n available only for services that support both gateway and interface endpoints. It routes\n traffic that originates from the VPC to the gateway endpoint and traffic that originates\n from on-premises to the interface endpoint.

" + } } }, "traits": { @@ -40471,6 +40857,16 @@ } } }, + "com.amazonaws.ec2#DrainSeconds": { + "type": "integer", + "traits": { + "smithy.api#default": 0, + "smithy.api#range": { + "min": 1, + "max": 4000 + } + } + }, "com.amazonaws.ec2#DynamicRoutingValue": { "type": "enum", "members": { @@ -40569,7 +40965,7 @@ "aws.protocols#ec2QueryName": "Encrypted", "smithy.api#clientOptional": {}, "smithy.api#default": false, - "smithy.api#documentation": "

Indicates whether the encryption state of an EBS volume is changed while being\n restored from a backing snapshot. The effect of setting the encryption state to true depends on \nthe volume origin (new or from a snapshot), starting encryption state, ownership, and whether encryption by default is enabled. For more information, see Amazon EBS encryption in the Amazon EC2 User Guide.

\n

In no case can you remove encryption from an encrypted volume.

\n

Encrypted volumes can only be attached to instances that support Amazon EBS encryption. For\n more information, see Supported instance types.

\n

This parameter is not returned by DescribeImageAttribute.

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

Indicates whether the encryption state of an EBS volume is changed while being\n restored from a backing snapshot. The effect of setting the encryption state to true depends on \nthe volume origin (new or from a snapshot), starting encryption state, ownership, and whether encryption by default is enabled. For more information, see Amazon EBS encryption in the Amazon EC2 User Guide.

\n

In no case can you remove encryption from an encrypted volume.

\n

Encrypted volumes can only be attached to instances that support Amazon EBS encryption. For\n more information, see Supported instance types.

\n

This parameter is not returned by DescribeImageAttribute.

\n

For CreateImage and RegisterImage, whether you can \n include this parameter, and the allowed values differ depending on the type of block \n device mapping you are creating.

\n
    \n
  • \n

    If you are creating a block device mapping for a new (empty) \n volume, you can include this parameter, and specify either true \n for an encrypted volume, or false for an unencrypted volume. If you omit \n this parameter, it defaults to false (unencrypted).

    \n
  • \n
  • \n

    If you are creating a block device mapping from an existing \n encrypted or unencrypted snapshot, you must omit this parameter. If you \n include this parameter, the request will fail, regardless of the value that you \n specify.

    \n
  • \n
  • \n

    If you are creating a block device mapping from an existing \n unencrypted volume, you can include this parameter, but you must specify \n false. If you specify true, the request will fail. In this \n case, we recommend that you omit the parameter.

    \n
  • \n
  • \n

    If you are creating a block device mapping from an existing \n encrypted volume, you can include this parameter, and specify either \n true or false. However, if you specify false, \n the parameter is ignored and the block device mapping is always encrypted. In this \n case, we recommend that you omit the parameter.

    \n
  • \n
", "smithy.api#xmlName": "encrypted" } } @@ -40877,6 +41273,18 @@ } } }, + "com.amazonaws.ec2#EipAllocationPublicIp": { + "type": "string" + }, + "com.amazonaws.ec2#EipAssociationIdList": { + "type": "list", + "member": { + "target": "com.amazonaws.ec2#ElasticIpAssociationId", + "traits": { + "smithy.api#xmlName": "item" + } + } + }, "com.amazonaws.ec2#ElasticGpuAssociation": { "type": "structure", "members": { @@ -41485,7 +41893,7 @@ "traits": { "smithy.api#clientOptional": {}, "smithy.api#default": 0, - "smithy.api#documentation": "

The maximum number of parallel instances to launch for creating resources. Value must be 6 or greater.

" + "smithy.api#documentation": "

The maximum number of instances that Amazon EC2 can launch at the same time to create \n\t\t\tpre-provisioned snapshots for Windows faster launching. Value must be \n\t\t\t6 or greater.

" } }, "DryRun": { @@ -41524,7 +41932,7 @@ "target": "com.amazonaws.ec2#FastLaunchSnapshotConfigurationResponse", "traits": { "aws.protocols#ec2QueryName": "SnapshotConfiguration", - "smithy.api#documentation": "

The configuration settings that were defined for creating and managing the pre-provisioned snapshots \n\t\t\tfor faster launching of the Windows AMI. This property is returned when the associated \n\t\t\tresourceType is snapshot.

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

Settings to create and manage the pre-provisioned snapshots that Amazon EC2 uses for faster \n\t\t\tlaunches from the Windows AMI. This property is returned when the associated \n\t\t\tresourceType is snapshot.

", "smithy.api#xmlName": "snapshotConfiguration" } }, @@ -41542,7 +41950,7 @@ "aws.protocols#ec2QueryName": "MaxParallelLaunches", "smithy.api#clientOptional": {}, "smithy.api#default": 0, - "smithy.api#documentation": "

The maximum number of parallel instances to launch for creating resources.

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

The maximum number of instances that Amazon EC2 can launch at the same time to \n\t\t\tcreate pre-provisioned snapshots for Windows faster launching.

", "smithy.api#xmlName": "maxParallelLaunches" } }, @@ -44208,7 +44616,7 @@ "target": "com.amazonaws.ec2#FleetExcessCapacityTerminationPolicy", "traits": { "aws.protocols#ec2QueryName": "ExcessCapacityTerminationPolicy", - "smithy.api#documentation": "

Indicates whether running instances should be terminated if the target capacity of the\n EC2 Fleet is decreased below the current size of the EC2 Fleet.

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

Indicates whether running instances should be terminated if the target capacity of the\n EC2 Fleet is decreased below the current size of the EC2 Fleet.

\n

Supported only for fleets of type maintain.

", "smithy.api#xmlName": "excessCapacityTerminationPolicy" } }, @@ -44480,7 +44888,7 @@ "target": "com.amazonaws.ec2#InstanceType", "traits": { "aws.protocols#ec2QueryName": "InstanceType", - "smithy.api#documentation": "

The instance type.

\n \n

If you specify InstanceType, you can't specify\n InstanceRequirements.

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

The instance type.

\n

\n mac1.metal is not supported as a launch template override.

\n \n

If you specify InstanceType, you can't specify\n InstanceRequirements.

\n
", "smithy.api#xmlName": "instanceType" } }, @@ -44581,7 +44989,7 @@ "InstanceType": { "target": "com.amazonaws.ec2#InstanceType", "traits": { - "smithy.api#documentation": "

The instance type.

\n \n

If you specify InstanceType, you can't specify\n InstanceRequirements.

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

The instance type.

\n

\n mac1.metal is not supported as a launch template override.

\n \n

If you specify InstanceType, you can't specify\n InstanceRequirements.

\n
" } }, "MaxPrice": { @@ -44627,7 +45035,7 @@ "InstanceRequirements": { "target": "com.amazonaws.ec2#InstanceRequirementsRequest", "traits": { - "smithy.api#documentation": "

The attributes for the instance types. When you specify instance attributes, Amazon EC2 will\n identify instance types with those attributes.

\n \n

If you specify InstanceRequirements, you can't specify\n InstanceType.

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

The attributes for the instance types. When you specify instance attributes, Amazon EC2 will\n identify instance types with those attributes.

\n \n

If you specify InstanceRequirements, you can't specify\n InstanceType.

\n
" } }, "ImageId": { @@ -45559,9 +45967,11 @@ "type": "structure", "members": { "CertificateArn": { - "target": "com.amazonaws.ec2#ResourceArn", + "target": "com.amazonaws.ec2#CertificateId", "traits": { - "smithy.api#documentation": "

The ARN of the ACM certificate for which to view the associated IAM roles, encryption keys, and Amazon \n\t\t\tS3 object information.

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

The ARN of the ACM certificate for which to view the associated IAM roles, encryption keys, and Amazon \n\t\t\tS3 object information.

", + "smithy.api#required": {} } }, "DryRun": { @@ -46525,13 +46935,13 @@ "traits": { "smithy.api#clientOptional": {}, "smithy.api#default": 0, - "smithy.api#documentation": "

The maximum number of results to return in a single call. Specify a value between 1 and\u2028 \n 1000. The default value is 1000. To retrieve the remaining results, make another call with\u2028 \n the returned NextToken value.

" + "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#String", "traits": { - "smithy.api#documentation": "

The token for the next set of results.

" + "smithy.api#documentation": "

The token returned from a previous paginated request. Pagination continues from the end of the items returned by the previous request.

" } } }, @@ -46554,7 +46964,7 @@ "target": "com.amazonaws.ec2#String", "traits": { "aws.protocols#ec2QueryName": "NextToken", - "smithy.api#documentation": "

The token for the next set of results.

", + "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" } } @@ -47098,7 +47508,7 @@ "target": "com.amazonaws.ec2#GetIpamResourceCidrsResult" }, "traits": { - "smithy.api#documentation": "

Returns resource CIDRs managed by IPAM in a given scope. If an IPAM is associated with more than one resource discovery, the resource CIDRs across all of the resource discoveries is returned. A resource discovery is an IPAM component that enables IPAM Service to manage and monitor resources that belong to the owning account.

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

Returns resource CIDRs managed by IPAM in a given scope. If an IPAM is associated with more than one resource discovery, the resource CIDRs across all of the resource discoveries is returned. A resource discovery is an IPAM component that enables IPAM to manage and monitor resources that belong to the owning account.

", "smithy.api#paginated": { "inputToken": "NextToken", "outputToken": "NextToken", @@ -47891,13 +48301,13 @@ "traits": { "smithy.api#clientOptional": {}, "smithy.api#default": 0, - "smithy.api#documentation": "

The maximum number of results to return in a single call. Specify a value between 1 and\u2028 \n 1000. The default value is 1000. To retrieve the remaining results, make another call with\u2028 \n the returned NextToken value.

" + "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#String", "traits": { - "smithy.api#documentation": "

The token for the next set of results.

" + "smithy.api#documentation": "

The token returned from a previous paginated request. Pagination continues from the end of the items returned by the previous request.

" } } }, @@ -47920,7 +48330,7 @@ "target": "com.amazonaws.ec2#String", "traits": { "aws.protocols#ec2QueryName": "NextToken", - "smithy.api#documentation": "

The token for the next set of results.

", + "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" } } @@ -48130,7 +48540,9 @@ "TransitGatewayMulticastDomainId": { "target": "com.amazonaws.ec2#TransitGatewayMulticastDomainId", "traits": { - "smithy.api#documentation": "

The ID of the transit gateway multicast domain.

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

The ID of the transit gateway multicast domain.

", + "smithy.api#required": {} } }, "Filters": { @@ -49279,6 +49691,14 @@ "smithy.api#documentation": "

The Amazon Resource Name (ARN) of the Amazon Web Services Outpost on which the\n Dedicated Host is allocated.

", "smithy.api#xmlName": "outpostArn" } + }, + "HostMaintenance": { + "target": "com.amazonaws.ec2#HostMaintenance", + "traits": { + "aws.protocols#ec2QueryName": "HostMaintenance", + "smithy.api#documentation": "

Indicates whether host maintenance is enabled or disabled for the Dedicated\n Host.

", + "smithy.api#xmlName": "hostMaintenance" + } } }, "traits": { @@ -49335,6 +49755,23 @@ } } }, + "com.amazonaws.ec2#HostMaintenance": { + "type": "enum", + "members": { + "on": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "on" + } + }, + "off": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "off" + } + } + } + }, "com.amazonaws.ec2#HostOffering": { "type": "structure", "members": { @@ -50707,7 +51144,7 @@ "target": "com.amazonaws.ec2#ImportImageResult" }, "traits": { - "smithy.api#documentation": "

Import single or multi-volume disk images or EBS snapshots into an Amazon Machine Image (AMI).

\n \n

Amazon Web Services VM Import/Export strongly recommends specifying a value for either the\n --license-type or --usage-operation parameter when you create a new\n VM Import task. This ensures your operating system is licensed appropriately and your billing is\n optimized.

\n
\n

For more information, see Importing a \n VM as an image using VM Import/Export in the VM Import/Export User Guide.

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

To import your virtual machines (VMs) with a console-based experience, you can use the\n Import virtual machine images to Amazon Web Services template in the Migration Hub Orchestrator console. For more\n information, see the \n Migration Hub Orchestrator User Guide\n .

\n
\n

Import single or multi-volume disk images or EBS snapshots into an Amazon Machine Image (AMI).

\n \n

Amazon Web Services VM Import/Export strongly recommends specifying a value for either the\n --license-type or --usage-operation parameter when you create a new\n VM Import task. This ensures your operating system is licensed appropriately and your billing is\n optimized.

\n
\n

For more information, see Importing a \n VM as an image using VM Import/Export in the VM Import/Export User Guide.

" } }, "com.amazonaws.ec2#ImportImageLicenseConfigurationRequest": { @@ -52334,7 +52771,7 @@ "target": "com.amazonaws.ec2#BootModeValues", "traits": { "aws.protocols#ec2QueryName": "BootMode", - "smithy.api#documentation": "

The boot mode of the instance. For more information, see Boot modes in the\n Amazon EC2 User Guide.

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

The boot mode that was specified by the AMI. If the value is uefi-preferred, \n the AMI supports both UEFI and Legacy BIOS. The currentInstanceBootMode parameter \n is the boot mode that is used to boot the instance at launch or start.

\n \n

The operating system contained in the AMI must be configured to support the specified boot mode.

\n
\n

For more information, see Boot modes in the\n Amazon EC2 User Guide.

", "smithy.api#xmlName": "bootMode" } }, @@ -52393,6 +52830,14 @@ "smithy.api#documentation": "

Provides information on the recovery and maintenance options of your instance.

", "smithy.api#xmlName": "maintenanceOptions" } + }, + "CurrentInstanceBootMode": { + "target": "com.amazonaws.ec2#InstanceBootModeValues", + "traits": { + "aws.protocols#ec2QueryName": "CurrentInstanceBootMode", + "smithy.api#documentation": "

The boot mode that is used to boot the instance at launch or start. For more information, see Boot modes in the\n Amazon EC2 User Guide.

", + "smithy.api#xmlName": "currentInstanceBootMode" + } } }, "traits": { @@ -52743,6 +53188,23 @@ } } }, + "com.amazonaws.ec2#InstanceBootModeValues": { + "type": "enum", + "members": { + "legacy_bios": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "legacy-bios" + } + }, + "uefi": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "uefi" + } + } + } + }, "com.amazonaws.ec2#InstanceCapacity": { "type": "structure", "members": { @@ -52862,7 +53324,9 @@ "InstanceId": { "target": "com.amazonaws.ec2#InstanceId", "traits": { - "smithy.api#documentation": "

The ID of the instance.

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

The ID of the instance.

", + "smithy.api#required": {} } }, "CpuCredits": { @@ -53329,6 +53793,9 @@ } } }, + "com.amazonaws.ec2#InstanceIdWithVolumeResolver": { + "type": "string" + }, "com.amazonaws.ec2#InstanceIdsSet": { "type": "list", "member": { @@ -54660,9 +55127,11 @@ "type": "structure", "members": { "InstanceId": { - "target": "com.amazonaws.ec2#InstanceId", + "target": "com.amazonaws.ec2#InstanceIdWithVolumeResolver", "traits": { - "smithy.api#documentation": "

The instance to specify which volumes should be snapshotted.

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

The instance to specify which volumes should be snapshotted.

", + "smithy.api#required": {} } }, "ExcludeBootVolume": { @@ -58807,6 +59276,120 @@ "traits": { "smithy.api#enumValue": "r6idn.32xlarge" } + }, + "c7g_metal": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "c7g.metal" + } + }, + "m7g_medium": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "m7g.medium" + } + }, + "m7g_large": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "m7g.large" + } + }, + "m7g_xlarge": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "m7g.xlarge" + } + }, + "m7g_2xlarge": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "m7g.2xlarge" + } + }, + "m7g_4xlarge": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "m7g.4xlarge" + } + }, + "m7g_8xlarge": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "m7g.8xlarge" + } + }, + "m7g_12xlarge": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "m7g.12xlarge" + } + }, + "m7g_16xlarge": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "m7g.16xlarge" + } + }, + "m7g_metal": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "m7g.metal" + } + }, + "r7g_medium": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "r7g.medium" + } + }, + "r7g_large": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "r7g.large" + } + }, + "r7g_xlarge": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "r7g.xlarge" + } + }, + "r7g_2xlarge": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "r7g.2xlarge" + } + }, + "r7g_4xlarge": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "r7g.4xlarge" + } + }, + "r7g_8xlarge": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "r7g.8xlarge" + } + }, + "r7g_12xlarge": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "r7g.12xlarge" + } + }, + "r7g_16xlarge": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "r7g.16xlarge" + } + }, + "r7g_metal": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "r7g.metal" + } } } }, @@ -59363,6 +59946,15 @@ } } }, + "com.amazonaws.ec2#IpList": { + "type": "list", + "member": { + "target": "com.amazonaws.ec2#String", + "traits": { + "smithy.api#xmlName": "item" + } + } + }, "com.amazonaws.ec2#IpPermission": { "type": "structure", "members": { @@ -60933,7 +61525,7 @@ } }, "traits": { - "smithy.api#documentation": "

A resource discovery is an IPAM component that enables IPAM Service to manage and monitor resources that belong to the owning account.

" + "smithy.api#documentation": "

A resource discovery is an IPAM component that enables IPAM to manage and monitor resources that belong to the owning account.

" } }, "com.amazonaws.ec2#IpamResourceDiscoveryAssociation": { @@ -62182,10 +62774,10 @@ "type": "structure", "members": { "UserData": { - "target": "com.amazonaws.ec2#String", + "target": "com.amazonaws.ec2#SensitiveUserData", "traits": { "aws.protocols#ec2QueryName": "UserData", - "smithy.api#documentation": "

The Base64-encoded user data for the instance.

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

The base64-encoded user data that instances use when starting up. User data is limited to 16 KB.

", "smithy.api#xmlName": "userData" } }, @@ -62574,7 +63166,7 @@ "target": "com.amazonaws.ec2#LaunchTemplateOverridesList", "traits": { "aws.protocols#ec2QueryName": "Overrides", - "smithy.api#documentation": "

Any parameters that you specify override the same parameters in the launch\n template.

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

Any parameters that you specify override the same parameters in the launch\n template.

", "smithy.api#xmlName": "overrides" } } @@ -63712,7 +64304,7 @@ "target": "com.amazonaws.ec2#String", "traits": { "aws.protocols#ec2QueryName": "SpotPrice", - "smithy.api#documentation": "

The maximum price per unit hour that you are willing to pay for a Spot Instance. We do not recommend using this parameter because it can lead to \n increased interruptions. If you do not specify this parameter, you will pay the current Spot price.

\n \n

If you specify a maximum price, your instances will be interrupted more frequently than if you do not specify this parameter.

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

The maximum price per unit hour that you are willing to pay for a Spot Instance. We do not recommend using this parameter because it can lead to \n increased interruptions. If you do not specify this parameter, you will pay the current Spot price.

\n \n

If you specify a maximum price, your instances will be interrupted more frequently than if you do not specify this parameter.

\n
", "smithy.api#xmlName": "spotPrice" } }, @@ -63748,7 +64340,7 @@ "aws.protocols#ec2QueryName": "Priority", "smithy.api#clientOptional": {}, "smithy.api#default": 0, - "smithy.api#documentation": "

The priority for the launch template override. The highest priority is launched\n first.

\n

If OnDemandAllocationStrategy is set to prioritized, Spot Fleet\n uses priority to determine which launch template override to use first in fulfilling\n On-Demand capacity.

\n

If the Spot AllocationStrategy is set to\n capacityOptimizedPrioritized, Spot Fleet uses priority on a best-effort basis\n to determine which launch template override to use in fulfilling Spot capacity, but\n optimizes for capacity first.

\n

Valid values are whole numbers starting at 0. The lower the number, the\n higher the priority. If no number is set, the launch template override has the lowest\n priority. You can set the same priority for different launch template overrides.

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

The priority for the launch template override. The highest priority is launched\n first.

\n

If OnDemandAllocationStrategy is set to prioritized, Spot Fleet\n uses priority to determine which launch template override to use first in fulfilling\n On-Demand capacity.

\n

If the Spot AllocationStrategy is set to\n capacityOptimizedPrioritized, Spot Fleet uses priority on a best-effort basis\n to determine which launch template override to use in fulfilling Spot capacity, but\n optimizes for capacity first.

\n

Valid values are whole numbers starting at 0. The lower the number, the\n higher the priority. If no number is set, the launch template override has the lowest\n priority. You can set the same priority for different launch template overrides.

", "smithy.api#xmlName": "priority" } }, @@ -63756,7 +64348,7 @@ "target": "com.amazonaws.ec2#InstanceRequirements", "traits": { "aws.protocols#ec2QueryName": "InstanceRequirements", - "smithy.api#documentation": "

The instance requirements. When you specify instance requirements, Amazon EC2 will identify\n instance types with the provided requirements, and then use your On-Demand and Spot\n allocation strategies to launch instances from these instance types, in the same way as\n when you specify a list of instance types.

\n \n

If you specify InstanceRequirements, you can't specify\n InstanceType.

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

The instance requirements. When you specify instance requirements, Amazon EC2 will identify\n instance types with the provided requirements, and then use your On-Demand and Spot\n allocation strategies to launch instances from these instance types, in the same way as\n when you specify a list of instance types.

\n \n

If you specify InstanceRequirements, you can't specify\n InstanceType.

\n
", "smithy.api#xmlName": "instanceRequirements" } } @@ -64381,13 +64973,13 @@ "NextToken": { "target": "com.amazonaws.ec2#String", "traits": { - "smithy.api#documentation": "

The token for the next page of results.

" + "smithy.api#documentation": "

The token returned from a previous paginated request. Pagination continues from the end of the items returned by the previous request.

" } }, "MaxResults": { "target": "com.amazonaws.ec2#ListImagesInRecycleBinMaxResults", "traits": { - "smithy.api#documentation": "

The maximum number of results to return with a single call.\n\tTo retrieve the remaining results, make another call with the returned nextToken value.

\n

If you do not specify a value for MaxResults, the request \n returns 1,000 items per page by default. For more information, see \n \n Pagination.

" + "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.

" } }, "DryRun": { @@ -64418,7 +65010,7 @@ "target": "com.amazonaws.ec2#String", "traits": { "aws.protocols#ec2QueryName": "NextToken", - "smithy.api#documentation": "

The token to use to retrieve the next page of results. This value is null when there are no more results to return.

", + "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" } } @@ -64457,13 +65049,13 @@ "MaxResults": { "target": "com.amazonaws.ec2#ListSnapshotsInRecycleBinMaxResults", "traits": { - "smithy.api#documentation": "

The maximum number of results to return with a single call.\n\tTo retrieve the remaining results, make another call with the returned nextToken value.

" + "smithy.api#documentation": "

The maximum number of items to return for this request.\n\tTo get the next page of items, make another request with the token returned in the output. \n\tFor more information, see Pagination.

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

The token for the next page of results.

" + "smithy.api#documentation": "

The token returned from a previous paginated request.\n Pagination continues from the end of the items returned by the previous request.

" } }, "SnapshotIds": { @@ -64501,7 +65093,7 @@ "target": "com.amazonaws.ec2#String", "traits": { "aws.protocols#ec2QueryName": "NextToken", - "smithy.api#documentation": "

The token to use to retrieve the next page of results. This value is null when there are no more results to return.

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

The token to include in another request to get the next page of items. \n This value is null when there are no more items to return.

", "smithy.api#xmlName": "nextToken" } } @@ -64826,6 +65418,14 @@ "smithy.api#documentation": "

The ID of the network interface.

", "smithy.api#xmlName": "networkInterfaceId" } + }, + "DestinationPrefixListId": { + "target": "com.amazonaws.ec2#PrefixListResourceId", + "traits": { + "aws.protocols#ec2QueryName": "DestinationPrefixListId", + "smithy.api#documentation": "

\n The ID of the prefix list.\n

", + "smithy.api#xmlName": "destinationPrefixListId" + } } }, "traits": { @@ -66391,7 +66991,7 @@ "ExcessCapacityTerminationPolicy": { "target": "com.amazonaws.ec2#FleetExcessCapacityTerminationPolicy", "traits": { - "smithy.api#documentation": "

Indicates whether running instances should be terminated if the total target capacity of\n the EC2 Fleet is decreased below the current size of the EC2 Fleet.

" + "smithy.api#documentation": "

Indicates whether running instances should be terminated if the total target capacity of\n the EC2 Fleet is decreased below the current size of the EC2 Fleet.

\n

Supported only for fleets of type maintain.

" } }, "LaunchTemplateConfigs": { @@ -66591,6 +67191,12 @@ "traits": { "smithy.api#documentation": "

Specifies the instance family to be supported by the Dedicated Host. Specify this\n parameter to modify a Dedicated Host to support multiple instance types within its\n current instance family.

\n

If you want to modify a Dedicated Host to support a specific instance type only, omit\n this parameter and specify InstanceType instead. You\n cannot specify InstanceFamily and InstanceType in the same request.

" } + }, + "HostMaintenance": { + "target": "com.amazonaws.ec2#HostMaintenance", + "traits": { + "smithy.api#documentation": "

Indicates whether to enable or disable host maintenance for the Dedicated Host. For\n more information, see Host\n maintenance in the Amazon EC2 User Guide.

" + } } }, "traits": { @@ -66715,7 +67321,7 @@ "target": "smithy.api#Unit" }, "traits": { - "smithy.api#documentation": "

Modifies the specified attribute of the specified AMI. You can specify only one attribute at a time.\n You can use the Attribute parameter to specify the attribute or one of the following parameters: \n Description or LaunchPermission.

\n

Images with an Amazon Web Services Marketplace product code cannot be made public.

\n

To enable the SriovNetSupport enhanced networking attribute of an image, enable SriovNetSupport on an instance \n and create an AMI from the instance.

" + "smithy.api#documentation": "

Modifies the specified attribute of the specified AMI. You can specify only one attribute at a time.

\n

To specify the attribute, you can use the Attribute parameter, or one of the following parameters: \n Description, ImdsSupport, or LaunchPermission.

\n

Images with an Amazon Web Services Marketplace product code cannot be made public.

\n

To enable the SriovNetSupport enhanced networking attribute of an image, enable SriovNetSupport on an instance \n and create an AMI from the instance.

" } }, "com.amazonaws.ec2#ModifyImageAttributeRequest": { @@ -66724,7 +67330,7 @@ "Attribute": { "target": "com.amazonaws.ec2#String", "traits": { - "smithy.api#documentation": "

The name of the attribute to modify.

\n

Valid values: description | launchPermission\n

" + "smithy.api#documentation": "

The name of the attribute to modify.

\n

Valid values: description | imdsSupport | launchPermission\n

" } }, "Description": { @@ -66777,7 +67383,7 @@ "Value": { "target": "com.amazonaws.ec2#String", "traits": { - "smithy.api#documentation": "

The value of the attribute being modified. \n This parameter can be used only when the Attribute parameter is description.

" + "smithy.api#documentation": "

The value of the attribute being modified. \n This parameter can be used only when the Attribute parameter is description or imdsSupport.

" } }, "DryRun": { @@ -66803,6 +67409,12 @@ "smithy.api#documentation": "

The Amazon Resource Name (ARN) of an organizational unit (OU). This parameter can be used only when the Attribute parameter is launchPermission.

", "smithy.api#xmlName": "OrganizationalUnitArn" } + }, + "ImdsSupport": { + "target": "com.amazonaws.ec2#AttributeValue", + "traits": { + "smithy.api#documentation": "

Set to v2.0 to indicate that IMDSv2 is specified in the AMI. Instances\n launched from this AMI will have HttpTokens automatically set to\n required so that, by default, the instance requires that IMDSv2 is used when\n requesting instance metadata. In addition, HttpPutResponseHopLimit is set to\n 2. For more information, see Configure\n the AMI in the Amazon EC2 User Guide.

\n \n

Do not use this parameter unless your AMI software supports IMDSv2. After you set the value to v2.0, \n you can't undo it. The only way to “reset” your AMI is to create a new AMI from the underlying snapshot.

\n
" + } } }, "traits": { @@ -67730,7 +68342,7 @@ "target": "com.amazonaws.ec2#ModifyIpamResourceDiscoveryResult" }, "traits": { - "smithy.api#documentation": "

Modifies a resource discovery. A resource discovery is an IPAM component that enables IPAM Service to manage and monitor resources that belong to the owning account.

" + "smithy.api#documentation": "

Modifies a resource discovery. A resource discovery is an IPAM component that enables IPAM to manage and monitor resources that belong to the owning account.

" } }, "com.amazonaws.ec2#ModifyIpamResourceDiscoveryRequest": { @@ -67942,9 +68554,7 @@ "DestinationCidrBlock": { "target": "com.amazonaws.ec2#String", "traits": { - "smithy.api#clientOptional": {}, - "smithy.api#documentation": "

The CIDR block used for destination matches. The value that you provide must match the CIDR of an existing route in the table.

", - "smithy.api#required": {} + "smithy.api#documentation": "

The CIDR block used for destination matches. The value that you provide must match the CIDR of an existing route in the table.

" } }, "LocalGatewayRouteTableId": { @@ -67974,6 +68584,12 @@ "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.

" } + }, + "DestinationPrefixListId": { + "target": "com.amazonaws.ec2#PrefixListResourceId", + "traits": { + "smithy.api#documentation": "

\n The ID of the prefix list. Use a prefix list in place of DestinationCidrBlock. You \n cannot use DestinationPrefixListId and DestinationCidrBlock in the same request.\n

" + } } }, "traits": { @@ -68182,7 +68798,9 @@ "InstanceId": { "target": "com.amazonaws.ec2#InstanceId", "traits": { - "smithy.api#documentation": "

The ID of the instance.

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

The ID of the instance.

", + "smithy.api#required": {} } }, "PrivateDnsHostnameType": { @@ -68502,7 +69120,7 @@ "target": "com.amazonaws.ec2#ExcessCapacityTerminationPolicy", "traits": { "aws.protocols#ec2QueryName": "ExcessCapacityTerminationPolicy", - "smithy.api#documentation": "

Indicates whether running Spot Instances should be terminated if the target capacity\n of the Spot Fleet request is decreased below the current size of the Spot Fleet.

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

Indicates whether running instances should be terminated if the target capacity\n of the Spot Fleet request is decreased below the current size of the Spot Fleet.

\n

Supported only for fleets of type maintain.

", "smithy.api#xmlName": "excessCapacityTerminationPolicy" } }, @@ -68743,7 +69361,7 @@ "type": "structure", "members": { "TrafficMirrorFilterRuleId": { - "target": "com.amazonaws.ec2#TrafficMirrorFilterRuleId", + "target": "com.amazonaws.ec2#TrafficMirrorFilterRuleIdWithResolver", "traits": { "smithy.api#clientOptional": {}, "smithy.api#documentation": "

The ID of the Traffic Mirror rule.

", @@ -70435,7 +71053,7 @@ "target": "com.amazonaws.ec2#ModifyVpcPeeringConnectionOptionsResult" }, "traits": { - "smithy.api#documentation": "\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

Modifies the VPC peering connection options on one side of a VPC peering connection. You can do the following:

\n
    \n
  • \n

    Enable/disable communication over the peering connection between an EC2-Classic instance that's linked to your VPC (using ClassicLink) and instances in the peer VPC.

    \n
  • \n
  • \n

    Enable/disable communication over the peering connection between instances in your VPC and an EC2-Classic instance that's linked to the peer VPC.

    \n
  • \n
  • \n

    Enable/disable the ability to resolve public DNS hostnames to private IP\n addresses when queried from instances in the peer VPC.

    \n
  • \n
\n

If the peered VPCs are in the same Amazon Web Services account, you can enable DNS resolution \n for queries from the local VPC. This ensures that queries from the local VPC resolve to private IP\n addresses in the peer VPC. This option is not available if the peered VPCs are in different\n different Amazon Web Services accounts or different Regions. For peered VPCs in different \n Amazon Web Services accounts, each Amazon Web Services account owner must initiate a separate request \n to modify the peering connection options. For inter-region peering connections, you must use the \n Region for the requester VPC to modify the requester VPC peering options and the Region for the \n accepter VPC to modify the accepter VPC peering options. To verify which VPCs are the accepter and \n the requester for a VPC peering connection, use the DescribeVpcPeeringConnections command.

" + "smithy.api#documentation": "\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

Modifies the VPC peering connection options on one side of a VPC peering connection. You can do the following:

\n
    \n
  • \n

    Enable/disable communication over the peering connection between an EC2-Classic instance that's linked to your VPC (using ClassicLink) and instances in the peer VPC.

    \n
  • \n
  • \n

    Enable/disable communication over the peering connection between instances in your VPC and an EC2-Classic instance that's linked to the peer VPC.

    \n
  • \n
  • \n

    Enable/disable the ability to resolve public DNS hostnames to private IP\n addresses when queried from instances in the peer VPC.

    \n
  • \n
\n

If the peered VPCs are in the same Amazon Web Services account, you can enable DNS\n resolution for queries from the local VPC. This ensures that queries from the local VPC\n resolve to private IP addresses in the peer VPC. This option is not available if the\n peered VPCs are in different Amazon Web Services accounts or different Regions. For\n peered VPCs in different Amazon Web Services accounts, each Amazon Web Services account\n owner must initiate a separate request to modify the peering connection options. For\n inter-region peering connections, you must use the Region for the requester VPC to\n modify the requester VPC peering options and the Region for the accepter VPC to modify\n the accepter VPC peering options. To verify which VPCs are the accepter and the\n requester for a VPC peering connection, use the DescribeVpcPeeringConnections command.

" } }, "com.amazonaws.ec2#ModifyVpcPeeringConnectionOptionsRequest": { @@ -71385,6 +72003,40 @@ "smithy.api#documentation": "

[Public NAT gateway only] The Elastic IP address associated with the NAT gateway.

", "smithy.api#xmlName": "publicIp" } + }, + "AssociationId": { + "target": "com.amazonaws.ec2#String", + "traits": { + "aws.protocols#ec2QueryName": "AssociationId", + "smithy.api#documentation": "

[Public NAT gateway only] The association ID of the Elastic IP address that's associated with the NAT gateway.

", + "smithy.api#xmlName": "associationId" + } + }, + "IsPrimary": { + "target": "com.amazonaws.ec2#Boolean", + "traits": { + "aws.protocols#ec2QueryName": "IsPrimary", + "smithy.api#clientOptional": {}, + "smithy.api#default": false, + "smithy.api#documentation": "

Defines if the IP address is the primary address.

", + "smithy.api#xmlName": "isPrimary" + } + }, + "FailureMessage": { + "target": "com.amazonaws.ec2#String", + "traits": { + "aws.protocols#ec2QueryName": "FailureMessage", + "smithy.api#documentation": "

The address failure message.

", + "smithy.api#xmlName": "failureMessage" + } + }, + "Status": { + "target": "com.amazonaws.ec2#NatGatewayAddressStatus", + "traits": { + "aws.protocols#ec2QueryName": "Status", + "smithy.api#documentation": "

The address status.

", + "smithy.api#xmlName": "status" + } } }, "traits": { @@ -71400,6 +72052,47 @@ } } }, + "com.amazonaws.ec2#NatGatewayAddressStatus": { + "type": "enum", + "members": { + "ASSIGNING": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "assigning" + } + }, + "UNASSIGNING": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "unassigning" + } + }, + "ASSOCIATING": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "associating" + } + }, + "DISASSOCIATING": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "disassociating" + } + }, + "SUCCEEDED": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "succeeded" + } + }, + "FAILED": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "failed" + } + } + } + }, "com.amazonaws.ec2#NatGatewayId": { "type": "string" }, @@ -75568,6 +76261,16 @@ } } }, + "com.amazonaws.ec2#PrivateIpAddressCount": { + "type": "integer", + "traits": { + "smithy.api#default": 0, + "smithy.api#range": { + "min": 1, + "max": 7 + } + } + }, "com.amazonaws.ec2#PrivateIpAddressSpecification": { "type": "structure", "members": { @@ -76608,22 +77311,32 @@ } }, "com.amazonaws.ec2#RIProductDescription": { - "type": "string", - "traits": { - "smithy.api#enum": [ - { - "value": "Linux/UNIX" - }, - { - "value": "Linux/UNIX (Amazon VPC)" - }, - { - "value": "Windows" - }, - { - "value": "Windows (Amazon VPC)" + "type": "enum", + "members": { + "Linux_UNIX": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "Linux/UNIX" } - ] + }, + "Linux_UNIX_Amazon_VPC_": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "Linux/UNIX (Amazon VPC)" + } + }, + "Windows": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "Windows" + } + }, + "Windows_Amazon_VPC_": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "Windows (Amazon VPC)" + } + } } }, "com.amazonaws.ec2#RamdiskId": { @@ -76957,7 +77670,7 @@ "BootMode": { "target": "com.amazonaws.ec2#BootModeValues", "traits": { - "smithy.api#documentation": "

The boot mode of the AMI. For more information, see Boot modes in the\n Amazon EC2 User Guide.

" + "smithy.api#documentation": "

The boot mode of the AMI. A value of uefi-preferred indicates that the AMI supports both UEFI and Legacy BIOS.

\n \n

The operating system contained in the AMI must be configured to support the specified boot mode.

\n
\n

For more information, see Boot modes in the\n Amazon EC2 User Guide.

" } }, "TpmSupport": { @@ -77088,7 +77801,9 @@ "TransitGatewayMulticastDomainId": { "target": "com.amazonaws.ec2#TransitGatewayMulticastDomainId", "traits": { - "smithy.api#documentation": "

The ID of the transit gateway multicast domain.

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

The ID of the transit gateway multicast domain.

", + "smithy.api#required": {} } }, "GroupIpAddress": { @@ -77100,7 +77815,9 @@ "NetworkInterfaceIds": { "target": "com.amazonaws.ec2#TransitGatewayNetworkInterfaceIdList", "traits": { - "smithy.api#documentation": "

The group members' network interface IDs to register with the transit gateway multicast group.

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

The group members' network interface IDs to register with the transit gateway multicast group.

", + "smithy.api#required": {} } }, "DryRun": { @@ -77147,7 +77864,9 @@ "TransitGatewayMulticastDomainId": { "target": "com.amazonaws.ec2#TransitGatewayMulticastDomainId", "traits": { - "smithy.api#documentation": "

The ID of the transit gateway multicast domain.

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

The ID of the transit gateway multicast domain.

", + "smithy.api#required": {} } }, "GroupIpAddress": { @@ -77159,7 +77878,9 @@ "NetworkInterfaceIds": { "target": "com.amazonaws.ec2#TransitGatewayNetworkInterfaceIdList", "traits": { - "smithy.api#documentation": "

The group sources' network interface IDs to register with the transit gateway multicast group.

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

The group sources' network interface IDs to register with the transit gateway multicast group.

", + "smithy.api#required": {} } }, "DryRun": { @@ -78613,7 +79334,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 \n Manager parameter instead of an AMI ID 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

For more information, see Use a Systems Manager parameter to find an AMI in the Amazon Elastic Compute Cloud User Guide.

" } }, "InstanceType": { @@ -79105,10 +79826,10 @@ } }, "UserData": { - "target": "com.amazonaws.ec2#String", + "target": "com.amazonaws.ec2#SensitiveUserData", "traits": { "aws.protocols#ec2QueryName": "UserData", - "smithy.api#documentation": "

The Base64-encoded user data for the instance. User data is limited to 16 KB.

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

The base64-encoded user data that instances use when starting up. User data is limited to 16 KB.

", "smithy.api#xmlName": "userData" } } @@ -81181,7 +81902,7 @@ } }, "UserData": { - "target": "com.amazonaws.ec2#String", + "target": "com.amazonaws.ec2#SensitiveUserData", "traits": { "aws.protocols#ec2QueryName": "UserData", "smithy.api#documentation": "

The user data for the instance.

", @@ -82064,6 +82785,9 @@ } } }, + "com.amazonaws.ec2#RoleId": { + "type": "string" + }, "com.amazonaws.ec2#RootDeviceType": { "type": "enum", "members": { @@ -82947,7 +83671,7 @@ "AWSAccessKeyId": { "target": "com.amazonaws.ec2#String", "traits": { - "smithy.api#documentation": "

The access key ID of the owner of the bucket. Before you specify a value for your access\n key ID, review and follow the guidance in Best practices for managing\n Amazon Web Services access keys.

" + "smithy.api#documentation": "

The access key ID of the owner of the bucket. Before you specify a value for your access\n key ID, review and follow the guidance in Best\n Practices for Amazon Web Services accounts in the Account ManagementReference\n Guide.

" } }, "Bucket": { @@ -83600,7 +84324,8 @@ } }, "traits": { - "smithy.api#documentation": "

Describes the launch specification for a Scheduled Instance.

\n

If you are launching the Scheduled Instance in EC2-VPC, you must specify the ID of the subnet.\n You can specify the subnet using either SubnetId or NetworkInterface.

" + "smithy.api#documentation": "

Describes the launch specification for a Scheduled Instance.

\n

If you are launching the Scheduled Instance in EC2-VPC, you must specify the ID of the subnet.\n You can specify the subnet using either SubnetId or NetworkInterface.

", + "smithy.api#sensitive": {} } }, "com.amazonaws.ec2#ScheduledInstancesMonitoring": { @@ -83804,7 +84529,7 @@ "Filters": { "target": "com.amazonaws.ec2#FilterList", "traits": { - "smithy.api#documentation": "

One or more filters.

\n
    \n
  • \n

    \n route-search.exact-match - The exact match of the specified filter.

    \n
  • \n
  • \n

    \n route-search.longest-prefix-match - The longest prefix that matches the route.

    \n
  • \n
  • \n

    \n route-search.subnet-of-match - The routes with a subnet that match the specified CIDR filter.

    \n
  • \n
  • \n

    \n route-search.supernet-of-match - The routes with a CIDR that encompass the CIDR filter. \n For example, if you have 10.0.1.0/29 and 10.0.1.0/31 routes in your route table and you specify supernet-of-match \n as 10.0.1.0/30, then the result returns 10.0.1.0/29.

    \n
  • \n
  • \n

    \n state - The state of the route.

    \n
  • \n
  • \n

    \n type - The route type.

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

One or more filters.

\n
    \n
  • \n

    \n prefix-list-id - The ID of the prefix list.

    \n
  • \n
  • \n

    \n route-search.exact-match - The exact match of the specified filter.

    \n
  • \n
  • \n

    \n route-search.longest-prefix-match - The longest prefix that matches the route.

    \n
  • \n
  • \n

    \n route-search.subnet-of-match - The routes with a subnet that match the specified CIDR filter.

    \n
  • \n
  • \n

    \n route-search.supernet-of-match - The routes with a CIDR that encompass the CIDR filter. \n For example, if you have 10.0.1.0/29 and 10.0.1.0/31 routes in your route table and you specify supernet-of-match \n as 10.0.1.0/30, then the result returns 10.0.1.0/29.

    \n
  • \n
  • \n

    \n state - The state of the route.

    \n
  • \n
  • \n

    \n type - The route type.

    \n
  • \n
", "smithy.api#xmlName": "Filter" } }, @@ -83878,7 +84603,9 @@ "TransitGatewayMulticastDomainId": { "target": "com.amazonaws.ec2#TransitGatewayMulticastDomainId", "traits": { - "smithy.api#documentation": "

The ID of the transit gateway multicast domain.

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

The ID of the transit gateway multicast domain.

", + "smithy.api#required": {} } }, "Filters": { @@ -84416,7 +85143,9 @@ "SecurityGroupRuleId": { "target": "com.amazonaws.ec2#SecurityGroupRuleId", "traits": { - "smithy.api#documentation": "

The ID of the security group rule.

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

The ID of the security group rule.

", + "smithy.api#required": {} } }, "SecurityGroupRule": { @@ -85911,10 +86640,10 @@ } }, "UserData": { - "target": "com.amazonaws.ec2#String", + "target": "com.amazonaws.ec2#SensitiveUserData", "traits": { "aws.protocols#ec2QueryName": "UserData", - "smithy.api#documentation": "

The Base64-encoded user data that instances use when starting up.

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

The base64-encoded user data that instances use when starting up. User data is limited to 16 KB.

", "smithy.api#xmlName": "userData" } }, @@ -85940,7 +86669,7 @@ "target": "com.amazonaws.ec2#InstanceRequirements", "traits": { "aws.protocols#ec2QueryName": "InstanceRequirements", - "smithy.api#documentation": "

The attributes for the instance types. When you specify instance attributes, Amazon EC2 will\n identify instance types with those attributes.

\n \n

If you specify InstanceRequirements, you can't specify\n InstanceType.

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

The attributes for the instance types. When you specify instance attributes, Amazon EC2 will\n identify instance types with those attributes.

\n \n

If you specify InstanceRequirements, you can't specify\n InstanceType.

\n
", "smithy.api#xmlName": "instanceRequirements" } } @@ -86062,7 +86791,7 @@ "target": "com.amazonaws.ec2#ExcessCapacityTerminationPolicy", "traits": { "aws.protocols#ec2QueryName": "ExcessCapacityTerminationPolicy", - "smithy.api#documentation": "

Indicates whether running Spot Instances should be terminated if you decrease the\n target capacity of the Spot Fleet request below the current size of the Spot\n Fleet.

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

Indicates whether running instances should be terminated if you decrease the\n target capacity of the Spot Fleet request below the current size of the Spot Fleet.

\n

Supported only for fleets of type maintain.

", "smithy.api#xmlName": "excessCapacityTerminationPolicy" } }, @@ -88491,7 +89220,7 @@ } }, "traits": { - "smithy.api#documentation": "

The tags to apply to a resource when the resource is being created.

\n \n

The Valid Values lists all the resource types that can be tagged.\n However, the action you're using might not support tagging all of these resource types.\n If you try to tag a resource type that is unsupported for the action you're using,\n you'll get an error.

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

The tags to apply to a resource when the resource is being created. When you specify a tag, you must \n specify the resource type to tag, otherwise the request will fail.

\n \n

The Valid Values lists all the resource types that can be tagged.\n However, the action you're using might not support tagging all of these resource types.\n If you try to tag a resource type that is unsupported for the action you're using,\n you'll get an error.

\n
" } }, "com.amazonaws.ec2#TagSpecificationList": { @@ -89455,7 +90184,7 @@ "target": "com.amazonaws.ec2#TrafficMirrorFilterRuleField" } }, - "com.amazonaws.ec2#TrafficMirrorFilterRuleId": { + "com.amazonaws.ec2#TrafficMirrorFilterRuleIdWithResolver": { "type": "string" }, "com.amazonaws.ec2#TrafficMirrorFilterRuleList": { @@ -93194,6 +93923,80 @@ "smithy.api#input": {} } }, + "com.amazonaws.ec2#UnassignPrivateNatGatewayAddress": { + "type": "operation", + "input": { + "target": "com.amazonaws.ec2#UnassignPrivateNatGatewayAddressRequest" + }, + "output": { + "target": "com.amazonaws.ec2#UnassignPrivateNatGatewayAddressResult" + }, + "traits": { + "smithy.api#documentation": "

Unassigns secondary private IPv4 addresses from a private NAT gateway. You cannot unassign your primary private IP. For more information, see Edit secondary IP address associations in the Amazon Virtual Private Cloud User Guide.

\n

While unassigning is in progress, you cannot assign/unassign additional IP addresses while the connections are being drained. You are, however, allowed to delete the NAT gateway.

\n

A private IP address will only be released at the end of MaxDrainDurationSeconds. The\n private IP addresses stay associated and support the existing connections but do not\n support any new connections (new connections are distributed across the remaining\n assigned private IP address). After the existing connections drain out, the private IP\n addresses get released.

\n

\n

" + } + }, + "com.amazonaws.ec2#UnassignPrivateNatGatewayAddressRequest": { + "type": "structure", + "members": { + "NatGatewayId": { + "target": "com.amazonaws.ec2#NatGatewayId", + "traits": { + "smithy.api#clientOptional": {}, + "smithy.api#documentation": "

The NAT gateway ID.

", + "smithy.api#required": {} + } + }, + "PrivateIpAddresses": { + "target": "com.amazonaws.ec2#IpList", + "traits": { + "smithy.api#clientOptional": {}, + "smithy.api#documentation": "

The private IPv4 addresses you want to unassign.

", + "smithy.api#required": {}, + "smithy.api#xmlName": "PrivateIpAddress" + } + }, + "MaxDrainDurationSeconds": { + "target": "com.amazonaws.ec2#DrainSeconds", + "traits": { + "smithy.api#clientOptional": {}, + "smithy.api#default": 0, + "smithy.api#documentation": "

The maximum amount of time to wait (in seconds) before forcibly releasing the IP addresses if connections are still in progress. Default value is 350 seconds.

" + } + }, + "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.

" + } + } + }, + "traits": { + "smithy.api#input": {} + } + }, + "com.amazonaws.ec2#UnassignPrivateNatGatewayAddressResult": { + "type": "structure", + "members": { + "NatGatewayId": { + "target": "com.amazonaws.ec2#NatGatewayId", + "traits": { + "aws.protocols#ec2QueryName": "NatGatewayId", + "smithy.api#documentation": "

The NAT gateway ID.

", + "smithy.api#xmlName": "natGatewayId" + } + }, + "NatGatewayAddresses": { + "target": "com.amazonaws.ec2#NatGatewayAddressList", + "traits": { + "aws.protocols#ec2QueryName": "NatGatewayAddressSet", + "smithy.api#documentation": "

Information about the NAT gateway IP addresses.

", + "smithy.api#xmlName": "natGatewayAddressSet" + } + } + } + }, "com.amazonaws.ec2#UnlimitedSupportedInstanceFamily": { "type": "enum", "members": { diff --git a/aws/sdk/aws-models/ecs.json b/aws/sdk/aws-models/ecs.json index eabba64bc6..b408252d21 100644 --- a/aws/sdk/aws-models/ecs.json +++ b/aws/sdk/aws-models/ecs.json @@ -113,6 +113,9 @@ { "target": "com.amazonaws.ecs#DeleteService" }, + { + "target": "com.amazonaws.ecs#DeleteTaskDefinitions" + }, { "target": "com.amazonaws.ecs#DeleteTaskSet" }, @@ -274,7 +277,7 @@ "parameters": { "Region": { "builtIn": "AWS::Region", - "required": true, + "required": false, "documentation": "The AWS region used to dispatch the request.", "type": "String" }, @@ -303,13 +306,12 @@ { "conditions": [ { - "fn": "aws.partition", + "fn": "isSet", "argv": [ { - "ref": "Region" + "ref": "Endpoint" } - ], - "assign": "PartitionResult" + ] } ], "type": "tree", @@ -317,14 +319,20 @@ { "conditions": [ { - "fn": "isSet", + "fn": "booleanEquals", "argv": [ { - "ref": "Endpoint" - } + "ref": "UseFIPS" + }, + true ] } ], + "error": "Invalid Configuration: FIPS and custom endpoint are not supported", + "type": "error" + }, + { + "conditions": [], "type": "tree", "rules": [ { @@ -333,67 +341,42 @@ "fn": "booleanEquals", "argv": [ { - "ref": "UseFIPS" + "ref": "UseDualStack" }, true ] } ], - "error": "Invalid Configuration: FIPS and custom endpoint are not supported", + "error": "Invalid Configuration: Dualstack 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" + "endpoint": { + "url": { + "ref": "Endpoint" }, - { - "conditions": [], - "endpoint": { - "url": { - "ref": "Endpoint" - }, - "properties": {}, - "headers": {} - }, - "type": "endpoint" - } - ] + "properties": {}, + "headers": {} + }, + "type": "endpoint" } ] - }, + } + ] + }, + { + "conditions": [], + "type": "tree", + "rules": [ { "conditions": [ { - "fn": "booleanEquals", - "argv": [ - { - "ref": "UseFIPS" - }, - true - ] - }, - { - "fn": "booleanEquals", + "fn": "isSet", "argv": [ { - "ref": "UseDualStack" - }, - true + "ref": "Region" + } ] } ], @@ -402,154 +385,215 @@ { "conditions": [ { - "fn": "booleanEquals", + "fn": "aws.partition", "argv": [ - true, { - "fn": "getAttr", + "ref": "Region" + } + ], + "assign": "PartitionResult" + } + ], + "type": "tree", + "rules": [ + { + "conditions": [ + { + "fn": "booleanEquals", "argv": [ { - "ref": "PartitionResult" + "ref": "UseFIPS" }, - "supportsFIPS" + true ] - } - ] - }, - { - "fn": "booleanEquals", - "argv": [ - true, + }, { - "fn": "getAttr", + "fn": "booleanEquals", "argv": [ { - "ref": "PartitionResult" + "ref": "UseDualStack" }, - "supportsDualStack" + true ] } - ] - } - ], - "type": "tree", - "rules": [ - { - "conditions": [], + ], "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://ecs-fips.{Region}.{PartitionResult#dualStackDnsSuffix}", + "properties": {}, + "headers": {} + }, + "type": "endpoint" + } + ] + } + ] + }, { "conditions": [], - "endpoint": { - "url": "https://ecs-fips.{Region}.{PartitionResult#dualStackDnsSuffix}", - "properties": {}, - "headers": {} - }, - "type": "endpoint" + "error": "FIPS and DualStack are enabled, but this partition does not support one or both", + "type": "error" } ] - } - ] - }, - { - "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, + "conditions": [ { - "fn": "getAttr", + "fn": "booleanEquals", "argv": [ { - "ref": "PartitionResult" + "ref": "UseFIPS" }, - "supportsFIPS" + true ] } - ] - } - ], - "type": "tree", - "rules": [ - { - "conditions": [], + ], "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://ecs-fips.{Region}.{PartitionResult#dnsSuffix}", + "properties": {}, + "headers": {} + }, + "type": "endpoint" + } + ] + } + ] + }, { "conditions": [], - "endpoint": { - "url": "https://ecs-fips.{Region}.{PartitionResult#dnsSuffix}", - "properties": {}, - "headers": {} - }, - "type": "endpoint" + "error": "FIPS is enabled but this partition does not support FIPS", + "type": "error" } ] - } - ] - }, - { - "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, + "conditions": [ { - "fn": "getAttr", + "fn": "booleanEquals", "argv": [ { - "ref": "PartitionResult" + "ref": "UseDualStack" }, - "supportsDualStack" + 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://ecs.{Region}.{PartitionResult#dualStackDnsSuffix}", + "properties": {}, + "headers": {} + }, + "type": "endpoint" + } + ] + } + ] + }, + { + "conditions": [], + "error": "DualStack is enabled but this partition does not support DualStack", + "type": "error" + } ] - } - ], - "type": "tree", - "rules": [ + }, { "conditions": [], "type": "tree", @@ -557,7 +601,7 @@ { "conditions": [], "endpoint": { - "url": "https://ecs.{Region}.{PartitionResult#dualStackDnsSuffix}", + "url": "https://ecs.{Region}.{PartitionResult#dnsSuffix}", "properties": {}, "headers": {} }, @@ -566,28 +610,13 @@ ] } ] - }, - { - "conditions": [], - "error": "DualStack is enabled but this partition does not support DualStack", - "type": "error" } ] }, { "conditions": [], - "type": "tree", - "rules": [ - { - "conditions": [], - "endpoint": { - "url": "https://ecs.{Region}.{PartitionResult#dnsSuffix}", - "properties": {}, - "headers": {} - }, - "type": "endpoint" - } - ] + "error": "Invalid Configuration: Missing Region", + "type": "error" } ] } @@ -596,16 +625,16 @@ "smithy.rules#endpointTests": { "testCases": [ { - "documentation": "For region sa-east-1 with FIPS disabled and DualStack disabled", + "documentation": "For region af-south-1 with FIPS disabled and DualStack disabled", "expect": { "endpoint": { - "url": "https://ecs.sa-east-1.amazonaws.com" + "url": "https://ecs.af-south-1.amazonaws.com" } }, "params": { - "UseFIPS": false, "UseDualStack": false, - "Region": "sa-east-1" + "UseFIPS": false, + "Region": "af-south-1" } }, { @@ -616,35 +645,61 @@ } }, "params": { - "UseFIPS": false, "UseDualStack": false, + "UseFIPS": false, "Region": "ap-east-1" } }, { - "documentation": "For region eu-south-1 with FIPS disabled and DualStack disabled", + "documentation": "For region ap-northeast-1 with FIPS disabled and DualStack disabled", "expect": { "endpoint": { - "url": "https://ecs.eu-south-1.amazonaws.com" + "url": "https://ecs.ap-northeast-1.amazonaws.com" } }, "params": { + "UseDualStack": false, "UseFIPS": false, + "Region": "ap-northeast-1" + } + }, + { + "documentation": "For region ap-northeast-2 with FIPS disabled and DualStack disabled", + "expect": { + "endpoint": { + "url": "https://ecs.ap-northeast-2.amazonaws.com" + } + }, + "params": { "UseDualStack": false, - "Region": "eu-south-1" + "UseFIPS": false, + "Region": "ap-northeast-2" } }, { - "documentation": "For region eu-central-1 with FIPS disabled and DualStack disabled", + "documentation": "For region ap-northeast-3 with FIPS disabled and DualStack disabled", "expect": { "endpoint": { - "url": "https://ecs.eu-central-1.amazonaws.com" + "url": "https://ecs.ap-northeast-3.amazonaws.com" } }, "params": { + "UseDualStack": false, "UseFIPS": false, + "Region": "ap-northeast-3" + } + }, + { + "documentation": "For region ap-south-1 with FIPS disabled and DualStack disabled", + "expect": { + "endpoint": { + "url": "https://ecs.ap-south-1.amazonaws.com" + } + }, + "params": { "UseDualStack": false, - "Region": "eu-central-1" + "UseFIPS": false, + "Region": "ap-south-1" } }, { @@ -655,8 +710,8 @@ } }, "params": { - "UseFIPS": false, "UseDualStack": false, + "UseFIPS": false, "Region": "ap-southeast-1" } }, @@ -668,8 +723,8 @@ } }, "params": { - "UseFIPS": false, "UseDualStack": false, + "UseFIPS": false, "Region": "ap-southeast-2" } }, @@ -681,8 +736,8 @@ } }, "params": { - "UseFIPS": false, "UseDualStack": false, + "UseFIPS": false, "Region": "ap-southeast-3" } }, @@ -694,126 +749,113 @@ } }, "params": { - "UseFIPS": false, "UseDualStack": false, + "UseFIPS": false, "Region": "ca-central-1" } }, { - "documentation": "For region us-west-1 with FIPS disabled and DualStack disabled", + "documentation": "For region eu-central-1 with FIPS disabled and DualStack disabled", "expect": { "endpoint": { - "url": "https://ecs.us-west-1.amazonaws.com" + "url": "https://ecs.eu-central-1.amazonaws.com" } }, "params": { - "UseFIPS": false, "UseDualStack": false, - "Region": "us-west-1" + "UseFIPS": false, + "Region": "eu-central-1" } }, { - "documentation": "For region us-west-1 with FIPS enabled and DualStack disabled", + "documentation": "For region eu-north-1 with FIPS disabled and DualStack disabled", "expect": { "endpoint": { - "url": "https://ecs-fips.us-west-1.amazonaws.com" + "url": "https://ecs.eu-north-1.amazonaws.com" } }, "params": { - "UseFIPS": true, "UseDualStack": false, - "Region": "us-west-1" - } - }, - { - "documentation": "For region us-west-2 with FIPS disabled and DualStack disabled", - "expect": { - "endpoint": { - "url": "https://ecs.us-west-2.amazonaws.com" - } - }, - "params": { "UseFIPS": false, - "UseDualStack": false, - "Region": "us-west-2" + "Region": "eu-north-1" } }, { - "documentation": "For region us-west-2 with FIPS enabled and DualStack disabled", + "documentation": "For region eu-south-1 with FIPS disabled and DualStack disabled", "expect": { "endpoint": { - "url": "https://ecs-fips.us-west-2.amazonaws.com" + "url": "https://ecs.eu-south-1.amazonaws.com" } }, "params": { - "UseFIPS": true, "UseDualStack": false, - "Region": "us-west-2" + "UseFIPS": false, + "Region": "eu-south-1" } }, { - "documentation": "For region af-south-1 with FIPS disabled and DualStack disabled", + "documentation": "For region eu-west-1 with FIPS disabled and DualStack disabled", "expect": { "endpoint": { - "url": "https://ecs.af-south-1.amazonaws.com" + "url": "https://ecs.eu-west-1.amazonaws.com" } }, "params": { - "UseFIPS": false, "UseDualStack": false, - "Region": "af-south-1" + "UseFIPS": false, + "Region": "eu-west-1" } }, { - "documentation": "For region ap-south-1 with FIPS disabled and DualStack disabled", + "documentation": "For region eu-west-2 with FIPS disabled and DualStack disabled", "expect": { "endpoint": { - "url": "https://ecs.ap-south-1.amazonaws.com" + "url": "https://ecs.eu-west-2.amazonaws.com" } }, "params": { - "UseFIPS": false, "UseDualStack": false, - "Region": "ap-south-1" + "UseFIPS": false, + "Region": "eu-west-2" } }, { - "documentation": "For region ap-northeast-1 with FIPS disabled and DualStack disabled", + "documentation": "For region eu-west-3 with FIPS disabled and DualStack disabled", "expect": { "endpoint": { - "url": "https://ecs.ap-northeast-1.amazonaws.com" + "url": "https://ecs.eu-west-3.amazonaws.com" } }, "params": { - "UseFIPS": false, "UseDualStack": false, - "Region": "ap-northeast-1" + "UseFIPS": false, + "Region": "eu-west-3" } }, { - "documentation": "For region ap-northeast-2 with FIPS disabled and DualStack disabled", + "documentation": "For region me-south-1 with FIPS disabled and DualStack disabled", "expect": { "endpoint": { - "url": "https://ecs.ap-northeast-2.amazonaws.com" + "url": "https://ecs.me-south-1.amazonaws.com" } }, "params": { - "UseFIPS": false, "UseDualStack": false, - "Region": "ap-northeast-2" + "UseFIPS": false, + "Region": "me-south-1" } }, { - "documentation": "For region ap-northeast-3 with FIPS disabled and DualStack disabled", + "documentation": "For region sa-east-1 with FIPS disabled and DualStack disabled", "expect": { "endpoint": { - "url": "https://ecs.ap-northeast-3.amazonaws.com" + "url": "https://ecs.sa-east-1.amazonaws.com" } }, "params": { - "UseFIPS": false, "UseDualStack": false, - "Region": "ap-northeast-3" + "UseFIPS": false, + "Region": "sa-east-1" } }, { @@ -824,8 +866,8 @@ } }, "params": { - "UseFIPS": false, "UseDualStack": false, + "UseFIPS": false, "Region": "us-east-1" } }, @@ -837,100 +879,87 @@ } }, "params": { - "UseFIPS": true, "UseDualStack": false, + "UseFIPS": true, "Region": "us-east-1" } }, { - "documentation": "For region eu-west-1 with FIPS disabled and DualStack disabled", + "documentation": "For region us-east-2 with FIPS disabled and DualStack disabled", "expect": { "endpoint": { - "url": "https://ecs.eu-west-1.amazonaws.com" + "url": "https://ecs.us-east-2.amazonaws.com" } }, "params": { - "UseFIPS": false, "UseDualStack": false, - "Region": "eu-west-1" - } - }, - { - "documentation": "For region eu-west-2 with FIPS disabled and DualStack disabled", - "expect": { - "endpoint": { - "url": "https://ecs.eu-west-2.amazonaws.com" - } - }, - "params": { "UseFIPS": false, - "UseDualStack": false, - "Region": "eu-west-2" + "Region": "us-east-2" } }, { - "documentation": "For region eu-west-3 with FIPS disabled and DualStack disabled", + "documentation": "For region us-east-2 with FIPS enabled and DualStack disabled", "expect": { "endpoint": { - "url": "https://ecs.eu-west-3.amazonaws.com" + "url": "https://ecs-fips.us-east-2.amazonaws.com" } }, "params": { - "UseFIPS": false, "UseDualStack": false, - "Region": "eu-west-3" + "UseFIPS": true, + "Region": "us-east-2" } }, { - "documentation": "For region me-south-1 with FIPS disabled and DualStack disabled", + "documentation": "For region us-west-1 with FIPS disabled and DualStack disabled", "expect": { "endpoint": { - "url": "https://ecs.me-south-1.amazonaws.com" + "url": "https://ecs.us-west-1.amazonaws.com" } }, "params": { - "UseFIPS": false, "UseDualStack": false, - "Region": "me-south-1" + "UseFIPS": false, + "Region": "us-west-1" } }, { - "documentation": "For region eu-north-1 with FIPS disabled and DualStack disabled", + "documentation": "For region us-west-1 with FIPS enabled and DualStack disabled", "expect": { "endpoint": { - "url": "https://ecs.eu-north-1.amazonaws.com" + "url": "https://ecs-fips.us-west-1.amazonaws.com" } }, "params": { - "UseFIPS": false, "UseDualStack": false, - "Region": "eu-north-1" + "UseFIPS": true, + "Region": "us-west-1" } }, { - "documentation": "For region us-east-2 with FIPS disabled and DualStack disabled", + "documentation": "For region us-west-2 with FIPS disabled and DualStack disabled", "expect": { "endpoint": { - "url": "https://ecs.us-east-2.amazonaws.com" + "url": "https://ecs.us-west-2.amazonaws.com" } }, "params": { - "UseFIPS": false, "UseDualStack": false, - "Region": "us-east-2" + "UseFIPS": false, + "Region": "us-west-2" } }, { - "documentation": "For region us-east-2 with FIPS enabled and DualStack disabled", + "documentation": "For region us-west-2 with FIPS enabled and DualStack disabled", "expect": { "endpoint": { - "url": "https://ecs-fips.us-east-2.amazonaws.com" + "url": "https://ecs-fips.us-west-2.amazonaws.com" } }, "params": { - "UseFIPS": true, "UseDualStack": false, - "Region": "us-east-2" + "UseFIPS": true, + "Region": "us-west-2" } }, { @@ -941,8 +970,8 @@ } }, "params": { - "UseFIPS": true, "UseDualStack": true, + "UseFIPS": true, "Region": "us-east-1" } }, @@ -954,241 +983,254 @@ } }, "params": { - "UseFIPS": false, "UseDualStack": true, + "UseFIPS": false, "Region": "us-east-1" } }, { - "documentation": "For region us-gov-west-1 with FIPS disabled and DualStack disabled", + "documentation": "For region cn-north-1 with FIPS disabled and DualStack disabled", "expect": { "endpoint": { - "url": "https://ecs.us-gov-west-1.amazonaws.com" + "url": "https://ecs.cn-north-1.amazonaws.com.cn" } }, "params": { - "UseFIPS": false, "UseDualStack": false, - "Region": "us-gov-west-1" + "UseFIPS": false, + "Region": "cn-north-1" } }, { - "documentation": "For region us-gov-west-1 with FIPS enabled and DualStack disabled", + "documentation": "For region cn-northwest-1 with FIPS disabled and DualStack disabled", "expect": { "endpoint": { - "url": "https://ecs-fips.us-gov-west-1.amazonaws.com" + "url": "https://ecs.cn-northwest-1.amazonaws.com.cn" } }, "params": { - "UseFIPS": true, "UseDualStack": false, - "Region": "us-gov-west-1" + "UseFIPS": false, + "Region": "cn-northwest-1" } }, { - "documentation": "For region us-gov-east-1 with FIPS disabled and DualStack disabled", + "documentation": "For region cn-north-1 with FIPS enabled and DualStack enabled", "expect": { "endpoint": { - "url": "https://ecs.us-gov-east-1.amazonaws.com" + "url": "https://ecs-fips.cn-north-1.api.amazonwebservices.com.cn" } }, "params": { - "UseFIPS": false, - "UseDualStack": false, - "Region": "us-gov-east-1" + "UseDualStack": true, + "UseFIPS": true, + "Region": "cn-north-1" } }, { - "documentation": "For region us-gov-east-1 with FIPS enabled and DualStack disabled", + "documentation": "For region cn-north-1 with FIPS enabled and DualStack disabled", "expect": { "endpoint": { - "url": "https://ecs-fips.us-gov-east-1.amazonaws.com" + "url": "https://ecs-fips.cn-north-1.amazonaws.com.cn" } }, "params": { - "UseFIPS": true, "UseDualStack": false, - "Region": "us-gov-east-1" + "UseFIPS": true, + "Region": "cn-north-1" } }, { - "documentation": "For region us-gov-east-1 with FIPS enabled and DualStack enabled", + "documentation": "For region cn-north-1 with FIPS disabled and DualStack enabled", "expect": { "endpoint": { - "url": "https://ecs-fips.us-gov-east-1.api.aws" + "url": "https://ecs.cn-north-1.api.amazonwebservices.com.cn" } }, "params": { - "UseFIPS": true, "UseDualStack": true, - "Region": "us-gov-east-1" + "UseFIPS": false, + "Region": "cn-north-1" } }, { - "documentation": "For region us-gov-east-1 with FIPS disabled and DualStack enabled", + "documentation": "For region us-gov-east-1 with FIPS disabled and DualStack disabled", "expect": { "endpoint": { - "url": "https://ecs.us-gov-east-1.api.aws" + "url": "https://ecs.us-gov-east-1.amazonaws.com" } }, "params": { + "UseDualStack": false, "UseFIPS": false, - "UseDualStack": true, "Region": "us-gov-east-1" } }, { - "documentation": "For region us-isob-east-1 with FIPS disabled and DualStack disabled", + "documentation": "For region us-gov-east-1 with FIPS enabled and DualStack disabled", "expect": { "endpoint": { - "url": "https://ecs.us-isob-east-1.sc2s.sgov.gov" + "url": "https://ecs-fips.us-gov-east-1.amazonaws.com" } }, "params": { - "UseFIPS": false, "UseDualStack": false, - "Region": "us-isob-east-1" + "UseFIPS": true, + "Region": "us-gov-east-1" } }, { - "documentation": "For region us-isob-east-1 with FIPS enabled and DualStack disabled", + "documentation": "For region us-gov-west-1 with FIPS disabled and DualStack disabled", "expect": { "endpoint": { - "url": "https://ecs-fips.us-isob-east-1.sc2s.sgov.gov" + "url": "https://ecs.us-gov-west-1.amazonaws.com" } }, "params": { - "UseFIPS": true, "UseDualStack": false, - "Region": "us-isob-east-1" + "UseFIPS": false, + "Region": "us-gov-west-1" } }, { - "documentation": "For region cn-northwest-1 with FIPS disabled and DualStack disabled", + "documentation": "For region us-gov-west-1 with FIPS enabled and DualStack disabled", "expect": { "endpoint": { - "url": "https://ecs.cn-northwest-1.amazonaws.com.cn" + "url": "https://ecs-fips.us-gov-west-1.amazonaws.com" } }, "params": { - "UseFIPS": false, "UseDualStack": false, - "Region": "cn-northwest-1" + "UseFIPS": true, + "Region": "us-gov-west-1" } }, { - "documentation": "For region cn-north-1 with FIPS disabled and DualStack disabled", + "documentation": "For region us-gov-east-1 with FIPS enabled and DualStack enabled", "expect": { "endpoint": { - "url": "https://ecs.cn-north-1.amazonaws.com.cn" + "url": "https://ecs-fips.us-gov-east-1.api.aws" } }, "params": { - "UseFIPS": false, - "UseDualStack": false, - "Region": "cn-north-1" + "UseDualStack": true, + "UseFIPS": true, + "Region": "us-gov-east-1" } }, { - "documentation": "For region cn-north-1 with FIPS enabled and DualStack enabled", + "documentation": "For region us-gov-east-1 with FIPS disabled and DualStack enabled", "expect": { "endpoint": { - "url": "https://ecs-fips.cn-north-1.api.amazonwebservices.com.cn" + "url": "https://ecs.us-gov-east-1.api.aws" } }, "params": { - "UseFIPS": true, "UseDualStack": true, - "Region": "cn-north-1" + "UseFIPS": false, + "Region": "us-gov-east-1" } }, { - "documentation": "For region cn-north-1 with FIPS enabled and DualStack disabled", + "documentation": "For region us-iso-east-1 with FIPS disabled and DualStack disabled", "expect": { "endpoint": { - "url": "https://ecs-fips.cn-north-1.amazonaws.com.cn" + "url": "https://ecs.us-iso-east-1.c2s.ic.gov" } }, "params": { - "UseFIPS": true, "UseDualStack": false, - "Region": "cn-north-1" + "UseFIPS": false, + "Region": "us-iso-east-1" } }, { - "documentation": "For region cn-north-1 with FIPS disabled and DualStack enabled", + "documentation": "For region us-iso-west-1 with FIPS disabled and DualStack disabled", "expect": { "endpoint": { - "url": "https://ecs.cn-north-1.api.amazonwebservices.com.cn" + "url": "https://ecs.us-iso-west-1.c2s.ic.gov" } }, "params": { + "UseDualStack": false, "UseFIPS": false, - "UseDualStack": true, - "Region": "cn-north-1" + "Region": "us-iso-west-1" } }, { - "documentation": "For region us-iso-west-1 with FIPS disabled and DualStack disabled", + "documentation": "For region us-iso-east-1 with FIPS enabled and DualStack disabled", "expect": { "endpoint": { - "url": "https://ecs.us-iso-west-1.c2s.ic.gov" + "url": "https://ecs-fips.us-iso-east-1.c2s.ic.gov" } }, "params": { - "UseFIPS": false, "UseDualStack": false, - "Region": "us-iso-west-1" + "UseFIPS": true, + "Region": "us-iso-east-1" } }, { - "documentation": "For region us-iso-east-1 with FIPS disabled and DualStack disabled", + "documentation": "For region us-isob-east-1 with FIPS disabled and DualStack disabled", "expect": { "endpoint": { - "url": "https://ecs.us-iso-east-1.c2s.ic.gov" + "url": "https://ecs.us-isob-east-1.sc2s.sgov.gov" } }, "params": { - "UseFIPS": false, "UseDualStack": false, - "Region": "us-iso-east-1" + "UseFIPS": false, + "Region": "us-isob-east-1" } }, { - "documentation": "For region us-iso-east-1 with FIPS enabled and DualStack disabled", + "documentation": "For region us-isob-east-1 with FIPS enabled and DualStack disabled", "expect": { "endpoint": { - "url": "https://ecs-fips.us-iso-east-1.c2s.ic.gov" + "url": "https://ecs-fips.us-isob-east-1.sc2s.sgov.gov" } }, "params": { - "UseFIPS": true, "UseDualStack": false, - "Region": "us-iso-east-1" + "UseFIPS": true, + "Region": "us-isob-east-1" } }, { - "documentation": "For custom endpoint with fips disabled and dualstack disabled", + "documentation": "For custom endpoint with region set and fips disabled and dualstack disabled", "expect": { "endpoint": { "url": "https://example.com" } }, "params": { - "UseFIPS": false, "UseDualStack": false, + "UseFIPS": false, "Region": "us-east-1", "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": { + "UseDualStack": false, + "UseFIPS": 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": { - "UseFIPS": true, "UseDualStack": false, + "UseFIPS": true, "Region": "us-east-1", "Endpoint": "https://example.com" } @@ -1199,8 +1241,8 @@ "error": "Invalid Configuration: Dualstack and custom endpoint are not supported" }, "params": { - "UseFIPS": false, "UseDualStack": true, + "UseFIPS": false, "Region": "us-east-1", "Endpoint": "https://example.com" } @@ -1392,7 +1434,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 disabled.

\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 enabled, Amazon ECS prevents the Amazon EC2 instances in\n\t\t\tan Auto Scaling group that contain tasks from being terminated during a scale-in action.\n\t\t\tThe Auto 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 disabled, your Amazon EC2 instances aren't protected\n\t\t\tfrom termination 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 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.

" } } }, @@ -1412,7 +1454,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.

\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 enabled, Amazon ECS prevents the Amazon EC2 instances in\n\t\t\tan Auto Scaling group that contain tasks from being terminated during a scale-in action.\n\t\t\tThe Auto Scaling group and each instance in the Auto Scaling group must have instance\n\t\t\tprotection from scale-in actions enabled. For more information, see Instance Protection in the Auto Scaling User Guide.

\n

When managed termination protection is disabled, your Amazon EC2 instances aren't protected\n\t\t\tfrom termination 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.

\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. 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.

" } } }, @@ -1756,7 +1798,7 @@ "settings": { "target": "com.amazonaws.ecs#ClusterSettings", "traits": { - "smithy.api#documentation": "

The settings for the cluster. This parameter indicates whether CloudWatch Container Insights\n\t\t\tis enabled or disabled for a cluster.

" + "smithy.api#documentation": "

The settings for the cluster. This parameter indicates whether CloudWatch Container Insights is on\n\t\t\tor off for a cluster.

" } }, "capacityProviders": { @@ -1938,7 +1980,7 @@ "value": { "target": "com.amazonaws.ecs#String", "traits": { - "smithy.api#documentation": "

The value to set for the cluster setting. The supported values are\n\t\t\t\tenabled and disabled. If enabled is\n\t\t\tspecified, CloudWatch Container Insights will be enabled for the cluster, otherwise it will be\n\t\t\tdisabled unless the containerInsights account setting is enabled. If a\n\t\t\tcluster value is specified, it will override the containerInsights value\n\t\t\tset 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. 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.

" } } }, @@ -2299,7 +2341,7 @@ "disableNetworking": { "target": "com.amazonaws.ecs#BoxedBoolean", "traits": { - "smithy.api#documentation": "

When this parameter is true, networking is disabled within the container. This\n\t\t\tparameter maps to NetworkDisabled in the Create a container\n\t\t\tsection of the Docker Remote API.

\n \n

This parameter is not supported for Windows containers.

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

When this parameter is true, networking is off within the container. This parameter maps to\n\t\t\t\tNetworkDisabled in the Create a container section of\n\t\t\tthe Docker Remote API.

\n \n

This parameter is not supported for Windows containers.

\n
" } }, "privileged": { @@ -2359,7 +2401,7 @@ "ulimits": { "target": "com.amazonaws.ecs#UlimitList", "traits": { - "smithy.api#documentation": "

A list of ulimits to set in the container. If a ulimit value\n\t\t\tis specified in a task definition, it overrides the default values set by Docker. This\n\t\t\tparameter maps to Ulimits in the Create a container section\n\t\t\tof the Docker Remote API and the --ulimit option to docker run. Valid naming values are displayed\n\t\t\tin the Ulimit data type.

\n

Amazon ECS tasks hosted on Fargate use the default\n\t\t\t\t\t\t\tresource limit values set by the operating system with the exception of\n\t\t\t\t\t\t\tthe nofile resource limit parameter which Fargate\n\t\t\t\t\t\t\toverrides. The nofile resource limit sets a restriction on\n\t\t\t\t\t\t\tthe number of open files that a container can use. The default\n\t\t\t\t\t\t\t\tnofile soft limit is 1024 and hard limit\n\t\t\t\t\t\t\tis 4096.

\n

This parameter requires version 1.18 of the Docker Remote API or greater on your container instance. To check the Docker Remote API version on your container instance, log in to your container instance and run the following command: sudo docker version --format '{{.Server.APIVersion}}'\n

\n \n

This parameter is not supported for Windows containers.

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

A list of ulimits to set in the container. If a ulimit value\n\t\t\tis specified in a task definition, it overrides the default values set by Docker. This\n\t\t\tparameter maps to Ulimits in the Create a container section\n\t\t\tof the Docker Remote API and the --ulimit option to docker run. Valid naming values are displayed\n\t\t\tin the Ulimit data type.

\n

Amazon ECS tasks hosted on Fargate use the default\n\t\t\t\t\t\t\tresource limit values set by the operating system with the exception of\n\t\t\t\t\t\t\tthe nofile resource limit parameter which Fargate\n\t\t\t\t\t\t\toverrides. The nofile resource limit sets a restriction on\n\t\t\t\t\t\t\tthe number of open files that a container can use. The default\n\t\t\t\t\t\t\t\tnofile soft limit is 1024 and the default hard limit\n\t\t\t\t\t\t\tis 4096.

\n

This parameter requires version 1.18 of the Docker Remote API or greater on your container instance. To check the Docker Remote API version on your container instance, log in to your container instance and run the following command: sudo docker version --format '{{.Server.APIVersion}}'\n

\n \n

This parameter is not supported for Windows containers.

\n
" } }, "logConfiguration": { @@ -2811,6 +2853,9 @@ "smithy.api#documentation": "

The metadata that you apply to the capacity provider to categorize and organize them\n\t\t\tmore conveniently. Each tag consists of a key and an optional value. You define both of\n\t\t\tthem.

\n

The following basic restrictions apply to tags:

\n
    \n
  • \n

    Maximum number of tags per resource - 50

    \n
  • \n
  • \n

    For each resource, each tag key must be unique, and each tag key can have only\n one value.

    \n
  • \n
  • \n

    Maximum key length - 128 Unicode characters in UTF-8

    \n
  • \n
  • \n

    Maximum value length - 256 Unicode characters in UTF-8

    \n
  • \n
  • \n

    If your tagging schema is used across multiple services and resources,\n remember that other services may have restrictions on allowed characters.\n Generally allowed characters are: letters, numbers, and spaces representable in\n UTF-8, and the following characters: + - = . _ : / @.

    \n
  • \n
  • \n

    Tag keys and values are case-sensitive.

    \n
  • \n
  • \n

    Do not use aws:, AWS:, or any upper or lowercase\n combination of such as a prefix for either keys or values as it is reserved for\n Amazon Web Services use. You cannot edit or delete tag keys or values with this prefix. Tags with\n this prefix do not count against your tags per resource limit.

    \n
  • \n
" } } + }, + "traits": { + "smithy.api#input": {} } }, "com.amazonaws.ecs#CreateCapacityProviderResponse": { @@ -2822,6 +2867,9 @@ "smithy.api#documentation": "

The full description of the new capacity provider.

" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ecs#CreateCluster": { @@ -2844,7 +2892,7 @@ } ], "traits": { - "smithy.api#documentation": "

Creates a new Amazon ECS cluster. By default, your account receives a default\n\t\t\tcluster when you launch your first container instance. However, you can create your own\n\t\t\tcluster with a unique name with the CreateCluster action.

\n \n

When you call the CreateCluster API operation, Amazon ECS attempts to\n\t\t\t\tcreate the Amazon ECS service-linked role for your account. This is so that it can manage\n\t\t\t\trequired resources in other Amazon Web Services services on your behalf. However, if the IAM user\n\t\t\t\tthat makes the call doesn't have permissions to create the service-linked role, it\n\t\t\t\tisn't created. For more information, see Using\n\t\t\t\t\tservice-linked roles for Amazon ECS in the Amazon Elastic Container Service Developer Guide.

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

Creates a new Amazon ECS cluster. By default, your account receives a default\n\t\t\tcluster when you launch your first container instance. However, you can create your own\n\t\t\tcluster with a unique name with the CreateCluster action.

\n \n

When you call the CreateCluster API operation, Amazon ECS attempts to\n\t\t\t\tcreate the Amazon ECS service-linked role for your account. This is so that it can manage\n\t\t\t\trequired resources in other Amazon Web Services services on your behalf. However, if the user\n\t\t\t\tthat makes the call doesn't have permissions to create the service-linked role, it\n\t\t\t\tisn't created. For more information, see Using\n\t\t\t\t\tservice-linked roles for Amazon ECS in the Amazon Elastic Container Service Developer Guide.

\n
" } }, "com.amazonaws.ecs#CreateClusterRequest": { @@ -2877,13 +2925,13 @@ "capacityProviders": { "target": "com.amazonaws.ecs#StringList", "traits": { - "smithy.api#documentation": "

The short name of one or more capacity providers to associate with the cluster. A\n\t\t\tcapacity provider must be associated with a cluster before it can be included as part of\n\t\t\tthe default capacity provider strategy of the cluster or used in a capacity provider\n\t\t\tstrategy when calling the CreateService or RunTask\n\t\t\tactions.

\n

If specifying a capacity provider that uses an Auto Scaling group, the capacity\n\t\t\tprovider must be created but not associated with another cluster. New Auto Scaling group\n\t\t\tcapacity providers can be created with the CreateCapacityProvider API\n\t\t\toperation.

\n

To use a Fargate capacity provider, specify either the FARGATE or\n\t\t\t\tFARGATE_SPOT capacity providers. The Fargate capacity providers are\n\t\t\tavailable to all accounts and only need to be associated with a cluster to be\n\t\t\tused.

\n

The PutClusterCapacityProviders API operation is used to update the\n\t\t\tlist of available capacity providers for a cluster after the cluster is created.

" + "smithy.api#documentation": "

The short name of one or more capacity providers to associate with the cluster. A\n\t\t\tcapacity provider must be associated with a cluster before it can be included as part of\n\t\t\tthe default capacity provider strategy of the cluster or used in a capacity provider\n\t\t\tstrategy when calling the CreateService or RunTask\n\t\t\tactions.

\n

If specifying a capacity provider that uses an Auto Scaling group, the capacity\n\t\t\tprovider must be created but not associated with another cluster. New Auto Scaling group\n\t\t\tcapacity providers can be created with the CreateCapacityProvider API\n\t\t\toperation.

\n

To use a Fargate capacity provider, specify either the FARGATE or\n\t\t\t\tFARGATE_SPOT capacity providers. The Fargate capacity providers are\n\t\t\tavailable to all accounts and only need to be associated with a cluster to be\n\t\t\tused.

\n

The PutCapacityProvider API operation is used to update the\n\t\t\tlist of available capacity providers for a cluster after the cluster is created.

" } }, "defaultCapacityProviderStrategy": { "target": "com.amazonaws.ecs#CapacityProviderStrategy", "traits": { - "smithy.api#documentation": "

The capacity provider strategy to set as the default for the cluster. After a default\n\t\t\tcapacity provider strategy is set for a cluster, when you call the RunTask or CreateService APIs with no capacity\n\t\t\tprovider strategy or launch type specified, the default capacity provider strategy for\n\t\t\tthe cluster is used.

\n

If a default capacity provider strategy isn't defined for a cluster when it was\n\t\t\tcreated, it can be defined later with the PutClusterCapacityProviders\n\t\t\tAPI operation.

" + "smithy.api#documentation": "

The capacity provider strategy to set as the default for the cluster. After a default\n\t\t\tcapacity provider strategy is set for a cluster, when you call the CreateService or RunTask APIs with no capacity\n\t\t\tprovider strategy or launch type specified, the default capacity provider strategy for\n\t\t\tthe cluster is used.

\n

If a default capacity provider strategy isn't defined for a cluster when it was\n\t\t\tcreated, it can be defined later with the PutClusterCapacityProviders\n\t\t\tAPI operation.

" } }, "serviceConnectDefaults": { @@ -2892,6 +2940,9 @@ "smithy.api#documentation": "

Use this parameter to set a default Service Connect namespace. After you set a default \n\tService Connect namespace, any new services with Service Connect turned on that are created in the cluster are added as\n\tclient services in the namespace. This setting only applies to new services that set the enabled parameter to\n\ttrue in the ServiceConnectConfiguration.\n\tYou can set the namespace of each service individually in the ServiceConnectConfiguration to override this default\n\tparameter.

\n

Tasks that run in a namespace can use short names to connect\n\tto services in the namespace. Tasks can connect to services across all of the clusters in the namespace.\n\tTasks connect through a managed proxy container\n\tthat collects logs and metrics for increased visibility.\n\tOnly the tasks that Amazon ECS services create are supported with Service Connect.\n\tFor more information, see Service Connect in the Amazon Elastic Container Service Developer Guide.

" } } + }, + "traits": { + "smithy.api#input": {} } }, "com.amazonaws.ecs#CreateClusterResponse": { @@ -2903,6 +2954,9 @@ "smithy.api#documentation": "

The full description of your new cluster.

" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ecs#CreateService": { @@ -3090,6 +3144,9 @@ "smithy.api#documentation": "

The configuration for this service to discover and connect to\n\tservices, and be discovered by, and connected from, other services within a namespace.

\n

Tasks that run in a namespace can use short names to connect\n\tto services in the namespace. Tasks can connect to services across all of the clusters in the namespace.\n\tTasks connect through a managed proxy container\n\tthat collects logs and metrics for increased visibility.\n\tOnly the tasks that Amazon ECS services create are supported with Service Connect.\n\tFor more information, see Service Connect in the Amazon Elastic Container Service Developer Guide.

" } } + }, + "traits": { + "smithy.api#input": {} } }, "com.amazonaws.ecs#CreateServiceResponse": { @@ -3101,6 +3158,9 @@ "smithy.api#documentation": "

The full description of your service following the create call.

\n

A service will return either a capacityProviderStrategy or\n\t\t\t\tlaunchType parameter, but not both, depending where one was specified\n\t\t\twhen it was created.

\n

If a service is using the ECS deployment controller, the\n\t\t\t\tdeploymentController and taskSets parameters will not be\n\t\t\treturned.

\n

if the service uses the CODE_DEPLOY deployment controller, the\n\t\t\t\tdeploymentController, taskSets and\n\t\t\t\tdeployments parameters will be returned, however the\n\t\t\t\tdeployments parameter will be an empty list.

" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ecs#CreateTaskSet": { @@ -3234,6 +3294,9 @@ "smithy.api#documentation": "

The metadata that you apply to the task set to help you categorize and organize them.\n\t\t\tEach tag consists of a key and an optional value. You define both. When a service is\n\t\t\tdeleted, the tags are deleted.

\n

The following basic restrictions apply to tags:

\n
    \n
  • \n

    Maximum number of tags per resource - 50

    \n
  • \n
  • \n

    For each resource, each tag key must be unique, and each tag key can have only\n one value.

    \n
  • \n
  • \n

    Maximum key length - 128 Unicode characters in UTF-8

    \n
  • \n
  • \n

    Maximum value length - 256 Unicode characters in UTF-8

    \n
  • \n
  • \n

    If your tagging schema is used across multiple services and resources,\n remember that other services may have restrictions on allowed characters.\n Generally allowed characters are: letters, numbers, and spaces representable in\n UTF-8, and the following characters: + - = . _ : / @.

    \n
  • \n
  • \n

    Tag keys and values are case-sensitive.

    \n
  • \n
  • \n

    Do not use aws:, AWS:, or any upper or lowercase\n combination of such as a prefix for either keys or values as it is reserved for\n Amazon Web Services use. You cannot edit or delete tag keys or values with this prefix. Tags with\n this prefix do not count against your tags per resource limit.

    \n
  • \n
" } } + }, + "traits": { + "smithy.api#input": {} } }, "com.amazonaws.ecs#CreateTaskSetResponse": { @@ -3245,6 +3308,9 @@ "smithy.api#documentation": "

Information about a set of Amazon ECS tasks in either an CodeDeploy or an\n\t\t\t\tEXTERNAL deployment. A task set includes details such as the desired\n\t\t\tnumber of tasks, how many tasks are running, and whether the task set serves production\n\t\t\ttraffic.

" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ecs#DeleteAccountSetting": { @@ -3267,7 +3333,7 @@ } ], "traits": { - "smithy.api#documentation": "

Disables an account setting for a specified IAM user, IAM role, or the root user for\n\t\t\tan account.

" + "smithy.api#documentation": "

Disables an account setting for a specified user, role, or the root user for\n\t\t\tan account.

" } }, "com.amazonaws.ecs#DeleteAccountSettingRequest": { @@ -3283,9 +3349,12 @@ "principalArn": { "target": "com.amazonaws.ecs#String", "traits": { - "smithy.api#documentation": "

The Amazon Resource Name (ARN) of the principal. It can be an IAM user, IAM role, or\n\t\t\tthe root user. If you specify the root user, it disables the account setting for all IAM\n\t\t\tusers, IAM roles, and the root user of the account unless an IAM user or role explicitly\n\t\t\toverrides these settings. If this field is omitted, the setting is changed only for the\n\t\t\tauthenticated user.

" + "smithy.api#documentation": "

The Amazon Resource Name (ARN) of the principal. It can be an user, role, or\n\t\t\tthe root user. If you specify the root user, it disables the account setting for all users, roles, and the root user of the account unless a user or role explicitly\n\t\t\toverrides these settings. If this field is omitted, the setting is changed only for the\n\t\t\tauthenticated user.

" } } + }, + "traits": { + "smithy.api#input": {} } }, "com.amazonaws.ecs#DeleteAccountSettingResponse": { @@ -3297,6 +3366,9 @@ "smithy.api#documentation": "

The account setting for the specified principal ARN.

" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ecs#DeleteAttributes": { @@ -3338,6 +3410,9 @@ "smithy.api#required": {} } } + }, + "traits": { + "smithy.api#input": {} } }, "com.amazonaws.ecs#DeleteAttributesResponse": { @@ -3349,6 +3424,9 @@ "smithy.api#documentation": "

A list of attribute objects that were successfully deleted from your resource.

" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ecs#DeleteCapacityProvider": { @@ -3384,6 +3462,9 @@ "smithy.api#required": {} } } + }, + "traits": { + "smithy.api#input": {} } }, "com.amazonaws.ecs#DeleteCapacityProviderResponse": { @@ -3395,6 +3476,9 @@ "smithy.api#documentation": "

The details of the capacity provider.

" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ecs#DeleteCluster": { @@ -3445,6 +3529,9 @@ "smithy.api#required": {} } } + }, + "traits": { + "smithy.api#input": {} } }, "com.amazonaws.ecs#DeleteClusterResponse": { @@ -3456,6 +3543,9 @@ "smithy.api#documentation": "

The full description of the deleted cluster.

" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ecs#DeleteService": { @@ -3509,6 +3599,9 @@ "smithy.api#documentation": "

If true, allows you to delete a service even if it wasn't scaled down to\n\t\t\tzero tasks. It's only necessary to use this if the service uses the REPLICA\n\t\t\tscheduling strategy.

" } } + }, + "traits": { + "smithy.api#input": {} } }, "com.amazonaws.ecs#DeleteServiceResponse": { @@ -3520,6 +3613,70 @@ "smithy.api#documentation": "

The full description of the deleted service.

" } } + }, + "traits": { + "smithy.api#output": {} + } + }, + "com.amazonaws.ecs#DeleteTaskDefinitions": { + "type": "operation", + "input": { + "target": "com.amazonaws.ecs#DeleteTaskDefinitionsRequest" + }, + "output": { + "target": "com.amazonaws.ecs#DeleteTaskDefinitionsResponse" + }, + "errors": [ + { + "target": "com.amazonaws.ecs#AccessDeniedException" + }, + { + "target": "com.amazonaws.ecs#ClientException" + }, + { + "target": "com.amazonaws.ecs#InvalidParameterException" + }, + { + "target": "com.amazonaws.ecs#ServerException" + } + ], + "traits": { + "smithy.api#documentation": "

Deletes one or more task definitions.

\n

You must deregister a task definition revision before you delete it. For more information,\n\t\t\tsee DeregisterTaskDefinition.

\n

When you delete a task definition revision, it is immediately transitions from the\n\t\tINACTIVE to DELETE_IN_PROGRESS. Existing tasks and services\n\t\tthat reference a DELETE_IN_PROGRESS task definition revision continue to run\n\t\twithout disruption. Existing services that reference a DELETE_IN_PROGRESS task\n\t\tdefinition revision can still scale up or down by modifying the service's desired\n\t\tcount.

\n

You can't use a DELETE_IN_PROGRESS task definition revision to run new tasks\n\t\t\tor create new services. You also can't update an existing service to reference a\n\t\t\tDELETE_IN_PROGRESS task definition revision.

\n

A task definition revision will stay in DELETE_IN_PROGRESS status until\n\t\t\tall the associated tasks and services have been terminated.

" + } + }, + "com.amazonaws.ecs#DeleteTaskDefinitionsRequest": { + "type": "structure", + "members": { + "taskDefinitions": { + "target": "com.amazonaws.ecs#StringList", + "traits": { + "smithy.api#documentation": "

The family and revision (family:revision) or\n\t\t\tfull Amazon Resource Name (ARN) of the task definition to delete. You must specify a\n\t\t\t\trevision.

\n

You can specify up to 10 task definitions as a comma separated list.

", + "smithy.api#required": {} + } + } + }, + "traits": { + "smithy.api#input": {} + } + }, + "com.amazonaws.ecs#DeleteTaskDefinitionsResponse": { + "type": "structure", + "members": { + "taskDefinitions": { + "target": "com.amazonaws.ecs#TaskDefinitionList", + "traits": { + "smithy.api#documentation": "

The list of deleted task definitions.

" + } + }, + "failures": { + "target": "com.amazonaws.ecs#Failures", + "traits": { + "smithy.api#documentation": "

Any failures associated with the call.

" + } + } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ecs#DeleteTaskSet": { @@ -3593,6 +3750,9 @@ "smithy.api#documentation": "

If true, you can delete a task set even if it hasn't been scaled down to\n\t\t\tzero.

" } } + }, + "traits": { + "smithy.api#input": {} } }, "com.amazonaws.ecs#DeleteTaskSetResponse": { @@ -3604,6 +3764,9 @@ "smithy.api#documentation": "

Details about the task set.

" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ecs#Deployment": { @@ -3778,7 +3941,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 that aren't behind a Classic Load Balancer.

\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 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.

" } }, "com.amazonaws.ecs#DeploymentConfiguration": { @@ -3787,7 +3950,7 @@ "deploymentCircuitBreaker": { "target": "com.amazonaws.ecs#DeploymentCircuitBreaker", "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 deployment\n\t\t\tcircuit breaker is enabled, a service deployment will transition to a failed state and\n\t\t\tstop launching new tasks. If rollback is enabled, when a service deployment fails, the\n\t\t\tservice is rolled back to the last deployment that completed successfully.

" + "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 you use the deployment\n\t\t\tcircuit breaker, a service deployment will transition to a failed state and\n\t\t\tstop launching new tasks. If you use the rollback option, when a service deployment fails, the\n\t\t\tservice is rolled back to the last deployment that completed successfully. For more information, see Rolling\n\t\t\t\tupdate in the Amazon Elastic Container Service Developer\n\t\t\t\t\tGuide\n

" } }, "maximumPercent": { @@ -3928,6 +4091,9 @@ "smithy.api#documentation": "

Forces the container instance to be deregistered. If you have tasks running on the\n\t\t\tcontainer instance when you deregister it with the force option, these\n\t\t\ttasks remain running until you terminate the instance or the tasks stop through some\n\t\t\tother means, but they're orphaned (no longer monitored or accounted for by Amazon ECS). If an\n\t\t\torphaned task on your container instance is part of an Amazon ECS service, then the service\n\t\t\tscheduler starts another copy of that task, on a different container instance if\n\t\t\tpossible.

\n

Any containers in orphaned service tasks that are registered with a Classic Load Balancer or an Application Load Balancer\n\t\t\ttarget group are deregistered. They begin connection draining according to the settings\n\t\t\ton the load balancer or target group.

" } } + }, + "traits": { + "smithy.api#input": {} } }, "com.amazonaws.ecs#DeregisterContainerInstanceResponse": { @@ -3939,6 +4105,9 @@ "smithy.api#documentation": "

The container instance that was deregistered.

" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ecs#DeregisterTaskDefinition": { @@ -3961,7 +4130,7 @@ } ], "traits": { - "smithy.api#documentation": "

Deregisters the specified task definition by family and revision. Upon deregistration,\n\t\t\tthe task definition is marked as INACTIVE. Existing tasks and services that\n\t\t\treference an INACTIVE task definition continue to run without disruption.\n\t\t\tExisting services that reference an INACTIVE task definition can still\n\t\t\tscale up or down by modifying the service's desired count.

\n

You can't use an INACTIVE task definition to run new tasks or create new\n\t\t\tservices, and you can't update an existing service to reference an INACTIVE\n\t\t\ttask definition. However, there may be up to a 10-minute window following deregistration\n\t\t\twhere these restrictions have not yet taken effect.

\n \n

At this time, INACTIVE task definitions remain discoverable in your\n\t\t\t\taccount indefinitely. However, this behavior is subject to change in the future. We\n\t\t\t\tdon't recommend that you rely on INACTIVE task definitions persisting\n\t\t\t\tbeyond the lifecycle of any associated tasks and services.

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

Deregisters the specified task definition by family and revision. Upon deregistration, the\n\t\t\ttask definition is marked as INACTIVE. Existing tasks and services that\n\t\t\treference an INACTIVE task definition continue to run without disruption.\n\t\t\tExisting services that reference an INACTIVE task definition can still\n\t\t\tscale up or down by modifying the service's desired count. If you want to delete a task\n\t\t\tdefinition revision, you must first deregister the task definition revision.

\n

You can't use an INACTIVE task definition to run new tasks or create new\n\t\t\tservices, and you can't update an existing service to reference an INACTIVE\n\t\t\ttask definition. However, there may be up to a 10-minute window following deregistration\n\t\t\twhere these restrictions have not yet taken effect.

\n \n

At this time, INACTIVE task definitions remain discoverable in your\n\t\t\t\taccount indefinitely. However, this behavior is subject to change in the future. We\n\t\t\t\tdon't recommend that you rely on INACTIVE task definitions persisting\n\t\t\t\tbeyond the lifecycle of any associated tasks and services.

\n
\n

You must deregister a task definition revision before you delete it. For more information,\n\t\t\tsee DeleteTaskDefinitions.

" } }, "com.amazonaws.ecs#DeregisterTaskDefinitionRequest": { @@ -3974,6 +4143,9 @@ "smithy.api#required": {} } } + }, + "traits": { + "smithy.api#input": {} } }, "com.amazonaws.ecs#DeregisterTaskDefinitionResponse": { @@ -3985,6 +4157,9 @@ "smithy.api#documentation": "

The full description of the deregistered task.

" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ecs#DescribeCapacityProviders": { @@ -4037,6 +4212,9 @@ "smithy.api#documentation": "

The nextToken value returned from a previous paginated\n\t\t\t\tDescribeCapacityProviders request where maxResults was\n\t\t\tused and the results exceeded the value of that parameter. Pagination continues from the\n\t\t\tend of the previous results that returned the nextToken value.

\n \n

This token should be treated as an opaque identifier that is only used to\n retrieve the next items in a list and not for other programmatic purposes.

\n
" } } + }, + "traits": { + "smithy.api#input": {} } }, "com.amazonaws.ecs#DescribeCapacityProvidersResponse": { @@ -4060,6 +4238,9 @@ "smithy.api#documentation": "

The nextToken value to include in a future\n\t\t\t\tDescribeCapacityProviders request. When the results of a\n\t\t\t\tDescribeCapacityProviders request exceed maxResults, this\n\t\t\tvalue can be used to retrieve the next page of results. This value is null\n\t\t\twhen there are no more results to return.

" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ecs#DescribeClusters": { @@ -4100,6 +4281,9 @@ "smithy.api#documentation": "

Determines whether to include additional information about the clusters in the\n\t\t\tresponse. If this field is omitted, this information isn't included.

\n

If ATTACHMENTS is specified, the attachments for the container instances\n\t\t\tor tasks within the cluster are included, for example the capacity providers.

\n

If SETTINGS is specified, the settings for the cluster are\n\t\t\tincluded.

\n

If CONFIGURATIONS is specified, the configuration for the cluster is\n\t\t\tincluded.

\n

If STATISTICS is specified, the task and service count is included,\n\t\t\tseparated by launch type.

\n

If TAGS is specified, the metadata tags associated with the cluster are\n\t\t\tincluded.

" } } + }, + "traits": { + "smithy.api#input": {} } }, "com.amazonaws.ecs#DescribeClustersResponse": { @@ -4117,6 +4301,9 @@ "smithy.api#documentation": "

Any failures associated with the call.

" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ecs#DescribeContainerInstances": { @@ -4167,6 +4354,9 @@ "smithy.api#documentation": "

Specifies whether you want to see the resource tags for the container instance. If\n\t\t\t\tTAGS is specified, the tags are included in the response. If\n\t\t\t\tCONTAINER_INSTANCE_HEALTH is specified, the container instance health\n\t\t\tis included in the response. If this field is omitted, tags and container instance\n\t\t\thealth status aren't included in the response.

" } } + }, + "traits": { + "smithy.api#input": {} } }, "com.amazonaws.ecs#DescribeContainerInstancesResponse": { @@ -4184,6 +4374,9 @@ "smithy.api#documentation": "

Any failures associated with the call.

" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ecs#DescribeServices": { @@ -4306,6 +4499,9 @@ "smithy.api#documentation": "

Determines whether you want to see the resource tags for the service. If\n\t\t\t\tTAGS is specified, the tags are included in the response. If this field\n\t\t\tis omitted, tags aren't included in the response.

" } } + }, + "traits": { + "smithy.api#input": {} } }, "com.amazonaws.ecs#DescribeServicesResponse": { @@ -4323,6 +4519,9 @@ "smithy.api#documentation": "

Any failures associated with the call.

" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ecs#DescribeTaskDefinition": { @@ -4364,6 +4563,9 @@ "smithy.api#documentation": "

Determines whether to see the resource tags for the task definition. If\n\t\t\t\tTAGS is specified, the tags are included in the response. If this field\n\t\t\tis omitted, tags aren't included in the response.

" } } + }, + "traits": { + "smithy.api#input": {} } }, "com.amazonaws.ecs#DescribeTaskDefinitionResponse": { @@ -4381,6 +4583,9 @@ "smithy.api#documentation": "

The metadata that's applied to the task definition to help you categorize and organize\n\t\t\tthem. Each tag consists of a key and an optional value. You define both.

\n

The following basic restrictions apply to tags:

\n
    \n
  • \n

    Maximum number of tags per resource - 50

    \n
  • \n
  • \n

    For each resource, each tag key must be unique, and each tag key can have only\n one value.

    \n
  • \n
  • \n

    Maximum key length - 128 Unicode characters in UTF-8

    \n
  • \n
  • \n

    Maximum value length - 256 Unicode characters in UTF-8

    \n
  • \n
  • \n

    If your tagging schema is used across multiple services and resources,\n remember that other services may have restrictions on allowed characters.\n Generally allowed characters are: letters, numbers, and spaces representable in\n UTF-8, and the following characters: + - = . _ : / @.

    \n
  • \n
  • \n

    Tag keys and values are case-sensitive.

    \n
  • \n
  • \n

    Do not use aws:, AWS:, or any upper or lowercase\n combination of such as a prefix for either keys or values as it is reserved for\n Amazon Web Services use. You cannot edit or delete tag keys or values with this prefix. Tags with\n this prefix do not count against your tags per resource limit.

    \n
  • \n
" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ecs#DescribeTaskSets": { @@ -4450,6 +4655,9 @@ "smithy.api#documentation": "

Specifies whether to see the resource tags for the task set. If TAGS is\n\t\t\tspecified, the tags are included in the response. If this field is omitted, tags aren't\n\t\t\tincluded in the response.

" } } + }, + "traits": { + "smithy.api#input": {} } }, "com.amazonaws.ecs#DescribeTaskSetsResponse": { @@ -4467,6 +4675,9 @@ "smithy.api#documentation": "

Any failures associated with the call.

" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ecs#DescribeTasks": { @@ -4569,6 +4780,9 @@ "smithy.api#documentation": "

Specifies whether you want to see the resource tags for the task. If TAGS\n\t\t\tis specified, the tags are included in the response. If this field is omitted, tags\n\t\t\taren't included in the response.

" } } + }, + "traits": { + "smithy.api#input": {} } }, "com.amazonaws.ecs#DescribeTasksResponse": { @@ -4586,6 +4800,9 @@ "smithy.api#documentation": "

Any failures associated with the call.

" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ecs#DesiredStatus": { @@ -4708,6 +4925,9 @@ "smithy.api#documentation": "

The short name or full Amazon Resource Name (ARN) of the cluster that the container instance belongs\n\t\t\tto.

" } } + }, + "traits": { + "smithy.api#input": {} } }, "com.amazonaws.ecs#DiscoverPollEndpointResponse": { @@ -4731,6 +4951,9 @@ "smithy.api#documentation": "

The endpoint for the Amazon ECS agent to poll for Service Connect configuration.\n\t\t\tFor more information, see Service Connect in the Amazon Elastic Container Service Developer Guide.

" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ecs#DockerLabelsMap": { @@ -4798,7 +5021,7 @@ "iam": { "target": "com.amazonaws.ecs#EFSAuthorizationConfigIAM", "traits": { - "smithy.api#documentation": "

Determines whether to use the Amazon ECS task IAM 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 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.

" } } }, @@ -5011,7 +5234,7 @@ "target": "com.amazonaws.ecs#Boolean", "traits": { "smithy.api#default": false, - "smithy.api#documentation": "

Determines whether to use encryption on the CloudWatch logs. If not specified,\n\t\t\tencryption will be disabled.

" + "smithy.api#documentation": "

Determines whether to use encryption on the CloudWatch logs. If not specified, encryption\n\t\t\twill be off.

" } }, "s3BucketName": { @@ -5098,6 +5321,9 @@ "smithy.api#required": {} } } + }, + "traits": { + "smithy.api#input": {} } }, "com.amazonaws.ecs#ExecuteCommandResponse": { @@ -5140,6 +5366,9 @@ "smithy.api#documentation": "

The Amazon Resource Name (ARN) of the task.

" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ecs#FSxWindowsFileServerAuthorizationConfig": { @@ -5323,6 +5552,9 @@ "smithy.api#documentation": "

A list of up to 100 task IDs or full ARN entries.

" } } + }, + "traits": { + "smithy.api#input": {} } }, "com.amazonaws.ecs#GetTaskProtectionResponse": { @@ -5340,6 +5572,9 @@ "smithy.api#documentation": "

Any failures associated with the call.

" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ecs#GpuIds": { @@ -5354,7 +5589,7 @@ "command": { "target": "com.amazonaws.ecs#StringList", "traits": { - "smithy.api#documentation": "

A string array representing the command that the container runs to determine if it is\n\t\t\thealthy. The string array must start with CMD to run the command arguments\n\t\t\tdirectly, or CMD-SHELL to run the command with the container's default\n\t\t\tshell.

\n

When you use the Amazon Web Services Management Console JSON panel, the Command Line Interface, or the APIs, enclose the list\n\t\t\tof commands in brackets.

\n

\n [ \"CMD-SHELL\", \"curl -f http://localhost/ || exit 1\" ]\n

\n

You don't need to include the brackets when you use the Amazon Web Services Management Console.

\n

\n \"CMD-SHELL\", \"curl -f http://localhost/ || exit 1\" \n

\n

An exit code of 0 indicates success, and non-zero exit code indicates failure. For\n\t\t\tmore information, see HealthCheck in the Create a container\n\t\t\tsection of the Docker Remote API.

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

A string array representing the command that the container runs to determine if it is\n\t\t\thealthy. The string array must start with CMD to run the command arguments\n\t\t\tdirectly, or CMD-SHELL to run the command with the container's default\n\t\t\tshell.

\n

When you use the Amazon Web Services Management Console JSON panel, the Command Line Interface, or the APIs, enclose the list of\n\t\t\tcommands in double quotes and brackets.

\n

\n [ \"CMD-SHELL\", \"curl -f http://localhost/ || exit 1\" ]\n

\n

You don't include the double quotes and brackets when you use the Amazon Web Services Management Console.

\n

\n CMD-SHELL, curl -f http://localhost/ || exit 1\n

\n

An exit code of 0 indicates success, and non-zero exit code indicates failure. For\n\t\t\tmore information, see HealthCheck in the Create a container\n\t\t\tsection of the Docker Remote API.

", "smithy.api#required": {} } }, @@ -5379,7 +5614,7 @@ "startPeriod": { "target": "com.amazonaws.ecs#BoxedInteger", "traits": { - "smithy.api#documentation": "

The optional grace period to provide containers time to bootstrap before failed health\n\t\t\tchecks count towards the maximum number of retries. You can specify between 0 and 300\n\t\t\tseconds. By default, the startPeriod is disabled.

\n \n

If a health check succeeds within the startPeriod, then the container\n\t\t\t\tis considered healthy and any subsequent failures count toward the maximum number of\n\t\t\t\tretries.

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

The optional grace period to provide containers time to bootstrap before failed health\n\t\t\tchecks count towards the maximum number of retries. You can specify between 0 and 300\n\t\t\tseconds. By default, the startPeriod is off.

\n \n

If a health check succeeds within the startPeriod, then the container\n\t\t\t\tis considered healthy and any subsequent failures count toward the maximum number of\n\t\t\t\tretries.

\n
" } } }, @@ -5797,7 +6032,7 @@ "principalArn": { "target": "com.amazonaws.ecs#String", "traits": { - "smithy.api#documentation": "

The ARN of the principal, which can be an IAM user, IAM role, or the root user. If\n\t\t\tthis field is omitted, the account settings are listed only for the authenticated\n\t\t\tuser.

\n \n

Federated users assume the account setting of the root user and can't have\n\t\t\t\texplicit account settings set for them.

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

The ARN of the principal, which can be a user, role, or the root user. If\n\t\t\tthis field is omitted, the account settings are listed only for the authenticated\n\t\t\tuser.

\n \n

Federated users assume the account setting of the root user and can't have\n\t\t\t\texplicit account settings set for them.

\n
" } }, "effectiveSettings": { @@ -5820,6 +6055,9 @@ "smithy.api#documentation": "

The maximum number of account setting results returned by\n\t\t\t\tListAccountSettings in paginated output. When this parameter is used,\n\t\t\t\tListAccountSettings only returns maxResults results in a\n\t\t\tsingle page along with a nextToken response element. The remaining results\n\t\t\tof the initial request can be seen by sending another ListAccountSettings\n\t\t\trequest with the returned nextToken value. This value can be between\n\t\t\t1 and 10. If this\n\t\t\tparameter isn't used, then ListAccountSettings returns up to\n\t\t\t10 results and a nextToken value\n\t\t\tif applicable.

" } } + }, + "traits": { + "smithy.api#input": {} } }, "com.amazonaws.ecs#ListAccountSettingsResponse": { @@ -5837,6 +6075,9 @@ "smithy.api#documentation": "

The nextToken value to include in a future\n\t\t\t\tListAccountSettings request. When the results of a\n\t\t\t\tListAccountSettings request exceed maxResults, this value\n\t\t\tcan be used to retrieve the next page of results. This value is null when\n\t\t\tthere are no more results to return.

" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ecs#ListAttributes": { @@ -5905,6 +6146,9 @@ "smithy.api#documentation": "

The maximum number of cluster results that ListAttributes returned in\n\t\t\tpaginated output. When this parameter is used, ListAttributes only returns\n\t\t\t\tmaxResults results in a single page along with a nextToken\n\t\t\tresponse element. The remaining results of the initial request can be seen by sending\n\t\t\tanother ListAttributes request with the returned nextToken\n\t\t\tvalue. This value can be between 1 and 100. If this\n\t\t\tparameter isn't used, then ListAttributes returns up to\n\t\t\t100 results and a nextToken value if applicable.

" } } + }, + "traits": { + "smithy.api#input": {} } }, "com.amazonaws.ecs#ListAttributesResponse": { @@ -5922,6 +6166,9 @@ "smithy.api#documentation": "

The nextToken value to include in a future ListAttributes\n\t\t\trequest. When the results of a ListAttributes request exceed\n\t\t\t\tmaxResults, this value can be used to retrieve the next page of\n\t\t\tresults. This value is null when there are no more results to\n\t\t\treturn.

" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ecs#ListClusters": { @@ -5968,6 +6215,9 @@ "smithy.api#documentation": "

The maximum number of cluster results that ListClusters returned in\n\t\t\tpaginated output. When this parameter is used, ListClusters only returns\n\t\t\t\tmaxResults results in a single page along with a nextToken\n\t\t\tresponse element. The remaining results of the initial request can be seen by sending\n\t\t\tanother ListClusters request with the returned nextToken\n\t\t\tvalue. This value can be between 1 and 100. If this\n\t\t\tparameter isn't used, then ListClusters returns up to 100\n\t\t\tresults and a nextToken value if applicable.

" } } + }, + "traits": { + "smithy.api#input": {} } }, "com.amazonaws.ecs#ListClustersResponse": { @@ -5985,6 +6235,9 @@ "smithy.api#documentation": "

The nextToken value to include in a future ListClusters\n\t\t\trequest. When the results of a ListClusters request exceed\n\t\t\t\tmaxResults, this value can be used to retrieve the next page of\n\t\t\tresults. This value is null when there are no more results to\n\t\t\treturn.

" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ecs#ListContainerInstances": { @@ -6052,6 +6305,9 @@ "smithy.api#documentation": "

Filters the container instances by status. For example, if you specify the\n\t\t\t\tDRAINING status, the results include only container instances that have\n\t\t\tbeen set to DRAINING using UpdateContainerInstancesState.\n\t\t\tIf you don't specify this parameter, the default is to include container instances set\n\t\t\tto all states other than INACTIVE.

" } } + }, + "traits": { + "smithy.api#input": {} } }, "com.amazonaws.ecs#ListContainerInstancesResponse": { @@ -6069,6 +6325,9 @@ "smithy.api#documentation": "

The nextToken value to include in a future\n\t\t\t\tListContainerInstances request. When the results of a\n\t\t\t\tListContainerInstances request exceed maxResults, this\n\t\t\tvalue can be used to retrieve the next page of results. This value is null\n\t\t\twhen there are no more results to return.

" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ecs#ListServices": { @@ -6157,6 +6416,9 @@ "smithy.api#documentation": "

The maximum number of service results that ListServicesByNamespace\n\t\t\treturns in paginated output. When this parameter is used,\n\t\t\t\tListServicesByNamespace only returns maxResults results in\n\t\t\ta single page along with a nextToken response element. The remaining\n\t\t\tresults of the initial request can be seen by sending another\n\t\t\t\tListServicesByNamespace request with the returned\n\t\t\t\tnextToken value. This value can be between 1 and\n\t\t\t100. If this parameter isn't used, then\n\t\t\t\tListServicesByNamespace returns up to\n\t\t\t10 results and a nextToken\n\t\t\tvalue if applicable.

" } } + }, + "traits": { + "smithy.api#input": {} } }, "com.amazonaws.ecs#ListServicesByNamespaceResponse": { @@ -6174,6 +6436,9 @@ "smithy.api#documentation": "

The nextToken value to include in a future\n\t\t\t\tListServicesByNamespace request. When the results of a\n\t\t\t\tListServicesByNamespace request exceed maxResults, this\n\t\t\tvalue can be used to retrieve the next page of results. When there are no more results\n\t\t\tto return, this value is null.

" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ecs#ListServicesRequest": { @@ -6209,6 +6474,9 @@ "smithy.api#documentation": "

The scheduling strategy to use when filtering the ListServices\n\t\t\tresults.

" } } + }, + "traits": { + "smithy.api#input": {} } }, "com.amazonaws.ecs#ListServicesResponse": { @@ -6226,6 +6494,9 @@ "smithy.api#documentation": "

The nextToken value to include in a future ListServices\n\t\t\trequest. When the results of a ListServices request exceed\n\t\t\t\tmaxResults, this value can be used to retrieve the next page of\n\t\t\tresults. This value is null when there are no more results to\n\t\t\treturn.

" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ecs#ListTagsForResource": { @@ -6264,6 +6535,9 @@ "smithy.api#required": {} } } + }, + "traits": { + "smithy.api#input": {} } }, "com.amazonaws.ecs#ListTagsForResourceResponse": { @@ -6275,6 +6549,9 @@ "smithy.api#documentation": "

The tags for the resource.

" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ecs#ListTaskDefinitionFamilies": { @@ -6333,6 +6610,9 @@ "smithy.api#documentation": "

The maximum number of task definition family results that\n\t\t\t\tListTaskDefinitionFamilies returned in paginated output. When this\n\t\t\tparameter is used, ListTaskDefinitions only returns maxResults\n\t\t\tresults in a single page along with a nextToken response element. The\n\t\t\tremaining results of the initial request can be seen by sending another\n\t\t\t\tListTaskDefinitionFamilies request with the returned\n\t\t\t\tnextToken value. This value can be between 1 and\n\t\t\t100. If this parameter isn't used, then\n\t\t\t\tListTaskDefinitionFamilies returns up to 100 results\n\t\t\tand a nextToken value if applicable.

" } } + }, + "traits": { + "smithy.api#input": {} } }, "com.amazonaws.ecs#ListTaskDefinitionFamiliesResponse": { @@ -6350,6 +6630,9 @@ "smithy.api#documentation": "

The nextToken value to include in a future\n\t\t\t\tListTaskDefinitionFamilies request. When the results of a\n\t\t\t\tListTaskDefinitionFamilies request exceed maxResults, this\n\t\t\tvalue can be used to retrieve the next page of results. This value is null\n\t\t\twhen there are no more results to return.

" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ecs#ListTaskDefinitions": { @@ -6414,6 +6697,9 @@ "smithy.api#documentation": "

The maximum number of task definition results that ListTaskDefinitions\n\t\t\treturned in paginated output. When this parameter is used,\n\t\t\t\tListTaskDefinitions only returns maxResults results in a\n\t\t\tsingle page along with a nextToken response element. The remaining results\n\t\t\tof the initial request can be seen by sending another ListTaskDefinitions\n\t\t\trequest with the returned nextToken value. This value can be between\n\t\t\t1 and 100. If this parameter isn't used, then\n\t\t\t\tListTaskDefinitions returns up to 100 results and a\n\t\t\t\tnextToken value if applicable.

" } } + }, + "traits": { + "smithy.api#input": {} } }, "com.amazonaws.ecs#ListTaskDefinitionsResponse": { @@ -6431,6 +6717,9 @@ "smithy.api#documentation": "

The nextToken value to include in a future\n\t\t\t\tListTaskDefinitions request. When the results of a\n\t\t\t\tListTaskDefinitions request exceed maxResults, this value\n\t\t\tcan be used to retrieve the next page of results. This value is null when\n\t\t\tthere are no more results to return.

" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ecs#ListTasks": { @@ -6525,6 +6814,9 @@ "smithy.api#documentation": "

The launch type to use when filtering the ListTasks results.

" } } + }, + "traits": { + "smithy.api#input": {} } }, "com.amazonaws.ecs#ListTasksResponse": { @@ -6542,6 +6834,9 @@ "smithy.api#documentation": "

The nextToken value to include in a future ListTasks\n\t\t\trequest. When the results of a ListTasks request exceed\n\t\t\t\tmaxResults, this value can be used to retrieve the next page of\n\t\t\tresults. This value is null when there are no more results to\n\t\t\treturn.

" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ecs#LoadBalancer": { @@ -6802,7 +7097,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 disabled, 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 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.

" } }, "com.amazonaws.ecs#ManagedScalingInstanceWarmupPeriod": { @@ -6952,7 +7247,7 @@ "containerPortRange": { "target": "com.amazonaws.ecs#String", "traits": { - "smithy.api#documentation": "

The port number range on the container that's bound to the dynamically mapped host port range.

\n

The following rules apply when you specify a containerPortRange:

\n
    \n
  • \n

    You must use either the bridge network mode or the awsvpc\n\t\t\t\t\tnetwork mode.

    \n
  • \n
  • \n

    This parameter is available for both the EC2 and Fargate launch types.

    \n
  • \n
  • \n

    This parameter is available for both the Linux and Windows operating systems.

    \n
  • \n
  • \n

    The container instance must have at least version 1.67.0 of the container agent\n\t\t\t\t\tand at least version 1.67.0-1 of the ecs-init package

    \n
  • \n
  • \n

    You can specify a maximum of 100 port ranges per container.

    \n
  • \n
  • \n

    You do not specify a hostPortRange. The value of the hostPortRange is set\n\t\t\t\t\tas follows:

    \n
      \n
    • \n

      For containers in a task with the awsvpc network mode,\n\t\t\t\t\t\t\tthe hostPort is set to the same value as the\n\t\t\t\t\t\t\t\tcontainerPort. This is a static mapping\n\t\t\t\t\t\t\tstrategy.

      \n
    • \n
    • \n

      For containers in a task with the bridge network mode, the Amazon ECS agent finds open host ports from the default ephemeral range and passes it to docker to bind them to the container ports.

      \n
    • \n
    \n
  • \n
  • \n

    The containerPortRange valid values are between 1 and\n\t\t\t\t\t65535.

    \n
  • \n
  • \n

    A port can only be included in one port mapping per container.

    \n
  • \n
  • \n

    You cannot specify overlapping port ranges.

    \n
  • \n
  • \n

    The first port in the range must be less than last port in the range.

    \n
  • \n
  • \n

    Docker recommends that you turn off the docker-proxy in the Docker daemon config file when you have a large number of ports.

    \n

    For more information, see Issue #11185 on the Github website.

    \n

    For information about how to turn off the docker-proxy in the Docker daemon config file, see Docker daemon in the Amazon ECS Developer Guide.

    \n
  • \n
\n

You can call \n DescribeTasks\n to view the hostPortRange which\n\t\t\tare the host ports that are bound to the container ports.

" + "smithy.api#documentation": "

The port number range on the container that's bound to the dynamically mapped host port\n\t\t\trange.

\n

The following rules apply when you specify a containerPortRange:

\n
    \n
  • \n

    You must use either the bridge network mode or the awsvpc\n\t\t\t\t\tnetwork mode.

    \n
  • \n
  • \n

    This parameter is available for both the EC2 and Fargate launch types.

    \n
  • \n
  • \n

    This parameter is available for both the Linux and Windows operating systems.

    \n
  • \n
  • \n

    The container instance must have at least version 1.67.0 of the container agent\n\t\t\t\t\tand at least version 1.67.0-1 of the ecs-init package

    \n
  • \n
  • \n

    You can specify a maximum of 100 port ranges per container.

    \n
  • \n
  • \n

    You do not specify a hostPortRange. The value of the hostPortRange is set\n\t\t\t\t\tas follows:

    \n
      \n
    • \n

      For containers in a task with the awsvpc network mode,\n\t\t\t\t\t\t\tthe hostPort is set to the same value as the\n\t\t\t\t\t\t\t\tcontainerPort. This is a static mapping\n\t\t\t\t\t\t\tstrategy.

      \n
    • \n
    • \n

      For containers in a task with the bridge network mode, the Amazon ECS agent finds open host ports from the default ephemeral range and passes it to docker to bind them to the container ports.

      \n
    • \n
    \n
  • \n
  • \n

    The containerPortRange valid values are between 1 and\n\t\t\t\t\t65535.

    \n
  • \n
  • \n

    A port can only be included in one port mapping per container.

    \n
  • \n
  • \n

    You cannot specify overlapping port ranges.

    \n
  • \n
  • \n

    The first port in the range must be less than last port in the range.

    \n
  • \n
  • \n

    Docker recommends that you turn off the docker-proxy in the Docker daemon config file when you have a large number of ports.

    \n

    For more information, see Issue #11185 on the Github website.

    \n

    For information about how to turn off the docker-proxy in the Docker daemon config file, see Docker daemon in the Amazon ECS Developer Guide.

    \n
  • \n
\n

You can call \n DescribeTasks\n to view the hostPortRange which\n\t\t\tare the host ports that are bound to the container ports.

" } }, "hostPortRange": { @@ -6983,7 +7278,7 @@ } }, "traits": { - "smithy.api#documentation": "

An object representing the network configuration for a task or service.

" + "smithy.api#documentation": "

The network configuration for a task or service.

" } }, "com.amazonaws.ecs#NetworkInterface": { @@ -7320,7 +7615,7 @@ "containerPortRange": { "target": "com.amazonaws.ecs#String", "traits": { - "smithy.api#documentation": "

The port number range on the container that's bound to the dynamically mapped host port range.

\n

The following rules apply when you specify a containerPortRange:

\n
    \n
  • \n

    You must use either the bridge network mode or the awsvpc\n\t\t\t\t\tnetwork mode.

    \n
  • \n
  • \n

    This parameter is available for both the EC2 and Fargate launch types.

    \n
  • \n
  • \n

    This parameter is available for both the Linux and Windows operating systems.

    \n
  • \n
  • \n

    The container instance must have at least version 1.67.0 of the container agent\n\t\t\t\t\tand at least version 1.67.0-1 of the ecs-init package

    \n
  • \n
  • \n

    You can specify a maximum of 100 port ranges per container.

    \n
  • \n
  • \n

    You do not specify a hostPortRange. The value of the hostPortRange is set\n\t\t\t\t\tas follows:

    \n
      \n
    • \n

      For containers in a task with the awsvpc network mode,\n\t\t\t\t\t\t\tthe hostPort is set to the same value as the\n\t\t\t\t\t\t\t\tcontainerPort. This is a static mapping\n\t\t\t\t\t\t\tstrategy.

      \n
    • \n
    • \n

      For containers in a task with the bridge network mode, the Amazon ECS agent finds open host ports from the default ephemeral range and passes it to docker to bind them to the container ports.

      \n
    • \n
    \n
  • \n
  • \n

    The containerPortRange valid values are between 1 and\n\t\t\t\t\t65535.

    \n
  • \n
  • \n

    A port can only be included in one port mapping per container.

    \n
  • \n
  • \n

    You cannot specify overlapping port ranges.

    \n
  • \n
  • \n

    The first port in the range must be less than last port in the range.

    \n
  • \n
  • \n

    Docker recommends that you turn off the docker-proxy in the Docker daemon config file when you have a large number of ports.

    \n

    For more information, see Issue #11185 on the Github website.

    \n

    For information about how to turn off the docker-proxy in the Docker daemon config file, see Docker daemon in the Amazon ECS Developer Guide.

    \n
  • \n
\n

You can call \n DescribeTasks\n to view the hostPortRange which\n\t\t\tare the host ports that are bound to the container ports.

" + "smithy.api#documentation": "

The port number range on the container that's bound to the dynamically mapped host port\n\t\t\trange.

\n

The following rules apply when you specify a containerPortRange:

\n
    \n
  • \n

    You must use either the bridge network mode or the awsvpc\n\t\t\t\t\tnetwork mode.

    \n
  • \n
  • \n

    This parameter is available for both the EC2 and Fargate launch types.

    \n
  • \n
  • \n

    This parameter is available for both the Linux and Windows operating systems.

    \n
  • \n
  • \n

    The container instance must have at least version 1.67.0 of the container agent\n\t\t\t\t\tand at least version 1.67.0-1 of the ecs-init package

    \n
  • \n
  • \n

    You can specify a maximum of 100 port ranges per container.

    \n
  • \n
  • \n

    You do not specify a hostPortRange. The value of the hostPortRange is set\n\t\t\t\t\tas follows:

    \n
      \n
    • \n

      For containers in a task with the awsvpc network mode,\n\t\t\t\t\t\t\tthe hostPort is set to the same value as the\n\t\t\t\t\t\t\t\tcontainerPort. This is a static mapping\n\t\t\t\t\t\t\tstrategy.

      \n
    • \n
    • \n

      For containers in a task with the bridge network mode, the Amazon ECS agent finds open host ports from the default ephemeral range and passes it to docker to bind them to the container ports.

      \n
    • \n
    \n
  • \n
  • \n

    The containerPortRange valid values are between 1 and\n\t\t\t\t\t65535.

    \n
  • \n
  • \n

    A port can only be included in one port mapping per container.

    \n
  • \n
  • \n

    You cannot specify overlapping port ranges.

    \n
  • \n
  • \n

    The first port in the range must be less than last port in the range.

    \n
  • \n
  • \n

    Docker recommends that you turn off the docker-proxy in the Docker daemon config file when you have a large number of ports.

    \n

    For more information, see Issue #11185 on the Github website.

    \n

    For information about how to turn off the docker-proxy in the Docker daemon config file, see Docker daemon in the Amazon ECS Developer Guide.

    \n
  • \n
\n

You can call \n DescribeTasks\n to view the hostPortRange which\n\t\t\tare the host ports that are bound to the container ports.

" } } }, @@ -7379,7 +7674,7 @@ "target": "com.amazonaws.ecs#Boolean", "traits": { "smithy.api#default": false, - "smithy.api#documentation": "

The protection status of the task. If scale-in protection is enabled for a task, the\n\t\t\tvalue is true. Otherwise, it is false.

" + "smithy.api#documentation": "

The protection status of the task. If scale-in protection is on for a task, the\n\t\t\tvalue is true. Otherwise, it is false.

" } }, "expirationDate": { @@ -7463,7 +7758,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 IAM 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 IAM user, IAM 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 IAM 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 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.

" } }, "com.amazonaws.ecs#PutAccountSettingDefault": { @@ -7486,7 +7781,7 @@ } ], "traits": { - "smithy.api#documentation": "

Modifies an account setting for all IAM users on an account for whom no individual\n\t\t\taccount setting has been specified. Account settings are set on a per-Region\n\t\t\tbasis.

" + "smithy.api#documentation": "

Modifies an account setting for all users on an account for whom no individual\n\t\t\taccount setting has been specified. Account settings are set on a per-Region\n\t\t\tbasis.

" } }, "com.amazonaws.ecs#PutAccountSettingDefaultRequest": { @@ -7506,6 +7801,9 @@ "smithy.api#required": {} } } + }, + "traits": { + "smithy.api#input": {} } }, "com.amazonaws.ecs#PutAccountSettingDefaultResponse": { @@ -7517,6 +7815,9 @@ "smithy.api#documentation": "

The current setting for a resource.

" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ecs#PutAccountSettingRequest": { @@ -7539,9 +7840,12 @@ "principalArn": { "target": "com.amazonaws.ecs#String", "traits": { - "smithy.api#documentation": "

The ARN of the principal, which can be an IAM user, IAM role, or the root user. If\n\t\t\tyou specify the root user, it modifies the account setting for all IAM users, IAM roles,\n\t\t\tand the root user of the account unless an IAM user or role explicitly overrides these\n\t\t\tsettings. If this field is omitted, the setting is changed only for the authenticated\n\t\t\tuser.

\n \n

Federated users assume the account setting of the root user and can't have\n\t\t\t\texplicit account settings set for them.

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

The ARN of the principal, which can be a user, role, or the root user. If\n\t\t\tyou specify the root user, it modifies the account setting for all users, roles,\n\t\t\tand the root user of the account unless a user or role explicitly overrides these\n\t\t\tsettings. If this field is omitted, the setting is changed only for the authenticated\n\t\t\tuser.

\n \n

Federated users assume the account setting of the root user and can't have\n\t\t\t\texplicit account settings set for them.

\n
" } } + }, + "traits": { + "smithy.api#input": {} } }, "com.amazonaws.ecs#PutAccountSettingResponse": { @@ -7553,6 +7857,9 @@ "smithy.api#documentation": "

The current account setting for a resource.

" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ecs#PutAttributes": { @@ -7597,6 +7904,9 @@ "smithy.api#required": {} } } + }, + "traits": { + "smithy.api#input": {} } }, "com.amazonaws.ecs#PutAttributesResponse": { @@ -7608,6 +7918,9 @@ "smithy.api#documentation": "

The attributes applied to your resource.

" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ecs#PutClusterCapacityProviders": { @@ -7666,6 +7979,9 @@ "smithy.api#required": {} } } + }, + "traits": { + "smithy.api#input": {} } }, "com.amazonaws.ecs#PutClusterCapacityProvidersResponse": { @@ -7677,6 +7993,9 @@ "smithy.api#documentation": "

Details about the cluster.

" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ecs#RegisterContainerInstance": { @@ -7759,6 +8078,9 @@ "smithy.api#documentation": "

The metadata that you apply to the container instance to help you categorize and\n\t\t\torganize them. Each tag consists of a key and an optional value. You define both.

\n

The following basic restrictions apply to tags:

\n
    \n
  • \n

    Maximum number of tags per resource - 50

    \n
  • \n
  • \n

    For each resource, each tag key must be unique, and each tag key can have only\n one value.

    \n
  • \n
  • \n

    Maximum key length - 128 Unicode characters in UTF-8

    \n
  • \n
  • \n

    Maximum value length - 256 Unicode characters in UTF-8

    \n
  • \n
  • \n

    If your tagging schema is used across multiple services and resources,\n remember that other services may have restrictions on allowed characters.\n Generally allowed characters are: letters, numbers, and spaces representable in\n UTF-8, and the following characters: + - = . _ : / @.

    \n
  • \n
  • \n

    Tag keys and values are case-sensitive.

    \n
  • \n
  • \n

    Do not use aws:, AWS:, or any upper or lowercase\n combination of such as a prefix for either keys or values as it is reserved for\n Amazon Web Services use. You cannot edit or delete tag keys or values with this prefix. Tags with\n this prefix do not count against your tags per resource limit.

    \n
  • \n
" } } + }, + "traits": { + "smithy.api#input": {} } }, "com.amazonaws.ecs#RegisterContainerInstanceResponse": { @@ -7770,6 +8092,9 @@ "smithy.api#documentation": "

The container instance that was registered.

" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ecs#RegisterTaskDefinition": { @@ -7792,7 +8117,7 @@ } ], "traits": { - "smithy.api#documentation": "

Registers a new task definition from the supplied family and\n\t\t\t\tcontainerDefinitions. Optionally, you can add data volumes to your\n\t\t\tcontainers with the volumes parameter. For more information about task\n\t\t\tdefinition parameters and defaults, see Amazon ECS Task\n\t\t\t\tDefinitions in the Amazon Elastic Container Service Developer Guide.

\n

You can specify an IAM role for your task with the taskRoleArn parameter.\n\t\t\tWhen you specify an IAM role for a task, its containers can then use the latest versions\n\t\t\tof the CLI or SDKs to make API requests to the Amazon Web Services services that are specified in\n\t\t\tthe IAM policy that's associated with the role. For more information, see IAM\n\t\t\t\tRoles for Tasks in the Amazon Elastic Container Service Developer Guide.

\n

You can specify a Docker networking mode for the containers in your task definition\n\t\t\twith the networkMode parameter. The available network modes correspond to\n\t\t\tthose described in Network\n\t\t\t\tsettings in the Docker run reference. If you specify the awsvpc\n\t\t\tnetwork mode, the task is allocated an elastic network interface, and you must specify a\n\t\t\t\tNetworkConfiguration when you create a service or run a task with\n\t\t\tthe task definition. For more information, see Task Networking\n\t\t\tin the Amazon Elastic Container Service Developer Guide.

" + "smithy.api#documentation": "

Registers a new task definition from the supplied family and\n\t\t\t\tcontainerDefinitions. Optionally, you can add data volumes to your\n\t\t\tcontainers with the volumes parameter. For more information about task\n\t\t\tdefinition parameters and defaults, see Amazon ECS Task\n\t\t\t\tDefinitions in the Amazon Elastic Container Service Developer Guide.

\n

You can specify a role for your task with the taskRoleArn parameter.\n\t\t\tWhen you specify a role for a task, its containers can then use the latest versions\n\t\t\tof the CLI or SDKs to make API requests to the Amazon Web Services services that are specified in\n\t\t\tthe policy that's associated with the role. For more information, see IAM\n\t\t\t\tRoles for Tasks in the Amazon Elastic Container Service Developer Guide.

\n

You can specify a Docker networking mode for the containers in your task definition\n\t\t\twith the networkMode parameter. The available network modes correspond to\n\t\t\tthose described in Network\n\t\t\t\tsettings in the Docker run reference. If you specify the awsvpc\n\t\t\tnetwork mode, the task is allocated an elastic network interface, and you must specify a\n\t\t\t\tNetworkConfiguration when you create a service or run a task with\n\t\t\tthe task definition. For more information, see Task Networking\n\t\t\tin the Amazon Elastic Container Service Developer Guide.

" } }, "com.amazonaws.ecs#RegisterTaskDefinitionRequest": { @@ -7902,6 +8227,9 @@ "smithy.api#documentation": "

The operating system that your tasks definitions run on. A platform family is\n\t\t\tspecified only for tasks using the Fargate launch type.

\n

When you specify a task definition in a service, this value must match the\n\t\t\t\truntimePlatform value of the service.

" } } + }, + "traits": { + "smithy.api#input": {} } }, "com.amazonaws.ecs#RegisterTaskDefinitionResponse": { @@ -7919,6 +8247,9 @@ "smithy.api#documentation": "

The list of tags associated with the task definition.

" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ecs#RepositoryCredentials": { @@ -8209,10 +8540,13 @@ "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. If a revision isn't specified,\n\t\t\tthe latest ACTIVE revision is used.

\n

When you create an IAM policy for run-task, you can set the resource to be the latest\n\t\t\ttask definition revision, or a specific revision.

\n

The full ARN value must match the value that you specified as the\n\t\t\t\tResource of the IAM principal's permissions policy.

\n

When you specify the policy resource as the latest task definition version (by setting\n\t\t\tthe Resource in the policy to\n\t\t\t\tarn:aws:ecs:us-east-1:111122223333:task-definition/TaskFamilyName),\n\t\t\tthen set this value to\n\t\t\t\tarn:aws:ecs:us-east-1:111122223333:task-definition/TaskFamilyName.

\n

When you specify the policy resource as a specific task definition version (by setting\n\t\t\tthe Resource in the policy to\n\t\t\t\tarn:aws:ecs:us-east-1:111122223333:task-definition/TaskFamilyName:1 or\n\t\t\t\tarn:aws:ecs:us-east-1:111122223333:task-definition/TaskFamilyName:*),\n\t\t\tthen set this value to\n\t\t\t\tarn:aws:ecs:us-east-1:111122223333:task-definition/TaskFamilyName:1.

\n

For more information, see Policy Resources for Amazon ECS in the Amazon Elastic Container Service developer Guide.

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

The family and revision (family:revision) or\n\t\t\tfull ARN of the task definition to run. If a revision isn't specified,\n\t\t\tthe latest ACTIVE revision is used.

\n

When you create a policy for run-task, you can set the resource to be the latest\n\t\t\ttask definition revision, or a specific revision.

\n

The full ARN value must match the value that you specified as the\n\t\t\t\tResource of the principal's permissions policy.

\n

When you specify the policy resource as the latest task definition version (by setting\n\t\t\tthe Resource in the policy to\n\t\t\t\tarn:aws:ecs:us-east-1:111122223333:task-definition/TaskFamilyName),\n\t\t\tthen set this value to\n\t\t\t\tarn:aws:ecs:us-east-1:111122223333:task-definition/TaskFamilyName.

\n

When you specify the policy resource as a specific task definition version (by setting\n\t\t\tthe Resource in the policy to\n\t\t\t\tarn:aws:ecs:us-east-1:111122223333:task-definition/TaskFamilyName:1 or\n\t\t\t\tarn:aws:ecs:us-east-1:111122223333:task-definition/TaskFamilyName:*),\n\t\t\tthen set this value to\n\t\t\t\tarn:aws:ecs:us-east-1:111122223333:task-definition/TaskFamilyName:1.

\n

For more information, see Policy Resources for Amazon ECS in the Amazon Elastic Container Service developer Guide.

", "smithy.api#required": {} } } + }, + "traits": { + "smithy.api#input": {} } }, "com.amazonaws.ecs#RunTaskResponse": { @@ -8230,6 +8564,9 @@ "smithy.api#documentation": "

Any failures associated with the call.

" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ecs#RuntimePlatform": { @@ -8841,13 +9178,13 @@ "value": { "target": "com.amazonaws.ecs#String", "traits": { - "smithy.api#documentation": "

Determines whether the account setting is enabled or disabled for the specified\n\t\t\tresource.

" + "smithy.api#documentation": "

Determines whether the account setting is on or off for the specified resource.

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

The ARN of the principal. It can be an IAM user, IAM role, or the root user. If this\n\t\t\tfield is omitted, the authenticated user is assumed.

" + "smithy.api#documentation": "

The ARN of the principal. It can be a user, role, or the root user. If this\n\t\t\tfield is omitted, the authenticated user is assumed.

" } } }, @@ -9035,6 +9372,9 @@ "smithy.api#required": {} } } + }, + "traits": { + "smithy.api#input": {} } }, "com.amazonaws.ecs#StartTaskResponse": { @@ -9052,6 +9392,9 @@ "smithy.api#documentation": "

Any failures associated with the call.

" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ecs#Statistics": { @@ -9098,7 +9441,7 @@ "task": { "target": "com.amazonaws.ecs#String", "traits": { - "smithy.api#documentation": "

The task ID or full Amazon Resource Name (ARN) of the task to stop.

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

The task ID of the task to stop.

", "smithy.api#required": {} } }, @@ -9108,6 +9451,9 @@ "smithy.api#documentation": "

An optional message specified when a task is stopped. For example, if you're using a\n\t\t\tcustom scheduler, you can use this parameter to specify the reason for stopping the task\n\t\t\there, and the message appears in subsequent DescribeTasks API\n\t\t\toperations on this task. Up to 255 characters are allowed in this message.

" } } + }, + "traits": { + "smithy.api#input": {} } }, "com.amazonaws.ecs#StopTaskResponse": { @@ -9119,6 +9465,9 @@ "smithy.api#documentation": "

The task that was stopped.

" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ecs#String": { @@ -9181,6 +9530,9 @@ "smithy.api#required": {} } } + }, + "traits": { + "smithy.api#input": {} } }, "com.amazonaws.ecs#SubmitAttachmentStateChangesResponse": { @@ -9192,6 +9544,9 @@ "smithy.api#documentation": "

Acknowledgement of the state change.

" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ecs#SubmitContainerStateChange": { @@ -9268,6 +9623,9 @@ "smithy.api#documentation": "

The network bindings of the container.

" } } + }, + "traits": { + "smithy.api#input": {} } }, "com.amazonaws.ecs#SubmitContainerStateChangeResponse": { @@ -9279,6 +9637,9 @@ "smithy.api#documentation": "

Acknowledgement of the state change.

" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ecs#SubmitTaskStateChange": { @@ -9370,6 +9731,9 @@ "smithy.api#documentation": "

The Unix timestamp for the time when the task execution stopped.

" } } + }, + "traits": { + "smithy.api#input": {} } }, "com.amazonaws.ecs#SubmitTaskStateChangeResponse": { @@ -9381,6 +9745,9 @@ "smithy.api#documentation": "

Acknowledgement of the state change.

" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ecs#SystemControl": { @@ -9491,11 +9858,17 @@ "smithy.api#required": {} } } + }, + "traits": { + "smithy.api#input": {} } }, "com.amazonaws.ecs#TagResourceResponse": { "type": "structure", - "members": {} + "members": {}, + "traits": { + "smithy.api#output": {} + } }, "com.amazonaws.ecs#TagValue": { "type": "string", @@ -9973,6 +10346,12 @@ "target": "com.amazonaws.ecs#TaskDefinitionField" } }, + "com.amazonaws.ecs#TaskDefinitionList": { + "type": "list", + "member": { + "target": "com.amazonaws.ecs#TaskDefinition" + } + }, "com.amazonaws.ecs#TaskDefinitionPlacementConstraint": { "type": "structure", "members": { @@ -10024,6 +10403,12 @@ "traits": { "smithy.api#enumValue": "INACTIVE" } + }, + "DELETE_IN_PROGRESS": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "DELETE_IN_PROGRESS" + } } } }, @@ -10068,7 +10453,7 @@ "executionRoleArn": { "target": "com.amazonaws.ecs#String", "traits": { - "smithy.api#documentation": "

The Amazon Resource Name (ARN) of the task execution IAM role override for the task. For more\n\t\t\tinformation, see Amazon ECS task\n\t\t\t\texecution IAM role in the Amazon Elastic Container Service Developer Guide.

" + "smithy.api#documentation": "

The Amazon Resource Name (ARN) of the task execution role override for the task. For more\n\t\t\tinformation, see Amazon ECS task\n\t\t\t\texecution IAM role in the Amazon Elastic Container Service Developer Guide.

" } }, "memory": { @@ -10080,7 +10465,7 @@ "taskRoleArn": { "target": "com.amazonaws.ecs#String", "traits": { - "smithy.api#documentation": "

The Amazon Resource Name (ARN) of the IAM role that containers in this task can assume. All containers\n\t\t\tin this task are granted the permissions that are specified in this role. For more\n\t\t\tinformation, see IAM Role for Tasks\n\t\t\tin the Amazon Elastic Container Service Developer Guide.

" + "smithy.api#documentation": "

The Amazon Resource Name (ARN) of the role that containers in this task can assume. All containers\n\t\t\tin this task are granted the permissions that are specified in this role. For more\n\t\t\tinformation, see IAM Role for Tasks\n\t\t\tin the Amazon Elastic Container Service Developer Guide.

" } }, "ephemeralStorage": { @@ -10414,7 +10799,7 @@ } }, "traits": { - "smithy.api#documentation": "

The ulimit settings to pass to the container.

\n

Amazon ECS tasks hosted on Fargate use the default\n\t\t\t\t\t\t\tresource limit values set by the operating system with the exception of\n\t\t\t\t\t\t\tthe nofile resource limit parameter which Fargate\n\t\t\t\t\t\t\toverrides. The nofile resource limit sets a restriction on\n\t\t\t\t\t\t\tthe number of open files that a container can use. The default\n\t\t\t\t\t\t\t\tnofile soft limit is 1024 and hard limit\n\t\t\t\t\t\t\tis 4096.

" + "smithy.api#documentation": "

The ulimit settings to pass to the container.

\n

Amazon ECS tasks hosted on Fargate use the default\n\t\t\t\t\t\t\tresource limit values set by the operating system with the exception of\n\t\t\t\t\t\t\tthe nofile resource limit parameter which Fargate\n\t\t\t\t\t\t\toverrides. The nofile resource limit sets a restriction on\n\t\t\t\t\t\t\tthe number of open files that a container can use. The default\n\t\t\t\t\t\t\t\tnofile soft limit is 1024 and the default hard limit\n\t\t\t\t\t\t\tis 4096.

\n

You can specify the ulimit settings for a container in a task\n\t\t\tdefinition.

" } }, "com.amazonaws.ecs#UlimitList": { @@ -10576,11 +10961,17 @@ "smithy.api#required": {} } } + }, + "traits": { + "smithy.api#input": {} } }, "com.amazonaws.ecs#UntagResourceResponse": { "type": "structure", - "members": {} + "members": {}, + "traits": { + "smithy.api#output": {} + } }, "com.amazonaws.ecs#UpdateCapacityProvider": { "type": "operation", @@ -10622,6 +11013,9 @@ "smithy.api#required": {} } } + }, + "traits": { + "smithy.api#input": {} } }, "com.amazonaws.ecs#UpdateCapacityProviderResponse": { @@ -10633,6 +11027,9 @@ "smithy.api#documentation": "

Details about the capacity provider.

" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ecs#UpdateCluster": { @@ -10689,6 +11086,9 @@ "smithy.api#documentation": "

Use this parameter to set a default Service Connect namespace. After you set a default \n\tService Connect namespace, any new services with Service Connect turned on that are created in the cluster are added as\n\tclient services in the namespace. This setting only applies to new services that set the enabled parameter to\n\ttrue in the ServiceConnectConfiguration.\n\tYou can set the namespace of each service individually in the ServiceConnectConfiguration to override this default\n\tparameter.

\n

Tasks that run in a namespace can use short names to connect\n\tto services in the namespace. Tasks can connect to services across all of the clusters in the namespace.\n\tTasks connect through a managed proxy container\n\tthat collects logs and metrics for increased visibility.\n\tOnly the tasks that Amazon ECS services create are supported with Service Connect.\n\tFor more information, see Service Connect in the Amazon Elastic Container Service Developer Guide.

" } } + }, + "traits": { + "smithy.api#input": {} } }, "com.amazonaws.ecs#UpdateClusterResponse": { @@ -10700,6 +11100,9 @@ "smithy.api#documentation": "

Details about the cluster.

" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ecs#UpdateClusterSettings": { @@ -10745,6 +11148,9 @@ "smithy.api#required": {} } } + }, + "traits": { + "smithy.api#input": {} } }, "com.amazonaws.ecs#UpdateClusterSettingsResponse": { @@ -10756,6 +11162,9 @@ "smithy.api#documentation": "

Details about the cluster

" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ecs#UpdateContainerAgent": { @@ -10809,6 +11218,9 @@ "smithy.api#required": {} } } + }, + "traits": { + "smithy.api#input": {} } }, "com.amazonaws.ecs#UpdateContainerAgentResponse": { @@ -10820,6 +11232,9 @@ "smithy.api#documentation": "

The container instance that the container agent was updated for.

" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ecs#UpdateContainerInstancesState": { @@ -10871,6 +11286,9 @@ "smithy.api#required": {} } } + }, + "traits": { + "smithy.api#input": {} } }, "com.amazonaws.ecs#UpdateContainerInstancesStateResponse": { @@ -10888,6 +11306,9 @@ "smithy.api#documentation": "

Any failures associated with the call.

" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ecs#UpdateInProgressException": { @@ -10943,7 +11364,7 @@ } ], "traits": { - "smithy.api#documentation": "

Modifies the parameters of a service.

\n

For services using the rolling update (ECS) you can update the desired\n\t\t\tcount, deployment configuration, network configuration, load balancers, service\n\t\t\tregistries, enable ECS managed tags option, propagate tags option, task placement\n\t\t\tconstraints and strategies, and task definition. When you update any of these\n\t\t\tparameters, Amazon ECS starts new tasks with the new configuration.

\n

For services using the blue/green (CODE_DEPLOY) deployment controller,\n\t\t\tonly the desired count, deployment configuration, health check grace period, task\n\t\t\tplacement constraints and strategies, enable ECS managed tags option, and propagate tags\n\t\t\tcan be updated using this API. If the network configuration, platform version, task\n\t\t\tdefinition, or load balancer need to be updated, create a new CodeDeploy deployment. For more\n\t\t\tinformation, see CreateDeployment in the CodeDeploy API Reference.

\n

For services using an external deployment controller, you can update only the desired\n\t\t\tcount, task placement constraints and strategies, health check grace period, enable ECS\n\t\t\tmanaged tags option, and propagate tags option, using this API. If the launch type, load\n\t\t\tbalancer, network configuration, platform version, or task definition need to be\n\t\t\tupdated, create a new task set For more information, see CreateTaskSet.

\n

You can add to or subtract from the number of instantiations of a task definition in a\n\t\t\tservice by specifying the cluster that the service is running in and a new\n\t\t\t\tdesiredCount parameter.

\n

If you have updated the Docker image of your application, you can create a new task\n\t\t\tdefinition with that image and deploy it to your service. The service scheduler uses the\n\t\t\tminimum healthy percent and maximum percent parameters (in the service's deployment\n\t\t\tconfiguration) to determine the deployment strategy.

\n \n

If your updated Docker image uses the same tag as what is in the existing task\n\t\t\t\tdefinition for your service (for example, my_image:latest), you don't\n\t\t\t\tneed to create a new revision of your task definition. You can update the service\n\t\t\t\tusing the forceNewDeployment option. The new tasks launched by the\n\t\t\t\tdeployment pull the current image/tag combination from your repository when they\n\t\t\t\tstart.

\n
\n

You can also update the deployment configuration of a service. When a deployment is\n\t\t\ttriggered by updating the task definition of a service, the service scheduler uses the\n\t\t\tdeployment configuration parameters, minimumHealthyPercent and\n\t\t\t\tmaximumPercent, to determine the deployment strategy.

\n
    \n
  • \n

    If minimumHealthyPercent is below 100%, the scheduler can ignore\n\t\t\t\t\t\tdesiredCount temporarily during a deployment. For example, if\n\t\t\t\t\t\tdesiredCount is four tasks, a minimum of 50% allows the\n\t\t\t\t\tscheduler to stop two existing tasks before starting two new tasks. Tasks for\n\t\t\t\t\tservices that don't use a load balancer are considered healthy if they're in the\n\t\t\t\t\t\tRUNNING state. Tasks for services that use a load balancer are\n\t\t\t\t\tconsidered healthy if they're in the RUNNING state and are reported\n\t\t\t\t\tas healthy by the load balancer.

    \n
  • \n
  • \n

    The maximumPercent parameter represents an upper limit on the\n\t\t\t\t\tnumber of running tasks during a deployment. You can use it to define the\n\t\t\t\t\tdeployment batch size. For example, if desiredCount is four tasks,\n\t\t\t\t\ta maximum of 200% starts four new tasks before stopping the four older tasks\n\t\t\t\t\t(provided that the cluster resources required to do this are available).

    \n
  • \n
\n

When UpdateService stops a task during a deployment, the equivalent\n\t\t\tof docker stop is issued to the containers running in the task. This\n\t\t\tresults in a SIGTERM and a 30-second timeout. After this,\n\t\t\t\tSIGKILL is sent and the containers are forcibly stopped. If the\n\t\t\tcontainer handles the SIGTERM gracefully and exits within 30 seconds from\n\t\t\treceiving it, no SIGKILL is sent.

\n

When the service scheduler launches new tasks, it determines task placement in your\n\t\t\tcluster with the following logic.

\n
    \n
  • \n

    Determine which of the container instances in your cluster can support your\n\t\t\t\t\tservice's task definition. For example, they have the required CPU, memory,\n\t\t\t\t\tports, and container instance attributes.

    \n
  • \n
  • \n

    By default, the service scheduler attempts to balance tasks across\n\t\t\t\t\tAvailability Zones in this manner even though you can choose a different\n\t\t\t\t\tplacement strategy.

    \n
      \n
    • \n

      Sort the valid container instances by the fewest number of running\n\t\t\t\t\t\t\ttasks for this service in the same Availability Zone as the instance.\n\t\t\t\t\t\t\tFor example, if zone A has one running service task and zones B and C\n\t\t\t\t\t\t\teach have zero, valid container instances in either zone B or C are\n\t\t\t\t\t\t\tconsidered optimal for placement.

      \n
    • \n
    • \n

      Place the new service task on a valid container instance in an optimal\n\t\t\t\t\t\t\tAvailability Zone (based on the previous steps), favoring container\n\t\t\t\t\t\t\tinstances with the fewest number of running tasks for this\n\t\t\t\t\t\t\tservice.

      \n
    • \n
    \n
  • \n
\n

When the service scheduler stops running tasks, it attempts to maintain balance across\n\t\t\tthe Availability Zones in your cluster using the following logic:

\n
    \n
  • \n

    Sort the container instances by the largest number of running tasks for this\n\t\t\t\t\tservice in the same Availability Zone as the instance. For example, if zone A\n\t\t\t\t\thas one running service task and zones B and C each have two, container\n\t\t\t\t\tinstances in either zone B or C are considered optimal for termination.

    \n
  • \n
  • \n

    Stop the task on a container instance in an optimal Availability Zone (based\n\t\t\t\t\ton the previous steps), favoring container instances with the largest number of\n\t\t\t\t\trunning tasks for this service.

    \n
  • \n
\n \n

You must have a service-linked role when you update any of the following service\n\t\t\t\tproperties. If you specified a custom IAM role when you created the service, Amazon ECS\n\t\t\t\tautomatically replaces the roleARN associated with the service with the ARN of your\n\t\t\t\tservice-linked role. For more information, see Service-linked roles in the Amazon Elastic Container Service Developer Guide.

\n
    \n
  • \n

    \n loadBalancers,\n

    \n
  • \n
  • \n

    \n serviceRegistries\n

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

Modifies the parameters of a service.

\n

For services using the rolling update (ECS) you can update the desired\n\t\t\tcount, deployment configuration, network configuration, load balancers, service\n\t\t\tregistries, enable ECS managed tags option, propagate tags option, task placement\n\t\t\tconstraints and strategies, and task definition. When you update any of these\n\t\t\tparameters, Amazon ECS starts new tasks with the new configuration.

\n

For services using the blue/green (CODE_DEPLOY) deployment controller,\n\t\t\tonly the desired count, deployment configuration, health check grace period, task\n\t\t\tplacement constraints and strategies, enable ECS managed tags option, and propagate tags\n\t\t\tcan be updated using this API. If the network configuration, platform version, task\n\t\t\tdefinition, or load balancer need to be updated, create a new CodeDeploy deployment. For more\n\t\t\tinformation, see CreateDeployment in the CodeDeploy API Reference.

\n

For services using an external deployment controller, you can update only the desired\n\t\t\tcount, task placement constraints and strategies, health check grace period, enable ECS\n\t\t\tmanaged tags option, and propagate tags option, using this API. If the launch type, load\n\t\t\tbalancer, network configuration, platform version, or task definition need to be\n\t\t\tupdated, create a new task set For more information, see CreateTaskSet.

\n

You can add to or subtract from the number of instantiations of a task definition in a\n\t\t\tservice by specifying the cluster that the service is running in and a new\n\t\t\t\tdesiredCount parameter.

\n

If you have updated the Docker image of your application, you can create a new task\n\t\t\tdefinition with that image and deploy it to your service. The service scheduler uses the\n\t\t\tminimum healthy percent and maximum percent parameters (in the service's deployment\n\t\t\tconfiguration) to determine the deployment strategy.

\n \n

If your updated Docker image uses the same tag as what is in the existing task\n\t\t\t\tdefinition for your service (for example, my_image:latest), you don't\n\t\t\t\tneed to create a new revision of your task definition. You can update the service\n\t\t\t\tusing the forceNewDeployment option. The new tasks launched by the\n\t\t\t\tdeployment pull the current image/tag combination from your repository when they\n\t\t\t\tstart.

\n
\n

You can also update the deployment configuration of a service. When a deployment is\n\t\t\ttriggered by updating the task definition of a service, the service scheduler uses the\n\t\t\tdeployment configuration parameters, minimumHealthyPercent and\n\t\t\t\tmaximumPercent, to determine the deployment strategy.

\n
    \n
  • \n

    If minimumHealthyPercent is below 100%, the scheduler can ignore\n\t\t\t\t\t\tdesiredCount temporarily during a deployment. For example, if\n\t\t\t\t\t\tdesiredCount is four tasks, a minimum of 50% allows the\n\t\t\t\t\tscheduler to stop two existing tasks before starting two new tasks. Tasks for\n\t\t\t\t\tservices that don't use a load balancer are considered healthy if they're in the\n\t\t\t\t\t\tRUNNING state. Tasks for services that use a load balancer are\n\t\t\t\t\tconsidered healthy if they're in the RUNNING state and are reported\n\t\t\t\t\tas healthy by the load balancer.

    \n
  • \n
  • \n

    The maximumPercent parameter represents an upper limit on the\n\t\t\t\t\tnumber of running tasks during a deployment. You can use it to define the\n\t\t\t\t\tdeployment batch size. For example, if desiredCount is four tasks,\n\t\t\t\t\ta maximum of 200% starts four new tasks before stopping the four older tasks\n\t\t\t\t\t(provided that the cluster resources required to do this are available).

    \n
  • \n
\n

When UpdateService stops a task during a deployment, the equivalent\n\t\t\tof docker stop is issued to the containers running in the task. This\n\t\t\tresults in a SIGTERM and a 30-second timeout. After this,\n\t\t\t\tSIGKILL is sent and the containers are forcibly stopped. If the\n\t\t\tcontainer handles the SIGTERM gracefully and exits within 30 seconds from\n\t\t\treceiving it, no SIGKILL is sent.

\n

When the service scheduler launches new tasks, it determines task placement in your\n\t\t\tcluster with the following logic.

\n
    \n
  • \n

    Determine which of the container instances in your cluster can support your\n\t\t\t\t\tservice's task definition. For example, they have the required CPU, memory,\n\t\t\t\t\tports, and container instance attributes.

    \n
  • \n
  • \n

    By default, the service scheduler attempts to balance tasks across\n\t\t\t\t\tAvailability Zones in this manner even though you can choose a different\n\t\t\t\t\tplacement strategy.

    \n
      \n
    • \n

      Sort the valid container instances by the fewest number of running\n\t\t\t\t\t\t\ttasks for this service in the same Availability Zone as the instance.\n\t\t\t\t\t\t\tFor example, if zone A has one running service task and zones B and C\n\t\t\t\t\t\t\teach have zero, valid container instances in either zone B or C are\n\t\t\t\t\t\t\tconsidered optimal for placement.

      \n
    • \n
    • \n

      Place the new service task on a valid container instance in an optimal\n\t\t\t\t\t\t\tAvailability Zone (based on the previous steps), favoring container\n\t\t\t\t\t\t\tinstances with the fewest number of running tasks for this\n\t\t\t\t\t\t\tservice.

      \n
    • \n
    \n
  • \n
\n

When the service scheduler stops running tasks, it attempts to maintain balance across\n\t\t\tthe Availability Zones in your cluster using the following logic:

\n
    \n
  • \n

    Sort the container instances by the largest number of running tasks for this\n\t\t\t\t\tservice in the same Availability Zone as the instance. For example, if zone A\n\t\t\t\t\thas one running service task and zones B and C each have two, container\n\t\t\t\t\tinstances in either zone B or C are considered optimal for termination.

    \n
  • \n
  • \n

    Stop the task on a container instance in an optimal Availability Zone (based\n\t\t\t\t\ton the previous steps), favoring container instances with the largest number of\n\t\t\t\t\trunning tasks for this service.

    \n
  • \n
\n \n

You must have a service-linked role when you update any of the following service\n\t\t\t\tproperties. If you specified a custom role when you created the service, Amazon ECS\n\t\t\t\tautomatically replaces the roleARN associated with the service with the ARN of your\n\t\t\t\tservice-linked role. For more information, see Service-linked roles in the Amazon Elastic Container Service Developer Guide.

\n
    \n
  • \n

    \n loadBalancers,\n

    \n
  • \n
  • \n

    \n serviceRegistries\n

    \n
  • \n
\n
" } }, "com.amazonaws.ecs#UpdateServicePrimaryTaskSet": { @@ -11011,6 +11432,9 @@ "smithy.api#required": {} } } + }, + "traits": { + "smithy.api#input": {} } }, "com.amazonaws.ecs#UpdateServicePrimaryTaskSetResponse": { @@ -11022,6 +11446,9 @@ "smithy.api#documentation": "

The details about the task set.

" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ecs#UpdateServiceRequest": { @@ -11137,6 +11564,9 @@ "smithy.api#documentation": "

The configuration for this service to discover and connect to\n\tservices, and be discovered by, and connected from, other services within a namespace.

\n

Tasks that run in a namespace can use short names to connect\n\tto services in the namespace. Tasks can connect to services across all of the clusters in the namespace.\n\tTasks connect through a managed proxy container\n\tthat collects logs and metrics for increased visibility.\n\tOnly the tasks that Amazon ECS services create are supported with Service Connect.\n\tFor more information, see Service Connect in the Amazon Elastic Container Service Developer Guide.

" } } + }, + "traits": { + "smithy.api#input": {} } }, "com.amazonaws.ecs#UpdateServiceResponse": { @@ -11148,6 +11578,9 @@ "smithy.api#documentation": "

The full description of your service following the update call.

" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ecs#UpdateTaskProtection": { @@ -11216,6 +11649,9 @@ "smithy.api#documentation": "

If you set protectionEnabled to true, you can specify the\n\t\t\tduration for task protection in minutes. You can specify a value from 1 minute to up to\n\t\t\t2,880 minutes (48 hours). During this time, your task will not be terminated by scale-in\n\t\t\tevents from Service Auto Scaling or deployments. After this time period lapses,\n\t\t\t\tprotectionEnabled will be reset to false.

\n

If you don’t specify the time, then the task is automatically protected for 120\n\t\t\tminutes (2 hours).

" } } + }, + "traits": { + "smithy.api#input": {} } }, "com.amazonaws.ecs#UpdateTaskProtectionResponse": { @@ -11233,6 +11669,9 @@ "smithy.api#documentation": "

Any failures associated with the call.

" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ecs#UpdateTaskSet": { @@ -11307,6 +11746,9 @@ "smithy.api#required": {} } } + }, + "traits": { + "smithy.api#input": {} } }, "com.amazonaws.ecs#UpdateTaskSetResponse": { @@ -11318,6 +11760,9 @@ "smithy.api#documentation": "

Details about the task set.

" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ecs#VersionInfo": { diff --git a/aws/sdk/aws-models/glacier.json b/aws/sdk/aws-models/glacier.json index 878e57eea2..6fc9d21fe1 100644 --- a/aws/sdk/aws-models/glacier.json +++ b/aws/sdk/aws-models/glacier.json @@ -89,7 +89,8 @@ } }, "traits": { - "smithy.api#documentation": "

Provides options to abort a multipart upload identified by the upload ID.

\n\n

For information about the underlying REST API, see Abort Multipart\n Upload. For conceptual information, see Working with Archives in\n Amazon S3 Glacier.

" + "smithy.api#documentation": "

Provides options to abort a multipart upload identified by the upload ID.

\n\n

For information about the underlying REST API, see Abort Multipart\n Upload. For conceptual information, see Working with Archives in\n Amazon S3 Glacier.

", + "smithy.api#input": {} } }, "com.amazonaws.glacier#AbortVaultLock": { @@ -144,7 +145,8 @@ } }, "traits": { - "smithy.api#documentation": "

The input values for AbortVaultLock.

" + "smithy.api#documentation": "

The input values for AbortVaultLock.

", + "smithy.api#input": {} } }, "com.amazonaws.glacier#AccessControlPolicyList": { @@ -154,22 +156,26 @@ } }, "com.amazonaws.glacier#ActionCode": { - "type": "string", - "traits": { - "smithy.api#enum": [ - { - "value": "ArchiveRetrieval", - "name": "ArchiveRetrieval" - }, - { - "value": "InventoryRetrieval", - "name": "InventoryRetrieval" - }, - { - "value": "Select", - "name": "Select" + "type": "enum", + "members": { + "ArchiveRetrieval": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "ArchiveRetrieval" + } + }, + "InventoryRetrieval": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "InventoryRetrieval" } - ] + }, + "Select": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "Select" + } + } } }, "com.amazonaws.glacier#AddTagsToVault": { @@ -233,7 +239,8 @@ } }, "traits": { - "smithy.api#documentation": "

The input values for AddTagsToVault.

" + "smithy.api#documentation": "

The input values for AddTagsToVault.

", + "smithy.api#input": {} } }, "com.amazonaws.glacier#ArchiveCreationOutput": { @@ -348,38 +355,50 @@ } }, "com.amazonaws.glacier#CannedACL": { - "type": "string", - "traits": { - "smithy.api#enum": [ - { - "value": "private", - "name": "Private" - }, - { - "value": "public-read", - "name": "PublicRead" - }, - { - "value": "public-read-write", - "name": "PublicReadWrite" - }, - { - "value": "aws-exec-read", - "name": "AwsExecRead" - }, - { - "value": "authenticated-read", - "name": "AuthenticatedRead" - }, - { - "value": "bucket-owner-read", - "name": "BucketOwnerRead" - }, - { - "value": "bucket-owner-full-control", - "name": "BucketOwnerFullControl" + "type": "enum", + "members": { + "Private": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "private" + } + }, + "PublicRead": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "public-read" + } + }, + "PublicReadWrite": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "public-read-write" + } + }, + "AwsExecRead": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "aws-exec-read" } - ] + }, + "AuthenticatedRead": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "authenticated-read" + } + }, + "BucketOwnerRead": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "bucket-owner-read" + } + }, + "BucketOwnerFullControl": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "bucket-owner-full-control" + } + } } }, "com.amazonaws.glacier#CompleteMultipartUpload": { @@ -456,7 +475,8 @@ } }, "traits": { - "smithy.api#documentation": "

Provides options to complete a multipart upload operation. This informs Amazon\n Glacier that all the archive parts have been uploaded and Amazon S3 Glacier (Glacier) can now assemble\n the archive from the uploaded parts. After assembling and saving the archive to the vault,\n Glacier returns the URI path of the newly created archive resource.

" + "smithy.api#documentation": "

Provides options to complete a multipart upload operation. This informs Amazon\n Glacier that all the archive parts have been uploaded and Amazon S3 Glacier (Glacier) can now assemble\n the archive from the uploaded parts. After assembling and saving the archive to the vault,\n Glacier returns the URI path of the newly created archive resource.

", + "smithy.api#input": {} } }, "com.amazonaws.glacier#CompleteVaultLock": { @@ -519,7 +539,8 @@ } }, "traits": { - "smithy.api#documentation": "

The input values for CompleteVaultLock.

" + "smithy.api#documentation": "

The input values for CompleteVaultLock.

", + "smithy.api#input": {} } }, "com.amazonaws.glacier#CreateVault": { @@ -574,7 +595,8 @@ } }, "traits": { - "smithy.api#documentation": "

Provides options to create a vault.

" + "smithy.api#documentation": "

Provides options to create a vault.

", + "smithy.api#input": {} } }, "com.amazonaws.glacier#CreateVaultOutput": { @@ -589,7 +611,8 @@ } }, "traits": { - "smithy.api#documentation": "

Contains the Amazon S3 Glacier response to your request.

" + "smithy.api#documentation": "

Contains the Amazon S3 Glacier response to your request.

", + "smithy.api#output": {} } }, "com.amazonaws.glacier#DataRetrievalPolicy": { @@ -695,7 +718,8 @@ } }, "traits": { - "smithy.api#documentation": "

Provides options for deleting an archive from an Amazon S3 Glacier vault.

" + "smithy.api#documentation": "

Provides options for deleting an archive from an Amazon S3 Glacier vault.

", + "smithy.api#input": {} } }, "com.amazonaws.glacier#DeleteVault": { @@ -781,7 +805,8 @@ } }, "traits": { - "smithy.api#documentation": "

DeleteVaultAccessPolicy input.

" + "smithy.api#documentation": "

DeleteVaultAccessPolicy input.

", + "smithy.api#input": {} } }, "com.amazonaws.glacier#DeleteVaultInput": { @@ -805,7 +830,8 @@ } }, "traits": { - "smithy.api#documentation": "

Provides options for deleting a vault from Amazon S3 Glacier.

" + "smithy.api#documentation": "

Provides options for deleting a vault from Amazon S3 Glacier.

", + "smithy.api#input": {} } }, "com.amazonaws.glacier#DeleteVaultNotifications": { @@ -860,7 +886,8 @@ } }, "traits": { - "smithy.api#documentation": "

Provides options for deleting a vault notification configuration from an Amazon\n Glacier vault.

" + "smithy.api#documentation": "

Provides options for deleting a vault notification configuration from an Amazon\n Glacier vault.

", + "smithy.api#input": {} } }, "com.amazonaws.glacier#DescribeJob": { @@ -923,7 +950,8 @@ } }, "traits": { - "smithy.api#documentation": "

Provides options for retrieving a job description.

" + "smithy.api#documentation": "

Provides options for retrieving a job description.

", + "smithy.api#input": {} } }, "com.amazonaws.glacier#DescribeVault": { @@ -1014,7 +1042,8 @@ } }, "traits": { - "smithy.api#documentation": "

Provides options for retrieving metadata for a specific vault in Amazon\n Glacier.

" + "smithy.api#documentation": "

Provides options for retrieving metadata for a specific vault in Amazon\n Glacier.

", + "smithy.api#input": {} } }, "com.amazonaws.glacier#DescribeVaultOutput": { @@ -1090,48 +1119,54 @@ } }, "com.amazonaws.glacier#EncryptionType": { - "type": "string", - "traits": { - "smithy.api#enum": [ - { - "value": "aws:kms", - "name": "KMS" - }, - { - "value": "AES256", - "name": "S3" + "type": "enum", + "members": { + "KMS": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "aws:kms" } - ] + }, + "S3": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "AES256" + } + } } }, "com.amazonaws.glacier#ExpressionType": { - "type": "string", - "traits": { - "smithy.api#enum": [ - { - "value": "SQL", - "name": "SQL" + "type": "enum", + "members": { + "SQL": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "SQL" } - ] + } } }, "com.amazonaws.glacier#FileHeaderInfo": { - "type": "string", - "traits": { - "smithy.api#enum": [ - { - "value": "USE", - "name": "Use" - }, - { - "value": "IGNORE", - "name": "Ignore" - }, - { - "value": "NONE", - "name": "None" + "type": "enum", + "members": { + "Use": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "USE" } - ] + }, + "Ignore": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "IGNORE" + } + }, + "None": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "NONE" + } + } } }, "com.amazonaws.glacier#GetDataRetrievalPolicy": { @@ -1175,7 +1210,8 @@ } }, "traits": { - "smithy.api#documentation": "

Input for GetDataRetrievalPolicy.

" + "smithy.api#documentation": "

Input for GetDataRetrievalPolicy.

", + "smithy.api#input": {} } }, "com.amazonaws.glacier#GetDataRetrievalPolicyOutput": { @@ -1189,7 +1225,8 @@ } }, "traits": { - "smithy.api#documentation": "

Contains the Amazon S3 Glacier response to the GetDataRetrievalPolicy\n request.

" + "smithy.api#documentation": "

Contains the Amazon S3 Glacier response to the GetDataRetrievalPolicy\n request.

", + "smithy.api#output": {} } }, "com.amazonaws.glacier#GetJobOutput": { @@ -1259,7 +1296,8 @@ } }, "traits": { - "smithy.api#documentation": "

Provides options for downloading output of an Amazon S3 Glacier job.

" + "smithy.api#documentation": "

Provides options for downloading output of an Amazon S3 Glacier job.

", + "smithy.api#input": {} } }, "com.amazonaws.glacier#GetJobOutputOutput": { @@ -1318,7 +1356,8 @@ } }, "traits": { - "smithy.api#documentation": "

Contains the Amazon S3 Glacier response to your request.

" + "smithy.api#documentation": "

Contains the Amazon S3 Glacier response to your request.

", + "smithy.api#output": {} } }, "com.amazonaws.glacier#GetVaultAccessPolicy": { @@ -1373,7 +1412,8 @@ } }, "traits": { - "smithy.api#documentation": "

Input for GetVaultAccessPolicy.

" + "smithy.api#documentation": "

Input for GetVaultAccessPolicy.

", + "smithy.api#input": {} } }, "com.amazonaws.glacier#GetVaultAccessPolicyOutput": { @@ -1388,7 +1428,8 @@ } }, "traits": { - "smithy.api#documentation": "

Output for GetVaultAccessPolicy.

" + "smithy.api#documentation": "

Output for GetVaultAccessPolicy.

", + "smithy.api#output": {} } }, "com.amazonaws.glacier#GetVaultLock": { @@ -1443,7 +1484,8 @@ } }, "traits": { - "smithy.api#documentation": "

The input values for GetVaultLock.

" + "smithy.api#documentation": "

The input values for GetVaultLock.

", + "smithy.api#input": {} } }, "com.amazonaws.glacier#GetVaultLockOutput": { @@ -1475,7 +1517,8 @@ } }, "traits": { - "smithy.api#documentation": "

Contains the Amazon S3 Glacier response to your request.

" + "smithy.api#documentation": "

Contains the Amazon S3 Glacier response to your request.

", + "smithy.api#output": {} } }, "com.amazonaws.glacier#GetVaultNotifications": { @@ -1530,7 +1573,8 @@ } }, "traits": { - "smithy.api#documentation": "

Provides options for retrieving the notification configuration set on an Amazon\n Glacier vault.

" + "smithy.api#documentation": "

Provides options for retrieving the notification configuration set on an Amazon\n Glacier vault.

", + "smithy.api#input": {} } }, "com.amazonaws.glacier#GetVaultNotificationsOutput": { @@ -1545,7 +1589,8 @@ } }, "traits": { - "smithy.api#documentation": "

Contains the Amazon S3 Glacier response to your request.

" + "smithy.api#documentation": "

Contains the Amazon S3 Glacier response to your request.

", + "smithy.api#output": {} } }, "com.amazonaws.glacier#Glacier": { @@ -1674,7 +1719,7 @@ "parameters": { "Region": { "builtIn": "AWS::Region", - "required": true, + "required": false, "documentation": "The AWS region used to dispatch the request.", "type": "String" }, @@ -1703,13 +1748,12 @@ { "conditions": [ { - "fn": "aws.partition", + "fn": "isSet", "argv": [ { - "ref": "Region" + "ref": "Endpoint" } - ], - "assign": "PartitionResult" + ] } ], "type": "tree", @@ -1717,14 +1761,20 @@ { "conditions": [ { - "fn": "isSet", + "fn": "booleanEquals", "argv": [ { - "ref": "Endpoint" - } + "ref": "UseFIPS" + }, + true ] } ], + "error": "Invalid Configuration: FIPS and custom endpoint are not supported", + "type": "error" + }, + { + "conditions": [], "type": "tree", "rules": [ { @@ -1733,67 +1783,42 @@ "fn": "booleanEquals", "argv": [ { - "ref": "UseFIPS" + "ref": "UseDualStack" }, true ] } ], - "error": "Invalid Configuration: FIPS and custom endpoint are not supported", + "error": "Invalid Configuration: Dualstack 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" + "endpoint": { + "url": { + "ref": "Endpoint" }, - { - "conditions": [], - "endpoint": { - "url": { - "ref": "Endpoint" - }, - "properties": {}, - "headers": {} - }, - "type": "endpoint" - } - ] + "properties": {}, + "headers": {} + }, + "type": "endpoint" } ] - }, + } + ] + }, + { + "conditions": [], + "type": "tree", + "rules": [ { "conditions": [ { - "fn": "booleanEquals", - "argv": [ - { - "ref": "UseFIPS" - }, - true - ] - }, - { - "fn": "booleanEquals", + "fn": "isSet", "argv": [ { - "ref": "UseDualStack" - }, - true + "ref": "Region" + } ] } ], @@ -1802,187 +1827,286 @@ { "conditions": [ { - "fn": "booleanEquals", + "fn": "aws.partition", "argv": [ - true, { - "fn": "getAttr", + "ref": "Region" + } + ], + "assign": "PartitionResult" + } + ], + "type": "tree", + "rules": [ + { + "conditions": [ + { + "fn": "booleanEquals", "argv": [ { - "ref": "PartitionResult" + "ref": "UseFIPS" }, - "supportsFIPS" + true ] - } - ] - }, - { - "fn": "booleanEquals", - "argv": [ - true, + }, { - "fn": "getAttr", + "fn": "booleanEquals", "argv": [ { - "ref": "PartitionResult" + "ref": "UseDualStack" }, - "supportsDualStack" + true ] } - ] - } - ], - "type": "tree", - "rules": [ - { - "conditions": [], + ], "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://glacier-fips.{Region}.{PartitionResult#dualStackDnsSuffix}", + "properties": {}, + "headers": {} + }, + "type": "endpoint" + } + ] + } + ] + }, { "conditions": [], - "endpoint": { - "url": "https://glacier-fips.{Region}.{PartitionResult#dualStackDnsSuffix}", - "properties": {}, - "headers": {} - }, - "type": "endpoint" + "error": "FIPS and DualStack are enabled, but this partition does not support one or both", + "type": "error" } ] - } - ] - }, - { - "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, + "conditions": [ { - "fn": "getAttr", + "fn": "booleanEquals", "argv": [ { - "ref": "PartitionResult" + "ref": "UseFIPS" }, - "supportsFIPS" + true ] } - ] - } - ], - "type": "tree", - "rules": [ - { - "conditions": [], + ], "type": "tree", "rules": [ { "conditions": [ { - "fn": "stringEquals", + "fn": "booleanEquals", "argv": [ - "aws-us-gov", + true, { "fn": "getAttr", "argv": [ { "ref": "PartitionResult" }, - "name" + "supportsFIPS" ] } ] } ], - "endpoint": { - "url": "https://glacier.{Region}.amazonaws.com", - "properties": {}, - "headers": {} - }, - "type": "endpoint" + "type": "tree", + "rules": [ + { + "conditions": [], + "type": "tree", + "rules": [ + { + "conditions": [ + { + "fn": "stringEquals", + "argv": [ + "aws-us-gov", + { + "fn": "getAttr", + "argv": [ + { + "ref": "PartitionResult" + }, + "name" + ] + } + ] + } + ], + "endpoint": { + "url": "https://glacier.{Region}.amazonaws.com", + "properties": {}, + "headers": {} + }, + "type": "endpoint" + }, + { + "conditions": [], + "endpoint": { + "url": "https://glacier-fips.{Region}.{PartitionResult#dnsSuffix}", + "properties": {}, + "headers": {} + }, + "type": "endpoint" + } + ] + } + ] }, { "conditions": [], - "endpoint": { - "url": "https://glacier-fips.{Region}.{PartitionResult#dnsSuffix}", - "properties": {}, - "headers": {} - }, - "type": "endpoint" + "error": "FIPS is enabled but this partition does not support FIPS", + "type": "error" } ] - } - ] - }, - { - "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, + "conditions": [ { - "fn": "getAttr", + "fn": "booleanEquals", "argv": [ { - "ref": "PartitionResult" + "ref": "UseDualStack" }, - "supportsDualStack" + 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://glacier.{Region}.{PartitionResult#dualStackDnsSuffix}", + "properties": {}, + "headers": {} + }, + "type": "endpoint" + } + ] + } ] + }, + { + "conditions": [], + "error": "DualStack is enabled but this partition does not support DualStack", + "type": "error" } ] - } - ], - "type": "tree", - "rules": [ + }, { "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": { - "url": "https://glacier.{Region}.{PartitionResult#dualStackDnsSuffix}", + "url": "https://glacier.{Region}.{PartitionResult#dnsSuffix}", "properties": {}, "headers": {} }, @@ -1991,66 +2115,13 @@ ] } ] - }, - { - "conditions": [], - "error": "DualStack is enabled but this partition does not support DualStack", - "type": "error" } ] }, { "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": { - "url": "https://glacier.{Region}.{PartitionResult#dnsSuffix}", - "properties": {}, - "headers": {} - }, - "type": "endpoint" - } - ] + "error": "Invalid Configuration: Missing Region", + "type": "error" } ] } @@ -2059,16 +2130,16 @@ "smithy.rules#endpointTests": { "testCases": [ { - "documentation": "For region sa-east-1 with FIPS disabled and DualStack disabled", + "documentation": "For region af-south-1 with FIPS disabled and DualStack disabled", "expect": { "endpoint": { - "url": "https://glacier.sa-east-1.amazonaws.com" + "url": "https://glacier.af-south-1.amazonaws.com" } }, "params": { - "UseFIPS": false, + "Region": "af-south-1", "UseDualStack": false, - "Region": "sa-east-1" + "UseFIPS": false } }, { @@ -2079,334 +2150,334 @@ } }, "params": { - "UseFIPS": false, + "Region": "ap-east-1", "UseDualStack": false, - "Region": "ap-east-1" + "UseFIPS": false } }, { - "documentation": "For region eu-south-1 with FIPS disabled and DualStack disabled", + "documentation": "For region ap-northeast-1 with FIPS disabled and DualStack disabled", "expect": { "endpoint": { - "url": "https://glacier.eu-south-1.amazonaws.com" + "url": "https://glacier.ap-northeast-1.amazonaws.com" } }, "params": { - "UseFIPS": false, + "Region": "ap-northeast-1", "UseDualStack": false, - "Region": "eu-south-1" + "UseFIPS": false } }, { - "documentation": "For region eu-central-1 with FIPS disabled and DualStack disabled", + "documentation": "For region ap-northeast-2 with FIPS disabled and DualStack disabled", "expect": { "endpoint": { - "url": "https://glacier.eu-central-1.amazonaws.com" + "url": "https://glacier.ap-northeast-2.amazonaws.com" } }, "params": { - "UseFIPS": false, + "Region": "ap-northeast-2", "UseDualStack": false, - "Region": "eu-central-1" + "UseFIPS": false } }, { - "documentation": "For region ap-southeast-1 with FIPS disabled and DualStack disabled", + "documentation": "For region ap-northeast-3 with FIPS disabled and DualStack disabled", "expect": { "endpoint": { - "url": "https://glacier.ap-southeast-1.amazonaws.com" + "url": "https://glacier.ap-northeast-3.amazonaws.com" } }, "params": { - "UseFIPS": false, + "Region": "ap-northeast-3", "UseDualStack": false, - "Region": "ap-southeast-1" + "UseFIPS": false } }, { - "documentation": "For region ap-southeast-2 with FIPS disabled and DualStack disabled", + "documentation": "For region ap-south-1 with FIPS disabled and DualStack disabled", "expect": { "endpoint": { - "url": "https://glacier.ap-southeast-2.amazonaws.com" + "url": "https://glacier.ap-south-1.amazonaws.com" } }, "params": { - "UseFIPS": false, + "Region": "ap-south-1", "UseDualStack": false, - "Region": "ap-southeast-2" + "UseFIPS": false } }, { - "documentation": "For region ap-southeast-3 with FIPS disabled and DualStack disabled", + "documentation": "For region ap-southeast-1 with FIPS disabled and DualStack disabled", "expect": { "endpoint": { - "url": "https://glacier.ap-southeast-3.amazonaws.com" + "url": "https://glacier.ap-southeast-1.amazonaws.com" } }, "params": { - "UseFIPS": false, + "Region": "ap-southeast-1", "UseDualStack": false, - "Region": "ap-southeast-3" + "UseFIPS": false } }, { - "documentation": "For region ca-central-1 with FIPS disabled and DualStack disabled", + "documentation": "For region ap-southeast-2 with FIPS disabled and DualStack disabled", "expect": { "endpoint": { - "url": "https://glacier.ca-central-1.amazonaws.com" + "url": "https://glacier.ap-southeast-2.amazonaws.com" } }, "params": { - "UseFIPS": false, + "Region": "ap-southeast-2", "UseDualStack": false, - "Region": "ca-central-1" + "UseFIPS": false } }, { - "documentation": "For region ca-central-1 with FIPS enabled and DualStack disabled", + "documentation": "For region ap-southeast-3 with FIPS disabled and DualStack disabled", "expect": { "endpoint": { - "url": "https://glacier-fips.ca-central-1.amazonaws.com" + "url": "https://glacier.ap-southeast-3.amazonaws.com" } }, "params": { - "UseFIPS": true, + "Region": "ap-southeast-3", "UseDualStack": false, - "Region": "ca-central-1" + "UseFIPS": false } }, { - "documentation": "For region us-west-1 with FIPS disabled and DualStack disabled", + "documentation": "For region ca-central-1 with FIPS disabled and DualStack disabled", "expect": { "endpoint": { - "url": "https://glacier.us-west-1.amazonaws.com" + "url": "https://glacier.ca-central-1.amazonaws.com" } }, "params": { - "UseFIPS": false, + "Region": "ca-central-1", "UseDualStack": false, - "Region": "us-west-1" + "UseFIPS": false } }, { - "documentation": "For region us-west-1 with FIPS enabled and DualStack disabled", + "documentation": "For region ca-central-1 with FIPS enabled and DualStack disabled", "expect": { "endpoint": { - "url": "https://glacier-fips.us-west-1.amazonaws.com" + "url": "https://glacier-fips.ca-central-1.amazonaws.com" } }, "params": { - "UseFIPS": true, + "Region": "ca-central-1", "UseDualStack": false, - "Region": "us-west-1" + "UseFIPS": true } }, { - "documentation": "For region us-west-2 with FIPS disabled and DualStack disabled", + "documentation": "For region eu-central-1 with FIPS disabled and DualStack disabled", "expect": { "endpoint": { - "url": "https://glacier.us-west-2.amazonaws.com" + "url": "https://glacier.eu-central-1.amazonaws.com" } }, "params": { - "UseFIPS": false, + "Region": "eu-central-1", "UseDualStack": false, - "Region": "us-west-2" + "UseFIPS": false } }, { - "documentation": "For region us-west-2 with FIPS enabled and DualStack disabled", + "documentation": "For region eu-north-1 with FIPS disabled and DualStack disabled", "expect": { "endpoint": { - "url": "https://glacier-fips.us-west-2.amazonaws.com" + "url": "https://glacier.eu-north-1.amazonaws.com" } }, "params": { - "UseFIPS": true, + "Region": "eu-north-1", "UseDualStack": false, - "Region": "us-west-2" + "UseFIPS": false } }, { - "documentation": "For region af-south-1 with FIPS disabled and DualStack disabled", + "documentation": "For region eu-south-1 with FIPS disabled and DualStack disabled", "expect": { "endpoint": { - "url": "https://glacier.af-south-1.amazonaws.com" + "url": "https://glacier.eu-south-1.amazonaws.com" } }, "params": { - "UseFIPS": false, + "Region": "eu-south-1", "UseDualStack": false, - "Region": "af-south-1" + "UseFIPS": false } }, { - "documentation": "For region ap-south-1 with FIPS disabled and DualStack disabled", + "documentation": "For region eu-west-1 with FIPS disabled and DualStack disabled", "expect": { "endpoint": { - "url": "https://glacier.ap-south-1.amazonaws.com" + "url": "https://glacier.eu-west-1.amazonaws.com" } }, "params": { - "UseFIPS": false, + "Region": "eu-west-1", "UseDualStack": false, - "Region": "ap-south-1" + "UseFIPS": false } }, { - "documentation": "For region ap-northeast-1 with FIPS disabled and DualStack disabled", + "documentation": "For region eu-west-2 with FIPS disabled and DualStack disabled", "expect": { "endpoint": { - "url": "https://glacier.ap-northeast-1.amazonaws.com" + "url": "https://glacier.eu-west-2.amazonaws.com" } }, "params": { - "UseFIPS": false, + "Region": "eu-west-2", "UseDualStack": false, - "Region": "ap-northeast-1" + "UseFIPS": false } }, { - "documentation": "For region ap-northeast-2 with FIPS disabled and DualStack disabled", + "documentation": "For region eu-west-3 with FIPS disabled and DualStack disabled", "expect": { "endpoint": { - "url": "https://glacier.ap-northeast-2.amazonaws.com" + "url": "https://glacier.eu-west-3.amazonaws.com" } }, "params": { - "UseFIPS": false, + "Region": "eu-west-3", "UseDualStack": false, - "Region": "ap-northeast-2" + "UseFIPS": false } }, { - "documentation": "For region ap-northeast-3 with FIPS disabled and DualStack disabled", + "documentation": "For region me-south-1 with FIPS disabled and DualStack disabled", "expect": { "endpoint": { - "url": "https://glacier.ap-northeast-3.amazonaws.com" + "url": "https://glacier.me-south-1.amazonaws.com" } }, "params": { - "UseFIPS": false, + "Region": "me-south-1", "UseDualStack": false, - "Region": "ap-northeast-3" + "UseFIPS": false } }, { - "documentation": "For region us-east-1 with FIPS disabled and DualStack disabled", + "documentation": "For region sa-east-1 with FIPS disabled and DualStack disabled", "expect": { "endpoint": { - "url": "https://glacier.us-east-1.amazonaws.com" + "url": "https://glacier.sa-east-1.amazonaws.com" } }, "params": { - "UseFIPS": false, + "Region": "sa-east-1", "UseDualStack": false, - "Region": "us-east-1" + "UseFIPS": false } }, { - "documentation": "For region us-east-1 with FIPS enabled and DualStack disabled", + "documentation": "For region us-east-1 with FIPS disabled and DualStack disabled", "expect": { "endpoint": { - "url": "https://glacier-fips.us-east-1.amazonaws.com" + "url": "https://glacier.us-east-1.amazonaws.com" } }, "params": { - "UseFIPS": true, + "Region": "us-east-1", "UseDualStack": false, - "Region": "us-east-1" + "UseFIPS": false } }, { - "documentation": "For region eu-west-1 with FIPS disabled and DualStack disabled", + "documentation": "For region us-east-1 with FIPS enabled and DualStack disabled", "expect": { "endpoint": { - "url": "https://glacier.eu-west-1.amazonaws.com" + "url": "https://glacier-fips.us-east-1.amazonaws.com" } }, "params": { - "UseFIPS": false, + "Region": "us-east-1", "UseDualStack": false, - "Region": "eu-west-1" + "UseFIPS": true } }, { - "documentation": "For region eu-west-2 with FIPS disabled and DualStack disabled", + "documentation": "For region us-east-2 with FIPS disabled and DualStack disabled", "expect": { "endpoint": { - "url": "https://glacier.eu-west-2.amazonaws.com" + "url": "https://glacier.us-east-2.amazonaws.com" } }, "params": { - "UseFIPS": false, + "Region": "us-east-2", "UseDualStack": false, - "Region": "eu-west-2" + "UseFIPS": false } }, { - "documentation": "For region eu-west-3 with FIPS disabled and DualStack disabled", + "documentation": "For region us-east-2 with FIPS enabled and DualStack disabled", "expect": { "endpoint": { - "url": "https://glacier.eu-west-3.amazonaws.com" + "url": "https://glacier-fips.us-east-2.amazonaws.com" } }, "params": { - "UseFIPS": false, + "Region": "us-east-2", "UseDualStack": false, - "Region": "eu-west-3" + "UseFIPS": true } }, { - "documentation": "For region me-south-1 with FIPS disabled and DualStack disabled", + "documentation": "For region us-west-1 with FIPS disabled and DualStack disabled", "expect": { "endpoint": { - "url": "https://glacier.me-south-1.amazonaws.com" + "url": "https://glacier.us-west-1.amazonaws.com" } }, "params": { - "UseFIPS": false, + "Region": "us-west-1", "UseDualStack": false, - "Region": "me-south-1" + "UseFIPS": false } }, { - "documentation": "For region eu-north-1 with FIPS disabled and DualStack disabled", + "documentation": "For region us-west-1 with FIPS enabled and DualStack disabled", "expect": { "endpoint": { - "url": "https://glacier.eu-north-1.amazonaws.com" + "url": "https://glacier-fips.us-west-1.amazonaws.com" } }, "params": { - "UseFIPS": false, + "Region": "us-west-1", "UseDualStack": false, - "Region": "eu-north-1" + "UseFIPS": true } }, { - "documentation": "For region us-east-2 with FIPS disabled and DualStack disabled", + "documentation": "For region us-west-2 with FIPS disabled and DualStack disabled", "expect": { "endpoint": { - "url": "https://glacier.us-east-2.amazonaws.com" + "url": "https://glacier.us-west-2.amazonaws.com" } }, "params": { - "UseFIPS": false, + "Region": "us-west-2", "UseDualStack": false, - "Region": "us-east-2" + "UseFIPS": false } }, { - "documentation": "For region us-east-2 with FIPS enabled and DualStack disabled", + "documentation": "For region us-west-2 with FIPS enabled and DualStack disabled", "expect": { "endpoint": { - "url": "https://glacier-fips.us-east-2.amazonaws.com" + "url": "https://glacier-fips.us-west-2.amazonaws.com" } }, "params": { - "UseFIPS": true, + "Region": "us-west-2", "UseDualStack": false, - "Region": "us-east-2" + "UseFIPS": true } }, { @@ -2417,9 +2488,9 @@ } }, "params": { - "UseFIPS": true, + "Region": "us-east-1", "UseDualStack": true, - "Region": "us-east-1" + "UseFIPS": true } }, { @@ -2430,230 +2501,243 @@ } }, "params": { - "UseFIPS": false, + "Region": "us-east-1", "UseDualStack": true, - "Region": "us-east-1" + "UseFIPS": false } }, { - "documentation": "For region us-gov-west-1 with FIPS disabled and DualStack disabled", + "documentation": "For region cn-north-1 with FIPS disabled and DualStack disabled", "expect": { "endpoint": { - "url": "https://glacier.us-gov-west-1.amazonaws.com" + "url": "https://glacier.cn-north-1.amazonaws.com.cn" } }, "params": { - "UseFIPS": false, + "Region": "cn-north-1", "UseDualStack": false, - "Region": "us-gov-west-1" + "UseFIPS": false } }, { - "documentation": "For region us-gov-west-1 with FIPS enabled and DualStack disabled", + "documentation": "For region cn-northwest-1 with FIPS disabled and DualStack disabled", "expect": { "endpoint": { - "url": "https://glacier.us-gov-west-1.amazonaws.com" + "url": "https://glacier.cn-northwest-1.amazonaws.com.cn" } }, "params": { - "UseFIPS": true, + "Region": "cn-northwest-1", "UseDualStack": false, - "Region": "us-gov-west-1" + "UseFIPS": false } }, { - "documentation": "For region us-gov-east-1 with FIPS disabled and DualStack disabled", + "documentation": "For region cn-north-1 with FIPS enabled and DualStack enabled", "expect": { "endpoint": { - "url": "https://glacier.us-gov-east-1.amazonaws.com" + "url": "https://glacier-fips.cn-north-1.api.amazonwebservices.com.cn" } }, "params": { - "UseFIPS": false, - "UseDualStack": false, - "Region": "us-gov-east-1" + "Region": "cn-north-1", + "UseDualStack": true, + "UseFIPS": true } }, { - "documentation": "For region us-gov-east-1 with FIPS enabled and DualStack disabled", + "documentation": "For region cn-north-1 with FIPS enabled and DualStack disabled", "expect": { "endpoint": { - "url": "https://glacier.us-gov-east-1.amazonaws.com" + "url": "https://glacier-fips.cn-north-1.amazonaws.com.cn" } }, "params": { - "UseFIPS": true, + "Region": "cn-north-1", "UseDualStack": false, - "Region": "us-gov-east-1" + "UseFIPS": true } }, { - "documentation": "For region us-gov-east-1 with FIPS enabled and DualStack enabled", + "documentation": "For region cn-north-1 with FIPS disabled and DualStack enabled", "expect": { "endpoint": { - "url": "https://glacier-fips.us-gov-east-1.api.aws" + "url": "https://glacier.cn-north-1.api.amazonwebservices.com.cn" } }, "params": { - "UseFIPS": true, + "Region": "cn-north-1", "UseDualStack": true, - "Region": "us-gov-east-1" + "UseFIPS": false } }, { - "documentation": "For region us-gov-east-1 with FIPS disabled and DualStack enabled", + "documentation": "For region us-gov-east-1 with FIPS disabled and DualStack disabled", "expect": { "endpoint": { - "url": "https://glacier.us-gov-east-1.api.aws" + "url": "https://glacier.us-gov-east-1.amazonaws.com" } }, "params": { - "UseFIPS": false, - "UseDualStack": true, - "Region": "us-gov-east-1" + "Region": "us-gov-east-1", + "UseDualStack": false, + "UseFIPS": false } }, { - "documentation": "For region us-isob-east-1 with FIPS disabled and DualStack disabled", + "documentation": "For region us-gov-east-1 with FIPS enabled and DualStack disabled", "expect": { "endpoint": { - "url": "https://glacier.us-isob-east-1.sc2s.sgov.gov" + "url": "https://glacier.us-gov-east-1.amazonaws.com" } }, "params": { - "UseFIPS": false, + "Region": "us-gov-east-1", "UseDualStack": false, - "Region": "us-isob-east-1" + "UseFIPS": true } }, { - "documentation": "For region us-isob-east-1 with FIPS enabled and DualStack disabled", + "documentation": "For region us-gov-west-1 with FIPS disabled and DualStack disabled", "expect": { "endpoint": { - "url": "https://glacier-fips.us-isob-east-1.sc2s.sgov.gov" + "url": "https://glacier.us-gov-west-1.amazonaws.com" } }, "params": { - "UseFIPS": true, + "Region": "us-gov-west-1", "UseDualStack": false, - "Region": "us-isob-east-1" + "UseFIPS": false } }, { - "documentation": "For region cn-northwest-1 with FIPS disabled and DualStack disabled", + "documentation": "For region us-gov-west-1 with FIPS enabled and DualStack disabled", "expect": { "endpoint": { - "url": "https://glacier.cn-northwest-1.amazonaws.com.cn" + "url": "https://glacier.us-gov-west-1.amazonaws.com" } }, "params": { - "UseFIPS": false, + "Region": "us-gov-west-1", "UseDualStack": false, - "Region": "cn-northwest-1" + "UseFIPS": true } }, { - "documentation": "For region cn-north-1 with FIPS disabled and DualStack disabled", + "documentation": "For region us-gov-east-1 with FIPS enabled and DualStack enabled", "expect": { "endpoint": { - "url": "https://glacier.cn-north-1.amazonaws.com.cn" + "url": "https://glacier-fips.us-gov-east-1.api.aws" } }, "params": { - "UseFIPS": false, - "UseDualStack": false, - "Region": "cn-north-1" + "Region": "us-gov-east-1", + "UseDualStack": true, + "UseFIPS": true } }, { - "documentation": "For region cn-north-1 with FIPS enabled and DualStack enabled", + "documentation": "For region us-gov-east-1 with FIPS disabled and DualStack enabled", "expect": { "endpoint": { - "url": "https://glacier-fips.cn-north-1.api.amazonwebservices.com.cn" + "url": "https://glacier.us-gov-east-1.api.aws" } }, "params": { - "UseFIPS": true, + "Region": "us-gov-east-1", "UseDualStack": true, - "Region": "cn-north-1" + "UseFIPS": false } }, { - "documentation": "For region cn-north-1 with FIPS enabled and DualStack disabled", + "documentation": "For region us-iso-east-1 with FIPS disabled and DualStack disabled", "expect": { "endpoint": { - "url": "https://glacier-fips.cn-north-1.amazonaws.com.cn" + "url": "https://glacier.us-iso-east-1.c2s.ic.gov" } }, "params": { - "UseFIPS": true, + "Region": "us-iso-east-1", "UseDualStack": false, - "Region": "cn-north-1" + "UseFIPS": false } }, { - "documentation": "For region cn-north-1 with FIPS disabled and DualStack enabled", + "documentation": "For region us-iso-west-1 with FIPS disabled and DualStack disabled", "expect": { "endpoint": { - "url": "https://glacier.cn-north-1.api.amazonwebservices.com.cn" + "url": "https://glacier.us-iso-west-1.c2s.ic.gov" } }, "params": { - "UseFIPS": false, - "UseDualStack": true, - "Region": "cn-north-1" + "Region": "us-iso-west-1", + "UseDualStack": false, + "UseFIPS": false } }, { - "documentation": "For region us-iso-west-1 with FIPS disabled and DualStack disabled", + "documentation": "For region us-iso-east-1 with FIPS enabled and DualStack disabled", "expect": { "endpoint": { - "url": "https://glacier.us-iso-west-1.c2s.ic.gov" + "url": "https://glacier-fips.us-iso-east-1.c2s.ic.gov" } }, "params": { - "UseFIPS": false, + "Region": "us-iso-east-1", "UseDualStack": false, - "Region": "us-iso-west-1" + "UseFIPS": true } }, { - "documentation": "For region us-iso-east-1 with FIPS disabled and DualStack disabled", + "documentation": "For region us-isob-east-1 with FIPS disabled and DualStack disabled", "expect": { "endpoint": { - "url": "https://glacier.us-iso-east-1.c2s.ic.gov" + "url": "https://glacier.us-isob-east-1.sc2s.sgov.gov" } }, "params": { - "UseFIPS": false, + "Region": "us-isob-east-1", "UseDualStack": false, - "Region": "us-iso-east-1" + "UseFIPS": false } }, { - "documentation": "For region us-iso-east-1 with FIPS enabled and DualStack disabled", + "documentation": "For region us-isob-east-1 with FIPS enabled and DualStack disabled", "expect": { "endpoint": { - "url": "https://glacier-fips.us-iso-east-1.c2s.ic.gov" + "url": "https://glacier-fips.us-isob-east-1.sc2s.sgov.gov" } }, "params": { - "UseFIPS": true, + "Region": "us-isob-east-1", "UseDualStack": false, - "Region": "us-iso-east-1" + "UseFIPS": true } }, { - "documentation": "For custom endpoint with fips disabled and dualstack disabled", + "documentation": "For custom endpoint with region set and fips disabled and dualstack disabled", "expect": { "endpoint": { "url": "https://example.com" } }, "params": { + "Region": "us-east-1", + "UseDualStack": false, "UseFIPS": 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": { "UseDualStack": false, - "Region": "us-east-1", + "UseFIPS": false, "Endpoint": "https://example.com" } }, @@ -2663,9 +2747,9 @@ "error": "Invalid Configuration: FIPS and custom endpoint are not supported" }, "params": { - "UseFIPS": true, - "UseDualStack": false, "Region": "us-east-1", + "UseDualStack": false, + "UseFIPS": true, "Endpoint": "https://example.com" } }, @@ -2675,9 +2759,9 @@ "error": "Invalid Configuration: Dualstack and custom endpoint are not supported" }, "params": { - "UseFIPS": false, - "UseDualStack": true, "Region": "us-east-1", + "UseDualStack": true, + "UseFIPS": false, "Endpoint": "https://example.com" } } @@ -2945,7 +3029,8 @@ } }, "traits": { - "smithy.api#documentation": "

Provides options for initiating an Amazon S3 Glacier job.

" + "smithy.api#documentation": "

Provides options for initiating an Amazon S3 Glacier job.

", + "smithy.api#input": {} } }, "com.amazonaws.glacier#InitiateJobOutput": { @@ -2974,7 +3059,8 @@ } }, "traits": { - "smithy.api#documentation": "

Contains the Amazon S3 Glacier response to your request.

" + "smithy.api#documentation": "

Contains the Amazon S3 Glacier response to your request.

", + "smithy.api#output": {} } }, "com.amazonaws.glacier#InitiateMultipartUpload": { @@ -3043,7 +3129,8 @@ } }, "traits": { - "smithy.api#documentation": "

Provides options for initiating a multipart upload to an Amazon S3 Glacier\n vault.

" + "smithy.api#documentation": "

Provides options for initiating a multipart upload to an Amazon S3 Glacier\n vault.

", + "smithy.api#input": {} } }, "com.amazonaws.glacier#InitiateMultipartUploadOutput": { @@ -3065,7 +3152,8 @@ } }, "traits": { - "smithy.api#documentation": "

The Amazon S3 Glacier response to your request.

" + "smithy.api#documentation": "

The Amazon S3 Glacier response to your request.

", + "smithy.api#output": {} } }, "com.amazonaws.glacier#InitiateVaultLock": { @@ -3127,7 +3215,8 @@ } }, "traits": { - "smithy.api#documentation": "

The input values for InitiateVaultLock.

" + "smithy.api#documentation": "

The input values for InitiateVaultLock.

", + "smithy.api#input": {} } }, "com.amazonaws.glacier#InitiateVaultLockOutput": { @@ -3142,7 +3231,8 @@ } }, "traits": { - "smithy.api#documentation": "

Contains the Amazon S3 Glacier response to your request.

" + "smithy.api#documentation": "

Contains the Amazon S3 Glacier response to your request.

", + "smithy.api#output": {} } }, "com.amazonaws.glacier#InputSerialization": { @@ -3464,7 +3554,8 @@ } }, "traits": { - "smithy.api#documentation": "

Provides options for retrieving a job list for an Amazon S3 Glacier vault.

" + "smithy.api#documentation": "

Provides options for retrieving a job list for an Amazon S3 Glacier vault.

", + "smithy.api#input": {} } }, "com.amazonaws.glacier#ListJobsOutput": { @@ -3484,7 +3575,8 @@ } }, "traits": { - "smithy.api#documentation": "

Contains the Amazon S3 Glacier response to your request.

" + "smithy.api#documentation": "

Contains the Amazon S3 Glacier response to your request.

", + "smithy.api#output": {} } }, "com.amazonaws.glacier#ListMultipartUploads": { @@ -3559,7 +3651,8 @@ } }, "traits": { - "smithy.api#documentation": "

Provides options for retrieving list of in-progress multipart uploads for an Amazon\n Glacier vault.

" + "smithy.api#documentation": "

Provides options for retrieving list of in-progress multipart uploads for an Amazon\n Glacier vault.

", + "smithy.api#input": {} } }, "com.amazonaws.glacier#ListMultipartUploadsOutput": { @@ -3579,7 +3672,8 @@ } }, "traits": { - "smithy.api#documentation": "

Contains the Amazon S3 Glacier response to your request.

" + "smithy.api#documentation": "

Contains the Amazon S3 Glacier response to your request.

", + "smithy.api#output": {} } }, "com.amazonaws.glacier#ListParts": { @@ -3662,7 +3756,8 @@ } }, "traits": { - "smithy.api#documentation": "

Provides options for retrieving a list of parts of an archive that have been uploaded\n in a specific multipart upload.

" + "smithy.api#documentation": "

Provides options for retrieving a list of parts of an archive that have been uploaded\n in a specific multipart upload.

", + "smithy.api#input": {} } }, "com.amazonaws.glacier#ListPartsOutput": { @@ -3713,7 +3808,8 @@ } }, "traits": { - "smithy.api#documentation": "

Contains the Amazon S3 Glacier response to your request.

" + "smithy.api#documentation": "

Contains the Amazon S3 Glacier response to your request.

", + "smithy.api#output": {} } }, "com.amazonaws.glacier#ListProvisionedCapacity": { @@ -3755,6 +3851,9 @@ "smithy.api#required": {} } } + }, + "traits": { + "smithy.api#input": {} } }, "com.amazonaws.glacier#ListProvisionedCapacityOutput": { @@ -3766,6 +3865,9 @@ "smithy.api#documentation": "

The response body contains the following JSON fields.

" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.glacier#ListTagsForVault": { @@ -3820,7 +3922,8 @@ } }, "traits": { - "smithy.api#documentation": "

The input value for ListTagsForVaultInput.

" + "smithy.api#documentation": "

The input value for ListTagsForVaultInput.

", + "smithy.api#input": {} } }, "com.amazonaws.glacier#ListTagsForVaultOutput": { @@ -3834,7 +3937,8 @@ } }, "traits": { - "smithy.api#documentation": "

Contains the Amazon S3 Glacier response to your request.

" + "smithy.api#documentation": "

Contains the Amazon S3 Glacier response to your request.

", + "smithy.api#output": {} } }, "com.amazonaws.glacier#ListVaults": { @@ -3901,7 +4005,8 @@ } }, "traits": { - "smithy.api#documentation": "

Provides options to retrieve the vault list owned by the calling user's account. The\n list provides metadata information for each vault.

" + "smithy.api#documentation": "

Provides options to retrieve the vault list owned by the calling user's account. The\n list provides metadata information for each vault.

", + "smithy.api#input": {} } }, "com.amazonaws.glacier#ListVaultsOutput": { @@ -3921,7 +4026,8 @@ } }, "traits": { - "smithy.api#documentation": "

Contains the Amazon S3 Glacier response to your request.

" + "smithy.api#documentation": "

Contains the Amazon S3 Glacier response to your request.

", + "smithy.api#output": {} } }, "com.amazonaws.glacier#MissingParameterValueException": { @@ -4016,30 +4122,38 @@ } }, "com.amazonaws.glacier#Permission": { - "type": "string", - "traits": { - "smithy.api#enum": [ - { - "value": "FULL_CONTROL", - "name": "FULL_CONTROL" - }, - { - "value": "WRITE", - "name": "WRITE" - }, - { - "value": "WRITE_ACP", - "name": "WRITE_ACP" - }, - { - "value": "READ", - "name": "READ" - }, - { - "value": "READ_ACP", - "name": "READ_ACP" + "type": "enum", + "members": { + "FULL_CONTROL": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "FULL_CONTROL" + } + }, + "WRITE": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "WRITE" + } + }, + "WRITE_ACP": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "WRITE_ACP" } - ] + }, + "READ": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "READ" + } + }, + "READ_ACP": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "READ_ACP" + } + } } }, "com.amazonaws.glacier#PolicyEnforcedException": { @@ -4144,6 +4258,9 @@ "smithy.api#required": {} } } + }, + "traits": { + "smithy.api#input": {} } }, "com.amazonaws.glacier#PurchaseProvisionedCapacityOutput": { @@ -4156,21 +4273,26 @@ "smithy.api#httpHeader": "x-amz-capacity-id" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.glacier#QuoteFields": { - "type": "string", - "traits": { - "smithy.api#enum": [ - { - "value": "ALWAYS", - "name": "Always" - }, - { - "value": "ASNEEDED", - "name": "AsNeeded" + "type": "enum", + "members": { + "Always": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "ALWAYS" + } + }, + "AsNeeded": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "ASNEEDED" } - ] + } } }, "com.amazonaws.glacier#RemoveTagsFromVault": { @@ -4231,7 +4353,8 @@ } }, "traits": { - "smithy.api#documentation": "

The input value for RemoveTagsFromVaultInput.

" + "smithy.api#documentation": "

The input value for RemoveTagsFromVaultInput.

", + "smithy.api#input": {} } }, "com.amazonaws.glacier#RequestTimeoutException": { @@ -4453,7 +4576,8 @@ } }, "traits": { - "smithy.api#documentation": "

SetDataRetrievalPolicy input.

" + "smithy.api#documentation": "

SetDataRetrievalPolicy input.

", + "smithy.api#input": {} } }, "com.amazonaws.glacier#SetVaultAccessPolicy": { @@ -4515,7 +4639,8 @@ } }, "traits": { - "smithy.api#documentation": "

SetVaultAccessPolicy input.

" + "smithy.api#documentation": "

SetVaultAccessPolicy input.

", + "smithy.api#input": {} } }, "com.amazonaws.glacier#SetVaultNotifications": { @@ -4577,48 +4702,57 @@ } }, "traits": { - "smithy.api#documentation": "

Provides options to configure notifications that will be sent when specific events\n happen to a vault.

" + "smithy.api#documentation": "

Provides options to configure notifications that will be sent when specific events\n happen to a vault.

", + "smithy.api#input": {} } }, "com.amazonaws.glacier#Size": { "type": "long" }, "com.amazonaws.glacier#StatusCode": { - "type": "string", - "traits": { - "smithy.api#enum": [ - { - "value": "InProgress", - "name": "InProgress" - }, - { - "value": "Succeeded", - "name": "Succeeded" - }, - { - "value": "Failed", - "name": "Failed" + "type": "enum", + "members": { + "InProgress": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "InProgress" } - ] + }, + "Succeeded": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "Succeeded" + } + }, + "Failed": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "Failed" + } + } } }, "com.amazonaws.glacier#StorageClass": { - "type": "string", - "traits": { - "smithy.api#enum": [ - { - "value": "STANDARD", - "name": "Standard" - }, - { - "value": "REDUCED_REDUNDANCY", - "name": "ReducedRedundancy" - }, - { - "value": "STANDARD_IA", - "name": "StandardInfrequentAccess" + "type": "enum", + "members": { + "Standard": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "STANDARD" + } + }, + "ReducedRedundancy": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "REDUCED_REDUNDANCY" + } + }, + "StandardInfrequentAccess": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "STANDARD_IA" } - ] + } } }, "com.amazonaws.glacier#Stream": { @@ -4649,22 +4783,26 @@ "type": "string" }, "com.amazonaws.glacier#Type": { - "type": "string", - "traits": { - "smithy.api#enum": [ - { - "value": "AmazonCustomerByEmail", - "name": "AmazonCustomerByEmail" - }, - { - "value": "CanonicalUser", - "name": "CanonicalUser" - }, - { - "value": "Group", - "name": "Group" + "type": "enum", + "members": { + "AmazonCustomerByEmail": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "AmazonCustomerByEmail" } - ] + }, + "CanonicalUser": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "CanonicalUser" + } + }, + "Group": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "Group" + } + } } }, "com.amazonaws.glacier#UploadArchive": { @@ -4744,7 +4882,8 @@ } }, "traits": { - "smithy.api#documentation": "

Provides options to add an archive to a vault.

" + "smithy.api#documentation": "

Provides options to add an archive to a vault.

", + "smithy.api#input": {} } }, "com.amazonaws.glacier#UploadListElement": { @@ -4871,7 +5010,8 @@ } }, "traits": { - "smithy.api#documentation": "

Provides options to upload a part of an archive in a multipart upload\n operation.

" + "smithy.api#documentation": "

Provides options to upload a part of an archive in a multipart upload\n operation.

", + "smithy.api#input": {} } }, "com.amazonaws.glacier#UploadMultipartPartOutput": { @@ -4886,7 +5026,8 @@ } }, "traits": { - "smithy.api#documentation": "

Contains the Amazon S3 Glacier response to your request.

" + "smithy.api#documentation": "

Contains the Amazon S3 Glacier response to your request.

", + "smithy.api#output": {} } }, "com.amazonaws.glacier#UploadsList": { diff --git a/aws/sdk/aws-models/iam.json b/aws/sdk/aws-models/iam.json index da5df834e7..bd2c1ac793 100644 --- a/aws/sdk/aws-models/iam.json +++ b/aws/sdk/aws-models/iam.json @@ -530,7 +530,7 @@ "parameters": { "Region": { "builtIn": "AWS::Region", - "required": true, + "required": false, "documentation": "The AWS region used to dispatch the request.", "type": "String" }, @@ -559,13 +559,12 @@ { "conditions": [ { - "fn": "aws.partition", + "fn": "isSet", "argv": [ { - "ref": "Region" + "ref": "Endpoint" } - ], - "assign": "PartitionResult" + ] } ], "type": "tree", @@ -573,14 +572,20 @@ { "conditions": [ { - "fn": "isSet", + "fn": "booleanEquals", "argv": [ { - "ref": "Endpoint" - } + "ref": "UseFIPS" + }, + true ] } ], + "error": "Invalid Configuration: FIPS and custom endpoint are not supported", + "type": "error" + }, + { + "conditions": [], "type": "tree", "rules": [ { @@ -589,64 +594,42 @@ "fn": "booleanEquals", "argv": [ { - "ref": "UseFIPS" + "ref": "UseDualStack" }, true ] } ], - "error": "Invalid Configuration: FIPS and custom endpoint are not supported", + "error": "Invalid Configuration: Dualstack 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" + "endpoint": { + "url": { + "ref": "Endpoint" }, - { - "conditions": [], - "endpoint": { - "url": { - "ref": "Endpoint" - }, - "properties": {}, - "headers": {} - }, - "type": "endpoint" - } - ] + "properties": {}, + "headers": {} + }, + "type": "endpoint" } ] - }, + } + ] + }, + { + "conditions": [], + "type": "tree", + "rules": [ { "conditions": [ { - "fn": "stringEquals", + "fn": "isSet", "argv": [ { - "fn": "getAttr", - "argv": [ - { - "ref": "PartitionResult" - }, - "name" - ] - }, - "aws" + "ref": "Region" + } ] } ], @@ -655,22 +638,13 @@ { "conditions": [ { - "fn": "booleanEquals", - "argv": [ - { - "ref": "UseFIPS" - }, - true - ] - }, - { - "fn": "booleanEquals", + "fn": "aws.partition", "argv": [ { - "ref": "UseDualStack" - }, - true - ] + "ref": "Region" + } + ], + "assign": "PartitionResult" } ], "type": "tree", @@ -678,100 +652,221 @@ { "conditions": [ { - "fn": "booleanEquals", + "fn": "stringEquals", "argv": [ - true, { "fn": "getAttr", "argv": [ { "ref": "PartitionResult" }, - "supportsFIPS" + "name" + ] + }, + "aws" + ] + } + ], + "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.aws", + "properties": {}, + "headers": {} + }, + "type": "endpoint" + } ] + }, + { + "conditions": [], + "error": "FIPS and DualStack are enabled, but this partition does not support one or both", + "type": "error" } ] }, { - "fn": "booleanEquals", - "argv": [ - true, + "conditions": [ { - "fn": "getAttr", + "fn": "booleanEquals", "argv": [ { - "ref": "PartitionResult" + "ref": "UseFIPS" }, - "supportsDualStack" + 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" + } ] + }, + { + "conditions": [], + "error": "FIPS is enabled but this partition does not support FIPS", + "type": "error" } ] - } - ], - "type": "tree", - "rules": [ - { - "conditions": [], - "endpoint": { - "url": "https://iam-fips.{Region}.api.aws", - "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, + "conditions": [ { - "fn": "getAttr", + "fn": "booleanEquals", "argv": [ { - "ref": "PartitionResult" + "ref": "UseDualStack" }, - "supportsFIPS" + true + ] + } + ], + "type": "tree", + "rules": [ + { + "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" + } ] + }, + { + "conditions": [], + "error": "DualStack is enabled but this partition does not support DualStack", + "type": "error" } ] - } - ], - "type": "tree", - "rules": [ + }, { "conditions": [], "endpoint": { - "url": "https://iam-fips.amazonaws.com", + "url": "https://iam.amazonaws.com", "properties": { "authSchemes": [ { "name": "sigv4", - "signingRegion": "us-east-1", - "signingName": "iam" + "signingName": "iam", + "signingRegion": "us-east-1" } ] }, @@ -781,433 +876,433 @@ } ] }, - { - "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", + "fn": "stringEquals", "argv": [ - true, { "fn": "getAttr", "argv": [ { "ref": "PartitionResult" }, - "supportsDualStack" + "name" ] - } + }, + "aws-cn" ] } ], "type": "tree", "rules": [ { - "conditions": [], - "endpoint": { - "url": "https://iam.{Region}.api.aws", - "properties": {}, - "headers": {} - }, - "type": "endpoint" - } - ] - }, - { - "conditions": [], - "error": "DualStack is enabled but this partition does not support DualStack", - "type": "error" - } - ] - }, - { - "conditions": [], - "endpoint": { - "url": "https://iam.amazonaws.com", - "properties": { - "authSchemes": [ - { - "name": "sigv4", - "signingRegion": "us-east-1", - "signingName": "iam" - } - ] - }, - "headers": {} - }, - "type": "endpoint" - } - ] - }, - { - "conditions": [ - { - "fn": "stringEquals", - "argv": [ - { - "fn": "getAttr", - "argv": [ - { - "ref": "PartitionResult" + "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" + } + ] }, - "name" - ] - }, - "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, + "conditions": [ { - "fn": "getAttr", + "fn": "booleanEquals", "argv": [ { - "ref": "PartitionResult" + "ref": "UseFIPS" }, - "supportsFIPS" + 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}.amazonaws.com.cn", + "properties": {}, + "headers": {} + }, + "type": "endpoint" + } + ] + }, + { + "conditions": [], + "error": "FIPS is enabled but this partition does not support FIPS", + "type": "error" + } ] }, { - "fn": "booleanEquals", - "argv": [ - true, + "conditions": [ { - "fn": "getAttr", + "fn": "booleanEquals", "argv": [ { - "ref": "PartitionResult" + "ref": "UseDualStack" }, - "supportsDualStack" + true + ] + } + ], + "type": "tree", + "rules": [ + { + "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" + } ] + }, + { + "conditions": [], + "error": "DualStack is enabled but this partition does not support DualStack", + "type": "error" } ] - } - ], - "type": "tree", - "rules": [ + }, { "conditions": [], "endpoint": { - "url": "https://iam-fips.{Region}.api.amazonwebservices.com.cn", - "properties": {}, + "url": "https://iam.cn-north-1.amazonaws.com.cn", + "properties": { + "authSchemes": [ + { + "name": "sigv4", + "signingName": "iam", + "signingRegion": "cn-north-1" + } + ] + }, "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", + "fn": "stringEquals", "argv": [ - true, { "fn": "getAttr", "argv": [ { "ref": "PartitionResult" }, - "supportsFIPS" + "name" ] - } + }, + "aws-us-gov" ] } ], "type": "tree", "rules": [ { - "conditions": [], - "endpoint": { - "url": "https://iam-fips.{Region}.amazonaws.com.cn", - "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, + "conditions": [ { - "fn": "getAttr", + "fn": "booleanEquals", "argv": [ { - "ref": "PartitionResult" + "ref": "UseFIPS" }, - "supportsDualStack" + true ] - } - ] - } - ], - "type": "tree", - "rules": [ - { - "conditions": [], - "endpoint": { - "url": "https://iam.{Region}.api.amazonwebservices.com.cn", - "properties": {}, - "headers": {} - }, - "type": "endpoint" - } - ] - }, - { - "conditions": [], - "error": "DualStack is enabled but this partition does not support DualStack", - "type": "error" - } - ] - }, - { - "conditions": [], - "endpoint": { - "url": "https://iam.cn-north-1.amazonaws.com.cn", - "properties": { - "authSchemes": [ - { - "name": "sigv4", - "signingRegion": "cn-north-1", - "signingName": "iam" - } - ] - }, - "headers": {} - }, - "type": "endpoint" - } - ] - }, - { - "conditions": [ - { - "fn": "stringEquals", - "argv": [ - { - "fn": "getAttr", - "argv": [ - { - "ref": "PartitionResult" - }, - "name" - ] - }, - "aws-us-gov" - ] - } - ], - "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", + "fn": "booleanEquals", "argv": [ { - "ref": "PartitionResult" + "ref": "UseDualStack" + }, + true + ] + } + ], + "type": "tree", + "rules": [ + { + "conditions": [ + { + "fn": "booleanEquals", + "argv": [ + true, + { + "fn": "getAttr", + "argv": [ + { + "ref": "PartitionResult" + }, + "supportsFIPS" + ] + } + ] }, - "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" + } ] + }, + { + "conditions": [], + "error": "FIPS and DualStack are enabled, but this partition does not support one or both", + "type": "error" } ] }, { - "fn": "booleanEquals", - "argv": [ - true, + "conditions": [ { - "fn": "getAttr", + "fn": "booleanEquals", "argv": [ { - "ref": "PartitionResult" + "ref": "UseFIPS" }, - "supportsDualStack" + 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" + } ] + }, + { + "conditions": [], + "error": "FIPS is enabled but this partition does not support FIPS", + "type": "error" } ] - } - ], - "type": "tree", - "rules": [ - { - "conditions": [], - "endpoint": { - "url": "https://iam-fips.{Region}.api.aws", - "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, + "conditions": [ { - "fn": "getAttr", + "fn": "booleanEquals", "argv": [ { - "ref": "PartitionResult" + "ref": "UseDualStack" }, - "supportsFIPS" + true ] } + ], + "type": "tree", + "rules": [ + { + "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" + } + ] + }, + { + "conditions": [], + "error": "DualStack is enabled but this partition does not support DualStack", + "type": "error" + } ] - } - ], - "type": "tree", - "rules": [ + }, { "conditions": [], "endpoint": { @@ -1216,8 +1311,8 @@ "authSchemes": [ { "name": "sigv4", - "signingRegion": "us-gov-west-1", - "signingName": "iam" + "signingName": "iam", + "signingRegion": "us-gov-west-1" } ] }, @@ -1227,234 +1322,182 @@ } ] }, - { - "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", + "fn": "stringEquals", "argv": [ - true, { "fn": "getAttr", "argv": [ { "ref": "PartitionResult" }, - "supportsDualStack" + "name" ] - } + }, + "aws-iso" ] } ], "type": "tree", "rules": [ { - "conditions": [], - "endpoint": { - "url": "https://iam.{Region}.api.aws", - "properties": {}, - "headers": {} - }, - "type": "endpoint" - } - ] - }, - { - "conditions": [], - "error": "DualStack is enabled but this partition does not support DualStack", - "type": "error" - } - ] - }, - { - "conditions": [], - "endpoint": { - "url": "https://iam.us-gov.amazonaws.com", - "properties": { - "authSchemes": [ - { - "name": "sigv4", - "signingRegion": "us-gov-west-1", - "signingName": "iam" - } - ] - }, - "headers": {} - }, - "type": "endpoint" - } - ] - }, - { - "conditions": [ - { - "fn": "stringEquals", - "argv": [ - { - "fn": "getAttr", - "argv": [ - { - "ref": "PartitionResult" - }, - "name" - ] - }, - "aws-iso" - ] - } - ], - "type": "tree", - "rules": [ - { - "conditions": [ - { - "fn": "booleanEquals", - "argv": [ - { - "ref": "UseFIPS" - }, - true - ] - } - ], - "type": "tree", - "rules": [ - { - "conditions": [ - { - "fn": "booleanEquals", - "argv": [ - true, + "conditions": [ { - "fn": "getAttr", + "fn": "booleanEquals", "argv": [ { - "ref": "PartitionResult" + "ref": "UseFIPS" }, - "supportsFIPS" + 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}.c2s.ic.gov", + "properties": {}, + "headers": {} + }, + "type": "endpoint" + } ] + }, + { + "conditions": [], + "error": "FIPS is enabled but this partition does not support FIPS", + "type": "error" } ] - } - ], - "type": "tree", - "rules": [ + }, { "conditions": [], "endpoint": { - "url": "https://iam-fips.{Region}.c2s.ic.gov", - "properties": {}, + "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": [], - "error": "FIPS is enabled but this partition does not support FIPS", - "type": "error" - } - ] - }, - { - "conditions": [], - "endpoint": { - "url": "https://iam.us-iso-east-1.c2s.ic.gov", - "properties": { - "authSchemes": [ - { - "name": "sigv4", - "signingRegion": "us-iso-east-1", - "signingName": "iam" - } - ] - }, - "headers": {} - }, - "type": "endpoint" - } - ] - }, - { - "conditions": [ - { - "fn": "stringEquals", - "argv": [ - { - "fn": "getAttr", - "argv": [ - { - "ref": "PartitionResult" - }, - "name" - ] - }, - "aws-iso-b" - ] - } - ], - "type": "tree", - "rules": [ - { - "conditions": [ - { - "fn": "booleanEquals", - "argv": [ - { - "ref": "UseFIPS" - }, - true - ] - } - ], - "type": "tree", - "rules": [ { "conditions": [ { - "fn": "booleanEquals", + "fn": "stringEquals", "argv": [ - true, { "fn": "getAttr", "argv": [ { "ref": "PartitionResult" }, - "supportsFIPS" + "name" ] - } + }, + "aws-iso-b" ] } ], "type": "tree", "rules": [ + { + "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": [], + "endpoint": { + "url": "https://iam-fips.{Region}.sc2s.sgov.gov", + "properties": {}, + "headers": {} + }, + "type": "endpoint" + } + ] + }, + { + "conditions": [], + "error": "FIPS is enabled but this partition does not support FIPS", + "type": "error" + } + ] + }, { "conditions": [], "endpoint": { - "url": "https://iam-fips.{Region}.sc2s.sgov.gov", - "properties": {}, + "url": "https://iam.us-isob-east-1.sc2s.sgov.gov", + "properties": { + "authSchemes": [ + { + "name": "sigv4", + "signingName": "iam", + "signingRegion": "us-isob-east-1" + } + ] + }, "headers": {} }, "type": "endpoint" @@ -1462,147 +1505,257 @@ ] }, { - "conditions": [], - "error": "FIPS is enabled but this partition does not support FIPS", - "type": "error" - } - ] - }, - { - "conditions": [], - "endpoint": { - "url": "https://iam.us-isob-east-1.sc2s.sgov.gov", - "properties": { - "authSchemes": [ + "conditions": [ { - "name": "sigv4", - "signingRegion": "us-isob-east-1", - "signingName": "iam" - } - ] - }, - "headers": {} - }, - "type": "endpoint" - } - ] - }, - { - "conditions": [ - { - "fn": "booleanEquals", - "argv": [ - { - "ref": "UseFIPS" - }, - true - ] - }, - { - "fn": "booleanEquals", - "argv": [ - { - "ref": "UseDualStack" - }, - true - ] - } - ], - "type": "tree", - "rules": [ - { - "conditions": [ - { - "fn": "booleanEquals", - "argv": [ - true, + "fn": "booleanEquals", + "argv": [ + { + "ref": "UseFIPS" + }, + true + ] + }, { - "fn": "getAttr", + "fn": "booleanEquals", "argv": [ { - "ref": "PartitionResult" + "ref": "UseDualStack" + }, + true + ] + } + ], + "type": "tree", + "rules": [ + { + "conditions": [ + { + "fn": "booleanEquals", + "argv": [ + true, + { + "fn": "getAttr", + "argv": [ + { + "ref": "PartitionResult" + }, + "supportsFIPS" + ] + } + ] }, - "supportsFIPS" + { + "fn": "booleanEquals", + "argv": [ + true, + { + "fn": "getAttr", + "argv": [ + { + "ref": "PartitionResult" + }, + "supportsDualStack" + ] + } + ] + } + ], + "type": "tree", + "rules": [ + { + "conditions": [], + "type": "tree", + "rules": [ + { + "conditions": [], + "endpoint": { + "url": "https://iam-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" } ] }, { - "fn": "booleanEquals", - "argv": [ - true, + "conditions": [ { - "fn": "getAttr", + "fn": "booleanEquals", "argv": [ { - "ref": "PartitionResult" + "ref": "UseFIPS" }, - "supportsDualStack" + true ] } - ] - } - ], - "type": "tree", - "rules": [ - { - "conditions": [], + ], "type": "tree", "rules": [ + { + "conditions": [ + { + "fn": "booleanEquals", + "argv": [ + true, + { + "fn": "getAttr", + "argv": [ + { + "ref": "PartitionResult" + }, + "supportsFIPS" + ] + } + ] + } + ], + "type": "tree", + "rules": [ + { + "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": { + "url": "https://iam-fips.{Region}.{PartitionResult#dnsSuffix}", + "properties": {}, + "headers": {} + }, + "type": "endpoint" + } + ] + } + ] + }, { "conditions": [], - "endpoint": { - "url": "https://iam-fips.{Region}.{PartitionResult#dualStackDnsSuffix}", - "properties": {}, - "headers": {} - }, - "type": "endpoint" + "error": "FIPS is enabled but this partition does not support FIPS", + "type": "error" } ] - } - ] - }, - { - "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, + "conditions": [ { - "fn": "getAttr", + "fn": "booleanEquals", "argv": [ { - "ref": "PartitionResult" + "ref": "UseDualStack" }, - "supportsFIPS" + 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://iam.{Region}.{PartitionResult#dualStackDnsSuffix}", + "properties": {}, + "headers": {} + }, + "type": "endpoint" + } + ] + } ] + }, + { + "conditions": [], + "error": "DualStack is enabled but this partition does not support DualStack", + "type": "error" } ] - } - ], - "type": "tree", - "rules": [ + }, { "conditions": [], "type": "tree", @@ -1620,13 +1773,13 @@ } ], "endpoint": { - "url": "https://iam-fips.amazonaws.com", + "url": "https://iam.amazonaws.com", "properties": { "authSchemes": [ { "name": "sigv4", - "signingRegion": "us-east-1", - "signingName": "iam" + "signingName": "iam", + "signingRegion": "us-east-1" } ] }, @@ -1642,18 +1795,18 @@ { "ref": "Region" }, - "aws-us-gov-global" + "aws-cn-global" ] } ], "endpoint": { - "url": "https://iam.us-gov.amazonaws.com", + "url": "https://iam.cn-north-1.amazonaws.com.cn", "properties": { "authSchemes": [ { "name": "sigv4", - "signingRegion": "us-gov-west-1", - "signingName": "iam" + "signingName": "iam", + "signingRegion": "cn-north-1" } ] }, @@ -1662,67 +1815,90 @@ "type": "endpoint" }, { - "conditions": [], + "conditions": [ + { + "fn": "stringEquals", + "argv": [ + { + "ref": "Region" + }, + "aws-us-gov-global" + ] + } + ], "endpoint": { - "url": "https://iam-fips.{Region}.{PartitionResult#dnsSuffix}", - "properties": {}, + "url": "https://iam.us-gov.amazonaws.com", + "properties": { + "authSchemes": [ + { + "name": "sigv4", + "signingName": "iam", + "signingRegion": "us-gov-west-1" + } + ] + }, "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, + }, + { + "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" + }, { - "fn": "getAttr", - "argv": [ + "conditions": [ { - "ref": "PartitionResult" + "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" + } + ] }, - "supportsDualStack" - ] - } - ] - } - ], - "type": "tree", - "rules": [ - { - "conditions": [], - "type": "tree", - "rules": [ + "headers": {} + }, + "type": "endpoint" + }, { "conditions": [], "endpoint": { - "url": "https://iam.{Region}.{PartitionResult#dualStackDnsSuffix}", + "url": "https://iam.{Region}.{PartitionResult#dnsSuffix}", "properties": {}, "headers": {} }, @@ -1731,163 +1907,13 @@ ] } ] - }, - { - "conditions": [], - "error": "DualStack is enabled but this partition does not support DualStack", - "type": "error" } ] }, { "conditions": [], - "type": "tree", - "rules": [ - { - "conditions": [ - { - "fn": "stringEquals", - "argv": [ - { - "ref": "Region" - }, - "aws-global" - ] - } - ], - "endpoint": { - "url": "https://iam.amazonaws.com", - "properties": { - "authSchemes": [ - { - "name": "sigv4", - "signingRegion": "us-east-1", - "signingName": "iam" - } - ] - }, - "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", - "signingRegion": "cn-north-1", - "signingName": "iam" - } - ] - }, - "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", - "signingRegion": "us-gov-west-1", - "signingName": "iam" - } - ] - }, - "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", - "signingRegion": "us-iso-east-1", - "signingName": "iam" - } - ] - }, - "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", - "signingRegion": "us-isob-east-1", - "signingName": "iam" - } - ] - }, - "headers": {} - }, - "type": "endpoint" - }, - { - "conditions": [], - "endpoint": { - "url": "https://iam.{Region}.{PartitionResult#dnsSuffix}", - "properties": {}, - "headers": {} - }, - "type": "endpoint" - } - ] + "error": "Invalid Configuration: Missing Region", + "type": "error" } ] } @@ -1902,8 +1928,8 @@ "properties": { "authSchemes": [ { - "signingName": "iam", "name": "sigv4", + "signingName": "iam", "signingRegion": "us-east-1" } ] @@ -1912,8 +1938,8 @@ } }, "params": { - "UseFIPS": false, "UseDualStack": false, + "UseFIPS": false, "Region": "aws-global" } }, @@ -1924,8 +1950,8 @@ "properties": { "authSchemes": [ { - "signingName": "iam", "name": "sigv4", + "signingName": "iam", "signingRegion": "us-east-1" } ] @@ -1934,8 +1960,8 @@ } }, "params": { - "UseFIPS": true, "UseDualStack": false, + "UseFIPS": true, "Region": "aws-global" } }, @@ -1947,8 +1973,8 @@ } }, "params": { - "UseFIPS": true, "UseDualStack": true, + "UseFIPS": true, "Region": "us-east-1" } }, @@ -1959,8 +1985,8 @@ "properties": { "authSchemes": [ { - "signingName": "iam", "name": "sigv4", + "signingName": "iam", "signingRegion": "us-east-1" } ] @@ -1969,8 +1995,8 @@ } }, "params": { - "UseFIPS": true, "UseDualStack": false, + "UseFIPS": true, "Region": "us-east-1" } }, @@ -1982,8 +2008,8 @@ } }, "params": { - "UseFIPS": false, "UseDualStack": true, + "UseFIPS": false, "Region": "us-east-1" } }, @@ -1994,8 +2020,8 @@ "properties": { "authSchemes": [ { - "signingName": "iam", "name": "sigv4", + "signingName": "iam", "signingRegion": "us-east-1" } ] @@ -2004,20 +2030,103 @@ } }, "params": { - "UseFIPS": false, "UseDualStack": false, + "UseFIPS": false, "Region": "us-east-1" } }, { - "documentation": "For region aws-us-gov-global with FIPS disabled and DualStack disabled", + "documentation": "For region aws-cn-global with FIPS disabled and DualStack disabled", + "expect": { + "endpoint": { + "properties": { + "authSchemes": [ + { + "name": "sigv4", + "signingName": "iam", + "signingRegion": "cn-north-1" + } + ] + }, + "url": "https://iam.cn-north-1.amazonaws.com.cn" + } + }, + "params": { + "UseDualStack": false, + "UseFIPS": false, + "Region": "aws-cn-global" + } + }, + { + "documentation": "For region cn-north-1 with FIPS enabled and DualStack enabled", + "expect": { + "endpoint": { + "url": "https://iam-fips.cn-north-1.api.amazonwebservices.com.cn" + } + }, + "params": { + "UseDualStack": true, + "UseFIPS": true, + "Region": "cn-north-1" + } + }, + { + "documentation": "For region cn-north-1 with FIPS enabled and DualStack disabled", + "expect": { + "endpoint": { + "url": "https://iam-fips.cn-north-1.amazonaws.com.cn" + } + }, + "params": { + "UseDualStack": false, + "UseFIPS": true, + "Region": "cn-north-1" + } + }, + { + "documentation": "For region cn-north-1 with FIPS disabled and DualStack enabled", + "expect": { + "endpoint": { + "url": "https://iam.cn-north-1.api.amazonwebservices.com.cn" + } + }, + "params": { + "UseDualStack": true, + "UseFIPS": false, + "Region": "cn-north-1" + } + }, + { + "documentation": "For region cn-north-1 with FIPS disabled and DualStack disabled", "expect": { "endpoint": { "properties": { "authSchemes": [ { + "name": "sigv4", "signingName": "iam", + "signingRegion": "cn-north-1" + } + ] + }, + "url": "https://iam.cn-north-1.amazonaws.com.cn" + } + }, + "params": { + "UseDualStack": false, + "UseFIPS": false, + "Region": "cn-north-1" + } + }, + { + "documentation": "For region aws-us-gov-global with FIPS disabled and DualStack disabled", + "expect": { + "endpoint": { + "properties": { + "authSchemes": [ + { "name": "sigv4", + "signingName": "iam", "signingRegion": "us-gov-west-1" } ] @@ -2026,8 +2135,8 @@ } }, "params": { - "UseFIPS": false, "UseDualStack": false, + "UseFIPS": false, "Region": "aws-us-gov-global" } }, @@ -2038,8 +2147,8 @@ "properties": { "authSchemes": [ { - "signingName": "iam", "name": "sigv4", + "signingName": "iam", "signingRegion": "us-gov-west-1" } ] @@ -2048,8 +2157,8 @@ } }, "params": { - "UseFIPS": true, "UseDualStack": false, + "UseFIPS": true, "Region": "aws-us-gov-global" } }, @@ -2061,8 +2170,8 @@ } }, "params": { - "UseFIPS": true, "UseDualStack": true, + "UseFIPS": true, "Region": "us-gov-east-1" } }, @@ -2073,8 +2182,8 @@ "properties": { "authSchemes": [ { - "signingName": "iam", "name": "sigv4", + "signingName": "iam", "signingRegion": "us-gov-west-1" } ] @@ -2083,8 +2192,8 @@ } }, "params": { - "UseFIPS": true, "UseDualStack": false, + "UseFIPS": true, "Region": "us-gov-east-1" } }, @@ -2096,8 +2205,8 @@ } }, "params": { - "UseFIPS": false, "UseDualStack": true, + "UseFIPS": false, "Region": "us-gov-east-1" } }, @@ -2108,8 +2217,8 @@ "properties": { "authSchemes": [ { - "signingName": "iam", "name": "sigv4", + "signingName": "iam", "signingRegion": "us-gov-west-1" } ] @@ -2118,219 +2227,149 @@ } }, "params": { - "UseFIPS": false, "UseDualStack": false, + "UseFIPS": false, "Region": "us-gov-east-1" } }, { - "documentation": "For region aws-iso-b-global with FIPS disabled and DualStack disabled", + "documentation": "For region aws-iso-global with FIPS disabled and DualStack disabled", "expect": { "endpoint": { "properties": { "authSchemes": [ { - "signingName": "iam", "name": "sigv4", - "signingRegion": "us-isob-east-1" + "signingName": "iam", + "signingRegion": "us-iso-east-1" } ] }, - "url": "https://iam.us-isob-east-1.sc2s.sgov.gov" + "url": "https://iam.us-iso-east-1.c2s.ic.gov" } }, "params": { - "UseFIPS": false, "UseDualStack": false, - "Region": "aws-iso-b-global" + "UseFIPS": false, + "Region": "aws-iso-global" } }, { - "documentation": "For region us-isob-east-1 with FIPS enabled and DualStack disabled", + "documentation": "For region us-iso-east-1 with FIPS enabled and DualStack disabled", "expect": { "endpoint": { - "url": "https://iam-fips.us-isob-east-1.sc2s.sgov.gov" + "url": "https://iam-fips.us-iso-east-1.c2s.ic.gov" } }, "params": { - "UseFIPS": true, "UseDualStack": false, - "Region": "us-isob-east-1" + "UseFIPS": true, + "Region": "us-iso-east-1" } }, { - "documentation": "For region us-isob-east-1 with FIPS disabled and DualStack disabled", + "documentation": "For region us-iso-east-1 with FIPS disabled and DualStack disabled", "expect": { "endpoint": { "properties": { "authSchemes": [ { - "signingName": "iam", "name": "sigv4", - "signingRegion": "us-isob-east-1" + "signingName": "iam", + "signingRegion": "us-iso-east-1" } ] }, - "url": "https://iam.us-isob-east-1.sc2s.sgov.gov" + "url": "https://iam.us-iso-east-1.c2s.ic.gov" } }, "params": { - "UseFIPS": false, "UseDualStack": false, - "Region": "us-isob-east-1" + "UseFIPS": false, + "Region": "us-iso-east-1" } }, { - "documentation": "For region aws-cn-global with FIPS disabled and DualStack disabled", + "documentation": "For region aws-iso-b-global with FIPS disabled and DualStack disabled", "expect": { "endpoint": { "properties": { "authSchemes": [ { - "signingName": "iam", "name": "sigv4", - "signingRegion": "cn-north-1" + "signingName": "iam", + "signingRegion": "us-isob-east-1" } ] }, - "url": "https://iam.cn-north-1.amazonaws.com.cn" - } - }, - "params": { - "UseFIPS": false, - "UseDualStack": false, - "Region": "aws-cn-global" - } - }, - { - "documentation": "For region cn-north-1 with FIPS enabled and DualStack enabled", - "expect": { - "endpoint": { - "url": "https://iam-fips.cn-north-1.api.amazonwebservices.com.cn" - } - }, - "params": { - "UseFIPS": true, - "UseDualStack": true, - "Region": "cn-north-1" - } - }, - { - "documentation": "For region cn-north-1 with FIPS enabled and DualStack disabled", - "expect": { - "endpoint": { - "url": "https://iam-fips.cn-north-1.amazonaws.com.cn" + "url": "https://iam.us-isob-east-1.sc2s.sgov.gov" } }, "params": { - "UseFIPS": true, "UseDualStack": false, - "Region": "cn-north-1" - } - }, - { - "documentation": "For region cn-north-1 with FIPS disabled and DualStack enabled", - "expect": { - "endpoint": { - "url": "https://iam.cn-north-1.api.amazonwebservices.com.cn" - } - }, - "params": { "UseFIPS": false, - "UseDualStack": true, - "Region": "cn-north-1" + "Region": "aws-iso-b-global" } }, { - "documentation": "For region cn-north-1 with FIPS disabled and DualStack disabled", + "documentation": "For region us-isob-east-1 with FIPS enabled and DualStack disabled", "expect": { "endpoint": { - "properties": { - "authSchemes": [ - { - "signingName": "iam", - "name": "sigv4", - "signingRegion": "cn-north-1" - } - ] - }, - "url": "https://iam.cn-north-1.amazonaws.com.cn" + "url": "https://iam-fips.us-isob-east-1.sc2s.sgov.gov" } }, "params": { - "UseFIPS": false, "UseDualStack": false, - "Region": "cn-north-1" + "UseFIPS": true, + "Region": "us-isob-east-1" } }, { - "documentation": "For region aws-iso-global with FIPS disabled and DualStack disabled", + "documentation": "For region us-isob-east-1 with FIPS disabled and DualStack disabled", "expect": { "endpoint": { "properties": { "authSchemes": [ { - "signingName": "iam", "name": "sigv4", - "signingRegion": "us-iso-east-1" + "signingName": "iam", + "signingRegion": "us-isob-east-1" } ] }, - "url": "https://iam.us-iso-east-1.c2s.ic.gov" + "url": "https://iam.us-isob-east-1.sc2s.sgov.gov" } }, "params": { - "UseFIPS": false, "UseDualStack": false, - "Region": "aws-iso-global" + "UseFIPS": false, + "Region": "us-isob-east-1" } }, { - "documentation": "For region us-iso-east-1 with FIPS enabled and DualStack disabled", + "documentation": "For custom endpoint with region set and fips disabled and dualstack disabled", "expect": { "endpoint": { - "url": "https://iam-fips.us-iso-east-1.c2s.ic.gov" + "url": "https://example.com" } }, "params": { - "UseFIPS": true, "UseDualStack": false, - "Region": "us-iso-east-1" - } - }, - { - "documentation": "For region us-iso-east-1 with FIPS disabled and DualStack disabled", - "expect": { - "endpoint": { - "properties": { - "authSchemes": [ - { - "signingName": "iam", - "name": "sigv4", - "signingRegion": "us-iso-east-1" - } - ] - }, - "url": "https://iam.us-iso-east-1.c2s.ic.gov" - } - }, - "params": { "UseFIPS": false, - "UseDualStack": false, - "Region": "us-iso-east-1" + "Region": "us-east-1", + "Endpoint": "https://example.com" } }, { - "documentation": "For custom endpoint with fips disabled and dualstack disabled", + "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, - "Region": "us-east-1", + "UseFIPS": false, "Endpoint": "https://example.com" } }, @@ -2340,8 +2379,8 @@ "error": "Invalid Configuration: FIPS and custom endpoint are not supported" }, "params": { - "UseFIPS": true, "UseDualStack": false, + "UseFIPS": true, "Region": "us-east-1", "Endpoint": "https://example.com" } @@ -2352,8 +2391,8 @@ "error": "Invalid Configuration: Dualstack and custom endpoint are not supported" }, "params": { - "UseFIPS": false, "UseDualStack": true, + "UseFIPS": false, "Region": "us-east-1", "Endpoint": "https://example.com" } @@ -2406,7 +2445,7 @@ "EntityPath": { "target": "com.amazonaws.iam#organizationsEntityPathType", "traits": { - "smithy.api#documentation": "

The path of the Organizations entity (root, organizational unit, or account) from which an\n authenticated principal last attempted to access the service. Amazon Web Services does not report\n unauthenticated requests.

\n

This field is null if no principals (IAM users, IAM roles, or root users) in the\n reported Organizations entity attempted to access the service within the tracking period.

" + "smithy.api#documentation": "

The path of the Organizations entity (root, organizational unit, or account) from which an\n authenticated principal last attempted to access the service. Amazon Web Services does not report\n unauthenticated requests.

\n

This field is null if no principals (IAM users, IAM roles, or root user) in the\n reported Organizations entity attempted to access the service within the tracking period.

" } }, "LastAuthenticatedTime": { @@ -2418,7 +2457,7 @@ "TotalAuthenticatedEntities": { "target": "com.amazonaws.iam#integerType", "traits": { - "smithy.api#documentation": "

The number of accounts with authenticated principals (root users, IAM users, and IAM\n roles) that attempted to access the service in the tracking period.

" + "smithy.api#documentation": "

The number of accounts with authenticated principals (root user, IAM users, and IAM\n roles) that attempted to access the service in the tracking period.

" } } }, @@ -2573,7 +2612,7 @@ } ], "traits": { - "smithy.api#documentation": "

Adds a new client ID (also known as audience) to the list of client IDs already\n registered for the specified IAM OpenID Connect (OIDC) provider resource.

\n

This operation is idempotent; it does not fail or return an error if you add an\n existing client ID to the provider.

" + "smithy.api#documentation": "

Adds a new client ID (also known as audience) to the list of client IDs already\n registered for the specified IAM OpenID Connect (OIDC) provider resource.

\n

This operation is idempotent; it does not fail or return an error if you add an\n existing client ID to the provider.

" } }, "com.amazonaws.iam#AddClientIDToOpenIDConnectProviderRequest": { @@ -2593,6 +2632,9 @@ "smithy.api#required": {} } } + }, + "traits": { + "smithy.api#input": {} } }, "com.amazonaws.iam#AddRoleToInstanceProfile": { @@ -2621,7 +2663,7 @@ } ], "traits": { - "smithy.api#documentation": "

Adds the specified IAM role to the specified instance profile. An instance profile\n can contain only one role, and this quota cannot be increased. You can remove the\n existing role and then add a different role to an instance profile. You must then wait\n for the change to appear across all of Amazon Web Services because of eventual\n consistency. To force the change, you must disassociate the instance profile and then associate the\n instance profile, or you can stop your instance and then restart it.

\n \n

The caller of this operation must be granted the PassRole permission\n on the IAM role by a permissions policy.

\n
\n

For more information about roles, see Working with roles. For more\n information about instance profiles, see About instance\n profiles.

" + "smithy.api#documentation": "

Adds the specified IAM role to the specified instance profile. An instance profile\n can contain only one role, and this quota cannot be increased. You can remove the\n existing role and then add a different role to an instance profile. You must then wait\n for the change to appear across all of Amazon Web Services because of eventual\n consistency. To force the change, you must disassociate the instance profile and then associate the\n instance profile, or you can stop your instance and then restart it.

\n \n

The caller of this operation must be granted the PassRole permission\n on the IAM role by a permissions policy.

\n
\n

For more information about roles, see Working with roles. For more\n information about instance profiles, see About instance\n profiles.

" } }, "com.amazonaws.iam#AddRoleToInstanceProfileRequest": { @@ -2630,17 +2672,20 @@ "InstanceProfileName": { "target": "com.amazonaws.iam#instanceProfileNameType", "traits": { - "smithy.api#documentation": "

The name of the instance profile to update.

\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 instance profile to update.

\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": {} } }, "RoleName": { "target": "com.amazonaws.iam#roleNameType", "traits": { - "smithy.api#documentation": "

The name of the role to add.

\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 role to add.

\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": {} } } + }, + "traits": { + "smithy.api#input": {} } }, "com.amazonaws.iam#AddUserToGroup": { @@ -2672,17 +2717,20 @@ "GroupName": { "target": "com.amazonaws.iam#groupNameType", "traits": { - "smithy.api#documentation": "

The name of the group to update.

\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 group to update.

\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": {} } }, "UserName": { "target": "com.amazonaws.iam#existingUserNameType", "traits": { - "smithy.api#documentation": "

The name of the user to add.

\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 user to add.

\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": {} } } + }, + "traits": { + "smithy.api#input": {} } }, "com.amazonaws.iam#ArnListType": { @@ -2717,7 +2765,7 @@ } ], "traits": { - "smithy.api#documentation": "

Attaches the specified managed policy to the specified IAM group.

\n

You use this operation to attach a managed policy to a group. To embed an inline\n policy in a group, use PutGroupPolicy.

\n

As a best practice, you can validate your IAM policies. \n To learn more, see Validating IAM policies \n in the IAM User Guide.

\n

For more information about policies, see Managed policies and inline\n policies in the IAM User Guide.

" + "smithy.api#documentation": "

Attaches the specified managed policy to the specified IAM group.

\n

You use this operation to attach a managed policy to a group. To embed an inline\n policy in a group, use PutGroupPolicy.

\n

As a best practice, you can validate your IAM policies. \n To learn more, see Validating IAM policies \n in the IAM User Guide.

\n

For more information about policies, see Managed policies and inline\n policies in the IAM User Guide.

" } }, "com.amazonaws.iam#AttachGroupPolicyRequest": { @@ -2726,17 +2774,20 @@ "GroupName": { "target": "com.amazonaws.iam#groupNameType", "traits": { - "smithy.api#documentation": "

The name (friendly name, not ARN) of the group to attach the policy to.

\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 (friendly name, not ARN) of the group to attach the policy to.

\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": {} } }, "PolicyArn": { "target": "com.amazonaws.iam#arnType", "traits": { - "smithy.api#documentation": "

The Amazon Resource Name (ARN) of the IAM policy you want to attach.

\n

For more information about ARNs, see Amazon Resource Names (ARNs) in the Amazon Web Services General Reference.

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

The Amazon Resource Name (ARN) of the IAM policy you want to attach.

\n

For more information about ARNs, see Amazon Resource Names (ARNs) in the Amazon Web Services General Reference.

", "smithy.api#required": {} } } + }, + "traits": { + "smithy.api#input": {} } }, "com.amazonaws.iam#AttachRolePolicy": { @@ -2768,7 +2819,7 @@ } ], "traits": { - "smithy.api#documentation": "

Attaches the specified managed policy to the specified IAM role. When you attach a\n managed policy to a role, the managed policy becomes part of the role's permission\n (access) policy.

\n \n

You cannot use a managed policy as the role's trust policy. The role's trust\n policy is created at the same time as the role, using CreateRole.\n You can update a role's trust policy using UpdateAssumeRolePolicy.

\n
\n

Use this operation to attach a managed policy to a role. To embed\n an inline policy in a role, use PutRolePolicy. For more information\n about policies, see Managed policies and inline\n policies in the IAM User Guide.

\n

As a best practice, you can validate your IAM policies. \n To learn more, see Validating IAM policies \n in the IAM User Guide.

" + "smithy.api#documentation": "

Attaches the specified managed policy to the specified IAM role. When you attach a\n managed policy to a role, the managed policy becomes part of the role's permission\n (access) policy.

\n \n

You cannot use a managed policy as the role's trust policy. The role's trust\n policy is created at the same time as the role, using CreateRole.\n You can update a role's trust policy using UpdateAssumeRolePolicy.

\n
\n

Use this operation to attach a managed policy to a role. To embed\n an inline policy in a role, use PutRolePolicy. For more information\n about policies, see Managed policies and inline\n policies in the IAM User Guide.

\n

As a best practice, you can validate your IAM policies. \n To learn more, see Validating IAM policies \n in the IAM User Guide.

" } }, "com.amazonaws.iam#AttachRolePolicyRequest": { @@ -2777,17 +2828,20 @@ "RoleName": { "target": "com.amazonaws.iam#roleNameType", "traits": { - "smithy.api#documentation": "

The name (friendly name, not ARN) of the role to attach the policy to.

\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 (friendly name, not ARN) of the role to attach the policy to.

\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": {} } }, "PolicyArn": { "target": "com.amazonaws.iam#arnType", "traits": { - "smithy.api#documentation": "

The Amazon Resource Name (ARN) of the IAM policy you want to attach.

\n

For more information about ARNs, see Amazon Resource Names (ARNs) in the Amazon Web Services General Reference.

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

The Amazon Resource Name (ARN) of the IAM policy you want to attach.

\n

For more information about ARNs, see Amazon Resource Names (ARNs) in the Amazon Web Services General Reference.

", "smithy.api#required": {} } } + }, + "traits": { + "smithy.api#input": {} } }, "com.amazonaws.iam#AttachUserPolicy": { @@ -2816,7 +2870,7 @@ } ], "traits": { - "smithy.api#documentation": "

Attaches the specified managed policy to the specified user.

\n

You use this operation to attach a managed policy to a user. To\n embed an inline policy in a user, use PutUserPolicy.

\n

As a best practice, you can validate your IAM policies. \n To learn more, see Validating IAM policies \n in the IAM User Guide.

\n

For more information about policies, see Managed policies and inline\n policies in the IAM User Guide.

" + "smithy.api#documentation": "

Attaches the specified managed policy to the specified user.

\n

You use this operation to attach a managed policy to a user. To\n embed an inline policy in a user, use PutUserPolicy.

\n

As a best practice, you can validate your IAM policies. \n To learn more, see Validating IAM policies \n in the IAM User Guide.

\n

For more information about policies, see Managed policies and inline\n policies in the IAM User Guide.

" } }, "com.amazonaws.iam#AttachUserPolicyRequest": { @@ -2825,17 +2879,20 @@ "UserName": { "target": "com.amazonaws.iam#userNameType", "traits": { - "smithy.api#documentation": "

The name (friendly name, not ARN) of the IAM user to attach the policy to.

\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 (friendly name, not ARN) of the IAM user to attach the policy to.

\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": {} } }, "PolicyArn": { "target": "com.amazonaws.iam#arnType", "traits": { - "smithy.api#documentation": "

The Amazon Resource Name (ARN) of the IAM policy you want to attach.

\n

For more information about ARNs, see Amazon Resource Names (ARNs) in the Amazon Web Services General Reference.

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

The Amazon Resource Name (ARN) of the IAM policy you want to attach.

\n

For more information about ARNs, see Amazon Resource Names (ARNs) in the Amazon Web Services General Reference.

", "smithy.api#required": {} } } + }, + "traits": { + "smithy.api#input": {} } }, "com.amazonaws.iam#AttachedPermissionsBoundary": { @@ -2910,7 +2967,7 @@ } ], "traits": { - "smithy.api#documentation": "

Changes the password of the IAM user who is calling this operation. This operation\n can be performed using the CLI, the Amazon Web Services API, or the My\n Security Credentials page in the Amazon Web Services Management Console. The Amazon Web Services account root user\n password is not affected by this operation.

\n

Use UpdateLoginProfile to use the CLI, the Amazon Web Services API, or the\n Users page in the IAM console to change the\n password for any IAM user. For more information about modifying passwords, see Managing\n passwords in the IAM User Guide.

" + "smithy.api#documentation": "

Changes the password of the IAM user who is calling this operation. This operation\n can be performed using the CLI, the Amazon Web Services API, or the My\n Security Credentials page in the Amazon Web Services Management Console. The Amazon Web Services account root user password is\n not affected by this operation.

\n

Use UpdateLoginProfile to use the CLI, the Amazon Web Services API, or the\n Users page in the IAM console to change the\n password for any IAM user. For more information about modifying passwords, see Managing\n passwords in the IAM User Guide.

" } }, "com.amazonaws.iam#ChangePasswordRequest": { @@ -2926,10 +2983,13 @@ "NewPassword": { "target": "com.amazonaws.iam#passwordType", "traits": { - "smithy.api#documentation": "

The new password. The new password must conform to the Amazon Web Services account's password\n policy, if one exists.

\n

The regex pattern \n that is used to validate this parameter is a string of characters. That string can include almost any printable \n ASCII character from the space (\\u0020) through the end of the ASCII character range (\\u00FF). \n You can also include the tab (\\u0009), line feed (\\u000A), and carriage return (\\u000D) \n characters. Any of these characters are valid in a password. However, many tools, such \n as the Amazon Web Services Management Console, might restrict the ability to type certain characters because they have \n special meaning within that tool.

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

The new password. The new password must conform to the Amazon Web Services account's password\n policy, if one exists.

\n

The regex pattern \n that is used to validate this parameter is a string of characters. That string can include almost any printable \n ASCII character from the space (\\u0020) through the end of the ASCII character range (\\u00FF). \n You can also include the tab (\\u0009), line feed (\\u000A), and carriage return (\\u000D) \n characters. Any of these characters are valid in a password. However, many tools, such \n as the Amazon Web Services Management Console, might restrict the ability to type certain characters because they have \n special meaning within that tool.

", "smithy.api#required": {} } } + }, + "traits": { + "smithy.api#input": {} } }, "com.amazonaws.iam#ColumnNumber": { @@ -3111,7 +3171,7 @@ } ], "traits": { - "smithy.api#documentation": "

Creates a new Amazon Web Services secret access key and corresponding Amazon Web Services access key ID for the\n specified user. The default status for new keys is Active.

\n

If you do not specify a user name, IAM determines the user name implicitly based on\n the Amazon Web Services access key ID signing the request. This operation works for access keys under\n the Amazon Web Services account. Consequently, you can use this operation to manage Amazon Web Services account root\n user credentials. This is true even if the Amazon Web Services account has no associated users.

\n

For information about quotas on the number of keys you can create, see IAM and STS\n quotas in the IAM User Guide.

\n \n

To ensure the security of your Amazon Web Services account, the secret access key is accessible\n only during key and user creation. You must save the key (for example, in a text\n file) if you want to be able to access it again. If a secret key is lost, you can\n delete the access keys for the associated user and then create new keys.

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

Creates a new Amazon Web Services secret access key and corresponding Amazon Web Services access key ID for the\n specified user. The default status for new keys is Active.

\n

If you do not specify a user name, IAM determines the user name implicitly based on\n the Amazon Web Services access key ID signing the request. This operation works for access keys under\n the Amazon Web Services account. Consequently, you can use this operation to manage Amazon Web Services account root\n user credentials. This is true even if the Amazon Web Services account has no associated users.

\n

For information about quotas on the number of keys you can create, see IAM and STS\n quotas in the IAM User Guide.

\n \n

To ensure the security of your Amazon Web Services account, the secret access key is accessible\n only during key and user creation. You must save the key (for example, in a text\n file) if you want to be able to access it again. If a secret key is lost, you can\n delete the access keys for the associated user and then create new keys.

\n
" } }, "com.amazonaws.iam#CreateAccessKeyRequest": { @@ -3120,9 +3180,12 @@ "UserName": { "target": "com.amazonaws.iam#existingUserNameType", "traits": { - "smithy.api#documentation": "

The name of the IAM user that the new key will belong to.

\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 IAM user that the new key will belong to.

\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: _+=,.@-

" } } + }, + "traits": { + "smithy.api#input": {} } }, "com.amazonaws.iam#CreateAccessKeyResponse": { @@ -3137,7 +3200,8 @@ } }, "traits": { - "smithy.api#documentation": "

Contains the response to a successful CreateAccessKey request.\n

" + "smithy.api#documentation": "

Contains the response to a successful CreateAccessKey request.\n

", + "smithy.api#output": {} } }, "com.amazonaws.iam#CreateAccountAlias": { @@ -3169,10 +3233,13 @@ "AccountAlias": { "target": "com.amazonaws.iam#accountAliasType", "traits": { - "smithy.api#documentation": "

The account alias to create.

\n

This parameter allows (through its regex pattern) a string of characters consisting of \n lowercase letters, digits, and dashes. You cannot start or finish with a dash, nor can you have \n two dashes in a row.

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

The account alias to create.

\n

This parameter allows (through its regex pattern) a string of characters consisting of \n lowercase letters, digits, and dashes. You cannot start or finish with a dash, nor can you have \n two dashes in a row.

", "smithy.api#required": {} } } + }, + "traits": { + "smithy.api#input": {} } }, "com.amazonaws.iam#CreateGroup": { @@ -3198,7 +3265,7 @@ } ], "traits": { - "smithy.api#documentation": "

Creates a new group.

\n

For information about the number of groups you can create, see IAM and STS\n quotas in the IAM User Guide.

" + "smithy.api#documentation": "

Creates a new group.

\n

For information about the number of groups you can create, see IAM and STS\n quotas in the IAM User Guide.

" } }, "com.amazonaws.iam#CreateGroupRequest": { @@ -3207,16 +3274,19 @@ "Path": { "target": "com.amazonaws.iam#pathType", "traits": { - "smithy.api#documentation": "

The path to the group. For more information about paths, see IAM\n identifiers in the IAM User Guide.

\n

This parameter is optional. If it is not included, it defaults to a slash (/).

\n

This parameter allows (through its regex pattern) a string of characters consisting \n of either a forward slash (/) by itself or a string that must begin and end with forward slashes.\n In addition, it can contain any ASCII character from the ! (\\u0021) through the DEL character (\\u007F), including \n most punctuation characters, digits, and upper and lowercased letters.

" + "smithy.api#documentation": "

The path to the group. For more information about paths, see IAM\n identifiers in the IAM User Guide.

\n

This parameter is optional. If it is not included, it defaults to a slash (/).

\n

This parameter allows (through its regex pattern) a string of characters consisting \n of either a forward slash (/) by itself or a string that must begin and end with forward slashes.\n In addition, it can contain any ASCII character from the ! (\\u0021) through the DEL character (\\u007F), including \n most punctuation characters, digits, and upper and lowercased letters.

" } }, "GroupName": { "target": "com.amazonaws.iam#groupNameType", "traits": { - "smithy.api#documentation": "

The name of the group to create. Do not include the path in this value.

\n

IAM user, group, role, and policy names must be unique within the account. Names are\n not distinguished by case. For example, you cannot create resources named both\n \"MyResource\" and \"myresource\".

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

The name of the group to create. Do not include the path in this value.

\n

IAM user, group, role, and policy names must be unique within the account. Names are\n not distinguished by case. For example, you cannot create resources named both\n \"MyResource\" and \"myresource\".

", "smithy.api#required": {} } } + }, + "traits": { + "smithy.api#input": {} } }, "com.amazonaws.iam#CreateGroupResponse": { @@ -3231,7 +3301,8 @@ } }, "traits": { - "smithy.api#documentation": "

Contains the response to a successful CreateGroup request.

" + "smithy.api#documentation": "

Contains the response to a successful CreateGroup request.

", + "smithy.api#output": {} } }, "com.amazonaws.iam#CreateInstanceProfile": { @@ -3260,7 +3331,7 @@ } ], "traits": { - "smithy.api#documentation": "

Creates a new instance profile. For information about instance profiles, see Using\n roles for applications on Amazon EC2 in the\n IAM User Guide, and Instance profiles in the Amazon EC2 User Guide.

\n

For information about the number of instance profiles you can create, see IAM object\n quotas in the IAM User Guide.

" + "smithy.api#documentation": "

Creates a new instance profile. For information about instance profiles, see Using\n roles for applications on Amazon EC2 in the\n IAM User Guide, and Instance profiles in the Amazon EC2 User Guide.

\n

For information about the number of instance profiles you can create, see IAM object\n quotas in the IAM User Guide.

" } }, "com.amazonaws.iam#CreateInstanceProfileRequest": { @@ -3269,14 +3340,14 @@ "InstanceProfileName": { "target": "com.amazonaws.iam#instanceProfileNameType", "traits": { - "smithy.api#documentation": "

The name of the instance profile to create.

\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 instance profile to create.

\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": {} } }, "Path": { "target": "com.amazonaws.iam#pathType", "traits": { - "smithy.api#documentation": "

The path to the instance profile. For more information about paths, see IAM\n Identifiers in the IAM User Guide.

\n

This parameter is optional. If it is not included, it defaults to a slash (/).

\n

This parameter allows (through its regex pattern) a string of characters consisting \n of either a forward slash (/) by itself or a string that must begin and end with forward slashes.\n In addition, it can contain any ASCII character from the ! (\\u0021) through the DEL character (\\u007F), including \n most punctuation characters, digits, and upper and lowercased letters.

" + "smithy.api#documentation": "

The path to the instance profile. For more information about paths, see IAM\n Identifiers in the IAM User Guide.

\n

This parameter is optional. If it is not included, it defaults to a slash (/).

\n

This parameter allows (through its regex pattern) a string of characters consisting \n of either a forward slash (/) by itself or a string that must begin and end with forward slashes.\n In addition, it can contain any ASCII character from the ! (\\u0021) through the DEL character (\\u007F), including \n most punctuation characters, digits, and upper and lowercased letters.

" } }, "Tags": { @@ -3285,6 +3356,9 @@ "smithy.api#documentation": "

A list of tags that you want to attach to the newly created IAM instance profile.\n Each tag consists of a key name and an associated value. For more information about tagging, see Tagging IAM resources in the\n IAM User Guide.

\n \n

If any one of the tags is invalid or if you exceed the allowed maximum number of tags, then the entire request \n fails and the resource is not created.

\n
" } } + }, + "traits": { + "smithy.api#input": {} } }, "com.amazonaws.iam#CreateInstanceProfileResponse": { @@ -3299,7 +3373,8 @@ } }, "traits": { - "smithy.api#documentation": "

Contains the response to a successful CreateInstanceProfile request.\n

" + "smithy.api#documentation": "

Contains the response to a successful CreateInstanceProfile request.\n

", + "smithy.api#output": {} } }, "com.amazonaws.iam#CreateLoginProfile": { @@ -3328,7 +3403,7 @@ } ], "traits": { - "smithy.api#documentation": "

Creates a password for the specified IAM user. A password allows an IAM user to\n access Amazon Web Services services through the Amazon Web Services Management Console.

\n

You can use the CLI, the Amazon Web Services API, or the Users\n page in the IAM console to create a password for any IAM user. Use ChangePassword to update your own existing password in the My Security Credentials page in the Amazon Web Services Management Console.

\n

For more information about managing passwords, see Managing passwords in the\n IAM User Guide.

" + "smithy.api#documentation": "

Creates a password for the specified IAM user. A password allows an IAM user to\n access Amazon Web Services services through the Amazon Web Services Management Console.

\n

You can use the CLI, the Amazon Web Services API, or the Users\n page in the IAM console to create a password for any IAM user. Use ChangePassword to update your own existing password in the My Security Credentials page in the Amazon Web Services Management Console.

\n

For more information about managing passwords, see Managing passwords in the\n IAM User Guide.

" } }, "com.amazonaws.iam#CreateLoginProfileRequest": { @@ -3337,14 +3412,14 @@ "UserName": { "target": "com.amazonaws.iam#userNameType", "traits": { - "smithy.api#documentation": "

The name of the IAM user to create a password for. The user must already\n exist.

\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 IAM user to create a password for. The user must already\n exist.

\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": {} } }, "Password": { "target": "com.amazonaws.iam#passwordType", "traits": { - "smithy.api#documentation": "

The new password for the user.

\n

The regex pattern \n that is used to validate this parameter is a string of characters. That string can include almost any printable \n ASCII character from the space (\\u0020) through the end of the ASCII character range (\\u00FF). \n You can also include the tab (\\u0009), line feed (\\u000A), and carriage return (\\u000D) \n characters. Any of these characters are valid in a password. However, many tools, such \n as the Amazon Web Services Management Console, might restrict the ability to type certain characters because they have \n special meaning within that tool.

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

The new password for the user.

\n

The regex pattern \n that is used to validate this parameter is a string of characters. That string can include almost any printable \n ASCII character from the space (\\u0020) through the end of the ASCII character range (\\u00FF). \n You can also include the tab (\\u0009), line feed (\\u000A), and carriage return (\\u000D) \n characters. Any of these characters are valid in a password. However, many tools, such \n as the Amazon Web Services Management Console, might restrict the ability to type certain characters because they have \n special meaning within that tool.

", "smithy.api#required": {} } }, @@ -3355,6 +3430,9 @@ "smithy.api#documentation": "

Specifies whether the user is required to set a new password on next sign-in.

" } } + }, + "traits": { + "smithy.api#input": {} } }, "com.amazonaws.iam#CreateLoginProfileResponse": { @@ -3369,7 +3447,8 @@ } }, "traits": { - "smithy.api#documentation": "

Contains the response to a successful CreateLoginProfile request.\n

" + "smithy.api#documentation": "

Contains the response to a successful CreateLoginProfile request.\n

", + "smithy.api#output": {} } }, "com.amazonaws.iam#CreateOpenIDConnectProvider": { @@ -3398,7 +3477,7 @@ } ], "traits": { - "smithy.api#documentation": "

Creates an IAM entity to describe an identity provider (IdP) that supports OpenID Connect (OIDC).

\n

The OIDC provider that you create with this operation can be used as a principal in a\n role's trust policy. Such a policy establishes a trust relationship between Amazon Web Services and\n the OIDC provider.

\n

If you are using an OIDC identity provider from Google, Facebook, or Amazon Cognito, you don't\n need to create a separate IAM identity provider. These OIDC identity providers are\n already built-in to Amazon Web Services and are available for your use. Instead, you can move directly\n to creating new roles using your identity provider. To learn more, see Creating\n a role for web identity or OpenID connect federation in the IAM\n User Guide.

\n

When you create the IAM OIDC provider, you specify the following:

\n
    \n
  • \n

    The URL of the OIDC identity provider (IdP) to trust

    \n
  • \n
  • \n

    A list of client IDs (also known as audiences) that identify the application\n or applications allowed to authenticate using the OIDC provider

    \n
  • \n
  • \n

    A list of thumbprints of one or more server certificates that the IdP\n uses

    \n
  • \n
\n

You get all of this information from the OIDC IdP you want to use to access\n Amazon Web Services.

\n \n

Amazon Web Services secures communication with some OIDC identity providers (IdPs) through our\n library of trusted certificate authorities (CAs) instead of using a certificate\n thumbprint to verify your IdP server certificate. These OIDC IdPs include Google, and\n those that use an Amazon S3 bucket to host a JSON Web Key Set (JWKS) endpoint. In these\n cases, your legacy thumbprint remains in your configuration, but is no longer used for validation.

\n
\n \n

The trust for the OIDC provider is derived from the IAM provider that this\n operation creates. Therefore, it is best to limit access to the CreateOpenIDConnectProvider operation to highly privileged\n users.

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

Creates an IAM entity to describe an identity provider (IdP) that supports OpenID Connect (OIDC).

\n

The OIDC provider that you create with this operation can be used as a principal in a\n role's trust policy. Such a policy establishes a trust relationship between Amazon Web Services and\n the OIDC provider.

\n

If you are using an OIDC identity provider from Google, Facebook, or Amazon Cognito, you don't\n need to create a separate IAM identity provider. These OIDC identity providers are\n already built-in to Amazon Web Services and are available for your use. Instead, you can move directly\n to creating new roles using your identity provider. To learn more, see Creating\n a role for web identity or OpenID connect federation in the IAM\n User Guide.

\n

When you create the IAM OIDC provider, you specify the following:

\n
    \n
  • \n

    The URL of the OIDC identity provider (IdP) to trust

    \n
  • \n
  • \n

    A list of client IDs (also known as audiences) that identify the application\n or applications allowed to authenticate using the OIDC provider

    \n
  • \n
  • \n

    A list of tags that are attached to the specified IAM OIDC provider

    \n
  • \n
  • \n

    A list of thumbprints of one or more server certificates that the IdP\n uses

    \n
  • \n
\n

You get all of this information from the OIDC IdP you want to use to access\n Amazon Web Services.

\n \n

Amazon Web Services secures communication with some OIDC identity providers (IdPs) through our\n library of trusted certificate authorities (CAs) instead of using a certificate\n thumbprint to verify your IdP server certificate. These OIDC IdPs include Google, Auth0,\n and those that use an Amazon S3 bucket to host a JSON Web Key Set (JWKS) endpoint. In these\n cases, your legacy thumbprint remains in your configuration, but is no longer used for\n validation.

\n
\n \n

The trust for the OIDC provider is derived from the IAM provider that this\n operation creates. Therefore, it is best to limit access to the CreateOpenIDConnectProvider operation to highly privileged\n users.

\n
" } }, "com.amazonaws.iam#CreateOpenIDConnectProviderRequest": { @@ -3407,20 +3486,20 @@ "Url": { "target": "com.amazonaws.iam#OpenIDConnectProviderUrlType", "traits": { - "smithy.api#documentation": "

The URL of the identity provider. The URL must begin with https:// and\n should correspond to the iss claim in the provider's OpenID Connect ID\n tokens. Per the OIDC standard, path components are allowed but query parameters are not.\n Typically the URL consists of only a hostname, like\n https://server.example.org or https://example.com. The URL\n should not contain a port number.

\n

You cannot register the same provider multiple times in a single Amazon Web Services account. If you\n try to submit a URL that has already been used for an OpenID Connect provider in the\n Amazon Web Services account, you will get an error.

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

The URL of the identity provider. The URL must begin with https:// and\n should correspond to the iss claim in the provider's OpenID Connect ID\n tokens. Per the OIDC standard, path components are allowed but query parameters are not.\n Typically the URL consists of only a hostname, like\n https://server.example.org or https://example.com. The URL\n should not contain a port number.

\n

You cannot register the same provider multiple times in a single Amazon Web Services account. If you\n try to submit a URL that has already been used for an OpenID Connect provider in the\n Amazon Web Services account, you will get an error.

", "smithy.api#required": {} } }, "ClientIDList": { "target": "com.amazonaws.iam#clientIDListType", "traits": { - "smithy.api#documentation": "

Provides a list of client IDs, also known as audiences. When a mobile or web app\n registers with an OpenID Connect provider, they establish a value that identifies the\n application. This is the value that's sent as the client_id parameter on\n OAuth requests.

\n

You can register multiple client IDs with the same provider. For example, you might\n have multiple applications that use the same OIDC provider. You cannot register more\n than 100 client IDs with a single IAM OIDC provider.

\n

There is no defined format for a client ID. The\n CreateOpenIDConnectProviderRequest operation accepts client IDs up to\n 255 characters long.

" + "smithy.api#documentation": "

Provides a list of client IDs, also known as audiences. When a mobile or web app\n registers with an OpenID Connect provider, they establish a value that identifies the\n application. This is the value that's sent as the client_id parameter on\n OAuth requests.

\n

You can register multiple client IDs with the same provider. For example, you might\n have multiple applications that use the same OIDC provider. You cannot register more\n than 100 client IDs with a single IAM OIDC provider.

\n

There is no defined format for a client ID. The\n CreateOpenIDConnectProviderRequest operation accepts client IDs up to\n 255 characters long.

" } }, "ThumbprintList": { "target": "com.amazonaws.iam#thumbprintListType", "traits": { - "smithy.api#documentation": "

A list of server certificate thumbprints for the OpenID Connect (OIDC) identity\n provider's server certificates. Typically this list includes only one entry. However,\n IAM lets you have up to five thumbprints for an OIDC provider. This lets you maintain\n multiple thumbprints if the identity provider is rotating certificates.

\n

The server certificate thumbprint is the hex-encoded SHA-1 hash value of the X.509\n certificate used by the domain where the OpenID Connect provider makes its keys\n available. It is always a 40-character string.

\n

You must provide at least one thumbprint when creating an IAM OIDC provider. For\n example, assume that the OIDC provider is server.example.com and the\n provider stores its keys at https://keys.server.example.com/openid-connect. In that\n case, the thumbprint string would be the hex-encoded SHA-1 hash value of the certificate\n used by https://keys.server.example.com.\n

\n

For more information about obtaining the OIDC provider thumbprint, see Obtaining the\n thumbprint for an OpenID Connect provider in the IAM User\n Guide.

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

A list of server certificate thumbprints for the OpenID Connect (OIDC) identity\n provider's server certificates. Typically this list includes only one entry. However,\n IAM lets you have up to five thumbprints for an OIDC provider. This lets you maintain\n multiple thumbprints if the identity provider is rotating certificates.

\n

The server certificate thumbprint is the hex-encoded SHA-1 hash value of the X.509\n certificate used by the domain where the OpenID Connect provider makes its keys\n available. It is always a 40-character string.

\n

You must provide at least one thumbprint when creating an IAM OIDC provider. For\n example, assume that the OIDC provider is server.example.com and the\n provider stores its keys at https://keys.server.example.com/openid-connect. In that\n case, the thumbprint string would be the hex-encoded SHA-1 hash value of the certificate\n used by https://keys.server.example.com.\n

\n

For more information about obtaining the OIDC provider thumbprint, see Obtaining the\n thumbprint for an OpenID Connect provider in the IAM user\n Guide.

", "smithy.api#required": {} } }, @@ -3430,6 +3509,9 @@ "smithy.api#documentation": "

A list of tags that you want to attach to the new IAM OpenID Connect (OIDC) provider.\n Each tag consists of a key name and an associated value. For more information about tagging, see Tagging IAM resources in the\n IAM User Guide.

\n \n

If any one of the tags is invalid or if you exceed the allowed maximum number of tags, then the entire request \n fails and the resource is not created.

\n
" } } + }, + "traits": { + "smithy.api#input": {} } }, "com.amazonaws.iam#CreateOpenIDConnectProviderResponse": { @@ -3449,7 +3531,8 @@ } }, "traits": { - "smithy.api#documentation": "

Contains the response to a successful CreateOpenIDConnectProvider\n request.

" + "smithy.api#documentation": "

Contains the response to a successful CreateOpenIDConnectProvider\n request.

", + "smithy.api#output": {} } }, "com.amazonaws.iam#CreatePolicy": { @@ -3481,7 +3564,7 @@ } ], "traits": { - "smithy.api#documentation": "

Creates a new managed policy for your Amazon Web Services account.

\n

This operation creates a policy version with a version identifier of v1\n and sets v1 as the policy's default version. For more information about policy versions,\n see Versioning for managed policies in the\n IAM User Guide.

\n

As a best practice, you can validate your IAM policies. \n To learn more, see Validating IAM policies \n in the IAM User Guide.

\n

For more information about managed policies in general, see Managed\n policies and inline policies in the\n IAM User Guide.

" + "smithy.api#documentation": "

Creates a new managed policy for your Amazon Web Services account.

\n

This operation creates a policy version with a version identifier of v1\n and sets v1 as the policy's default version. For more information about policy versions,\n see Versioning for managed policies in the\n IAM User Guide.

\n

As a best practice, you can validate your IAM policies. \n To learn more, see Validating IAM policies \n in the IAM User Guide.

\n

For more information about managed policies in general, see Managed\n policies and inline policies in the\n IAM User Guide.

" } }, "com.amazonaws.iam#CreatePolicyRequest": { @@ -3490,27 +3573,27 @@ "PolicyName": { "target": "com.amazonaws.iam#policyNameType", "traits": { - "smithy.api#documentation": "

The friendly name of the policy.

\n

IAM user, group, role, and policy names must be unique within the account. Names are\n not distinguished by case. For example, you cannot create resources named both\n \"MyResource\" and \"myresource\".

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

The friendly name of the policy.

\n

IAM user, group, role, and policy names must be unique within the account. Names are\n not distinguished by case. For example, you cannot create resources named both\n \"MyResource\" and \"myresource\".

", "smithy.api#required": {} } }, "Path": { "target": "com.amazonaws.iam#policyPathType", "traits": { - "smithy.api#documentation": "

The path for the policy.

\n

For more information about paths, see IAM identifiers in the\n IAM User Guide.

\n

This parameter is optional. If it is not included, it defaults to a slash (/).

\n

This parameter allows (through its regex pattern) a string of characters consisting \n of either a forward slash (/) by itself or a string that must begin and end with forward slashes.\n In addition, it can contain any ASCII character from the ! (\\u0021) through the DEL character (\\u007F), including \n most punctuation characters, digits, and upper and lowercased letters.

\n \n

You cannot use an asterisk (*) in the path name.

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

The path for the policy.

\n

For more information about paths, see IAM identifiers in the\n IAM User Guide.

\n

This parameter is optional. If it is not included, it defaults to a slash (/).

\n

This parameter allows (through its regex pattern) a string of characters consisting \n of either a forward slash (/) by itself or a string that must begin and end with forward slashes.\n In addition, it can contain any ASCII character from the ! (\\u0021) through the DEL character (\\u007F), including \n most punctuation characters, digits, and upper and lowercased letters.

\n \n

You cannot use an asterisk (*) in the path name.

\n
" } }, "PolicyDocument": { "target": "com.amazonaws.iam#policyDocumentType", "traits": { - "smithy.api#documentation": "

The JSON policy document that you want to use as the content for the new\n policy.

\n

You must provide policies in JSON format in IAM. However, for CloudFormation\n templates formatted in YAML, you can provide the policy in JSON or YAML format. CloudFormation always converts a YAML policy to JSON format before submitting it to\n IAM.

\n

The maximum length of the policy document that you can pass in this operation,\n including whitespace, is listed below. To view the maximum character counts of a managed policy with no whitespaces, see IAM and STS character quotas.

\n

To learn more about JSON policy grammar, see Grammar of the IAM JSON\n policy language in the IAM User Guide.

\n

The regex pattern \n used to validate this parameter is a string of characters consisting of the following:

\n
    \n
  • \n

    Any printable ASCII \n character ranging from the space character (\\u0020) through the end of the ASCII character range

    \n
  • \n
  • \n

    The printable characters in the Basic Latin and Latin-1 Supplement character set \n (through \\u00FF)

    \n
  • \n
  • \n

    The special characters tab (\\u0009), line feed (\\u000A), and \n carriage return (\\u000D)

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

The JSON policy document that you want to use as the content for the new\n policy.

\n

You must provide policies in JSON format in IAM. However, for CloudFormation\n templates formatted in YAML, you can provide the policy in JSON or YAML format. CloudFormation always converts a YAML policy to JSON format before submitting it to\n IAM.

\n

The maximum length of the policy document that you can pass in this operation,\n including whitespace, is listed below. To view the maximum character counts of a managed policy with no whitespaces, see IAM and STS character quotas.

\n

To learn more about JSON policy grammar, see Grammar of the IAM JSON\n policy language in the IAM User Guide.

\n

The regex pattern \n used to validate this parameter is a string of characters consisting of the following:

\n
    \n
  • \n

    Any printable ASCII \n character ranging from the space character (\\u0020) through the end of the ASCII character range

    \n
  • \n
  • \n

    The printable characters in the Basic Latin and Latin-1 Supplement character set \n (through \\u00FF)

    \n
  • \n
  • \n

    The special characters tab (\\u0009), line feed (\\u000A), and \n carriage return (\\u000D)

    \n
  • \n
", "smithy.api#required": {} } }, "Description": { "target": "com.amazonaws.iam#policyDescriptionType", "traits": { - "smithy.api#documentation": "

A friendly description of the policy.

\n

Typically used to store information about the permissions defined in the policy. For\n example, \"Grants access to production DynamoDB tables.\"

\n

The policy description is immutable. After a value is assigned, it cannot be\n changed.

" + "smithy.api#documentation": "

A friendly description of the policy.

\n

Typically used to store information about the permissions defined in the policy. For\n example, \"Grants access to production DynamoDB tables.\"

\n

The policy description is immutable. After a value is assigned, it cannot be\n changed.

" } }, "Tags": { @@ -3519,6 +3602,9 @@ "smithy.api#documentation": "

A list of tags that you want to attach to the new IAM customer managed policy.\n Each tag consists of a key name and an associated value. For more information about tagging, see Tagging IAM resources in the\n IAM User Guide.

\n \n

If any one of the tags is invalid or if you exceed the allowed maximum number of tags, then the entire request \n fails and the resource is not created.

\n
" } } + }, + "traits": { + "smithy.api#input": {} } }, "com.amazonaws.iam#CreatePolicyResponse": { @@ -3532,7 +3618,8 @@ } }, "traits": { - "smithy.api#documentation": "

Contains the response to a successful CreatePolicy request.\n

" + "smithy.api#documentation": "

Contains the response to a successful CreatePolicy request.\n

", + "smithy.api#output": {} } }, "com.amazonaws.iam#CreatePolicyVersion": { @@ -3561,7 +3648,7 @@ } ], "traits": { - "smithy.api#documentation": "

Creates a new version of the specified managed policy. To update a managed policy, you\n create a new policy version. A managed policy can have up to five versions. If the\n policy has five versions, you must delete an existing version using DeletePolicyVersion before you create a new version.

\n

Optionally, you can set the new version as the policy's default version. The default\n version is the version that is in effect for the IAM users, groups, and roles to which\n the policy is attached.

\n

For more information about managed policy versions, see Versioning for managed\n policies in the IAM User Guide.

" + "smithy.api#documentation": "

Creates a new version of the specified managed policy. To update a managed policy, you\n create a new policy version. A managed policy can have up to five versions. If the\n policy has five versions, you must delete an existing version using DeletePolicyVersion before you create a new version.

\n

Optionally, you can set the new version as the policy's default version. The default\n version is the version that is in effect for the IAM users, groups, and roles to which\n the policy is attached.

\n

For more information about managed policy versions, see Versioning for managed\n policies in the IAM User Guide.

" } }, "com.amazonaws.iam#CreatePolicyVersionRequest": { @@ -3570,14 +3657,14 @@ "PolicyArn": { "target": "com.amazonaws.iam#arnType", "traits": { - "smithy.api#documentation": "

The Amazon Resource Name (ARN) of the IAM policy to which you want to add a new\n version.

\n

For more information about ARNs, see Amazon Resource Names (ARNs) in the Amazon Web Services General Reference.

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

The Amazon Resource Name (ARN) of the IAM policy to which you want to add a new\n version.

\n

For more information about ARNs, see Amazon Resource Names (ARNs) in the Amazon Web Services General Reference.

", "smithy.api#required": {} } }, "PolicyDocument": { "target": "com.amazonaws.iam#policyDocumentType", "traits": { - "smithy.api#documentation": "

The JSON policy document that you want to use as the content for this new version of\n the policy.

\n

You must provide policies in JSON format in IAM. However, for CloudFormation\n templates formatted in YAML, you can provide the policy in JSON or YAML format. CloudFormation always converts a YAML policy to JSON format before submitting it to\n IAM.

\n

The maximum length of the policy document that you can pass in this operation,\n including whitespace, is listed below. To view the maximum character counts of a managed policy with no whitespaces, see IAM and STS character quotas.

\n

The regex pattern \n used to validate this parameter is a string of characters consisting of the following:

\n
    \n
  • \n

    Any printable ASCII \n character ranging from the space character (\\u0020) through the end of the ASCII character range

    \n
  • \n
  • \n

    The printable characters in the Basic Latin and Latin-1 Supplement character set \n (through \\u00FF)

    \n
  • \n
  • \n

    The special characters tab (\\u0009), line feed (\\u000A), and \n carriage return (\\u000D)

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

The JSON policy document that you want to use as the content for this new version of\n the policy.

\n

You must provide policies in JSON format in IAM. However, for CloudFormation\n templates formatted in YAML, you can provide the policy in JSON or YAML format. CloudFormation always converts a YAML policy to JSON format before submitting it to\n IAM.

\n

The maximum length of the policy document that you can pass in this operation,\n including whitespace, is listed below. To view the maximum character counts of a managed policy with no whitespaces, see IAM and STS character quotas.

\n

The regex pattern \n used to validate this parameter is a string of characters consisting of the following:

\n
    \n
  • \n

    Any printable ASCII \n character ranging from the space character (\\u0020) through the end of the ASCII character range

    \n
  • \n
  • \n

    The printable characters in the Basic Latin and Latin-1 Supplement character set \n (through \\u00FF)

    \n
  • \n
  • \n

    The special characters tab (\\u0009), line feed (\\u000A), and \n carriage return (\\u000D)

    \n
  • \n
", "smithy.api#required": {} } }, @@ -3585,9 +3672,12 @@ "target": "com.amazonaws.iam#booleanType", "traits": { "smithy.api#default": false, - "smithy.api#documentation": "

Specifies whether to set this version as the policy's default version.

\n

When this parameter is true, the new policy version becomes the operative\n version. That is, it becomes the version that is in effect for the IAM users, groups,\n and roles that the policy is attached to.

\n

For more information about managed policy versions, see Versioning for managed\n policies in the IAM User Guide.

" + "smithy.api#documentation": "

Specifies whether to set this version as the policy's default version.

\n

When this parameter is true, the new policy version becomes the operative\n version. That is, it becomes the version that is in effect for the IAM users, groups,\n and roles that the policy is attached to.

\n

For more information about managed policy versions, see Versioning for managed\n policies in the IAM User Guide.

" } } + }, + "traits": { + "smithy.api#input": {} } }, "com.amazonaws.iam#CreatePolicyVersionResponse": { @@ -3601,7 +3691,8 @@ } }, "traits": { - "smithy.api#documentation": "

Contains the response to a successful CreatePolicyVersion request.\n

" + "smithy.api#documentation": "

Contains the response to a successful CreatePolicyVersion request.\n

", + "smithy.api#output": {} } }, "com.amazonaws.iam#CreateRole": { @@ -3642,20 +3733,20 @@ "Path": { "target": "com.amazonaws.iam#pathType", "traits": { - "smithy.api#documentation": "

The path to the role. For more information about paths, see IAM\n Identifiers in the IAM User Guide.

\n

This parameter is optional. If it is not included, it defaults to a slash (/).

\n

This parameter allows (through its regex pattern) a string of characters consisting \n of either a forward slash (/) by itself or a string that must begin and end with forward slashes.\n In addition, it can contain any ASCII character from the ! (\\u0021) through the DEL character (\\u007F), including \n most punctuation characters, digits, and upper and lowercased letters.

" + "smithy.api#documentation": "

The path to the role. For more information about paths, see IAM\n Identifiers in the IAM User Guide.

\n

This parameter is optional. If it is not included, it defaults to a slash (/).

\n

This parameter allows (through its regex pattern) a string of characters consisting \n of either a forward slash (/) by itself or a string that must begin and end with forward slashes.\n In addition, it can contain any ASCII character from the ! (\\u0021) through the DEL character (\\u007F), including \n most punctuation characters, digits, and upper and lowercased letters.

" } }, "RoleName": { "target": "com.amazonaws.iam#roleNameType", "traits": { - "smithy.api#documentation": "

The name of the role to create.

\n

IAM user, group, role, and policy names must be unique within the account. Names are\n not distinguished by case. For example, you cannot create resources named both\n \"MyResource\" and \"myresource\".

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

The name of the role to create.

\n

IAM user, group, role, and policy names must be unique within the account. Names are\n not distinguished by case. For example, you cannot create resources named both\n \"MyResource\" and \"myresource\".

\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": {} } }, "AssumeRolePolicyDocument": { "target": "com.amazonaws.iam#policyDocumentType", "traits": { - "smithy.api#documentation": "

The trust relationship policy document that grants an entity permission to assume the\n role.

\n

In IAM, you must provide a JSON policy that has been converted to a string. However,\n for CloudFormation templates formatted in YAML, you can provide the policy in JSON or YAML\n format. CloudFormation always converts a YAML policy to JSON format before submitting it to\n IAM.

\n

The regex pattern \n used to validate this parameter is a string of characters consisting of the following:

\n
    \n
  • \n

    Any printable ASCII \n character ranging from the space character (\\u0020) through the end of the ASCII character range

    \n
  • \n
  • \n

    The printable characters in the Basic Latin and Latin-1 Supplement character set \n (through \\u00FF)

    \n
  • \n
  • \n

    The special characters tab (\\u0009), line feed (\\u000A), and \n carriage return (\\u000D)

    \n
  • \n
\n

Upon success, the response includes the same trust policy in JSON format.

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

The trust relationship policy document that grants an entity permission to assume the\n role.

\n

In IAM, you must provide a JSON policy that has been converted to a string. However,\n for CloudFormation templates formatted in YAML, you can provide the policy in JSON or YAML\n format. CloudFormation always converts a YAML policy to JSON format before submitting it to\n IAM.

\n

The regex pattern \n used to validate this parameter is a string of characters consisting of the following:

\n
    \n
  • \n

    Any printable ASCII \n character ranging from the space character (\\u0020) through the end of the ASCII character range

    \n
  • \n
  • \n

    The printable characters in the Basic Latin and Latin-1 Supplement character set \n (through \\u00FF)

    \n
  • \n
  • \n

    The special characters tab (\\u0009), line feed (\\u000A), and \n carriage return (\\u000D)

    \n
  • \n
\n

Upon success, the response includes the same trust policy in JSON format.

", "smithy.api#required": {} } }, @@ -3668,13 +3759,13 @@ "MaxSessionDuration": { "target": "com.amazonaws.iam#roleMaxSessionDurationType", "traits": { - "smithy.api#documentation": "

The maximum session duration (in seconds) that you want to set for the specified role.\n If you do not specify a value for this setting, the default value of one hour is\n applied. This setting can have a value from 1 hour to 12 hours.

\n

Anyone who assumes the role from the CLI or API can use the DurationSeconds\n API parameter or the duration-seconds CLI parameter to request a longer\n session. The MaxSessionDuration setting determines the maximum duration\n that can be requested using the DurationSeconds parameter. If users don't\n specify a value for the DurationSeconds parameter, their security\n credentials are valid for one hour by default. This applies when you use the\n AssumeRole* API operations or the assume-role* CLI\n operations but does not apply when you use those operations to create a console URL. For\n more information, see Using IAM roles in the IAM User Guide.

" + "smithy.api#documentation": "

The maximum session duration (in seconds) that you want to set for the specified role.\n If you do not specify a value for this setting, the default value of one hour is\n applied. This setting can have a value from 1 hour to 12 hours.

\n

Anyone who assumes the role from the CLI or API can use the\n DurationSeconds API parameter or the duration-seconds\n CLI parameter to request a longer session. The MaxSessionDuration setting\n determines the maximum duration that can be requested using the\n DurationSeconds parameter. If users don't specify a value for the\n DurationSeconds parameter, their security credentials are valid for one\n hour by default. This applies when you use the AssumeRole* API operations\n or the assume-role* CLI operations but does not apply when you use those\n operations to create a console URL. For more information, see Using IAM\n roles in the IAM User Guide.

" } }, "PermissionsBoundary": { "target": "com.amazonaws.iam#arnType", "traits": { - "smithy.api#documentation": "

The ARN of the policy that is used to set the permissions boundary for the\n role.

" + "smithy.api#documentation": "

The ARN of the managed policy that is used to set the permissions boundary for the\n role.

\n

A permissions boundary policy defines the maximum permissions that identity-based\n policies can grant to an entity, but does not grant permissions. Permissions boundaries\n do not define the maximum permissions that a resource-based policy can grant to an\n entity. To learn more, see Permissions boundaries\n for IAM entities in the IAM User Guide.

\n

For more information about policy types, see Policy types\n in the IAM User Guide.

" } }, "Tags": { @@ -3683,6 +3774,9 @@ "smithy.api#documentation": "

A list of tags that you want to attach to the new role. Each tag consists of a key name and an associated value.\n For more information about tagging, see Tagging IAM resources in the\n IAM User Guide.

\n \n

If any one of the tags is invalid or if you exceed the allowed maximum number of tags, then the entire request \n fails and the resource is not created.

\n
" } } + }, + "traits": { + "smithy.api#input": {} } }, "com.amazonaws.iam#CreateRoleResponse": { @@ -3697,7 +3791,8 @@ } }, "traits": { - "smithy.api#documentation": "

Contains the response to a successful CreateRole request.

" + "smithy.api#documentation": "

Contains the response to a successful CreateRole request.

", + "smithy.api#output": {} } }, "com.amazonaws.iam#CreateSAMLProvider": { @@ -3726,7 +3821,7 @@ } ], "traits": { - "smithy.api#documentation": "

Creates an IAM resource that describes an identity provider (IdP) that supports SAML\n 2.0.

\n

The SAML provider resource that you create with this operation can be used as a\n principal in an IAM role's trust policy. Such a policy can enable federated users who\n sign in using the SAML IdP to assume the role. You can create an IAM role that\n supports Web-based single sign-on (SSO) to the Amazon Web Services Management Console or one that supports API access\n to Amazon Web Services.

\n

When you create the SAML provider resource, you upload a SAML metadata document that\n you get from your IdP. That document includes the issuer's name, expiration information,\n and keys that can be used to validate the SAML authentication response (assertions) that\n the IdP sends. You must generate the metadata document using the identity management\n software that is used as your organization's IdP.

\n \n

This operation requires Signature Version 4.

\n
\n

For more information, see Enabling SAML 2.0\n federated users to access the Amazon Web Services Management Console and About SAML 2.0-based\n federation in the IAM User Guide.

" + "smithy.api#documentation": "

Creates an IAM resource that describes an identity provider (IdP) that supports SAML\n 2.0.

\n

The SAML provider resource that you create with this operation can be used as a\n principal in an IAM role's trust policy. Such a policy can enable federated users who\n sign in using the SAML IdP to assume the role. You can create an IAM role that\n supports Web-based single sign-on (SSO) to the Amazon Web Services Management Console or one that supports API access\n to Amazon Web Services.

\n

When you create the SAML provider resource, you upload a SAML metadata document that\n you get from your IdP. That document includes the issuer's name, expiration information,\n and keys that can be used to validate the SAML authentication response (assertions) that\n the IdP sends. You must generate the metadata document using the identity management\n software that is used as your organization's IdP.

\n \n

This operation requires Signature Version 4.

\n
\n

For more information, see Enabling SAML 2.0\n federated users to access the Amazon Web Services Management Console and About SAML 2.0-based\n federation in the IAM User Guide.

" } }, "com.amazonaws.iam#CreateSAMLProviderRequest": { @@ -3735,14 +3830,14 @@ "SAMLMetadataDocument": { "target": "com.amazonaws.iam#SAMLMetadataDocumentType", "traits": { - "smithy.api#documentation": "

An XML document generated by an identity provider (IdP) that supports SAML 2.0. The\n document includes the issuer's name, expiration information, and keys that can be used\n to validate the SAML authentication response (assertions) that are received from the\n IdP. You must generate the metadata document using the identity management software that\n is used as your organization's IdP.

\n

For more information, see About SAML 2.0-based\n federation in the IAM User Guide\n

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

An XML document generated by an identity provider (IdP) that supports SAML 2.0. The\n document includes the issuer's name, expiration information, and keys that can be used\n to validate the SAML authentication response (assertions) that are received from the\n IdP. You must generate the metadata document using the identity management software that\n is used as your organization's IdP.

\n

For more information, see About SAML 2.0-based\n federation in the IAM User Guide\n

", "smithy.api#required": {} } }, "Name": { "target": "com.amazonaws.iam#SAMLProviderNameType", "traits": { - "smithy.api#documentation": "

The name of the provider to create.

\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 provider to create.

\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": {} } }, @@ -3752,6 +3847,9 @@ "smithy.api#documentation": "

A list of tags that you want to attach to the new IAM SAML provider.\n Each tag consists of a key name and an associated value. For more information about tagging, see Tagging IAM resources in the\n IAM User Guide.

\n \n

If any one of the tags is invalid or if you exceed the allowed maximum number of tags, then the entire request \n fails and the resource is not created.

\n
" } } + }, + "traits": { + "smithy.api#input": {} } }, "com.amazonaws.iam#CreateSAMLProviderResponse": { @@ -3771,7 +3869,8 @@ } }, "traits": { - "smithy.api#documentation": "

Contains the response to a successful CreateSAMLProvider request.\n

" + "smithy.api#documentation": "

Contains the response to a successful CreateSAMLProvider request.\n

", + "smithy.api#output": {} } }, "com.amazonaws.iam#CreateServiceLinkedRole": { @@ -3797,7 +3896,7 @@ } ], "traits": { - "smithy.api#documentation": "

Creates an IAM role that is linked to a specific Amazon Web Services service. The service controls\n the attached policies and when the role can be deleted. This helps ensure that the\n service is not broken by an unexpectedly changed or deleted role, which could put your\n Amazon Web Services resources into an unknown state. Allowing the service to control the role helps\n improve service stability and proper cleanup when a service and its role are no longer\n needed. For more information, see Using service-linked\n roles in the IAM User Guide.

\n

To attach a policy to this service-linked role, you must make the request using the\n Amazon Web Services service that depends on this role.

" + "smithy.api#documentation": "

Creates an IAM role that is linked to a specific Amazon Web Services service. The service controls\n the attached policies and when the role can be deleted. This helps ensure that the\n service is not broken by an unexpectedly changed or deleted role, which could put your\n Amazon Web Services resources into an unknown state. Allowing the service to control the role helps\n improve service stability and proper cleanup when a service and its role are no longer\n needed. For more information, see Using service-linked\n roles in the IAM User Guide.

\n

To attach a policy to this service-linked role, you must make the request using the\n Amazon Web Services service that depends on this role.

" } }, "com.amazonaws.iam#CreateServiceLinkedRoleRequest": { @@ -3806,7 +3905,7 @@ "AWSServiceName": { "target": "com.amazonaws.iam#groupNameType", "traits": { - "smithy.api#documentation": "

The service principal for the Amazon Web Services service to which this role is attached. You use a\n string similar to a URL but without the http:// in front. For example:\n elasticbeanstalk.amazonaws.com.

\n

Service principals are unique and case-sensitive. To find the exact service principal\n for your service-linked role, see Amazon Web Services services\n that work with IAM in the IAM User Guide. Look for\n the services that have Yes in the Service-Linked Role column. Choose the Yes link to view the service-linked role documentation for that\n service.

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

The service principal for the Amazon Web Services service to which this role is attached. You use a\n string similar to a URL but without the http:// in front. For example:\n elasticbeanstalk.amazonaws.com.

\n

Service principals are unique and case-sensitive. To find the exact service principal\n for your service-linked role, see Amazon Web Services services\n that work with IAM in the IAM User Guide. Look for\n the services that have Yes in the Service-Linked Role column. Choose the Yes link to view the service-linked role documentation for that\n service.

", "smithy.api#required": {} } }, @@ -3819,9 +3918,12 @@ "CustomSuffix": { "target": "com.amazonaws.iam#customSuffixType", "traits": { - "smithy.api#documentation": "

\n

A string that you provide, which is combined with the service-provided prefix to form\n the complete role name. If you make multiple requests for the same service, then you\n must supply a different CustomSuffix for each request. Otherwise the\n request fails with a duplicate role name error. For example, you could add\n -1 or -debug to the suffix.

\n

Some services do not support the CustomSuffix parameter. If you provide\n an optional suffix and the operation fails, try the operation again without the\n suffix.

" + "smithy.api#documentation": "

\n

A string that you provide, which is combined with the service-provided prefix to form\n the complete role name. If you make multiple requests for the same service, then you\n must supply a different CustomSuffix for each request. Otherwise the\n request fails with a duplicate role name error. For example, you could add\n -1 or -debug to the suffix.

\n

Some services do not support the CustomSuffix parameter. If you provide\n an optional suffix and the operation fails, try the operation again without the\n suffix.

" } } + }, + "traits": { + "smithy.api#input": {} } }, "com.amazonaws.iam#CreateServiceLinkedRoleResponse": { @@ -3833,6 +3935,9 @@ "smithy.api#documentation": "

A Role object that contains details about the newly created\n role.

" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.iam#CreateServiceSpecificCredential": { @@ -3855,7 +3960,7 @@ } ], "traits": { - "smithy.api#documentation": "

Generates a set of credentials consisting of a user name and password that can be used\n to access the service specified in the request. These credentials are generated by\n IAM, and can be used only for the specified service.

\n

You can have a maximum of two sets of service-specific credentials for each supported\n service per user.

\n

You can create service-specific credentials for CodeCommit and Amazon Keyspaces (for Apache\n Cassandra).

\n

You can reset the password to a new service-generated value by calling ResetServiceSpecificCredential.

\n

For more information about service-specific credentials, see Using IAM\n with CodeCommit: Git credentials, SSH keys, and Amazon Web Services access keys in the\n IAM User Guide.

" + "smithy.api#documentation": "

Generates a set of credentials consisting of a user name and password that can be used\n to access the service specified in the request. These credentials are generated by\n IAM, and can be used only for the specified service.

\n

You can have a maximum of two sets of service-specific credentials for each supported\n service per user.

\n

You can create service-specific credentials for CodeCommit and Amazon Keyspaces (for Apache\n Cassandra).

\n

You can reset the password to a new service-generated value by calling ResetServiceSpecificCredential.

\n

For more information about service-specific credentials, see Using IAM\n with CodeCommit: Git credentials, SSH keys, and Amazon Web Services access keys in the\n IAM User Guide.

" } }, "com.amazonaws.iam#CreateServiceSpecificCredentialRequest": { @@ -3864,7 +3969,7 @@ "UserName": { "target": "com.amazonaws.iam#userNameType", "traits": { - "smithy.api#documentation": "

The name of the IAM user that is to be associated with the credentials. The new\n service-specific credentials have the same permissions as the associated user except\n that they can be used only to access the specified service.

\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 IAM user that is to be associated with the credentials. The new\n service-specific credentials have the same permissions as the associated user except\n that they can be used only to access the specified service.

\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": {} } }, @@ -3875,6 +3980,9 @@ "smithy.api#required": {} } } + }, + "traits": { + "smithy.api#input": {} } }, "com.amazonaws.iam#CreateServiceSpecificCredentialResponse": { @@ -3883,9 +3991,12 @@ "ServiceSpecificCredential": { "target": "com.amazonaws.iam#ServiceSpecificCredential", "traits": { - "smithy.api#documentation": "

A structure that contains information about the newly created service-specific\n credential.

\n \n

This is the only time that the password for this credential set is available. It\n cannot be recovered later. Instead, you must reset the password with ResetServiceSpecificCredential.

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

A structure that contains information about the newly created service-specific\n credential.

\n \n

This is the only time that the password for this credential set is available. It\n cannot be recovered later. Instead, you must reset the password with ResetServiceSpecificCredential.

\n
" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.iam#CreateUser": { @@ -3917,7 +4028,7 @@ } ], "traits": { - "smithy.api#documentation": "

Creates a new IAM user for your Amazon Web Services account.

\n

For information about quotas for the number of IAM users you can create, see IAM and STS\n quotas in the IAM User Guide.

" + "smithy.api#documentation": "

Creates a new IAM user for your Amazon Web Services account.

\n

For information about quotas for the number of IAM users you can create, see IAM and STS\n quotas in the IAM User Guide.

" } }, "com.amazonaws.iam#CreateUserRequest": { @@ -3926,20 +4037,20 @@ "Path": { "target": "com.amazonaws.iam#pathType", "traits": { - "smithy.api#documentation": "

The path for the user name. For more information about paths, see IAM\n identifiers in the IAM User Guide.

\n

This parameter is optional. If it is not included, it defaults to a slash (/).

\n

This parameter allows (through its regex pattern) a string of characters consisting \n of either a forward slash (/) by itself or a string that must begin and end with forward slashes.\n In addition, it can contain any ASCII character from the ! (\\u0021) through the DEL character (\\u007F), including \n most punctuation characters, digits, and upper and lowercased letters.

" + "smithy.api#documentation": "

The path for the user name. For more information about paths, see IAM\n identifiers in the IAM User Guide.

\n

This parameter is optional. If it is not included, it defaults to a slash (/).

\n

This parameter allows (through its regex pattern) a string of characters consisting \n of either a forward slash (/) by itself or a string that must begin and end with forward slashes.\n In addition, it can contain any ASCII character from the ! (\\u0021) through the DEL character (\\u007F), including \n most punctuation characters, digits, and upper and lowercased letters.

" } }, "UserName": { "target": "com.amazonaws.iam#userNameType", "traits": { - "smithy.api#documentation": "

The name of the user to create.

\n

IAM user, group, role, and policy names must be unique within the account. Names are\n not distinguished by case. For example, you cannot create resources named both\n \"MyResource\" and \"myresource\".

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

The name of the user to create.

\n

IAM user, group, role, and policy names must be unique within the account. Names are\n not distinguished by case. For example, you cannot create resources named both\n \"MyResource\" and \"myresource\".

", "smithy.api#required": {} } }, "PermissionsBoundary": { "target": "com.amazonaws.iam#arnType", "traits": { - "smithy.api#documentation": "

The ARN of the policy that is used to set the permissions boundary for the\n user.

" + "smithy.api#documentation": "

The ARN of the managed policy that is used to set the permissions boundary for the\n user.

\n

A permissions boundary policy defines the maximum permissions that identity-based\n policies can grant to an entity, but does not grant permissions. Permissions boundaries\n do not define the maximum permissions that a resource-based policy can grant to an\n entity. To learn more, see Permissions boundaries\n for IAM entities in the IAM User Guide.

\n

For more information about policy types, see Policy types\n in the IAM User Guide.

" } }, "Tags": { @@ -3948,6 +4059,9 @@ "smithy.api#documentation": "

A list of tags that you want to attach to the new user. Each tag consists of a key name and an associated value.\n For more information about tagging, see Tagging IAM resources in the\n IAM User Guide.

\n \n

If any one of the tags is invalid or if you exceed the allowed maximum number of tags, then the entire request \n fails and the resource is not created.

\n
" } } + }, + "traits": { + "smithy.api#input": {} } }, "com.amazonaws.iam#CreateUserResponse": { @@ -3961,7 +4075,8 @@ } }, "traits": { - "smithy.api#documentation": "

Contains the response to a successful CreateUser request.

" + "smithy.api#documentation": "

Contains the response to a successful CreateUser request.

", + "smithy.api#output": {} } }, "com.amazonaws.iam#CreateVirtualMFADevice": { @@ -3990,7 +4105,7 @@ } ], "traits": { - "smithy.api#documentation": "

Creates a new virtual MFA device for the Amazon Web Services account. After creating the virtual\n MFA, use EnableMFADevice to attach the MFA device to an IAM user.\n For more information about creating and working with virtual MFA devices, see Using a virtual MFA\n device in the IAM User Guide.

\n

For information about the maximum number of MFA devices you can create, see IAM and STS\n quotas in the IAM User Guide.

\n \n

The seed information contained in the QR code and the Base32 string should be\n treated like any other secret access information. In other words, protect the seed\n information as you would your Amazon Web Services access keys or your passwords. After you\n provision your virtual device, you should ensure that the information is destroyed\n following secure procedures.

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

Creates a new virtual MFA device for the Amazon Web Services account. After creating the virtual\n MFA, use EnableMFADevice to attach the MFA device to an IAM user.\n For more information about creating and working with virtual MFA devices, see Using a virtual MFA\n device in the IAM User Guide.

\n

For information about the maximum number of MFA devices you can create, see IAM and STS\n quotas in the IAM User Guide.

\n \n

The seed information contained in the QR code and the Base32 string should be\n treated like any other secret access information. In other words, protect the seed\n information as you would your Amazon Web Services access keys or your passwords. After you\n provision your virtual device, you should ensure that the information is destroyed\n following secure procedures.

\n
" } }, "com.amazonaws.iam#CreateVirtualMFADeviceRequest": { @@ -3999,13 +4114,13 @@ "Path": { "target": "com.amazonaws.iam#pathType", "traits": { - "smithy.api#documentation": "

The path for the virtual MFA device. For more information about paths, see IAM\n identifiers in the IAM User Guide.

\n

This parameter is optional. If it is not included, it defaults to a slash (/).

\n

This parameter allows (through its regex pattern) a string of characters consisting \n of either a forward slash (/) by itself or a string that must begin and end with forward slashes.\n In addition, it can contain any ASCII character from the ! (\\u0021) through the DEL character (\\u007F), including \n most punctuation characters, digits, and upper and lowercased letters.

" + "smithy.api#documentation": "

The path for the virtual MFA device. For more information about paths, see IAM\n identifiers in the IAM User Guide.

\n

This parameter is optional. If it is not included, it defaults to a slash (/).

\n

This parameter allows (through its regex pattern) a string of characters consisting \n of either a forward slash (/) by itself or a string that must begin and end with forward slashes.\n In addition, it can contain any ASCII character from the ! (\\u0021) through the DEL character (\\u007F), including \n most punctuation characters, digits, and upper and lowercased letters.

" } }, "VirtualMFADeviceName": { "target": "com.amazonaws.iam#virtualMFADeviceName", "traits": { - "smithy.api#documentation": "

The name of the virtual MFA device. 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 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#required": {} } }, @@ -4015,6 +4130,9 @@ "smithy.api#documentation": "

A list of tags that you want to attach to the new IAM virtual MFA device.\n Each tag consists of a key name and an associated value. For more information about tagging, see Tagging IAM resources in the\n IAM User Guide.

\n \n

If any one of the tags is invalid or if you exceed the allowed maximum number of tags, then the entire request \n fails and the resource is not created.

\n
" } } + }, + "traits": { + "smithy.api#input": {} } }, "com.amazonaws.iam#CreateVirtualMFADeviceResponse": { @@ -4029,7 +4147,8 @@ } }, "traits": { - "smithy.api#documentation": "

Contains the response to a successful CreateVirtualMFADevice request.\n

" + "smithy.api#documentation": "

Contains the response to a successful CreateVirtualMFADevice request.\n

", + "smithy.api#output": {} } }, "com.amazonaws.iam#CredentialReportExpiredException": { @@ -4106,7 +4225,7 @@ } ], "traits": { - "smithy.api#documentation": "

Deactivates the specified MFA device and removes it from association with the user\n name for which it was originally enabled.

\n

For more information about creating and working with virtual MFA devices, see Enabling a virtual\n multi-factor authentication (MFA) device in the\n IAM User Guide.

" + "smithy.api#documentation": "

Deactivates the specified MFA device and removes it from association with the user\n name for which it was originally enabled.

\n

For more information about creating and working with virtual MFA devices, see Enabling a virtual\n multi-factor authentication (MFA) device in the\n IAM User Guide.

" } }, "com.amazonaws.iam#DeactivateMFADeviceRequest": { @@ -4115,17 +4234,20 @@ "UserName": { "target": "com.amazonaws.iam#existingUserNameType", "traits": { - "smithy.api#documentation": "

The name of the user whose MFA device you want to deactivate.

\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 user whose MFA device you want to deactivate.

\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": {} } }, "SerialNumber": { "target": "com.amazonaws.iam#serialNumberType", "traits": { - "smithy.api#documentation": "

The serial number that uniquely identifies the MFA device. For virtual MFA devices,\n the serial number is the device ARN.

\n

This parameter allows (through its regex pattern) a string of characters consisting \n of upper and lowercase alphanumeric characters with no spaces. You can also include any of the \n following characters: =,.@:/-

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

The serial number that uniquely identifies the MFA device. For virtual MFA devices,\n the serial number is the device ARN.

\n

This parameter allows (through its regex pattern) a string of characters consisting \n of upper and lowercase alphanumeric characters with no spaces. You can also include any of the \n following characters: =,.@:/-

", "smithy.api#required": {} } } + }, + "traits": { + "smithy.api#input": {} } }, "com.amazonaws.iam#DeleteAccessKey": { @@ -4148,7 +4270,7 @@ } ], "traits": { - "smithy.api#documentation": "

Deletes the access key pair associated with the specified IAM user.

\n

If you do not specify a user name, IAM determines the user name implicitly based on\n the Amazon Web Services access key ID signing the request. This operation works for access keys under\n the Amazon Web Services account. Consequently, you can use this operation to manage Amazon Web Services account root\n user credentials even if the Amazon Web Services account has no associated users.

" + "smithy.api#documentation": "

Deletes the access key pair associated with the specified IAM user.

\n

If you do not specify a user name, IAM determines the user name implicitly based on\n the Amazon Web Services access key ID signing the request. This operation works for access keys under\n the Amazon Web Services account. Consequently, you can use this operation to manage Amazon Web Services account root\n user credentials even if the Amazon Web Services account has no associated users.

" } }, "com.amazonaws.iam#DeleteAccessKeyRequest": { @@ -4157,16 +4279,19 @@ "UserName": { "target": "com.amazonaws.iam#existingUserNameType", "traits": { - "smithy.api#documentation": "

The name of the user whose access key pair you want to delete.

\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 user whose access key pair you want to delete.

\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: _+=,.@-

" } }, "AccessKeyId": { "target": "com.amazonaws.iam#accessKeyIdType", "traits": { - "smithy.api#documentation": "

The access key ID for the access key ID and secret access key you want to\n delete.

\n

This parameter allows (through its regex pattern) a string of characters that can \n consist of any upper or lowercased letter or digit.

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

The access key ID for the access key ID and secret access key you want to\n delete.

\n

This parameter allows (through its regex pattern) a string of characters that can \n consist of any upper or lowercased letter or digit.

", "smithy.api#required": {} } } + }, + "traits": { + "smithy.api#input": {} } }, "com.amazonaws.iam#DeleteAccountAlias": { @@ -4198,10 +4323,13 @@ "AccountAlias": { "target": "com.amazonaws.iam#accountAliasType", "traits": { - "smithy.api#documentation": "

The name of the account alias to delete.

\n

This parameter allows (through its regex pattern) a string of characters consisting of \n lowercase letters, digits, and dashes. You cannot start or finish with a dash, nor can you have \n two dashes in a row.

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

The name of the account alias to delete.

\n

This parameter allows (through its regex pattern) a string of characters consisting of \n lowercase letters, digits, and dashes. You cannot start or finish with a dash, nor can you have \n two dashes in a row.

", "smithy.api#required": {} } } + }, + "traits": { + "smithy.api#input": {} } }, "com.amazonaws.iam#DeleteAccountPasswordPolicy": { @@ -4290,7 +4418,7 @@ } ], "traits": { - "smithy.api#documentation": "

Deletes the specified inline policy that is embedded in the specified IAM\n group.

\n

A group can also have managed policies attached to it. To detach a managed policy from\n a group, use DetachGroupPolicy. For more information about policies,\n refer to Managed policies and inline\n policies in the IAM User Guide.

" + "smithy.api#documentation": "

Deletes the specified inline policy that is embedded in the specified IAM\n group.

\n

A group can also have managed policies attached to it. To detach a managed policy from\n a group, use DetachGroupPolicy. For more information about policies,\n refer to Managed policies and inline\n policies in the IAM User Guide.

" } }, "com.amazonaws.iam#DeleteGroupPolicyRequest": { @@ -4299,17 +4427,20 @@ "GroupName": { "target": "com.amazonaws.iam#groupNameType", "traits": { - "smithy.api#documentation": "

The name (friendly name, not ARN) identifying the group that the policy is embedded\n in.

\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 (friendly name, not ARN) identifying the group that the policy is embedded\n in.

\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": {} } }, "PolicyName": { "target": "com.amazonaws.iam#policyNameType", "traits": { - "smithy.api#documentation": "

The name identifying the policy document to delete.

\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 identifying the policy document to delete.

\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": {} } } + }, + "traits": { + "smithy.api#input": {} } }, "com.amazonaws.iam#DeleteGroupRequest": { @@ -4318,10 +4449,13 @@ "GroupName": { "target": "com.amazonaws.iam#groupNameType", "traits": { - "smithy.api#documentation": "

The name of the IAM group to delete.

\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 IAM group to delete.

\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": {} } } + }, + "traits": { + "smithy.api#input": {} } }, "com.amazonaws.iam#DeleteInstanceProfile": { @@ -4347,7 +4481,7 @@ } ], "traits": { - "smithy.api#documentation": "

Deletes the specified instance profile. The instance profile must not have an\n associated role.

\n \n

Make sure that you do not have any Amazon EC2 instances running with the instance\n profile you are about to delete. Deleting a role or instance profile that is\n associated with a running instance will break any applications running on the\n instance.

\n
\n

For more information about instance profiles, see About instance\n profiles.

" + "smithy.api#documentation": "

Deletes the specified instance profile. The instance profile must not have an\n associated role.

\n \n

Make sure that you do not have any Amazon EC2 instances running with the instance\n profile you are about to delete. Deleting a role or instance profile that is\n associated with a running instance will break any applications running on the\n instance.

\n
\n

For more information about instance profiles, see About instance\n profiles.

" } }, "com.amazonaws.iam#DeleteInstanceProfileRequest": { @@ -4356,10 +4490,13 @@ "InstanceProfileName": { "target": "com.amazonaws.iam#instanceProfileNameType", "traits": { - "smithy.api#documentation": "

The name of the instance profile to delete.

\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 instance profile to delete.

\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": {} } } + }, + "traits": { + "smithy.api#input": {} } }, "com.amazonaws.iam#DeleteLoginProfile": { @@ -4385,7 +4522,7 @@ } ], "traits": { - "smithy.api#documentation": "

Deletes the password for the specified IAM user, For more information, see Managing\n passwords for IAM users.

\n

You can use the CLI, the Amazon Web Services API, or the Users\n page in the IAM console to delete a password for any IAM user. You can use ChangePassword to update, but not delete, your own password in the\n My Security Credentials page in the\n Amazon Web Services Management Console.

\n \n

Deleting a user's password does not prevent a user from accessing Amazon Web Services through\n the command line interface or the API. To prevent all user access, you must also\n either make any access keys inactive or delete them. For more information about\n making keys inactive or deleting them, see UpdateAccessKey and\n DeleteAccessKey.

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

Deletes the password for the specified IAM user, For more information, see Managing\n passwords for IAM users.

\n

You can use the CLI, the Amazon Web Services API, or the Users\n page in the IAM console to delete a password for any IAM user. You can use ChangePassword to update, but not delete, your own password in the\n My Security Credentials page in the\n Amazon Web Services Management Console.

\n \n

Deleting a user's password does not prevent a user from accessing Amazon Web Services through\n the command line interface or the API. To prevent all user access, you must also\n either make any access keys inactive or delete them. For more information about\n making keys inactive or deleting them, see UpdateAccessKey and\n DeleteAccessKey.

\n
" } }, "com.amazonaws.iam#DeleteLoginProfileRequest": { @@ -4394,10 +4531,13 @@ "UserName": { "target": "com.amazonaws.iam#userNameType", "traits": { - "smithy.api#documentation": "

The name of the user whose password you want to delete.

\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 user whose password you want to delete.

\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": {} } } + }, + "traits": { + "smithy.api#input": {} } }, "com.amazonaws.iam#DeleteOpenIDConnectProvider": { @@ -4420,7 +4560,7 @@ } ], "traits": { - "smithy.api#documentation": "

Deletes an OpenID Connect identity provider (IdP) resource object in IAM.

\n

Deleting an IAM OIDC provider resource does not update any roles that reference the\n provider as a principal in their trust policies. Any attempt to assume a role that\n references a deleted provider fails.

\n

This operation is idempotent; it does not fail or return an error if you call the\n operation for a provider that does not exist.

" + "smithy.api#documentation": "

Deletes an OpenID Connect identity provider (IdP) resource object in IAM.

\n

Deleting an IAM OIDC provider resource does not update any roles that reference the\n provider as a principal in their trust policies. Any attempt to assume a role that\n references a deleted provider fails.

\n

This operation is idempotent; it does not fail or return an error if you call the\n operation for a provider that does not exist.

" } }, "com.amazonaws.iam#DeleteOpenIDConnectProviderRequest": { @@ -4433,6 +4573,9 @@ "smithy.api#required": {} } } + }, + "traits": { + "smithy.api#input": {} } }, "com.amazonaws.iam#DeletePolicy": { @@ -4461,7 +4604,7 @@ } ], "traits": { - "smithy.api#documentation": "

Deletes the specified managed policy.

\n

Before you can delete a managed policy, you must first detach the policy from all\n users, groups, and roles that it is attached to. In addition, you must delete all the\n policy's versions. The following steps describe the process for deleting a managed\n policy:

\n
    \n
  • \n

    Detach the policy from all users, groups, and roles that the policy is\n attached to, using DetachUserPolicy, DetachGroupPolicy, or DetachRolePolicy. To\n list all the users, groups, and roles that a policy is attached to, use ListEntitiesForPolicy.

    \n
  • \n
  • \n

    Delete all versions of the policy using DeletePolicyVersion.\n To list the policy's versions, use ListPolicyVersions. You\n cannot use DeletePolicyVersion to delete the version that is\n marked as the default version. You delete the policy's default version in the\n next step of the process.

    \n
  • \n
  • \n

    Delete the policy (this automatically deletes the policy's default version)\n using this operation.

    \n
  • \n
\n

For information about managed policies, see Managed policies and inline\n policies in the IAM User Guide.

" + "smithy.api#documentation": "

Deletes the specified managed policy.

\n

Before you can delete a managed policy, you must first detach the policy from all\n users, groups, and roles that it is attached to. In addition, you must delete all the\n policy's versions. The following steps describe the process for deleting a managed\n policy:

\n
    \n
  • \n

    Detach the policy from all users, groups, and roles that the policy is\n attached to, using DetachUserPolicy, DetachGroupPolicy, or DetachRolePolicy. To\n list all the users, groups, and roles that a policy is attached to, use ListEntitiesForPolicy.

    \n
  • \n
  • \n

    Delete all versions of the policy using DeletePolicyVersion.\n To list the policy's versions, use ListPolicyVersions. You\n cannot use DeletePolicyVersion to delete the version that is\n marked as the default version. You delete the policy's default version in the\n next step of the process.

    \n
  • \n
  • \n

    Delete the policy (this automatically deletes the policy's default version)\n using this operation.

    \n
  • \n
\n

For information about managed policies, see Managed policies and inline\n policies in the IAM User Guide.

" } }, "com.amazonaws.iam#DeletePolicyRequest": { @@ -4470,10 +4613,13 @@ "PolicyArn": { "target": "com.amazonaws.iam#arnType", "traits": { - "smithy.api#documentation": "

The Amazon Resource Name (ARN) of the IAM policy you want to delete.

\n

For more information about ARNs, see Amazon Resource Names (ARNs) in the Amazon Web Services General Reference.

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

The Amazon Resource Name (ARN) of the IAM policy you want to delete.

\n

For more information about ARNs, see Amazon Resource Names (ARNs) in the Amazon Web Services General Reference.

", "smithy.api#required": {} } } + }, + "traits": { + "smithy.api#input": {} } }, "com.amazonaws.iam#DeletePolicyVersion": { @@ -4502,7 +4648,7 @@ } ], "traits": { - "smithy.api#documentation": "

Deletes the specified version from the specified managed policy.

\n

You cannot delete the default version from a policy using this operation. To delete\n the default version from a policy, use DeletePolicy. To find out which\n version of a policy is marked as the default version, use ListPolicyVersions.

\n

For information about versions for managed policies, see Versioning for managed\n policies in the IAM User Guide.

" + "smithy.api#documentation": "

Deletes the specified version from the specified managed policy.

\n

You cannot delete the default version from a policy using this operation. To delete\n the default version from a policy, use DeletePolicy. To find out which\n version of a policy is marked as the default version, use ListPolicyVersions.

\n

For information about versions for managed policies, see Versioning for managed\n policies in the IAM User Guide.

" } }, "com.amazonaws.iam#DeletePolicyVersionRequest": { @@ -4511,17 +4657,20 @@ "PolicyArn": { "target": "com.amazonaws.iam#arnType", "traits": { - "smithy.api#documentation": "

The Amazon Resource Name (ARN) of the IAM policy from which you want to delete a\n version.

\n

For more information about ARNs, see Amazon Resource Names (ARNs) in the Amazon Web Services General Reference.

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

The Amazon Resource Name (ARN) of the IAM policy from which you want to delete a\n version.

\n

For more information about ARNs, see Amazon Resource Names (ARNs) in the Amazon Web Services General Reference.

", "smithy.api#required": {} } }, "VersionId": { "target": "com.amazonaws.iam#policyVersionIdType", "traits": { - "smithy.api#documentation": "

The policy version to delete.

\n

This parameter allows (through its regex pattern) a string of characters that \n consists of the lowercase letter 'v' followed by one or two digits, and optionally \n followed by a period '.' and a string of letters and digits.

\n

For more information about managed policy versions, see Versioning for managed\n policies in the IAM User Guide.

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

The policy version to delete.

\n

This parameter allows (through its regex pattern) a string of characters that \n consists of the lowercase letter 'v' followed by one or two digits, and optionally \n followed by a period '.' and a string of letters and digits.

\n

For more information about managed policy versions, see Versioning for managed\n policies in the IAM User Guide.

", "smithy.api#required": {} } } + }, + "traits": { + "smithy.api#input": {} } }, "com.amazonaws.iam#DeleteRole": { @@ -4553,7 +4702,7 @@ } ], "traits": { - "smithy.api#documentation": "

Deletes the specified role. The role must not have any policies attached. For more\n information about roles, see Working with roles.

\n \n

Make sure that you do not have any Amazon EC2 instances running with the role you\n are about to delete. Deleting a role or instance profile that is associated with a\n running instance will break any applications running on the instance.

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

Deletes the specified role. Unlike the Amazon Web Services Management Console, when you delete a role\n programmatically, you must delete the items attached to the role manually, or the\n deletion fails. For more information, see Deleting an IAM role. Before attempting to delete a role, remove the\n following attached items:

\n \n \n

Make sure that you do not have any Amazon EC2 instances running with the role you\n are about to delete. Deleting a role or instance profile that is associated with a\n running instance will break any applications running on the instance.

\n
" } }, "com.amazonaws.iam#DeleteRolePermissionsBoundary": { @@ -4576,7 +4725,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 \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": { @@ -4589,6 +4738,9 @@ "smithy.api#required": {} } } + }, + "traits": { + "smithy.api#input": {} } }, "com.amazonaws.iam#DeleteRolePolicy": { @@ -4614,7 +4766,7 @@ } ], "traits": { - "smithy.api#documentation": "

Deletes the specified inline policy that is embedded in the specified IAM\n role.

\n

A role can also have managed policies attached to it. To detach a managed policy from\n a role, use DetachRolePolicy. For more information about policies,\n refer to Managed policies and inline\n policies in the IAM User Guide.

" + "smithy.api#documentation": "

Deletes the specified inline policy that is embedded in the specified IAM\n role.

\n

A role can also have managed policies attached to it. To detach a managed policy from\n a role, use DetachRolePolicy. For more information about policies,\n refer to Managed policies and inline\n policies in the IAM User Guide.

" } }, "com.amazonaws.iam#DeleteRolePolicyRequest": { @@ -4623,17 +4775,20 @@ "RoleName": { "target": "com.amazonaws.iam#roleNameType", "traits": { - "smithy.api#documentation": "

The name (friendly name, not ARN) identifying the role that the policy is embedded\n in.

\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 (friendly name, not ARN) identifying the role that the policy is embedded\n in.

\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": {} } }, "PolicyName": { "target": "com.amazonaws.iam#policyNameType", "traits": { - "smithy.api#documentation": "

The name of the inline policy to delete from the specified IAM role.

\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 inline policy to delete from the specified IAM role.

\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": {} } } + }, + "traits": { + "smithy.api#input": {} } }, "com.amazonaws.iam#DeleteRoleRequest": { @@ -4642,10 +4797,13 @@ "RoleName": { "target": "com.amazonaws.iam#roleNameType", "traits": { - "smithy.api#documentation": "

The name of the role to delete.

\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 role to delete.

\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": {} } } + }, + "traits": { + "smithy.api#input": {} } }, "com.amazonaws.iam#DeleteSAMLProvider": { @@ -4671,7 +4829,7 @@ } ], "traits": { - "smithy.api#documentation": "

Deletes a SAML provider resource in IAM.

\n

Deleting the provider resource from IAM does not update any roles that reference the\n SAML provider resource's ARN as a principal in their trust policies. Any attempt to\n assume a role that references a non-existent provider resource ARN fails.

\n \n

This operation requires Signature Version 4.

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

Deletes a SAML provider resource in IAM.

\n

Deleting the provider resource from IAM does not update any roles that reference the\n SAML provider resource's ARN as a principal in their trust policies. Any attempt to\n assume a role that references a non-existent provider resource ARN fails.

\n \n

This operation requires Signature Version 4.

\n
" } }, "com.amazonaws.iam#DeleteSAMLProviderRequest": { @@ -4684,6 +4842,9 @@ "smithy.api#required": {} } } + }, + "traits": { + "smithy.api#input": {} } }, "com.amazonaws.iam#DeleteSSHPublicKey": { @@ -4700,7 +4861,7 @@ } ], "traits": { - "smithy.api#documentation": "

Deletes the specified SSH public key.

\n

The SSH public key deleted by this operation is used only for authenticating the\n associated IAM user to an CodeCommit repository. For more information about using SSH keys\n to authenticate to an CodeCommit repository, see Set up CodeCommit for\n SSH connections in the CodeCommit User Guide.

" + "smithy.api#documentation": "

Deletes the specified SSH public key.

\n

The SSH public key deleted by this operation is used only for authenticating the\n associated IAM user to an CodeCommit repository. For more information about using SSH keys\n to authenticate to an CodeCommit repository, see Set up CodeCommit for\n SSH connections in the CodeCommit User Guide.

" } }, "com.amazonaws.iam#DeleteSSHPublicKeyRequest": { @@ -4709,17 +4870,20 @@ "UserName": { "target": "com.amazonaws.iam#userNameType", "traits": { - "smithy.api#documentation": "

The name of the IAM user associated with the SSH public key.

\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 IAM user associated with the SSH public key.

\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": {} } }, "SSHPublicKeyId": { "target": "com.amazonaws.iam#publicKeyIdType", "traits": { - "smithy.api#documentation": "

The unique identifier for the SSH public key.

\n

This parameter allows (through its regex pattern) a string of characters that can \n consist of any upper or lowercased letter or digit.

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

The unique identifier for the SSH public key.

\n

This parameter allows (through its regex pattern) a string of characters that can \n consist of any upper or lowercased letter or digit.

", "smithy.api#required": {} } } + }, + "traits": { + "smithy.api#input": {} } }, "com.amazonaws.iam#DeleteServerCertificate": { @@ -4745,7 +4909,7 @@ } ], "traits": { - "smithy.api#documentation": "

Deletes the specified server certificate.

\n

For more information about working with server certificates, see Working\n with server certificates in the IAM User Guide. This\n topic also includes a list of Amazon Web Services services that can use the server certificates that\n you manage with IAM.

\n \n

If you are using a server certificate with Elastic Load Balancing, deleting the\n certificate could have implications for your application. If Elastic Load Balancing\n doesn't detect the deletion of bound certificates, it may continue to use the\n certificates. This could cause Elastic Load Balancing to stop accepting traffic. We\n recommend that you remove the reference to the certificate from Elastic Load\n Balancing before using this command to delete the certificate. For more information,\n see DeleteLoadBalancerListeners in the Elastic Load Balancing API\n Reference.

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

Deletes the specified server certificate.

\n

For more information about working with server certificates, see Working\n with server certificates in the IAM User Guide. This\n topic also includes a list of Amazon Web Services services that can use the server certificates that\n you manage with IAM.

\n \n

If you are using a server certificate with Elastic Load Balancing, deleting the\n certificate could have implications for your application. If Elastic Load Balancing\n doesn't detect the deletion of bound certificates, it may continue to use the\n certificates. This could cause Elastic Load Balancing to stop accepting traffic. We\n recommend that you remove the reference to the certificate from Elastic Load\n Balancing before using this command to delete the certificate. For more information,\n see DeleteLoadBalancerListeners in the Elastic Load Balancing API\n Reference.

\n
" } }, "com.amazonaws.iam#DeleteServerCertificateRequest": { @@ -4754,10 +4918,13 @@ "ServerCertificateName": { "target": "com.amazonaws.iam#serverCertificateNameType", "traits": { - "smithy.api#documentation": "

The name of the server certificate you want to delete.

\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 server certificate you want to delete.

\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": {} } } + }, + "traits": { + "smithy.api#input": {} } }, "com.amazonaws.iam#DeleteServiceLinkedRole": { @@ -4780,7 +4947,7 @@ } ], "traits": { - "smithy.api#documentation": "

Submits a service-linked role deletion request and returns a\n DeletionTaskId, which you can use to check the status of the deletion.\n Before you call this operation, confirm that the role has no active sessions and that\n any resources used by the role in the linked service are deleted. If you call this\n operation more than once for the same service-linked role and an earlier deletion task\n is not complete, then the DeletionTaskId of the earlier request is\n returned.

\n

If you submit a deletion request for a service-linked role whose linked service is\n still accessing a resource, then the deletion task fails. If it fails, the GetServiceLinkedRoleDeletionStatus operation returns the reason for the\n failure, usually including the resources that must be deleted. To delete the\n service-linked role, you must first remove those resources from the linked service and\n then submit the deletion request again. Resources are specific to the service that is\n linked to the role. For more information about removing resources from a service, see\n the Amazon Web Services documentation for your\n service.

\n

For more information about service-linked roles, see Roles terms and concepts: Amazon Web Services service-linked role in the\n IAM User Guide.

" + "smithy.api#documentation": "

Submits a service-linked role deletion request and returns a\n DeletionTaskId, which you can use to check the status of the deletion.\n Before you call this operation, confirm that the role has no active sessions and that\n any resources used by the role in the linked service are deleted. If you call this\n operation more than once for the same service-linked role and an earlier deletion task\n is not complete, then the DeletionTaskId of the earlier request is\n returned.

\n

If you submit a deletion request for a service-linked role whose linked service is\n still accessing a resource, then the deletion task fails. If it fails, the GetServiceLinkedRoleDeletionStatus operation returns the reason for the\n failure, usually including the resources that must be deleted. To delete the\n service-linked role, you must first remove those resources from the linked service and\n then submit the deletion request again. Resources are specific to the service that is\n linked to the role. For more information about removing resources from a service, see\n the Amazon Web Services documentation for your\n service.

\n

For more information about service-linked roles, see Roles terms and concepts: Amazon Web Services service-linked role in the\n IAM User Guide.

" } }, "com.amazonaws.iam#DeleteServiceLinkedRoleRequest": { @@ -4793,6 +4960,9 @@ "smithy.api#required": {} } } + }, + "traits": { + "smithy.api#input": {} } }, "com.amazonaws.iam#DeleteServiceLinkedRoleResponse": { @@ -4805,6 +4975,9 @@ "smithy.api#required": {} } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.iam#DeleteServiceSpecificCredential": { @@ -4830,16 +5003,19 @@ "UserName": { "target": "com.amazonaws.iam#userNameType", "traits": { - "smithy.api#documentation": "

The name of the IAM user associated with the service-specific credential. If this\n value is not specified, then the operation assumes the user whose credentials are used\n to call the operation.

\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 IAM user associated with the service-specific credential. If this\n value is not specified, then the operation assumes the user whose credentials are used\n to call the operation.

\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: _+=,.@-

" } }, "ServiceSpecificCredentialId": { "target": "com.amazonaws.iam#serviceSpecificCredentialId", "traits": { - "smithy.api#documentation": "

The unique identifier of the service-specific credential. You can get this value by\n calling ListServiceSpecificCredentials.

\n

This parameter allows (through its regex pattern) a string of characters that can \n consist of any upper or lowercased letter or digit.

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

The unique identifier of the service-specific credential. You can get this value by\n calling ListServiceSpecificCredentials.

\n

This parameter allows (through its regex pattern) a string of characters that can \n consist of any upper or lowercased letter or digit.

", "smithy.api#required": {} } } + }, + "traits": { + "smithy.api#input": {} } }, "com.amazonaws.iam#DeleteSigningCertificate": { @@ -4862,7 +5038,7 @@ } ], "traits": { - "smithy.api#documentation": "

Deletes a signing certificate associated with the specified IAM user.

\n

If you do not specify a user name, IAM determines the user name implicitly based on\n the Amazon Web Services access key ID signing the request. This operation works for access keys under\n the Amazon Web Services account. Consequently, you can use this operation to manage Amazon Web Services account root\n user credentials even if the Amazon Web Services account has no associated IAM users.

" + "smithy.api#documentation": "

Deletes a signing certificate associated with the specified IAM user.

\n

If you do not specify a user name, IAM determines the user name implicitly based on\n the Amazon Web Services access key ID signing the request. This operation works for access keys under\n the Amazon Web Services account. Consequently, you can use this operation to manage Amazon Web Services account root\n user credentials even if the Amazon Web Services account has no associated IAM users.

" } }, "com.amazonaws.iam#DeleteSigningCertificateRequest": { @@ -4871,16 +5047,19 @@ "UserName": { "target": "com.amazonaws.iam#existingUserNameType", "traits": { - "smithy.api#documentation": "

The name of the user the signing certificate belongs to.

\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 user the signing certificate belongs to.

\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: _+=,.@-

" } }, "CertificateId": { "target": "com.amazonaws.iam#certificateIdType", "traits": { - "smithy.api#documentation": "

The ID of the signing certificate to delete.

\n

The format of this parameter, as described by its regex pattern, is a string of\n characters that can be upper- or lower-cased letters or digits.

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

The ID of the signing certificate to delete.

\n

The format of this parameter, as described by its regex pattern, is a string of\n characters that can be upper- or lower-cased letters or digits.

", "smithy.api#required": {} } } + }, + "traits": { + "smithy.api#input": {} } }, "com.amazonaws.iam#DeleteUser": { @@ -4909,7 +5088,7 @@ } ], "traits": { - "smithy.api#documentation": "

Deletes the specified IAM user. Unlike the Amazon Web Services Management Console, when you delete a user\n programmatically, you must delete the items attached to the user manually, or the\n deletion fails. For more information, see Deleting an IAM\n user. Before attempting to delete a user, remove the following items:

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

Deletes the specified IAM user. Unlike the Amazon Web Services Management Console, when you delete a user\n programmatically, you must delete the items attached to the user manually, or the\n deletion fails. For more information, see Deleting an IAM\n user. Before attempting to delete a user, remove the following items:

\n " } }, "com.amazonaws.iam#DeleteUserPermissionsBoundary": { @@ -4929,7 +5108,7 @@ } ], "traits": { - "smithy.api#documentation": "

Deletes the permissions boundary for the specified IAM user.

\n \n

Deleting the permissions boundary for a user might increase its permissions by\n allowing the user to perform all the actions granted in its permissions policies.\n

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

Deletes the permissions boundary for the specified IAM user.

\n \n

Deleting the permissions boundary for a user might increase its permissions by\n allowing the user to perform all the actions granted in its permissions policies.\n

\n
" } }, "com.amazonaws.iam#DeleteUserPermissionsBoundaryRequest": { @@ -4942,6 +5121,9 @@ "smithy.api#required": {} } } + }, + "traits": { + "smithy.api#input": {} } }, "com.amazonaws.iam#DeleteUserPolicy": { @@ -4964,7 +5146,7 @@ } ], "traits": { - "smithy.api#documentation": "

Deletes the specified inline policy that is embedded in the specified IAM\n user.

\n

A user can also have managed policies attached to it. To detach a managed policy from\n a user, use DetachUserPolicy. For more information about policies,\n refer to Managed policies and inline\n policies in the IAM User Guide.

" + "smithy.api#documentation": "

Deletes the specified inline policy that is embedded in the specified IAM\n user.

\n

A user can also have managed policies attached to it. To detach a managed policy from\n a user, use DetachUserPolicy. For more information about policies,\n refer to Managed policies and inline\n policies in the IAM User Guide.

" } }, "com.amazonaws.iam#DeleteUserPolicyRequest": { @@ -4973,17 +5155,20 @@ "UserName": { "target": "com.amazonaws.iam#existingUserNameType", "traits": { - "smithy.api#documentation": "

The name (friendly name, not ARN) identifying the user that the policy is embedded\n in.

\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 (friendly name, not ARN) identifying the user that the policy is embedded\n in.

\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": {} } }, "PolicyName": { "target": "com.amazonaws.iam#policyNameType", "traits": { - "smithy.api#documentation": "

The name identifying the policy document to delete.

\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 identifying the policy document to delete.

\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": {} } } + }, + "traits": { + "smithy.api#input": {} } }, "com.amazonaws.iam#DeleteUserRequest": { @@ -4992,10 +5177,13 @@ "UserName": { "target": "com.amazonaws.iam#existingUserNameType", "traits": { - "smithy.api#documentation": "

The name of the user to delete.

\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 user to delete.

\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": {} } } + }, + "traits": { + "smithy.api#input": {} } }, "com.amazonaws.iam#DeleteVirtualMFADevice": { @@ -5021,7 +5209,7 @@ } ], "traits": { - "smithy.api#documentation": "

Deletes a virtual MFA device.

\n \n

You must deactivate a user's virtual MFA device before you can delete it. For\n information about deactivating MFA devices, see DeactivateMFADevice.

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

Deletes a virtual MFA device.

\n \n

You must deactivate a user's virtual MFA device before you can delete it. For\n information about deactivating MFA devices, see DeactivateMFADevice.

\n
" } }, "com.amazonaws.iam#DeleteVirtualMFADeviceRequest": { @@ -5030,10 +5218,13 @@ "SerialNumber": { "target": "com.amazonaws.iam#serialNumberType", "traits": { - "smithy.api#documentation": "

The serial number that uniquely identifies the MFA device. For virtual MFA devices,\n the serial number is the same as the ARN.

\n

This parameter allows (through its regex pattern) a string of characters consisting \n of upper and lowercase alphanumeric characters with no spaces. You can also include any of the \n following characters: =,.@:/-

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

The serial number that uniquely identifies the MFA device. For virtual MFA devices,\n the serial number is the same as the ARN.

\n

This parameter allows (through its regex pattern) a string of characters consisting \n of upper and lowercase alphanumeric characters with no spaces. You can also include any of the \n following characters: =,.@:/-

", "smithy.api#required": {} } } + }, + "traits": { + "smithy.api#input": {} } }, "com.amazonaws.iam#DeletionTaskFailureReasonType": { @@ -5117,7 +5308,7 @@ } ], "traits": { - "smithy.api#documentation": "

Removes the specified managed policy from the specified IAM group.

\n

A group can also have inline policies embedded with it. To delete an inline policy,\n use DeleteGroupPolicy. For information about policies, see Managed\n policies and inline policies in the\n IAM User Guide.

" + "smithy.api#documentation": "

Removes the specified managed policy from the specified IAM group.

\n

A group can also have inline policies embedded with it. To delete an inline policy,\n use DeleteGroupPolicy. For information about policies, see Managed\n policies and inline policies in the\n IAM User Guide.

" } }, "com.amazonaws.iam#DetachGroupPolicyRequest": { @@ -5126,17 +5317,20 @@ "GroupName": { "target": "com.amazonaws.iam#groupNameType", "traits": { - "smithy.api#documentation": "

The name (friendly name, not ARN) of the IAM group to detach the policy from.

\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 (friendly name, not ARN) of the IAM group to detach the policy from.

\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": {} } }, "PolicyArn": { "target": "com.amazonaws.iam#arnType", "traits": { - "smithy.api#documentation": "

The Amazon Resource Name (ARN) of the IAM policy you want to detach.

\n

For more information about ARNs, see Amazon Resource Names (ARNs) in the Amazon Web Services General Reference.

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

The Amazon Resource Name (ARN) of the IAM policy you want to detach.

\n

For more information about ARNs, see Amazon Resource Names (ARNs) in the Amazon Web Services General Reference.

", "smithy.api#required": {} } } + }, + "traits": { + "smithy.api#input": {} } }, "com.amazonaws.iam#DetachRolePolicy": { @@ -5165,7 +5359,7 @@ } ], "traits": { - "smithy.api#documentation": "

Removes the specified managed policy from the specified role.

\n

A role can also have inline policies embedded with it. To delete an inline policy, use\n DeleteRolePolicy. For information about policies, see Managed\n policies and inline policies in the\n IAM User Guide.

" + "smithy.api#documentation": "

Removes the specified managed policy from the specified role.

\n

A role can also have inline policies embedded with it. To delete an inline policy, use\n DeleteRolePolicy. For information about policies, see Managed\n policies and inline policies in the\n IAM User Guide.

" } }, "com.amazonaws.iam#DetachRolePolicyRequest": { @@ -5174,17 +5368,20 @@ "RoleName": { "target": "com.amazonaws.iam#roleNameType", "traits": { - "smithy.api#documentation": "

The name (friendly name, not ARN) of the IAM role to detach the policy from.

\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 (friendly name, not ARN) of the IAM role to detach the policy from.

\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": {} } }, "PolicyArn": { "target": "com.amazonaws.iam#arnType", "traits": { - "smithy.api#documentation": "

The Amazon Resource Name (ARN) of the IAM policy you want to detach.

\n

For more information about ARNs, see Amazon Resource Names (ARNs) in the Amazon Web Services General Reference.

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

The Amazon Resource Name (ARN) of the IAM policy you want to detach.

\n

For more information about ARNs, see Amazon Resource Names (ARNs) in the Amazon Web Services General Reference.

", "smithy.api#required": {} } } + }, + "traits": { + "smithy.api#input": {} } }, "com.amazonaws.iam#DetachUserPolicy": { @@ -5210,7 +5407,7 @@ } ], "traits": { - "smithy.api#documentation": "

Removes the specified managed policy from the specified user.

\n

A user can also have inline policies embedded with it. To delete an inline policy, use\n DeleteUserPolicy. For information about policies, see Managed\n policies and inline policies in the\n IAM User Guide.

" + "smithy.api#documentation": "

Removes the specified managed policy from the specified user.

\n

A user can also have inline policies embedded with it. To delete an inline policy, use\n DeleteUserPolicy. For information about policies, see Managed\n policies and inline policies in the\n IAM User Guide.

" } }, "com.amazonaws.iam#DetachUserPolicyRequest": { @@ -5219,17 +5416,20 @@ "UserName": { "target": "com.amazonaws.iam#userNameType", "traits": { - "smithy.api#documentation": "

The name (friendly name, not ARN) of the IAM user to detach the policy from.

\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 (friendly name, not ARN) of the IAM user to detach the policy from.

\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": {} } }, "PolicyArn": { "target": "com.amazonaws.iam#arnType", "traits": { - "smithy.api#documentation": "

The Amazon Resource Name (ARN) of the IAM policy you want to detach.

\n

For more information about ARNs, see Amazon Resource Names (ARNs) in the Amazon Web Services General Reference.

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

The Amazon Resource Name (ARN) of the IAM policy you want to detach.

\n

For more information about ARNs, see Amazon Resource Names (ARNs) in the Amazon Web Services General Reference.

", "smithy.api#required": {} } } + }, + "traits": { + "smithy.api#input": {} } }, "com.amazonaws.iam#DuplicateCertificateException": { @@ -5304,31 +5504,34 @@ "UserName": { "target": "com.amazonaws.iam#existingUserNameType", "traits": { - "smithy.api#documentation": "

The name of the IAM user for whom you want to enable the 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#documentation": "

The name of the IAM user for whom you want to enable the 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": {} } }, "SerialNumber": { "target": "com.amazonaws.iam#serialNumberType", "traits": { - "smithy.api#documentation": "

The serial number that uniquely identifies the MFA device. For virtual MFA devices,\n the serial number is the device ARN.

\n

This parameter allows (through its regex pattern) a string of characters consisting \n of upper and lowercase alphanumeric characters with no spaces. You can also include any of the \n following characters: =,.@:/-

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

The serial number that uniquely identifies the MFA device. For virtual MFA devices,\n the serial number is the device ARN.

\n

This parameter allows (through its regex pattern) a string of characters consisting \n of upper and lowercase alphanumeric characters with no spaces. You can also include any of the \n following characters: =,.@:/-

", "smithy.api#required": {} } }, "AuthenticationCode1": { "target": "com.amazonaws.iam#authenticationCodeType", "traits": { - "smithy.api#documentation": "

An authentication code emitted by the device.

\n

The format for this parameter is a string of six digits.

\n \n

Submit your request immediately after generating the authentication codes. If you\n generate the codes and then wait too long to submit the request, the MFA device\n successfully associates with the user but the MFA device becomes out of sync. This\n happens because time-based one-time passwords (TOTP) expire after a short period of\n time. If this happens, you can resync the\n device.

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

An authentication code emitted by the device.

\n

The format for this parameter is a string of six digits.

\n \n

Submit your request immediately after generating the authentication codes. If you\n generate the codes and then wait too long to submit the request, the MFA device\n successfully associates with the user but the MFA device becomes out of sync. This\n happens because time-based one-time passwords (TOTP) expire after a short period of\n time. If this happens, you can resync the\n device.

\n
", "smithy.api#required": {} } }, "AuthenticationCode2": { "target": "com.amazonaws.iam#authenticationCodeType", "traits": { - "smithy.api#documentation": "

A subsequent authentication code emitted by the device.

\n

The format for this parameter is a string of six digits.

\n \n

Submit your request immediately after generating the authentication codes. If you\n generate the codes and then wait too long to submit the request, the MFA device\n successfully associates with the user but the MFA device becomes out of sync. This\n happens because time-based one-time passwords (TOTP) expire after a short period of\n time. If this happens, you can resync the\n device.

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

A subsequent authentication code emitted by the device.

\n

The format for this parameter is a string of six digits.

\n \n

Submit your request immediately after generating the authentication codes. If you\n generate the codes and then wait too long to submit the request, the MFA device\n successfully associates with the user but the MFA device becomes out of sync. This\n happens because time-based one-time passwords (TOTP) expire after a short period of\n time. If this happens, you can resync the\n device.

\n
", "smithy.api#required": {} } } + }, + "traits": { + "smithy.api#input": {} } }, "com.amazonaws.iam#EntityAlreadyExistsException": { @@ -5609,7 +5812,8 @@ } }, "traits": { - "smithy.api#documentation": "

Contains the response to a successful GenerateCredentialReport\n request.

" + "smithy.api#documentation": "

Contains the response to a successful GenerateCredentialReport\n request.

", + "smithy.api#output": {} } }, "com.amazonaws.iam#GenerateOrganizationsAccessReport": { @@ -5626,7 +5830,7 @@ } ], "traits": { - "smithy.api#documentation": "

Generates a report for service last accessed data for Organizations. You can generate a\n report for any entities (organization root, organizational unit, or account) or policies\n in your organization.

\n

To call this operation, you must be signed in using your Organizations management account\n credentials. You can use your long-term IAM user or root user credentials, or\n temporary credentials from assuming an IAM role. SCPs must be enabled for your\n organization root. You must have the required IAM and Organizations permissions. For more\n information, see Refining permissions using\n service last accessed data in the\n IAM User Guide.

\n

You can generate a service last accessed data report for entities by specifying only\n the entity's path. This data includes a list of services that are allowed by any service\n control policies (SCPs) that apply to the entity.

\n

You can generate a service last accessed data report for a policy by specifying an\n entity's path and an optional Organizations policy ID. This data includes a list of services that\n are allowed by the specified SCP.

\n

For each service in both report types, the data includes the most recent account\n activity that the policy allows to account principals in the entity or the entity's\n children. For important information about the data, reporting period, permissions\n required, troubleshooting, and supported Regions see Reducing permissions using\n service last accessed data in the\n IAM User Guide.

\n \n

The data includes all attempts to access Amazon Web Services, not just the successful ones. This\n includes all attempts that were made using the Amazon Web Services Management Console, the Amazon Web Services API through any\n of the SDKs, or any of the command line tools. An unexpected entry in the service\n last accessed data does not mean that an account has been compromised, because the\n request might have been denied. Refer to your CloudTrail logs as the authoritative\n source for information about all API calls and whether they were successful or\n denied access. For more information, see Logging IAM events with\n CloudTrail in the IAM User Guide.

\n
\n

This operation returns a JobId. Use this parameter in the \n GetOrganizationsAccessReport\n operation to check the status of\n the report generation. To check the status of this request, use the JobId\n parameter in the \n GetOrganizationsAccessReport\n operation\n and test the JobStatus response parameter. When the job is complete, you\n can retrieve the report.

\n

To generate a service last accessed data report for entities, specify an entity path\n without specifying the optional Organizations policy ID. The type of entity that you specify\n determines the data returned in the report.

\n
    \n
  • \n

    \n Root – When you specify the\n organizations root as the entity, the resulting report lists all of the services\n allowed by SCPs that are attached to your root. For each service, the report\n includes data for all accounts in your organization except the\n management account, because the management account is not limited by SCPs.

    \n
  • \n
  • \n

    \n OU – When you specify an\n organizational unit (OU) as the entity, the resulting report lists all of the\n services allowed by SCPs that are attached to the OU and its parents. For each\n service, the report includes data for all accounts in the OU or its children.\n This data excludes the management account, because the management account is not\n limited by SCPs.

    \n
  • \n
  • \n

    \n management account – When you specify the\n management account, the resulting report lists all Amazon Web Services services, because the\n management account is not limited by SCPs. For each service, the report includes\n data for only the management account.

    \n
  • \n
  • \n

    \n Account – When you specify another\n account as the entity, the resulting report lists all of the services allowed by\n SCPs that are attached to the account and its parents. For each service, the\n report includes data for only the specified account.

    \n
  • \n
\n

To generate a service last accessed data report for policies, specify an entity path\n and the optional Organizations policy ID. The type of entity that you specify determines the data\n returned for each service.

\n
    \n
  • \n

    \n Root – When you specify the root\n entity and a policy ID, the resulting report lists all of the services that are\n allowed by the specified SCP. For each service, the report includes data for all\n accounts in your organization to which the SCP applies. This data excludes the\n management account, because the management account is not limited by SCPs. If the\n SCP is not attached to any entities in the organization, then the report will\n return a list of services with no data.

    \n
  • \n
  • \n

    \n OU – When you specify an OU entity and\n a policy ID, the resulting report lists all of the services that are allowed by\n the specified SCP. For each service, the report includes data for all accounts\n in the OU or its children to which the SCP applies. This means that other\n accounts outside the OU that are affected by the SCP might not be included in\n the data. This data excludes the management account, because the\n management account is not limited by SCPs. If the SCP is not attached to the OU\n or one of its children, the report will return a list of services with no\n data.

    \n
  • \n
  • \n

    \n management account – When you specify the\n management account, the resulting report lists all Amazon Web Services services, because the\n management account is not limited by SCPs. If you specify a policy ID in the CLI\n or API, the policy is ignored. For each service, the report includes data for\n only the management account.

    \n
  • \n
  • \n

    \n Account – When you specify another\n account entity and a policy ID, the resulting report lists all of the services\n that are allowed by the specified SCP. For each service, the report includes\n data for only the specified account. This means that other accounts in the\n organization that are affected by the SCP might not be included in the data. If\n the SCP is not attached to the account, the report will return a list of\n services with no data.

    \n
  • \n
\n \n

Service last accessed data does not use other policy types when determining\n whether a principal could access a service. These other policy types include\n identity-based policies, resource-based policies, access control lists, IAM\n permissions boundaries, and STS assume role policies. It only applies SCP logic.\n For more about the evaluation of policy types, see Evaluating policies in the\n IAM User Guide.

\n
\n

For more information about service last accessed data, see Reducing policy scope by\n viewing user activity in the IAM User Guide.

" + "smithy.api#documentation": "

Generates a report for service last accessed data for Organizations. You can generate a\n report for any entities (organization root, organizational unit, or account) or policies\n in your organization.

\n

To call this operation, you must be signed in using your Organizations management account\n credentials. You can use your long-term IAM user or root user credentials, or temporary\n credentials from assuming an IAM role. SCPs must be enabled for your organization\n root. You must have the required IAM and Organizations permissions. For more information, see\n Refining permissions using service last accessed data in the\n IAM User Guide.

\n

You can generate a service last accessed data report for entities by specifying only\n the entity's path. This data includes a list of services that are allowed by any service\n control policies (SCPs) that apply to the entity.

\n

You can generate a service last accessed data report for a policy by specifying an\n entity's path and an optional Organizations policy ID. This data includes a list of services that\n are allowed by the specified SCP.

\n

For each service in both report types, the data includes the most recent account\n activity that the policy allows to account principals in the entity or the entity's\n children. For important information about the data, reporting period, permissions\n required, troubleshooting, and supported Regions see Reducing permissions using\n service last accessed data in the\n IAM User Guide.

\n \n

The data includes all attempts to access Amazon Web Services, not just the successful ones. This\n includes all attempts that were made using the Amazon Web Services Management Console, the Amazon Web Services API through any\n of the SDKs, or any of the command line tools. An unexpected entry in the service\n last accessed data does not mean that an account has been compromised, because the\n request might have been denied. Refer to your CloudTrail logs as the authoritative\n source for information about all API calls and whether they were successful or\n denied access. For more information, see Logging IAM events with\n CloudTrail in the IAM User Guide.

\n
\n

This operation returns a JobId. Use this parameter in the \n GetOrganizationsAccessReport\n operation to check the status of\n the report generation. To check the status of this request, use the JobId\n parameter in the \n GetOrganizationsAccessReport\n operation\n and test the JobStatus response parameter. When the job is complete, you\n can retrieve the report.

\n

To generate a service last accessed data report for entities, specify an entity path\n without specifying the optional Organizations policy ID. The type of entity that you specify\n determines the data returned in the report.

\n
    \n
  • \n

    \n Root – When you specify the\n organizations root as the entity, the resulting report lists all of the services\n allowed by SCPs that are attached to your root. For each service, the report\n includes data for all accounts in your organization except the\n management account, because the management account is not limited by SCPs.

    \n
  • \n
  • \n

    \n OU – When you specify an\n organizational unit (OU) as the entity, the resulting report lists all of the\n services allowed by SCPs that are attached to the OU and its parents. For each\n service, the report includes data for all accounts in the OU or its children.\n This data excludes the management account, because the management account is not\n limited by SCPs.

    \n
  • \n
  • \n

    \n management account – When you specify the\n management account, the resulting report lists all Amazon Web Services services, because the\n management account is not limited by SCPs. For each service, the report includes\n data for only the management account.

    \n
  • \n
  • \n

    \n Account – When you specify another\n account as the entity, the resulting report lists all of the services allowed by\n SCPs that are attached to the account and its parents. For each service, the\n report includes data for only the specified account.

    \n
  • \n
\n

To generate a service last accessed data report for policies, specify an entity path\n and the optional Organizations policy ID. The type of entity that you specify determines the data\n returned for each service.

\n
    \n
  • \n

    \n Root – When you specify the root\n entity and a policy ID, the resulting report lists all of the services that are\n allowed by the specified SCP. For each service, the report includes data for all\n accounts in your organization to which the SCP applies. This data excludes the\n management account, because the management account is not limited by SCPs. If the\n SCP is not attached to any entities in the organization, then the report will\n return a list of services with no data.

    \n
  • \n
  • \n

    \n OU – When you specify an OU entity and\n a policy ID, the resulting report lists all of the services that are allowed by\n the specified SCP. For each service, the report includes data for all accounts\n in the OU or its children to which the SCP applies. This means that other\n accounts outside the OU that are affected by the SCP might not be included in\n the data. This data excludes the management account, because the\n management account is not limited by SCPs. If the SCP is not attached to the OU\n or one of its children, the report will return a list of services with no\n data.

    \n
  • \n
  • \n

    \n management account – When you specify the\n management account, the resulting report lists all Amazon Web Services services, because the\n management account is not limited by SCPs. If you specify a policy ID in the CLI\n or API, the policy is ignored. For each service, the report includes data for\n only the management account.

    \n
  • \n
  • \n

    \n Account – When you specify another\n account entity and a policy ID, the resulting report lists all of the services\n that are allowed by the specified SCP. For each service, the report includes\n data for only the specified account. This means that other accounts in the\n organization that are affected by the SCP might not be included in the data. If\n the SCP is not attached to the account, the report will return a list of\n services with no data.

    \n
  • \n
\n \n

Service last accessed data does not use other policy types when determining\n whether a principal could access a service. These other policy types include\n identity-based policies, resource-based policies, access control lists, IAM\n permissions boundaries, and STS assume role policies. It only applies SCP logic.\n For more about the evaluation of policy types, see Evaluating policies in the\n IAM User Guide.

\n
\n

For more information about service last accessed data, see Reducing policy scope by\n viewing user activity in the IAM User Guide.

" } }, "com.amazonaws.iam#GenerateOrganizationsAccessReportRequest": { @@ -5642,9 +5846,12 @@ "OrganizationsPolicyId": { "target": "com.amazonaws.iam#organizationsPolicyIdType", "traits": { - "smithy.api#documentation": "

The identifier of the Organizations service control policy (SCP). This parameter is\n optional.

\n

This ID is used to generate information about when an account principal that is\n limited by the SCP attempted to access an Amazon Web Services service.

" + "smithy.api#documentation": "

The identifier of the Organizations service control policy (SCP). This parameter is\n optional.

\n

This ID is used to generate information about when an account principal that is\n limited by the SCP attempted to access an Amazon Web Services service.

" } } + }, + "traits": { + "smithy.api#input": {} } }, "com.amazonaws.iam#GenerateOrganizationsAccessReportResponse": { @@ -5656,6 +5863,9 @@ "smithy.api#documentation": "

The job identifier that you can use in the GetOrganizationsAccessReport operation.

" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.iam#GenerateServiceLastAccessedDetails": { @@ -5675,7 +5885,7 @@ } ], "traits": { - "smithy.api#documentation": "

Generates a report that includes details about when an IAM resource (user, group,\n role, or policy) was last used in an attempt to access Amazon Web Services services. Recent activity\n usually appears within four hours. IAM reports activity for at least the last 400\n days, or less if your Region began supporting this feature within the last year. For\n more information, see Regions where data is tracked.

\n \n

The service last accessed data includes all attempts to access an Amazon Web Services API, not\n just the successful ones. This includes all attempts that were made using the\n Amazon Web Services Management Console, the Amazon Web Services API through any of the SDKs, or any of the command line tools.\n An unexpected entry in the service last accessed data does not mean that your\n account has been compromised, because the request might have been denied. Refer to\n your CloudTrail logs as the authoritative source for information about all API calls\n and whether they were successful or denied access. For more information, see Logging\n IAM events with CloudTrail in the\n IAM User Guide.

\n
\n

The GenerateServiceLastAccessedDetails operation returns a\n JobId. Use this parameter in the following operations to retrieve the\n following details from your report:

\n
    \n
  • \n

    \n GetServiceLastAccessedDetails – Use this operation\n for users, groups, roles, or policies to list every Amazon Web Services service that the\n resource could access using permissions policies. For each service, the response\n includes information about the most recent access attempt.

    \n

    The JobId returned by\n GenerateServiceLastAccessedDetail must be used by the same role\n within a session, or by the same user when used to call\n GetServiceLastAccessedDetail.

    \n
  • \n
  • \n

    \n GetServiceLastAccessedDetailsWithEntities – Use this\n operation for groups and policies to list information about the associated\n entities (users or roles) that attempted to access a specific Amazon Web Services service.\n

    \n
  • \n
\n

To check the status of the GenerateServiceLastAccessedDetails request,\n use the JobId parameter in the same operations and test the\n JobStatus response parameter.

\n

For additional information about the permissions policies that allow an identity\n (user, group, or role) to access specific services, use the ListPoliciesGrantingServiceAccess operation.

\n \n

Service last accessed data does not use other policy types when determining\n whether a resource could access a service. These other policy types include\n resource-based policies, access control lists, Organizations policies, IAM permissions\n boundaries, and STS assume role policies. It only applies permissions policy\n logic. For more about the evaluation of policy types, see Evaluating policies in the\n IAM User Guide.

\n
\n

For more information about service and action last accessed data, see Reducing permissions using service last accessed data in the\n IAM User Guide.

" + "smithy.api#documentation": "

Generates a report that includes details about when an IAM resource (user, group,\n role, or policy) was last used in an attempt to access Amazon Web Services services. Recent activity\n usually appears within four hours. IAM reports activity for at least the last 400\n days, or less if your Region began supporting this feature within the last year. For\n more information, see Regions where data is tracked.

\n \n

The service last accessed data includes all attempts to access an Amazon Web Services API, not\n just the successful ones. This includes all attempts that were made using the\n Amazon Web Services Management Console, the Amazon Web Services API through any of the SDKs, or any of the command line tools.\n An unexpected entry in the service last accessed data does not mean that your\n account has been compromised, because the request might have been denied. Refer to\n your CloudTrail logs as the authoritative source for information about all API calls\n and whether they were successful or denied access. For more information, see Logging\n IAM events with CloudTrail in the\n IAM User Guide.

\n
\n

The GenerateServiceLastAccessedDetails operation returns a\n JobId. Use this parameter in the following operations to retrieve the\n following details from your report:

\n
    \n
  • \n

    \n GetServiceLastAccessedDetails – Use this operation\n for users, groups, roles, or policies to list every Amazon Web Services service that the\n resource could access using permissions policies. For each service, the response\n includes information about the most recent access attempt.

    \n

    The JobId returned by\n GenerateServiceLastAccessedDetail must be used by the same role\n within a session, or by the same user when used to call\n GetServiceLastAccessedDetail.

    \n
  • \n
  • \n

    \n GetServiceLastAccessedDetailsWithEntities – Use this\n operation for groups and policies to list information about the associated\n entities (users or roles) that attempted to access a specific Amazon Web Services service.\n

    \n
  • \n
\n

To check the status of the GenerateServiceLastAccessedDetails request,\n use the JobId parameter in the same operations and test the\n JobStatus response parameter.

\n

For additional information about the permissions policies that allow an identity\n (user, group, or role) to access specific services, use the ListPoliciesGrantingServiceAccess operation.

\n \n

Service last accessed data does not use other policy types when determining\n whether a resource could access a service. These other policy types include\n resource-based policies, access control lists, Organizations policies, IAM permissions\n boundaries, and STS assume role policies. It only applies permissions policy\n logic. For more about the evaluation of policy types, see Evaluating policies in the\n IAM User Guide.

\n
\n

For more information about service and action last accessed data, see Reducing permissions using service last accessed data in the\n IAM User Guide.

" } }, "com.amazonaws.iam#GenerateServiceLastAccessedDetailsRequest": { @@ -5694,6 +5904,9 @@ "smithy.api#documentation": "

The level of detail that you want to generate. You can specify whether you want to\n generate information about the last attempt to access services or actions. If you\n specify service-level granularity, this operation generates only service data. If you\n specify action-level granularity, it generates service and action data. If you don't\n include this optional parameter, the operation generates service data.

" } } + }, + "traits": { + "smithy.api#input": {} } }, "com.amazonaws.iam#GenerateServiceLastAccessedDetailsResponse": { @@ -5705,6 +5918,9 @@ "smithy.api#documentation": "

The JobId that you can use in the GetServiceLastAccessedDetails or GetServiceLastAccessedDetailsWithEntities operations. The\n JobId returned by GenerateServiceLastAccessedDetail must\n be used by the same role within a session, or by the same user when used to call\n GetServiceLastAccessedDetail.

" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.iam#GetAccessKeyLastUsed": { @@ -5730,10 +5946,13 @@ "AccessKeyId": { "target": "com.amazonaws.iam#accessKeyIdType", "traits": { - "smithy.api#documentation": "

The identifier of an access key.

\n

This parameter allows (through its regex pattern) a string of characters that can \n consist of any upper or lowercased letter or digit.

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

The identifier of an access key.

\n

This parameter allows (through its regex pattern) a string of characters that can \n consist of any upper or lowercased letter or digit.

", "smithy.api#required": {} } } + }, + "traits": { + "smithy.api#input": {} } }, "com.amazonaws.iam#GetAccessKeyLastUsedResponse": { @@ -5742,7 +5961,7 @@ "UserName": { "target": "com.amazonaws.iam#existingUserNameType", "traits": { - "smithy.api#documentation": "

The name of the IAM user that owns this access key.

\n

" + "smithy.api#documentation": "

The name of the IAM user that owns this access key.

\n

" } }, "AccessKeyLastUsed": { @@ -5753,7 +5972,8 @@ } }, "traits": { - "smithy.api#documentation": "

Contains the response to a successful GetAccessKeyLastUsed request.\n It is also returned as a member of the AccessKeyMetaData structure returned\n by the ListAccessKeys action.

" + "smithy.api#documentation": "

Contains the response to a successful GetAccessKeyLastUsed request.\n It is also returned as a member of the AccessKeyMetaData structure returned\n by the ListAccessKeys action.

", + "smithy.api#output": {} } }, "com.amazonaws.iam#GetAccountAuthorizationDetails": { @@ -5770,7 +5990,7 @@ } ], "traits": { - "smithy.api#documentation": "

Retrieves information about all IAM users, groups, roles, and policies in your Amazon Web Services\n account, including their relationships to one another. Use this operation to obtain a\n snapshot of the configuration of IAM permissions (users, groups, roles, and policies)\n in your account.

\n \n

Policies returned by this operation are URL-encoded compliant \n with RFC 3986. You can use a URL \n decoding method to convert the policy back to plain JSON text. For example, if you use Java, you \n can use the decode method of the java.net.URLDecoder utility class in \n the Java SDK. Other languages and SDKs provide similar functionality.

\n
\n

You can optionally filter the results using the Filter parameter. You can\n paginate the results using the MaxItems and Marker\n parameters.

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

Retrieves information about all IAM users, groups, roles, and policies in your Amazon Web Services\n account, including their relationships to one another. Use this operation to obtain a\n snapshot of the configuration of IAM permissions (users, groups, roles, and policies)\n in your account.

\n \n

Policies returned by this operation are URL-encoded compliant \n with RFC 3986. You can use a URL \n decoding method to convert the policy back to plain JSON text. For example, if you use Java, you \n can use the decode method of the java.net.URLDecoder utility class in \n the Java SDK. Other languages and SDKs provide similar functionality.

\n
\n

You can optionally filter the results using the Filter parameter. You can\n paginate the results using the MaxItems and Marker\n parameters.

", "smithy.api#paginated": { "inputToken": "Marker", "outputToken": "Marker", @@ -5784,7 +6004,7 @@ "Filter": { "target": "com.amazonaws.iam#entityListType", "traits": { - "smithy.api#documentation": "

A list of entity types used to filter the results. Only the entities that match the\n types you specify are included in the output. Use the value\n LocalManagedPolicy to include customer managed policies.

\n

The format for this parameter is a comma-separated (if more than one) list of strings.\n Each string value in the list must be one of the valid values listed below.

" + "smithy.api#documentation": "

A list of entity types used to filter the results. Only the entities that match the\n types you specify are included in the output. Use the value\n LocalManagedPolicy to include customer managed policies.

\n

The format for this parameter is a comma-separated (if more than one) list of strings.\n Each string value in the list must be one of the valid values listed below.

" } }, "MaxItems": { @@ -5799,6 +6019,9 @@ "smithy.api#documentation": "

Use this parameter only when paginating results and only after \n you receive a response indicating that the results are truncated. Set it to the value of the\n Marker element in the response that you received to indicate where the next call \n should start.

" } } + }, + "traits": { + "smithy.api#input": {} } }, "com.amazonaws.iam#GetAccountAuthorizationDetailsResponse": { @@ -5843,7 +6066,8 @@ } }, "traits": { - "smithy.api#documentation": "

Contains the response to a successful GetAccountAuthorizationDetails\n request.

" + "smithy.api#documentation": "

Contains the response to a successful GetAccountAuthorizationDetails\n request.

", + "smithy.api#output": {} } }, "com.amazonaws.iam#GetAccountPasswordPolicy": { @@ -5878,7 +6102,8 @@ } }, "traits": { - "smithy.api#documentation": "

Contains the response to a successful GetAccountPasswordPolicy\n request.

" + "smithy.api#documentation": "

Contains the response to a successful GetAccountPasswordPolicy\n request.

", + "smithy.api#output": {} } }, "com.amazonaws.iam#GetAccountSummary": { @@ -5895,7 +6120,7 @@ } ], "traits": { - "smithy.api#documentation": "

Retrieves information about IAM entity usage and IAM quotas in the Amazon Web Services\n account.

\n

For information about IAM quotas, see IAM and STS quotas in the\n IAM User Guide.

" + "smithy.api#documentation": "

Retrieves information about IAM entity usage and IAM quotas in the Amazon Web Services\n account.

\n

For information about IAM quotas, see IAM and STS quotas in the\n IAM User Guide.

" } }, "com.amazonaws.iam#GetAccountSummaryResponse": { @@ -5909,7 +6134,8 @@ } }, "traits": { - "smithy.api#documentation": "

Contains the response to a successful GetAccountSummary request.\n

" + "smithy.api#documentation": "

Contains the response to a successful GetAccountSummary request.\n

", + "smithy.api#output": {} } }, "com.amazonaws.iam#GetContextKeysForCustomPolicy": { @@ -5926,7 +6152,7 @@ } ], "traits": { - "smithy.api#documentation": "

Gets a list of all of the context keys referenced in the input policies. The policies\n are supplied as a list of one or more strings. To get the context keys from policies\n associated with an IAM user, group, or role, use GetContextKeysForPrincipalPolicy.

\n

Context keys are variables maintained by Amazon Web Services and its services that provide details\n about the context of an API query request. Context keys can be evaluated by testing\n against a value specified in an IAM policy. Use\n GetContextKeysForCustomPolicy to understand what key names and values\n you must supply when you call SimulateCustomPolicy. Note that all\n parameters are shown in unencoded form here for clarity but must be URL encoded to be\n included as a part of a real HTML request.

" + "smithy.api#documentation": "

Gets a list of all of the context keys referenced in the input policies. The policies\n are supplied as a list of one or more strings. To get the context keys from policies\n associated with an IAM user, group, or role, use GetContextKeysForPrincipalPolicy.

\n

Context keys are variables maintained by Amazon Web Services and its services that provide details\n about the context of an API query request. Context keys can be evaluated by testing\n against a value specified in an IAM policy. Use\n GetContextKeysForCustomPolicy to understand what key names and values\n you must supply when you call SimulateCustomPolicy. Note that all\n parameters are shown in unencoded form here for clarity but must be URL encoded to be\n included as a part of a real HTML request.

" } }, "com.amazonaws.iam#GetContextKeysForCustomPolicyRequest": { @@ -5935,10 +6161,13 @@ "PolicyInputList": { "target": "com.amazonaws.iam#SimulationPolicyListType", "traits": { - "smithy.api#documentation": "

A list of policies for which you want the list of context keys referenced in those\n policies. Each document is specified as a string containing the complete, valid JSON\n text of an IAM policy.

\n

The regex pattern \n used to validate this parameter is a string of characters consisting of the following:

\n
    \n
  • \n

    Any printable ASCII \n character ranging from the space character (\\u0020) through the end of the ASCII character range

    \n
  • \n
  • \n

    The printable characters in the Basic Latin and Latin-1 Supplement character set \n (through \\u00FF)

    \n
  • \n
  • \n

    The special characters tab (\\u0009), line feed (\\u000A), and \n carriage return (\\u000D)

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

A list of policies for which you want the list of context keys referenced in those\n policies. Each document is specified as a string containing the complete, valid JSON\n text of an IAM policy.

\n

The regex pattern \n used to validate this parameter is a string of characters consisting of the following:

\n
    \n
  • \n

    Any printable ASCII \n character ranging from the space character (\\u0020) through the end of the ASCII character range

    \n
  • \n
  • \n

    The printable characters in the Basic Latin and Latin-1 Supplement character set \n (through \\u00FF)

    \n
  • \n
  • \n

    The special characters tab (\\u0009), line feed (\\u000A), and \n carriage return (\\u000D)

    \n
  • \n
", "smithy.api#required": {} } } + }, + "traits": { + "smithy.api#input": {} } }, "com.amazonaws.iam#GetContextKeysForPolicyResponse": { @@ -5972,7 +6201,7 @@ } ], "traits": { - "smithy.api#documentation": "

Gets a list of all of the context keys referenced in all the IAM policies that are\n attached to the specified IAM entity. The entity can be an IAM user, group, or role.\n If you specify a user, then the request also includes all of the policies attached to\n groups that the user is a member of.

\n

You can optionally include a list of one or more additional policies, specified as\n strings. If you want to include only a list of policies by string,\n use GetContextKeysForCustomPolicy instead.

\n

\n Note: This operation discloses information about the\n permissions granted to other users. If you do not want users to see other user's\n permissions, then consider allowing them to use GetContextKeysForCustomPolicy instead.

\n

Context keys are variables maintained by Amazon Web Services and its services that provide details\n about the context of an API query request. Context keys can be evaluated by testing\n against a value in an IAM policy. Use GetContextKeysForPrincipalPolicy to understand what key names and values you must supply when you call SimulatePrincipalPolicy.

" + "smithy.api#documentation": "

Gets a list of all of the context keys referenced in all the IAM policies that are\n attached to the specified IAM entity. The entity can be an IAM user, group, or role.\n If you specify a user, then the request also includes all of the policies attached to\n groups that the user is a member of.

\n

You can optionally include a list of one or more additional policies, specified as\n strings. If you want to include only a list of policies by string,\n use GetContextKeysForCustomPolicy instead.

\n

\n Note: This operation discloses information about the\n permissions granted to other users. If you do not want users to see other user's\n permissions, then consider allowing them to use GetContextKeysForCustomPolicy instead.

\n

Context keys are variables maintained by Amazon Web Services and its services that provide details\n about the context of an API query request. Context keys can be evaluated by testing\n against a value in an IAM policy. Use GetContextKeysForPrincipalPolicy to understand what key names and values you must supply when you call SimulatePrincipalPolicy.

" } }, "com.amazonaws.iam#GetContextKeysForPrincipalPolicyRequest": { @@ -5981,16 +6210,19 @@ "PolicySourceArn": { "target": "com.amazonaws.iam#arnType", "traits": { - "smithy.api#documentation": "

The ARN of a user, group, or role whose policies contain the context keys that you\n want listed. If you specify a user, the list includes context keys that are found in all\n policies that are attached to the user. The list also includes all groups that the user\n is a member of. If you pick a group or a role, then it includes only those context keys\n that are found in policies attached to that entity. Note that all parameters are shown\n in unencoded form here for clarity, but must be URL encoded to be included as a part of\n a real HTML request.

\n

For more information about ARNs, see Amazon Resource Names (ARNs) in the Amazon Web Services General Reference.

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

The ARN of a user, group, or role whose policies contain the context keys that you\n want listed. If you specify a user, the list includes context keys that are found in all\n policies that are attached to the user. The list also includes all groups that the user\n is a member of. If you pick a group or a role, then it includes only those context keys\n that are found in policies attached to that entity. Note that all parameters are shown\n in unencoded form here for clarity, but must be URL encoded to be included as a part of\n a real HTML request.

\n

For more information about ARNs, see Amazon Resource Names (ARNs) in the Amazon Web Services General Reference.

", "smithy.api#required": {} } }, "PolicyInputList": { "target": "com.amazonaws.iam#SimulationPolicyListType", "traits": { - "smithy.api#documentation": "

An optional list of additional policies for which you want the list of context keys\n that are referenced.

\n

The regex pattern \n used to validate this parameter is a string of characters consisting of the following:

\n
    \n
  • \n

    Any printable ASCII \n character ranging from the space character (\\u0020) through the end of the ASCII character range

    \n
  • \n
  • \n

    The printable characters in the Basic Latin and Latin-1 Supplement character set \n (through \\u00FF)

    \n
  • \n
  • \n

    The special characters tab (\\u0009), line feed (\\u000A), and \n carriage return (\\u000D)

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

An optional list of additional policies for which you want the list of context keys\n that are referenced.

\n

The regex pattern \n used to validate this parameter is a string of characters consisting of the following:

\n
    \n
  • \n

    Any printable ASCII \n character ranging from the space character (\\u0020) through the end of the ASCII character range

    \n
  • \n
  • \n

    The printable characters in the Basic Latin and Latin-1 Supplement character set \n (through \\u00FF)

    \n
  • \n
  • \n

    The special characters tab (\\u0009), line feed (\\u000A), and \n carriage return (\\u000D)

    \n
  • \n
" } } + }, + "traits": { + "smithy.api#input": {} } }, "com.amazonaws.iam#GetCredentialReport": { @@ -6042,7 +6274,8 @@ } }, "traits": { - "smithy.api#documentation": "

Contains the response to a successful GetCredentialReport request.\n

" + "smithy.api#documentation": "

Contains the response to a successful GetCredentialReport request.\n

", + "smithy.api#output": {} } }, "com.amazonaws.iam#GetGroup": { @@ -6088,7 +6321,7 @@ } ], "traits": { - "smithy.api#documentation": "

Retrieves the specified inline policy document that is embedded in the specified IAM\n group.

\n \n

Policies returned by this operation are URL-encoded compliant \n with RFC 3986. You can use a URL \n decoding method to convert the policy back to plain JSON text. For example, if you use Java, you \n can use the decode method of the java.net.URLDecoder utility class in \n the Java SDK. Other languages and SDKs provide similar functionality.

\n
\n

An IAM group can also have managed policies attached to it. To retrieve a managed\n policy document that is attached to a group, use GetPolicy to\n determine the policy's default version, then use GetPolicyVersion to\n retrieve the policy document.

\n

For more information about policies, see Managed policies and inline\n policies in the IAM User Guide.

" + "smithy.api#documentation": "

Retrieves the specified inline policy document that is embedded in the specified IAM\n group.

\n \n

Policies returned by this operation are URL-encoded compliant \n with RFC 3986. You can use a URL \n decoding method to convert the policy back to plain JSON text. For example, if you use Java, you \n can use the decode method of the java.net.URLDecoder utility class in \n the Java SDK. Other languages and SDKs provide similar functionality.

\n
\n

An IAM group can also have managed policies attached to it. To retrieve a managed\n policy document that is attached to a group, use GetPolicy to\n determine the policy's default version, then use GetPolicyVersion to\n retrieve the policy document.

\n

For more information about policies, see Managed policies and inline\n policies in the IAM User Guide.

" } }, "com.amazonaws.iam#GetGroupPolicyRequest": { @@ -6097,17 +6330,20 @@ "GroupName": { "target": "com.amazonaws.iam#groupNameType", "traits": { - "smithy.api#documentation": "

The name of the group the policy is associated with.

\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 group the policy is associated with.

\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": {} } }, "PolicyName": { "target": "com.amazonaws.iam#policyNameType", "traits": { - "smithy.api#documentation": "

The name of the policy document to get.

\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 policy document to get.

\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": {} } } + }, + "traits": { + "smithy.api#input": {} } }, "com.amazonaws.iam#GetGroupPolicyResponse": { @@ -6130,13 +6366,14 @@ "PolicyDocument": { "target": "com.amazonaws.iam#policyDocumentType", "traits": { - "smithy.api#documentation": "

The policy document.

\n\n

IAM stores policies in JSON format. However, resources that were created using CloudFormation\n templates can be formatted in YAML. CloudFormation always converts a YAML policy to JSON format\n before submitting it to IAM.

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

The policy document.

\n

IAM stores policies in JSON format. However, resources that were created using CloudFormation\n templates can be formatted in YAML. CloudFormation always converts a YAML policy to JSON format\n before submitting it to IAM.

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

Contains the response to a successful GetGroupPolicy request.\n

" + "smithy.api#documentation": "

Contains the response to a successful GetGroupPolicy request.\n

", + "smithy.api#output": {} } }, "com.amazonaws.iam#GetGroupRequest": { @@ -6145,7 +6382,7 @@ "GroupName": { "target": "com.amazonaws.iam#groupNameType", "traits": { - "smithy.api#documentation": "

The name of the group.

\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 group.

\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": {} } }, @@ -6161,6 +6398,9 @@ "smithy.api#documentation": "

Use this only when paginating results to indicate the \n maximum number of items you want in the response. If additional items exist beyond the maximum \n you specify, the IsTruncated response element is true.

\n

If you do not include this parameter, the number of items defaults to 100. Note that\n IAM might return fewer results, even when there are more results available. In that case, the\n IsTruncated response element returns true, and Marker \n contains a value to include in the subsequent call that tells the service where to continue \n from.

" } } + }, + "traits": { + "smithy.api#input": {} } }, "com.amazonaws.iam#GetGroupResponse": { @@ -6195,7 +6435,8 @@ } }, "traits": { - "smithy.api#documentation": "

Contains the response to a successful GetGroup request.

" + "smithy.api#documentation": "

Contains the response to a successful GetGroup request.

", + "smithy.api#output": {} } }, "com.amazonaws.iam#GetInstanceProfile": { @@ -6243,10 +6484,13 @@ "InstanceProfileName": { "target": "com.amazonaws.iam#instanceProfileNameType", "traits": { - "smithy.api#documentation": "

The name of the instance profile to get information about.

\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 instance profile to get information about.

\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": {} } } + }, + "traits": { + "smithy.api#input": {} } }, "com.amazonaws.iam#GetInstanceProfileResponse": { @@ -6261,7 +6505,8 @@ } }, "traits": { - "smithy.api#documentation": "

Contains the response to a successful GetInstanceProfile request.\n

" + "smithy.api#documentation": "

Contains the response to a successful GetInstanceProfile request.\n

", + "smithy.api#output": {} } }, "com.amazonaws.iam#GetLoginProfile": { @@ -6281,7 +6526,7 @@ } ], "traits": { - "smithy.api#documentation": "

Retrieves the user name for the specified IAM user. A login profile is created when\n you create a password for the user to access the Amazon Web Services Management Console. If the user does not exist\n or does not have a password, the operation returns a 404 (NoSuchEntity)\n error.

\n

If you create an IAM user with access to the console, the CreateDate\n reflects the date you created the initial password for the user.

\n

If you create an IAM user with programmatic access, and then later add a password\n for the user to access the Amazon Web Services Management Console, the CreateDate reflects the initial\n password creation date. A user with programmatic access does not have a login profile\n unless you create a password for the user to access the Amazon Web Services Management Console.

" + "smithy.api#documentation": "

Retrieves the user name for the specified IAM user. A login profile is created when\n you create a password for the user to access the Amazon Web Services Management Console. If the user does not exist\n or does not have a password, the operation returns a 404 (NoSuchEntity)\n error.

\n

If you create an IAM user with access to the console, the CreateDate\n reflects the date you created the initial password for the user.

\n

If you create an IAM user with programmatic access, and then later add a password\n for the user to access the Amazon Web Services Management Console, the CreateDate reflects the initial\n password creation date. A user with programmatic access does not have a login profile\n unless you create a password for the user to access the Amazon Web Services Management Console.

" } }, "com.amazonaws.iam#GetLoginProfileRequest": { @@ -6290,10 +6535,13 @@ "UserName": { "target": "com.amazonaws.iam#userNameType", "traits": { - "smithy.api#documentation": "

The name of the user whose login profile you want to retrieve.

\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 user whose login profile you want to retrieve.

\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": {} } } + }, + "traits": { + "smithy.api#input": {} } }, "com.amazonaws.iam#GetLoginProfileResponse": { @@ -6308,7 +6556,8 @@ } }, "traits": { - "smithy.api#documentation": "

Contains the response to a successful GetLoginProfile request.\n

" + "smithy.api#documentation": "

Contains the response to a successful GetLoginProfile request.\n

", + "smithy.api#output": {} } }, "com.amazonaws.iam#GetOpenIDConnectProvider": { @@ -6340,10 +6589,13 @@ "OpenIDConnectProviderArn": { "target": "com.amazonaws.iam#arnType", "traits": { - "smithy.api#documentation": "

The Amazon Resource Name (ARN) of the OIDC provider resource object in IAM to get\n information for. You can get a list of OIDC provider resource ARNs by using the ListOpenIDConnectProviders operation.

\n

For more information about ARNs, see Amazon Resource Names (ARNs) in the Amazon Web Services General Reference.

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

The Amazon Resource Name (ARN) of the OIDC provider resource object in IAM to get\n information for. You can get a list of OIDC provider resource ARNs by using the ListOpenIDConnectProviders operation.

\n

For more information about ARNs, see Amazon Resource Names (ARNs) in the Amazon Web Services General Reference.

", "smithy.api#required": {} } } + }, + "traits": { + "smithy.api#input": {} } }, "com.amazonaws.iam#GetOpenIDConnectProviderResponse": { @@ -6381,7 +6633,8 @@ } }, "traits": { - "smithy.api#documentation": "

Contains the response to a successful GetOpenIDConnectProvider\n request.

" + "smithy.api#documentation": "

Contains the response to a successful GetOpenIDConnectProvider\n request.

", + "smithy.api#output": {} } }, "com.amazonaws.iam#GetOrganizationsAccessReport": { @@ -6398,7 +6651,7 @@ } ], "traits": { - "smithy.api#documentation": "

Retrieves the service last accessed data report for Organizations that was previously\n generated using the \n GenerateOrganizationsAccessReport\n \n operation. This operation retrieves the status of your report job and the report\n contents.

\n

Depending on the parameters that you passed when you generated the report, the data\n returned could include different information. For details, see GenerateOrganizationsAccessReport.

\n

To call this operation, you must be signed in to the management account in your\n organization. SCPs must be enabled for your organization root. You must have permissions\n to perform this operation. For more information, see Refining permissions using\n service last accessed data in the\n IAM User Guide.

\n

For each service that principals in an account (root users, IAM users, or IAM\n roles) could access using SCPs, the operation returns details about the most recent\n access attempt. If there was no attempt, the service is listed without details about the\n most recent attempt to access the service. If the operation fails, it returns the reason\n that it failed.

\n

By default, the list is sorted by service namespace.

" + "smithy.api#documentation": "

Retrieves the service last accessed data report for Organizations that was previously\n generated using the \n GenerateOrganizationsAccessReport\n \n operation. This operation retrieves the status of your report job and the report\n contents.

\n

Depending on the parameters that you passed when you generated the report, the data\n returned could include different information. For details, see GenerateOrganizationsAccessReport.

\n

To call this operation, you must be signed in to the management account in your\n organization. SCPs must be enabled for your organization root. You must have permissions\n to perform this operation. For more information, see Refining permissions using\n service last accessed data in the\n IAM User Guide.

\n

For each service that principals in an account (root user, IAM users, or IAM roles)\n could access using SCPs, the operation returns details about the most recent access\n attempt. If there was no attempt, the service is listed without details about the most\n recent attempt to access the service. If the operation fails, it returns the reason that\n it failed.

\n

By default, the list is sorted by service namespace.

" } }, "com.amazonaws.iam#GetOrganizationsAccessReportRequest": { @@ -6429,6 +6682,9 @@ "smithy.api#documentation": "

The key that is used to sort the results. If you choose the namespace key, the results\n are returned in alphabetical order. If you choose the time key, the results are sorted\n numerically by the date and time.

" } } + }, + "traits": { + "smithy.api#input": {} } }, "com.amazonaws.iam#GetOrganizationsAccessReportResponse": { @@ -6451,7 +6707,7 @@ "JobCompletionDate": { "target": "com.amazonaws.iam#dateType", "traits": { - "smithy.api#documentation": "

The date and time, in ISO 8601 date-time\n format, when the generated report job was completed or failed.

\n

This field is null if the job is still in progress, as indicated by a job status value\n of IN_PROGRESS.

" + "smithy.api#documentation": "

The date and time, in ISO 8601 date-time\n format, when the generated report job was completed or failed.

\n

This field is null if the job is still in progress, as indicated by a job status value\n of IN_PROGRESS.

" } }, "NumberOfServicesAccessible": { @@ -6488,6 +6744,9 @@ "ErrorDetails": { "target": "com.amazonaws.iam#ErrorDetails" } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.iam#GetPolicy": { @@ -6510,7 +6769,7 @@ } ], "traits": { - "smithy.api#documentation": "

Retrieves information about the specified managed policy, including the policy's\n default version and the total number of IAM users, groups, and roles to which the\n policy is attached. To retrieve the list of the specific users, groups, and roles that\n the policy is attached to, use ListEntitiesForPolicy. This operation\n returns metadata about the policy. To retrieve the actual policy document for a specific\n version of the policy, use GetPolicyVersion.

\n

This operation retrieves information about managed policies. To retrieve information\n about an inline policy that is embedded with an IAM user, group, or role, use GetUserPolicy, GetGroupPolicy, or GetRolePolicy.

\n

For more information about policies, see Managed policies and inline\n policies in the IAM User Guide.

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

Retrieves information about the specified managed policy, including the policy's\n default version and the total number of IAM users, groups, and roles to which the\n policy is attached. To retrieve the list of the specific users, groups, and roles that\n the policy is attached to, use ListEntitiesForPolicy. This operation\n returns metadata about the policy. To retrieve the actual policy document for a specific\n version of the policy, use GetPolicyVersion.

\n

This operation retrieves information about managed policies. To retrieve information\n about an inline policy that is embedded with an IAM user, group, or role, use GetUserPolicy, GetGroupPolicy, or GetRolePolicy.

\n

For more information about policies, see Managed policies and inline\n policies in the IAM User Guide.

", "smithy.api#suppress": [ "WaitableTraitInvalidErrorType" ], @@ -6541,10 +6800,13 @@ "PolicyArn": { "target": "com.amazonaws.iam#arnType", "traits": { - "smithy.api#documentation": "

The Amazon Resource Name (ARN) of the managed policy that you want information\n about.

\n

For more information about ARNs, see Amazon Resource Names (ARNs) in the Amazon Web Services General Reference.

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

The Amazon Resource Name (ARN) of the managed policy that you want information\n about.

\n

For more information about ARNs, see Amazon Resource Names (ARNs) in the Amazon Web Services General Reference.

", "smithy.api#required": {} } } + }, + "traits": { + "smithy.api#input": {} } }, "com.amazonaws.iam#GetPolicyResponse": { @@ -6558,7 +6820,8 @@ } }, "traits": { - "smithy.api#documentation": "

Contains the response to a successful GetPolicy request.

" + "smithy.api#documentation": "

Contains the response to a successful GetPolicy request.

", + "smithy.api#output": {} } }, "com.amazonaws.iam#GetPolicyVersion": { @@ -6581,7 +6844,7 @@ } ], "traits": { - "smithy.api#documentation": "

Retrieves information about the specified version of the specified managed policy,\n including the policy document.

\n \n

Policies returned by this operation are URL-encoded compliant \n with RFC 3986. You can use a URL \n decoding method to convert the policy back to plain JSON text. For example, if you use Java, you \n can use the decode method of the java.net.URLDecoder utility class in \n the Java SDK. Other languages and SDKs provide similar functionality.

\n
\n

To list the available versions for a policy, use ListPolicyVersions.

\n

This operation retrieves information about managed policies. To retrieve information\n about an inline policy that is embedded in a user, group, or role, use GetUserPolicy, GetGroupPolicy, or GetRolePolicy.

\n

For more information about the types of policies, see Managed policies and inline\n policies in the IAM User Guide.

\n

For more information about managed policy versions, see Versioning for managed\n policies in the IAM User Guide.

" + "smithy.api#documentation": "

Retrieves information about the specified version of the specified managed policy,\n including the policy document.

\n \n

Policies returned by this operation are URL-encoded compliant \n with RFC 3986. You can use a URL \n decoding method to convert the policy back to plain JSON text. For example, if you use Java, you \n can use the decode method of the java.net.URLDecoder utility class in \n the Java SDK. Other languages and SDKs provide similar functionality.

\n
\n

To list the available versions for a policy, use ListPolicyVersions.

\n

This operation retrieves information about managed policies. To retrieve information\n about an inline policy that is embedded in a user, group, or role, use GetUserPolicy, GetGroupPolicy, or GetRolePolicy.

\n

For more information about the types of policies, see Managed policies and inline\n policies in the IAM User Guide.

\n

For more information about managed policy versions, see Versioning for managed\n policies in the IAM User Guide.

" } }, "com.amazonaws.iam#GetPolicyVersionRequest": { @@ -6590,17 +6853,20 @@ "PolicyArn": { "target": "com.amazonaws.iam#arnType", "traits": { - "smithy.api#documentation": "

The Amazon Resource Name (ARN) of the managed policy that you want information\n about.

\n

For more information about ARNs, see Amazon Resource Names (ARNs) in the Amazon Web Services General Reference.

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

The Amazon Resource Name (ARN) of the managed policy that you want information\n about.

\n

For more information about ARNs, see Amazon Resource Names (ARNs) in the Amazon Web Services General Reference.

", "smithy.api#required": {} } }, "VersionId": { "target": "com.amazonaws.iam#policyVersionIdType", "traits": { - "smithy.api#documentation": "

Identifies the policy version to retrieve.

\n

This parameter allows (through its regex pattern) a string of characters that \n consists of the lowercase letter 'v' followed by one or two digits, and optionally \n followed by a period '.' and a string of letters and digits.

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

Identifies the policy version to retrieve.

\n

This parameter allows (through its regex pattern) a string of characters that \n consists of the lowercase letter 'v' followed by one or two digits, and optionally \n followed by a period '.' and a string of letters and digits.

", "smithy.api#required": {} } } + }, + "traits": { + "smithy.api#input": {} } }, "com.amazonaws.iam#GetPolicyVersionResponse": { @@ -6614,7 +6880,8 @@ } }, "traits": { - "smithy.api#documentation": "

Contains the response to a successful GetPolicyVersion request.\n

" + "smithy.api#documentation": "

Contains the response to a successful GetPolicyVersion request.\n

", + "smithy.api#output": {} } }, "com.amazonaws.iam#GetRole": { @@ -6634,7 +6901,7 @@ } ], "traits": { - "smithy.api#documentation": "

Retrieves information about the specified role, including the role's path, GUID, ARN,\n and the role's trust policy that grants permission to assume the role. For more\n information about roles, see Working with roles.

\n \n

Policies returned by this operation are URL-encoded compliant \n with RFC 3986. You can use a URL \n decoding method to convert the policy back to plain JSON text. For example, if you use Java, you \n can use the decode method of the java.net.URLDecoder utility class in \n the Java SDK. Other languages and SDKs provide similar functionality.

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

Retrieves information about the specified role, including the role's path, GUID, ARN,\n and the role's trust policy that grants permission to assume the role. For more\n information about roles, see Working with roles.

\n \n

Policies returned by this operation are URL-encoded compliant \n with RFC 3986. You can use a URL \n decoding method to convert the policy back to plain JSON text. For example, if you use Java, you \n can use the decode method of the java.net.URLDecoder utility class in \n the Java SDK. Other languages and SDKs provide similar functionality.

\n
", "smithy.api#suppress": [ "WaitableTraitInvalidErrorType" ], @@ -6676,7 +6943,7 @@ } ], "traits": { - "smithy.api#documentation": "

Retrieves the specified inline policy document that is embedded with the specified\n IAM role.

\n \n

Policies returned by this operation are URL-encoded compliant \n with RFC 3986. You can use a URL \n decoding method to convert the policy back to plain JSON text. For example, if you use Java, you \n can use the decode method of the java.net.URLDecoder utility class in \n the Java SDK. Other languages and SDKs provide similar functionality.

\n
\n

An IAM role can also have managed policies attached to it. To retrieve a managed\n policy document that is attached to a role, use GetPolicy to determine\n the policy's default version, then use GetPolicyVersion to retrieve\n the policy document.

\n

For more information about policies, see Managed policies and inline\n policies in the IAM User Guide.

\n

For more information about roles, see Using roles to delegate permissions and\n federate identities.

" + "smithy.api#documentation": "

Retrieves the specified inline policy document that is embedded with the specified\n IAM role.

\n \n

Policies returned by this operation are URL-encoded compliant \n with RFC 3986. You can use a URL \n decoding method to convert the policy back to plain JSON text. For example, if you use Java, you \n can use the decode method of the java.net.URLDecoder utility class in \n the Java SDK. Other languages and SDKs provide similar functionality.

\n
\n

An IAM role can also have managed policies attached to it. To retrieve a managed\n policy document that is attached to a role, use GetPolicy to determine\n the policy's default version, then use GetPolicyVersion to retrieve\n the policy document.

\n

For more information about policies, see Managed policies and inline\n policies in the IAM User Guide.

\n

For more information about roles, see Using roles to delegate permissions and\n federate identities.

" } }, "com.amazonaws.iam#GetRolePolicyRequest": { @@ -6685,17 +6952,20 @@ "RoleName": { "target": "com.amazonaws.iam#roleNameType", "traits": { - "smithy.api#documentation": "

The name of the role associated with the policy.

\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 role associated with the policy.

\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": {} } }, "PolicyName": { "target": "com.amazonaws.iam#policyNameType", "traits": { - "smithy.api#documentation": "

The name of the policy document to get.

\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 policy document to get.

\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": {} } } + }, + "traits": { + "smithy.api#input": {} } }, "com.amazonaws.iam#GetRolePolicyResponse": { @@ -6718,13 +6988,14 @@ "PolicyDocument": { "target": "com.amazonaws.iam#policyDocumentType", "traits": { - "smithy.api#documentation": "

The policy document.

\n

IAM stores policies in JSON format. However, resources that were created using CloudFormation\n templates can be formatted in YAML. CloudFormation always converts a YAML policy to JSON format\n before submitting it to IAM.

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

The policy document.

\n

IAM stores policies in JSON format. However, resources that were created using CloudFormation\n templates can be formatted in YAML. CloudFormation always converts a YAML policy to JSON format\n before submitting it to IAM.

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

Contains the response to a successful GetRolePolicy request.\n

" + "smithy.api#documentation": "

Contains the response to a successful GetRolePolicy request.\n

", + "smithy.api#output": {} } }, "com.amazonaws.iam#GetRoleRequest": { @@ -6733,10 +7004,13 @@ "RoleName": { "target": "com.amazonaws.iam#roleNameType", "traits": { - "smithy.api#documentation": "

The name of the IAM role to get information about.

\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 IAM role to get information about.

\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": {} } } + }, + "traits": { + "smithy.api#input": {} } }, "com.amazonaws.iam#GetRoleResponse": { @@ -6751,7 +7025,8 @@ } }, "traits": { - "smithy.api#documentation": "

Contains the response to a successful GetRole request.

" + "smithy.api#documentation": "

Contains the response to a successful GetRole request.

", + "smithy.api#output": {} } }, "com.amazonaws.iam#GetSAMLProvider": { @@ -6774,7 +7049,7 @@ } ], "traits": { - "smithy.api#documentation": "

Returns the SAML provider metadocument that was uploaded when the IAM SAML provider\n resource object was created or updated.

\n \n

This operation requires Signature Version 4.

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

Returns the SAML provider metadocument that was uploaded when the IAM SAML provider\n resource object was created or updated.

\n \n

This operation requires Signature Version 4.

\n
" } }, "com.amazonaws.iam#GetSAMLProviderRequest": { @@ -6783,10 +7058,13 @@ "SAMLProviderArn": { "target": "com.amazonaws.iam#arnType", "traits": { - "smithy.api#documentation": "

The Amazon Resource Name (ARN) of the SAML provider resource object in IAM to get\n information about.

\n

For more information about ARNs, see Amazon Resource Names (ARNs) in the Amazon Web Services General Reference.

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

The Amazon Resource Name (ARN) of the SAML provider resource object in IAM to get\n information about.

\n

For more information about ARNs, see Amazon Resource Names (ARNs) in the Amazon Web Services General Reference.

", "smithy.api#required": {} } } + }, + "traits": { + "smithy.api#input": {} } }, "com.amazonaws.iam#GetSAMLProviderResponse": { @@ -6818,7 +7096,8 @@ } }, "traits": { - "smithy.api#documentation": "

Contains the response to a successful GetSAMLProvider request.\n

" + "smithy.api#documentation": "

Contains the response to a successful GetSAMLProvider request.\n

", + "smithy.api#output": {} } }, "com.amazonaws.iam#GetSSHPublicKey": { @@ -6838,7 +7117,7 @@ } ], "traits": { - "smithy.api#documentation": "

Retrieves the specified SSH public key, including metadata about the key.

\n

The SSH public key retrieved by this operation is used only for authenticating the\n associated IAM user to an CodeCommit repository. For more information about using SSH keys\n to authenticate to an CodeCommit repository, see Set up CodeCommit for SSH\n connections in the CodeCommit User Guide.

" + "smithy.api#documentation": "

Retrieves the specified SSH public key, including metadata about the key.

\n

The SSH public key retrieved by this operation is used only for authenticating the\n associated IAM user to an CodeCommit repository. For more information about using SSH keys\n to authenticate to an CodeCommit repository, see Set up CodeCommit for SSH\n connections in the CodeCommit User Guide.

" } }, "com.amazonaws.iam#GetSSHPublicKeyRequest": { @@ -6847,14 +7126,14 @@ "UserName": { "target": "com.amazonaws.iam#userNameType", "traits": { - "smithy.api#documentation": "

The name of the IAM user associated with the SSH public key.

\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 IAM user associated with the SSH public key.

\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": {} } }, "SSHPublicKeyId": { "target": "com.amazonaws.iam#publicKeyIdType", "traits": { - "smithy.api#documentation": "

The unique identifier for the SSH public key.

\n

This parameter allows (through its regex pattern) a string of characters that can \n consist of any upper or lowercased letter or digit.

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

The unique identifier for the SSH public key.

\n

This parameter allows (through its regex pattern) a string of characters that can \n consist of any upper or lowercased letter or digit.

", "smithy.api#required": {} } }, @@ -6865,6 +7144,9 @@ "smithy.api#required": {} } } + }, + "traits": { + "smithy.api#input": {} } }, "com.amazonaws.iam#GetSSHPublicKeyResponse": { @@ -6878,7 +7160,8 @@ } }, "traits": { - "smithy.api#documentation": "

Contains the response to a successful GetSSHPublicKey\n request.

" + "smithy.api#documentation": "

Contains the response to a successful GetSSHPublicKey\n request.

", + "smithy.api#output": {} } }, "com.amazonaws.iam#GetServerCertificate": { @@ -6898,7 +7181,7 @@ } ], "traits": { - "smithy.api#documentation": "

Retrieves information about the specified server certificate stored in IAM.

\n

For more information about working with server certificates, see Working\n with server certificates in the IAM User Guide. This\n topic includes a list of Amazon Web Services services that can use the server certificates that you\n manage with IAM.

" + "smithy.api#documentation": "

Retrieves information about the specified server certificate stored in IAM.

\n

For more information about working with server certificates, see Working\n with server certificates in the IAM User Guide. This\n topic includes a list of Amazon Web Services services that can use the server certificates that you\n manage with IAM.

" } }, "com.amazonaws.iam#GetServerCertificateRequest": { @@ -6907,10 +7190,13 @@ "ServerCertificateName": { "target": "com.amazonaws.iam#serverCertificateNameType", "traits": { - "smithy.api#documentation": "

The name of the server certificate you want to retrieve information about.

\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 server certificate you want to retrieve information about.

\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": {} } } + }, + "traits": { + "smithy.api#input": {} } }, "com.amazonaws.iam#GetServerCertificateResponse": { @@ -6925,7 +7211,8 @@ } }, "traits": { - "smithy.api#documentation": "

Contains the response to a successful GetServerCertificate request.\n

" + "smithy.api#documentation": "

Contains the response to a successful GetServerCertificate request.\n

", + "smithy.api#output": {} } }, "com.amazonaws.iam#GetServiceLastAccessedDetails": { @@ -6945,7 +7232,7 @@ } ], "traits": { - "smithy.api#documentation": "

Retrieves a service last accessed report that was created using the\n GenerateServiceLastAccessedDetails operation. You can use the\n JobId parameter in GetServiceLastAccessedDetails to\n retrieve the status of your report job. When the report is complete, you can retrieve\n the generated report. The report includes a list of Amazon Web Services services that the resource\n (user, group, role, or managed policy) can access.

\n \n

Service last accessed data does not use other policy types when determining\n whether a resource could access a service. These other policy types include\n resource-based policies, access control lists, Organizations policies, IAM permissions\n boundaries, and STS assume role policies. It only applies permissions policy\n logic. For more about the evaluation of policy types, see Evaluating policies in the\n IAM User Guide.

\n
\n

For each service that the resource could access using permissions policies, the\n operation returns details about the most recent access attempt. If there was no attempt,\n the service is listed without details about the most recent attempt to access the\n service. If the operation fails, the GetServiceLastAccessedDetails\n operation returns the reason that it failed.

\n

The GetServiceLastAccessedDetails operation returns a list of services.\n This list includes the number of entities that have attempted to access the service and\n the date and time of the last attempt. It also returns the ARN of the following entity,\n depending on the resource ARN that you used to generate the report:

\n
    \n
  • \n

    \n User – Returns the user ARN that you\n used to generate the report

    \n
  • \n
  • \n

    \n Group – Returns the ARN of the group\n member (user) that last attempted to access the service

    \n
  • \n
  • \n

    \n Role – Returns the role ARN that you\n used to generate the report

    \n
  • \n
  • \n

    \n Policy – Returns the ARN of the user\n or role that last used the policy to attempt to access the service

    \n
  • \n
\n

By default, the list is sorted by service namespace.

\n

If you specified ACTION_LEVEL granularity when you generated the report,\n this operation returns service and action last accessed data. This includes the most\n recent access attempt for each tracked action within a service. Otherwise, this\n operation returns only service data.

\n

For more information about service and action last accessed data, see Reducing permissions using service last accessed data in the\n IAM User Guide.

" + "smithy.api#documentation": "

Retrieves a service last accessed report that was created using the\n GenerateServiceLastAccessedDetails operation. You can use the\n JobId parameter in GetServiceLastAccessedDetails to\n retrieve the status of your report job. When the report is complete, you can retrieve\n the generated report. The report includes a list of Amazon Web Services services that the resource\n (user, group, role, or managed policy) can access.

\n \n

Service last accessed data does not use other policy types when determining\n whether a resource could access a service. These other policy types include\n resource-based policies, access control lists, Organizations policies, IAM permissions\n boundaries, and STS assume role policies. It only applies permissions policy\n logic. For more about the evaluation of policy types, see Evaluating policies in the\n IAM User Guide.

\n
\n

For each service that the resource could access using permissions policies, the\n operation returns details about the most recent access attempt. If there was no attempt,\n the service is listed without details about the most recent attempt to access the\n service. If the operation fails, the GetServiceLastAccessedDetails\n operation returns the reason that it failed.

\n

The GetServiceLastAccessedDetails operation returns a list of services.\n This list includes the number of entities that have attempted to access the service and\n the date and time of the last attempt. It also returns the ARN of the following entity,\n depending on the resource ARN that you used to generate the report:

\n
    \n
  • \n

    \n User – Returns the user ARN that you\n used to generate the report

    \n
  • \n
  • \n

    \n Group – Returns the ARN of the group\n member (user) that last attempted to access the service

    \n
  • \n
  • \n

    \n Role – Returns the role ARN that you\n used to generate the report

    \n
  • \n
  • \n

    \n Policy – Returns the ARN of the user\n or role that last used the policy to attempt to access the service

    \n
  • \n
\n

By default, the list is sorted by service namespace.

\n

If you specified ACTION_LEVEL granularity when you generated the report,\n this operation returns service and action last accessed data. This includes the most\n recent access attempt for each tracked action within a service. Otherwise, this\n operation returns only service data.

\n

For more information about service and action last accessed data, see Reducing permissions using service last accessed data in the\n IAM User Guide.

" } }, "com.amazonaws.iam#GetServiceLastAccessedDetailsRequest": { @@ -6970,6 +7257,9 @@ "smithy.api#documentation": "

Use this parameter only when paginating results and only after \n you receive a response indicating that the results are truncated. Set it to the value of the\n Marker element in the response that you received to indicate where the next call \n should start.

" } } + }, + "traits": { + "smithy.api#input": {} } }, "com.amazonaws.iam#GetServiceLastAccessedDetailsResponse": { @@ -7005,7 +7295,7 @@ "JobCompletionDate": { "target": "com.amazonaws.iam#dateType", "traits": { - "smithy.api#documentation": "

The date and time, in ISO 8601 date-time\n format, when the generated report job was completed or failed.

\n

This field is null if the job is still in progress, as indicated by a job status value\n of IN_PROGRESS.

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

The date and time, in ISO 8601 date-time\n format, when the generated report job was completed or failed.

\n

This field is null if the job is still in progress, as indicated by a job status value\n of IN_PROGRESS.

", "smithy.api#required": {} } }, @@ -7028,6 +7318,9 @@ "smithy.api#documentation": "

An object that contains details about the reason the operation failed.

" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.iam#GetServiceLastAccessedDetailsWithEntities": { @@ -7047,7 +7340,7 @@ } ], "traits": { - "smithy.api#documentation": "

After you generate a group or policy report using the\n GenerateServiceLastAccessedDetails operation, you can use the\n JobId parameter in\n GetServiceLastAccessedDetailsWithEntities. This operation retrieves the\n status of your report job and a list of entities that could have used group or policy\n permissions to access the specified service.

\n
    \n
  • \n

    \n Group – For a group report, this\n operation returns a list of users in the group that could have used the group’s\n policies in an attempt to access the service.

    \n
  • \n
  • \n

    \n Policy – For a policy report, this\n operation returns a list of entities (users or roles) that could have used the\n policy in an attempt to access the service.

    \n
  • \n
\n

You can also use this operation for user or role reports to retrieve details about\n those entities.

\n

If the operation fails, the GetServiceLastAccessedDetailsWithEntities\n operation returns the reason that it failed.

\n

By default, the list of associated entities is sorted by date, with the most recent\n access listed first.

" + "smithy.api#documentation": "

After you generate a group or policy report using the\n GenerateServiceLastAccessedDetails operation, you can use the\n JobId parameter in\n GetServiceLastAccessedDetailsWithEntities. This operation retrieves the\n status of your report job and a list of entities that could have used group or policy\n permissions to access the specified service.

\n
    \n
  • \n

    \n Group – For a group report, this\n operation returns a list of users in the group that could have used the group’s\n policies in an attempt to access the service.

    \n
  • \n
  • \n

    \n Policy – For a policy report, this\n operation returns a list of entities (users or roles) that could have used the\n policy in an attempt to access the service.

    \n
  • \n
\n

You can also use this operation for user or role reports to retrieve details about\n those entities.

\n

If the operation fails, the GetServiceLastAccessedDetailsWithEntities\n operation returns the reason that it failed.

\n

By default, the list of associated entities is sorted by date, with the most recent\n access listed first.

" } }, "com.amazonaws.iam#GetServiceLastAccessedDetailsWithEntitiesRequest": { @@ -7063,7 +7356,7 @@ "ServiceNamespace": { "target": "com.amazonaws.iam#serviceNamespaceType", "traits": { - "smithy.api#documentation": "

The service namespace for an Amazon Web Services service. Provide the service namespace to learn\n when the IAM entity last attempted to access the specified service.

\n

To learn the service namespace for a service, see Actions, resources, and condition keys for Amazon Web Services services in the\n IAM User Guide. Choose the name of the service to view\n details for that service. In the first paragraph, find the service prefix. For example,\n (service prefix: a4b). For more information about service namespaces,\n see Amazon Web Services\n service namespaces in the Amazon Web Services General Reference.

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

The service namespace for an Amazon Web Services service. Provide the service namespace to learn\n when the IAM entity last attempted to access the specified service.

\n

To learn the service namespace for a service, see Actions, resources, and condition keys for Amazon Web Services services in the\n IAM User Guide. Choose the name of the service to view\n details for that service. In the first paragraph, find the service prefix. For example,\n (service prefix: a4b). For more information about service namespaces,\n see Amazon Web Services\n service namespaces in the Amazon Web Services General Reference.

", "smithy.api#required": {} } }, @@ -7079,6 +7372,9 @@ "smithy.api#documentation": "

Use this parameter only when paginating results and only after \n you receive a response indicating that the results are truncated. Set it to the value of the\n Marker element in the response that you received to indicate where the next call \n should start.

" } } + }, + "traits": { + "smithy.api#input": {} } }, "com.amazonaws.iam#GetServiceLastAccessedDetailsWithEntitiesResponse": { @@ -7101,7 +7397,7 @@ "JobCompletionDate": { "target": "com.amazonaws.iam#dateType", "traits": { - "smithy.api#documentation": "

The date and time, in ISO 8601 date-time\n format, when the generated report job was completed or failed.

\n

This field is null if the job is still in progress, as indicated by a job status value\n of IN_PROGRESS.

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

The date and time, in ISO 8601 date-time\n format, when the generated report job was completed or failed.

\n

This field is null if the job is still in progress, as indicated by a job status value\n of IN_PROGRESS.

", "smithy.api#required": {} } }, @@ -7131,6 +7427,9 @@ "smithy.api#documentation": "

An object that contains details about the reason the operation failed.

" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.iam#GetServiceLinkedRoleDeletionStatus": { @@ -7166,6 +7465,9 @@ "smithy.api#required": {} } } + }, + "traits": { + "smithy.api#input": {} } }, "com.amazonaws.iam#GetServiceLinkedRoleDeletionStatusResponse": { @@ -7184,6 +7486,9 @@ "smithy.api#documentation": "

An object that contains details about the reason the deletion failed.

" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.iam#GetUser": { @@ -7203,7 +7508,7 @@ } ], "traits": { - "smithy.api#documentation": "

Retrieves information about the specified IAM user, including the user's creation\n date, path, unique ID, and ARN.

\n

If you do not specify a user name, IAM determines the user name implicitly based on\n the Amazon Web Services access key ID used to sign the request to this operation.

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

Retrieves information about the specified IAM user, including the user's creation\n date, path, unique ID, and ARN.

\n

If you do not specify a user name, IAM determines the user name implicitly based on\n the Amazon Web Services access key ID used to sign the request to this operation.

", "smithy.api#suppress": [ "WaitableTraitInvalidErrorType" ], @@ -7245,7 +7550,7 @@ } ], "traits": { - "smithy.api#documentation": "

Retrieves the specified inline policy document that is embedded in the specified IAM\n user.

\n \n

Policies returned by this operation are URL-encoded compliant \n with RFC 3986. You can use a URL \n decoding method to convert the policy back to plain JSON text. For example, if you use Java, you \n can use the decode method of the java.net.URLDecoder utility class in \n the Java SDK. Other languages and SDKs provide similar functionality.

\n
\n

An IAM user can also have managed policies attached to it. To retrieve a managed\n policy document that is attached to a user, use GetPolicy to determine\n the policy's default version. Then use GetPolicyVersion to retrieve\n the policy document.

\n

For more information about policies, see Managed policies and inline\n policies in the IAM User Guide.

" + "smithy.api#documentation": "

Retrieves the specified inline policy document that is embedded in the specified IAM\n user.

\n \n

Policies returned by this operation are URL-encoded compliant \n with RFC 3986. You can use a URL \n decoding method to convert the policy back to plain JSON text. For example, if you use Java, you \n can use the decode method of the java.net.URLDecoder utility class in \n the Java SDK. Other languages and SDKs provide similar functionality.

\n
\n

An IAM user can also have managed policies attached to it. To retrieve a managed\n policy document that is attached to a user, use GetPolicy to determine\n the policy's default version. Then use GetPolicyVersion to retrieve\n the policy document.

\n

For more information about policies, see Managed policies and inline\n policies in the IAM User Guide.

" } }, "com.amazonaws.iam#GetUserPolicyRequest": { @@ -7254,17 +7559,20 @@ "UserName": { "target": "com.amazonaws.iam#existingUserNameType", "traits": { - "smithy.api#documentation": "

The name of the user who the policy is associated with.

\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 user who the policy is associated with.

\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": {} } }, "PolicyName": { "target": "com.amazonaws.iam#policyNameType", "traits": { - "smithy.api#documentation": "

The name of the policy document to get.

\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 policy document to get.

\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": {} } } + }, + "traits": { + "smithy.api#input": {} } }, "com.amazonaws.iam#GetUserPolicyResponse": { @@ -7287,13 +7595,14 @@ "PolicyDocument": { "target": "com.amazonaws.iam#policyDocumentType", "traits": { - "smithy.api#documentation": "

The policy document.

\n\n

IAM stores policies in JSON format. However, resources that were created using CloudFormation\n templates can be formatted in YAML. CloudFormation always converts a YAML policy to JSON format\n before submitting it to IAM.

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

The policy document.

\n

IAM stores policies in JSON format. However, resources that were created using CloudFormation\n templates can be formatted in YAML. CloudFormation always converts a YAML policy to JSON format\n before submitting it to IAM.

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

Contains the response to a successful GetUserPolicy request.\n

" + "smithy.api#documentation": "

Contains the response to a successful GetUserPolicy request.\n

", + "smithy.api#output": {} } }, "com.amazonaws.iam#GetUserRequest": { @@ -7302,9 +7611,12 @@ "UserName": { "target": "com.amazonaws.iam#existingUserNameType", "traits": { - "smithy.api#documentation": "

The name of the user to get information about.

\n

This parameter is optional. If it is not included, it defaults to the user making the\n request. 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 user to get information about.

\n

This parameter is optional. If it is not included, it defaults to the user making the\n request. 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: _+=,.@-

" } } + }, + "traits": { + "smithy.api#input": {} } }, "com.amazonaws.iam#GetUserResponse": { @@ -7313,13 +7625,14 @@ "User": { "target": "com.amazonaws.iam#User", "traits": { - "smithy.api#documentation": "

A structure containing details about the IAM user.

\n \n

Due to a service issue, password last used data does not include password use from\n May 3, 2018 22:50 PDT to May 23, 2018 14:08 PDT. This affects last sign-in dates shown in the IAM console and password last used\n dates in the IAM credential\n report, and returned by this operation. If users signed in during the\n affected time, the password last used date that is returned is the date the user\n last signed in before May 3, 2018. For users that signed in after May 23, 2018 14:08\n PDT, the returned password last used date is accurate.

\n

You can use password last used information to identify unused credentials for\n deletion. For example, you might delete users who did not sign in to Amazon Web Services in the\n last 90 days. In cases like this, we recommend that you adjust your evaluation\n window to include dates after May 23, 2018. Alternatively, if your users use access\n keys to access Amazon Web Services programmatically you can refer to access key last used\n information because it is accurate for all dates.

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

A structure containing details about the IAM user.

\n \n

Due to a service issue, password last used data does not include password use from\n May 3, 2018 22:50 PDT to May 23, 2018 14:08 PDT. This affects last sign-in dates shown in the IAM console and password last used\n dates in the IAM credential\n report, and returned by this operation. If users signed in during the\n affected time, the password last used date that is returned is the date the user\n last signed in before May 3, 2018. For users that signed in after May 23, 2018 14:08\n PDT, the returned password last used date is accurate.

\n

You can use password last used information to identify unused credentials for\n deletion. For example, you might delete users who did not sign in to Amazon Web Services in the\n last 90 days. In cases like this, we recommend that you adjust your evaluation\n window to include dates after May 23, 2018. Alternatively, if your users use access\n keys to access Amazon Web Services programmatically you can refer to access key last used\n information because it is accurate for all dates.

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

Contains the response to a successful GetUser request.

" + "smithy.api#documentation": "

Contains the response to a successful GetUser request.

", + "smithy.api#output": {} } }, "com.amazonaws.iam#Group": { @@ -7582,7 +7895,7 @@ "code": "LimitExceeded", "httpResponseCode": 409 }, - "smithy.api#documentation": "

The request was rejected because it attempted to create resources beyond the current Amazon Web Services\n account limits. The error message describes the limit exceeded.

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

The request was rejected because it attempted to create resources beyond the current\n Amazon Web Services account limits. The error message describes the limit exceeded.

", "smithy.api#error": "client", "smithy.api#httpError": 409 } @@ -7610,7 +7923,7 @@ } ], "traits": { - "smithy.api#documentation": "

Returns information about the access key IDs associated with the specified IAM user.\n If there is none, the operation returns an empty list.

\n

Although each user is limited to a small number of keys, you can still paginate the\n results using the MaxItems and Marker parameters.

\n

If the UserName is not specified, the user name is determined implicitly\n based on the Amazon Web Services access key ID used to sign the request. If a temporary access key is\n used, then UserName is required. If a long-term key is assigned to the\n user, then UserName is not required. This operation works for access keys\n under the Amazon Web Services account. Consequently, you can use this operation to manage\n Amazon Web Services account root user credentials even if the Amazon Web Services account has no associated\n users.

\n \n

To ensure the security of your Amazon Web Services account, the secret access key is accessible\n only during key and user creation.

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

Returns information about the access key IDs associated with the specified IAM user.\n If there is none, the operation returns an empty list.

\n

Although each user is limited to a small number of keys, you can still paginate the\n results using the MaxItems and Marker parameters.

\n

If the UserName is not specified, the user name is determined implicitly\n based on the Amazon Web Services access key ID used to sign the request. If a temporary access key is\n used, then UserName is required. If a long-term key is assigned to the\n user, then UserName is not required. This operation works for access keys\n under the Amazon Web Services account. Consequently, you can use this operation to manage Amazon Web Services account root user\n credentials even if the Amazon Web Services account has no associated users.

\n \n

To ensure the security of your Amazon Web Services account, the secret access key is accessible\n only during key and user creation.

\n
", "smithy.api#paginated": { "inputToken": "Marker", "outputToken": "Marker", @@ -7625,7 +7938,7 @@ "UserName": { "target": "com.amazonaws.iam#existingUserNameType", "traits": { - "smithy.api#documentation": "

The name of the user.

\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 user.

\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: _+=,.@-

" } }, "Marker": { @@ -7640,6 +7953,9 @@ "smithy.api#documentation": "

Use this only when paginating results to indicate the \n maximum number of items you want in the response. If additional items exist beyond the maximum \n you specify, the IsTruncated response element is true.

\n

If you do not include this parameter, the number of items defaults to 100. Note that\n IAM might return fewer results, even when there are more results available. In that case, the\n IsTruncated response element returns true, and Marker \n contains a value to include in the subsequent call that tells the service where to continue \n from.

" } } + }, + "traits": { + "smithy.api#input": {} } }, "com.amazonaws.iam#ListAccessKeysResponse": { @@ -7667,7 +7983,8 @@ } }, "traits": { - "smithy.api#documentation": "

Contains the response to a successful ListAccessKeys request.\n

" + "smithy.api#documentation": "

Contains the response to a successful ListAccessKeys request.\n

", + "smithy.api#output": {} } }, "com.amazonaws.iam#ListAccountAliases": { @@ -7708,6 +8025,9 @@ "smithy.api#documentation": "

Use this only when paginating results to indicate the \n maximum number of items you want in the response. If additional items exist beyond the maximum \n you specify, the IsTruncated response element is true.

\n

If you do not include this parameter, the number of items defaults to 100. Note that\n IAM might return fewer results, even when there are more results available. In that case, the\n IsTruncated response element returns true, and Marker \n contains a value to include in the subsequent call that tells the service where to continue \n from.

" } } + }, + "traits": { + "smithy.api#input": {} } }, "com.amazonaws.iam#ListAccountAliasesResponse": { @@ -7735,7 +8055,8 @@ } }, "traits": { - "smithy.api#documentation": "

Contains the response to a successful ListAccountAliases request.\n

" + "smithy.api#documentation": "

Contains the response to a successful ListAccountAliases request.\n

", + "smithy.api#output": {} } }, "com.amazonaws.iam#ListAttachedGroupPolicies": { @@ -7758,7 +8079,7 @@ } ], "traits": { - "smithy.api#documentation": "

Lists all managed policies that are attached to the specified IAM group.

\n

An IAM group can also have inline policies embedded with it. To list the inline\n policies for a group, use ListGroupPolicies. For information about\n policies, see Managed policies and inline\n policies in the IAM User Guide.

\n

You can paginate the results using the MaxItems and Marker\n parameters. You can use the PathPrefix parameter to limit the list of\n policies to only those matching the specified path prefix. If there are no policies\n attached to the specified group (or none that match the specified path prefix), the\n operation returns an empty list.

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

Lists all managed policies that are attached to the specified IAM group.

\n

An IAM group can also have inline policies embedded with it. To list the inline\n policies for a group, use ListGroupPolicies. For information about\n policies, see Managed policies and inline\n policies in the IAM User Guide.

\n

You can paginate the results using the MaxItems and Marker\n parameters. You can use the PathPrefix parameter to limit the list of\n policies to only those matching the specified path prefix. If there are no policies\n attached to the specified group (or none that match the specified path prefix), the\n operation returns an empty list.

", "smithy.api#paginated": { "inputToken": "Marker", "outputToken": "Marker", @@ -7773,14 +8094,14 @@ "GroupName": { "target": "com.amazonaws.iam#groupNameType", "traits": { - "smithy.api#documentation": "

The name (friendly name, not ARN) of the group to list attached policies for.

\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 (friendly name, not ARN) of the group to list attached policies for.

\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": {} } }, "PathPrefix": { "target": "com.amazonaws.iam#policyPathType", "traits": { - "smithy.api#documentation": "

The path prefix for filtering the results. This parameter is optional. If it is not\n included, it defaults to a slash (/), listing all policies.

\n

This parameter allows (through its regex pattern) a string of characters consisting \n of either a forward slash (/) by itself or a string that must begin and end with forward slashes.\n In addition, it can contain any ASCII character from the ! (\\u0021) through the DEL character (\\u007F), including \n most punctuation characters, digits, and upper and lowercased letters.

" + "smithy.api#documentation": "

The path prefix for filtering the results. This parameter is optional. If it is not\n included, it defaults to a slash (/), listing all policies.

\n

This parameter allows (through its regex pattern) a string of characters consisting \n of either a forward slash (/) by itself or a string that must begin and end with forward slashes.\n In addition, it can contain any ASCII character from the ! (\\u0021) through the DEL character (\\u007F), including \n most punctuation characters, digits, and upper and lowercased letters.

" } }, "Marker": { @@ -7795,6 +8116,9 @@ "smithy.api#documentation": "

Use this only when paginating results to indicate the \n maximum number of items you want in the response. If additional items exist beyond the maximum \n you specify, the IsTruncated response element is true.

\n

If you do not include this parameter, the number of items defaults to 100. Note that\n IAM might return fewer results, even when there are more results available. In that case, the\n IsTruncated response element returns true, and Marker \n contains a value to include in the subsequent call that tells the service where to continue \n from.

" } } + }, + "traits": { + "smithy.api#input": {} } }, "com.amazonaws.iam#ListAttachedGroupPoliciesResponse": { @@ -7821,7 +8145,8 @@ } }, "traits": { - "smithy.api#documentation": "

Contains the response to a successful ListAttachedGroupPolicies\n request.

" + "smithy.api#documentation": "

Contains the response to a successful ListAttachedGroupPolicies\n request.

", + "smithy.api#output": {} } }, "com.amazonaws.iam#ListAttachedRolePolicies": { @@ -7844,7 +8169,7 @@ } ], "traits": { - "smithy.api#documentation": "

Lists all managed policies that are attached to the specified IAM role.

\n

An IAM role can also have inline policies embedded with it. To list the inline\n policies for a role, use ListRolePolicies. For information about\n policies, see Managed policies and inline\n policies in the IAM User Guide.

\n

You can paginate the results using the MaxItems and Marker\n parameters. You can use the PathPrefix parameter to limit the list of\n policies to only those matching the specified path prefix. If there are no policies\n attached to the specified role (or none that match the specified path prefix), the\n operation returns an empty list.

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

Lists all managed policies that are attached to the specified IAM role.

\n

An IAM role can also have inline policies embedded with it. To list the inline\n policies for a role, use ListRolePolicies. For information about\n policies, see Managed policies and inline\n policies in the IAM User Guide.

\n

You can paginate the results using the MaxItems and Marker\n parameters. You can use the PathPrefix parameter to limit the list of\n policies to only those matching the specified path prefix. If there are no policies\n attached to the specified role (or none that match the specified path prefix), the\n operation returns an empty list.

", "smithy.api#paginated": { "inputToken": "Marker", "outputToken": "Marker", @@ -7859,14 +8184,14 @@ "RoleName": { "target": "com.amazonaws.iam#roleNameType", "traits": { - "smithy.api#documentation": "

The name (friendly name, not ARN) of the role to list attached policies for.

\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 (friendly name, not ARN) of the role to list attached policies for.

\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": {} } }, "PathPrefix": { "target": "com.amazonaws.iam#policyPathType", "traits": { - "smithy.api#documentation": "

The path prefix for filtering the results. This parameter is optional. If it is not\n included, it defaults to a slash (/), listing all policies.

\n

This parameter allows (through its regex pattern) a string of characters consisting \n of either a forward slash (/) by itself or a string that must begin and end with forward slashes.\n In addition, it can contain any ASCII character from the ! (\\u0021) through the DEL character (\\u007F), including \n most punctuation characters, digits, and upper and lowercased letters.

" + "smithy.api#documentation": "

The path prefix for filtering the results. This parameter is optional. If it is not\n included, it defaults to a slash (/), listing all policies.

\n

This parameter allows (through its regex pattern) a string of characters consisting \n of either a forward slash (/) by itself or a string that must begin and end with forward slashes.\n In addition, it can contain any ASCII character from the ! (\\u0021) through the DEL character (\\u007F), including \n most punctuation characters, digits, and upper and lowercased letters.

" } }, "Marker": { @@ -7881,6 +8206,9 @@ "smithy.api#documentation": "

Use this only when paginating results to indicate the \n maximum number of items you want in the response. If additional items exist beyond the maximum \n you specify, the IsTruncated response element is true.

\n

If you do not include this parameter, the number of items defaults to 100. Note that\n IAM might return fewer results, even when there are more results available. In that case, the\n IsTruncated response element returns true, and Marker \n contains a value to include in the subsequent call that tells the service where to continue \n from.

" } } + }, + "traits": { + "smithy.api#input": {} } }, "com.amazonaws.iam#ListAttachedRolePoliciesResponse": { @@ -7907,7 +8235,8 @@ } }, "traits": { - "smithy.api#documentation": "

Contains the response to a successful ListAttachedRolePolicies\n request.

" + "smithy.api#documentation": "

Contains the response to a successful ListAttachedRolePolicies\n request.

", + "smithy.api#output": {} } }, "com.amazonaws.iam#ListAttachedUserPolicies": { @@ -7930,7 +8259,7 @@ } ], "traits": { - "smithy.api#documentation": "

Lists all managed policies that are attached to the specified IAM user.

\n

An IAM user can also have inline policies embedded with it. To list the inline\n policies for a user, use ListUserPolicies. For information about\n policies, see Managed policies and inline\n policies in the IAM User Guide.

\n

You can paginate the results using the MaxItems and Marker\n parameters. You can use the PathPrefix parameter to limit the list of\n policies to only those matching the specified path prefix. If there are no policies\n attached to the specified group (or none that match the specified path prefix), the\n operation returns an empty list.

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

Lists all managed policies that are attached to the specified IAM user.

\n

An IAM user can also have inline policies embedded with it. To list the inline\n policies for a user, use ListUserPolicies. For information about\n policies, see Managed policies and inline\n policies in the IAM User Guide.

\n

You can paginate the results using the MaxItems and Marker\n parameters. You can use the PathPrefix parameter to limit the list of\n policies to only those matching the specified path prefix. If there are no policies\n attached to the specified group (or none that match the specified path prefix), the\n operation returns an empty list.

", "smithy.api#paginated": { "inputToken": "Marker", "outputToken": "Marker", @@ -7945,14 +8274,14 @@ "UserName": { "target": "com.amazonaws.iam#userNameType", "traits": { - "smithy.api#documentation": "

The name (friendly name, not ARN) of the user to list attached policies for.

\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 (friendly name, not ARN) of the user to list attached policies for.

\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": {} } }, "PathPrefix": { "target": "com.amazonaws.iam#policyPathType", "traits": { - "smithy.api#documentation": "

The path prefix for filtering the results. This parameter is optional. If it is not\n included, it defaults to a slash (/), listing all policies.

\n

This parameter allows (through its regex pattern) a string of characters consisting \n of either a forward slash (/) by itself or a string that must begin and end with forward slashes.\n In addition, it can contain any ASCII character from the ! (\\u0021) through the DEL character (\\u007F), including \n most punctuation characters, digits, and upper and lowercased letters.

" + "smithy.api#documentation": "

The path prefix for filtering the results. This parameter is optional. If it is not\n included, it defaults to a slash (/), listing all policies.

\n

This parameter allows (through its regex pattern) a string of characters consisting \n of either a forward slash (/) by itself or a string that must begin and end with forward slashes.\n In addition, it can contain any ASCII character from the ! (\\u0021) through the DEL character (\\u007F), including \n most punctuation characters, digits, and upper and lowercased letters.

" } }, "Marker": { @@ -7967,6 +8296,9 @@ "smithy.api#documentation": "

Use this only when paginating results to indicate the \n maximum number of items you want in the response. If additional items exist beyond the maximum \n you specify, the IsTruncated response element is true.

\n

If you do not include this parameter, the number of items defaults to 100. Note that\n IAM might return fewer results, even when there are more results available. In that case, the\n IsTruncated response element returns true, and Marker \n contains a value to include in the subsequent call that tells the service where to continue \n from.

" } } + }, + "traits": { + "smithy.api#input": {} } }, "com.amazonaws.iam#ListAttachedUserPoliciesResponse": { @@ -7993,7 +8325,8 @@ } }, "traits": { - "smithy.api#documentation": "

Contains the response to a successful ListAttachedUserPolicies\n request.

" + "smithy.api#documentation": "

Contains the response to a successful ListAttachedUserPolicies\n request.

", + "smithy.api#output": {} } }, "com.amazonaws.iam#ListEntitiesForPolicy": { @@ -8016,7 +8349,7 @@ } ], "traits": { - "smithy.api#documentation": "

Lists all IAM users, groups, and roles that the specified managed policy is attached\n to.

\n

You can use the optional EntityFilter parameter to limit the results to a\n particular type of entity (users, groups, or roles). For example, to list only the roles\n that are attached to the specified policy, set EntityFilter to\n Role.

\n

You can paginate the results using the MaxItems and Marker\n parameters.

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

Lists all IAM users, groups, and roles that the specified managed policy is attached\n to.

\n

You can use the optional EntityFilter parameter to limit the results to a\n particular type of entity (users, groups, or roles). For example, to list only the roles\n that are attached to the specified policy, set EntityFilter to\n Role.

\n

You can paginate the results using the MaxItems and Marker\n parameters.

", "smithy.api#paginated": { "inputToken": "Marker", "outputToken": "Marker", @@ -8030,26 +8363,26 @@ "PolicyArn": { "target": "com.amazonaws.iam#arnType", "traits": { - "smithy.api#documentation": "

The Amazon Resource Name (ARN) of the IAM policy for which you want the\n versions.

\n

For more information about ARNs, see Amazon Resource Names (ARNs) in the Amazon Web Services General Reference.

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

The Amazon Resource Name (ARN) of the IAM policy for which you want the\n versions.

\n

For more information about ARNs, see Amazon Resource Names (ARNs) in the Amazon Web Services General Reference.

", "smithy.api#required": {} } }, "EntityFilter": { "target": "com.amazonaws.iam#EntityType", "traits": { - "smithy.api#documentation": "

The entity type to use for filtering the results.

\n

For example, when EntityFilter is Role, only the roles that\n are attached to the specified policy are returned. This parameter is optional. If it is\n not included, all attached entities (users, groups, and roles) are returned. The\n argument for this parameter must be one of the valid values listed below.

" + "smithy.api#documentation": "

The entity type to use for filtering the results.

\n

For example, when EntityFilter is Role, only the roles that\n are attached to the specified policy are returned. This parameter is optional. If it is\n not included, all attached entities (users, groups, and roles) are returned. The\n argument for this parameter must be one of the valid values listed below.

" } }, "PathPrefix": { "target": "com.amazonaws.iam#pathType", "traits": { - "smithy.api#documentation": "

The path prefix for filtering the results. This parameter is optional. If it is not\n included, it defaults to a slash (/), listing all entities.

\n

This parameter allows (through its regex pattern) a string of characters consisting \n of either a forward slash (/) by itself or a string that must begin and end with forward slashes.\n In addition, it can contain any ASCII character from the ! (\\u0021) through the DEL character (\\u007F), including \n most punctuation characters, digits, and upper and lowercased letters.

" + "smithy.api#documentation": "

The path prefix for filtering the results. This parameter is optional. If it is not\n included, it defaults to a slash (/), listing all entities.

\n

This parameter allows (through its regex pattern) a string of characters consisting \n of either a forward slash (/) by itself or a string that must begin and end with forward slashes.\n In addition, it can contain any ASCII character from the ! (\\u0021) through the DEL character (\\u007F), including \n most punctuation characters, digits, and upper and lowercased letters.

" } }, "PolicyUsageFilter": { "target": "com.amazonaws.iam#PolicyUsageType", "traits": { - "smithy.api#documentation": "

The policy usage method to use for filtering the results.

\n

To list only permissions policies,\n set PolicyUsageFilter to PermissionsPolicy. To list only\n the policies used to set permissions boundaries, set the value\n to PermissionsBoundary.

\n

This parameter is optional. If it is not included, all policies are returned.

" + "smithy.api#documentation": "

The policy usage method to use for filtering the results.

\n

To list only permissions policies,\n set PolicyUsageFilter to PermissionsPolicy. To list only\n the policies used to set permissions boundaries, set the value\n to PermissionsBoundary.

\n

This parameter is optional. If it is not included, all policies are returned.

" } }, "Marker": { @@ -8064,6 +8397,9 @@ "smithy.api#documentation": "

Use this only when paginating results to indicate the \n maximum number of items you want in the response. If additional items exist beyond the maximum \n you specify, the IsTruncated response element is true.

\n

If you do not include this parameter, the number of items defaults to 100. Note that\n IAM might return fewer results, even when there are more results available. In that case, the\n IsTruncated response element returns true, and Marker \n contains a value to include in the subsequent call that tells the service where to continue \n from.

" } } + }, + "traits": { + "smithy.api#input": {} } }, "com.amazonaws.iam#ListEntitiesForPolicyResponse": { @@ -8102,7 +8438,8 @@ } }, "traits": { - "smithy.api#documentation": "

Contains the response to a successful ListEntitiesForPolicy request.\n

" + "smithy.api#documentation": "

Contains the response to a successful ListEntitiesForPolicy request.\n

", + "smithy.api#output": {} } }, "com.amazonaws.iam#ListGroupPolicies": { @@ -8122,7 +8459,7 @@ } ], "traits": { - "smithy.api#documentation": "

Lists the names of the inline policies that are embedded in the specified IAM\n group.

\n

An IAM group can also have managed policies attached to it. To list the managed\n policies that are attached to a group, use ListAttachedGroupPolicies.\n For more information about policies, see Managed policies and inline\n policies in the IAM User Guide.

\n

You can paginate the results using the MaxItems and Marker\n parameters. If there are no inline policies embedded with the specified group, the\n operation returns an empty list.

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

Lists the names of the inline policies that are embedded in the specified IAM\n group.

\n

An IAM group can also have managed policies attached to it. To list the managed\n policies that are attached to a group, use ListAttachedGroupPolicies.\n For more information about policies, see Managed policies and inline\n policies in the IAM User Guide.

\n

You can paginate the results using the MaxItems and Marker\n parameters. If there are no inline policies embedded with the specified group, the\n operation returns an empty list.

", "smithy.api#paginated": { "inputToken": "Marker", "outputToken": "Marker", @@ -8137,7 +8474,7 @@ "GroupName": { "target": "com.amazonaws.iam#groupNameType", "traits": { - "smithy.api#documentation": "

The name of the group to list policies for.

\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 group to list policies for.

\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": {} } }, @@ -8153,6 +8490,9 @@ "smithy.api#documentation": "

Use this only when paginating results to indicate the \n maximum number of items you want in the response. If additional items exist beyond the maximum \n you specify, the IsTruncated response element is true.

\n

If you do not include this parameter, the number of items defaults to 100. Note that\n IAM might return fewer results, even when there are more results available. In that case, the\n IsTruncated response element returns true, and Marker \n contains a value to include in the subsequent call that tells the service where to continue \n from.

" } } + }, + "traits": { + "smithy.api#input": {} } }, "com.amazonaws.iam#ListGroupPoliciesResponse": { @@ -8161,7 +8501,7 @@ "PolicyNames": { "target": "com.amazonaws.iam#policyNameListType", "traits": { - "smithy.api#documentation": "

A list of policy names.

\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": "

A list of policy names.

\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": {} } }, @@ -8180,7 +8520,8 @@ } }, "traits": { - "smithy.api#documentation": "

Contains the response to a successful ListGroupPolicies request.\n

" + "smithy.api#documentation": "

Contains the response to a successful ListGroupPolicies request.\n

", + "smithy.api#output": {} } }, "com.amazonaws.iam#ListGroups": { @@ -8197,7 +8538,7 @@ } ], "traits": { - "smithy.api#documentation": "

Lists the IAM groups that have the specified path prefix.

\n

You can paginate the results using the MaxItems and Marker\n parameters.

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

Lists the IAM groups that have the specified path prefix.

\n

You can paginate the results using the MaxItems and Marker\n parameters.

", "smithy.api#paginated": { "inputToken": "Marker", "outputToken": "Marker", @@ -8223,7 +8564,7 @@ } ], "traits": { - "smithy.api#documentation": "

Lists the IAM groups that the specified IAM user belongs to.

\n

You can paginate the results using the MaxItems and Marker\n parameters.

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

Lists the IAM groups that the specified IAM user belongs to.

\n

You can paginate the results using the MaxItems and Marker\n parameters.

", "smithy.api#paginated": { "inputToken": "Marker", "outputToken": "Marker", @@ -8238,7 +8579,7 @@ "UserName": { "target": "com.amazonaws.iam#existingUserNameType", "traits": { - "smithy.api#documentation": "

The name of the user to list groups for.

\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 user to list groups for.

\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": {} } }, @@ -8254,6 +8595,9 @@ "smithy.api#documentation": "

Use this only when paginating results to indicate the \n maximum number of items you want in the response. If additional items exist beyond the maximum \n you specify, the IsTruncated response element is true.

\n

If you do not include this parameter, the number of items defaults to 100. Note that\n IAM might return fewer results, even when there are more results available. In that case, the\n IsTruncated response element returns true, and Marker \n contains a value to include in the subsequent call that tells the service where to continue \n from.

" } } + }, + "traits": { + "smithy.api#input": {} } }, "com.amazonaws.iam#ListGroupsForUserResponse": { @@ -8281,7 +8625,8 @@ } }, "traits": { - "smithy.api#documentation": "

Contains the response to a successful ListGroupsForUser request.\n

" + "smithy.api#documentation": "

Contains the response to a successful ListGroupsForUser request.\n

", + "smithy.api#output": {} } }, "com.amazonaws.iam#ListGroupsRequest": { @@ -8290,7 +8635,7 @@ "PathPrefix": { "target": "com.amazonaws.iam#pathPrefixType", "traits": { - "smithy.api#documentation": "

The path prefix for filtering the results. For example, the prefix\n /division_abc/subdivision_xyz/ gets all groups whose path starts with\n /division_abc/subdivision_xyz/.

\n

This parameter is optional. If it is not included, it defaults to a slash (/), listing\n all groups. This parameter allows (through its regex pattern) a string of characters consisting \n of either a forward slash (/) by itself or a string that must begin and end with forward slashes.\n In addition, it can contain any ASCII character from the ! (\\u0021) through the DEL character (\\u007F), including \n most punctuation characters, digits, and upper and lowercased letters.

" + "smithy.api#documentation": "

The path prefix for filtering the results. For example, the prefix\n /division_abc/subdivision_xyz/ gets all groups whose path starts with\n /division_abc/subdivision_xyz/.

\n

This parameter is optional. If it is not included, it defaults to a slash (/), listing\n all groups. This parameter allows (through its regex pattern) a string of characters consisting \n of either a forward slash (/) by itself or a string that must begin and end with forward slashes.\n In addition, it can contain any ASCII character from the ! (\\u0021) through the DEL character (\\u007F), including \n most punctuation characters, digits, and upper and lowercased letters.

" } }, "Marker": { @@ -8305,6 +8650,9 @@ "smithy.api#documentation": "

Use this only when paginating results to indicate the \n maximum number of items you want in the response. If additional items exist beyond the maximum \n you specify, the IsTruncated response element is true.

\n

If you do not include this parameter, the number of items defaults to 100. Note that\n IAM might return fewer results, even when there are more results available. In that case, the\n IsTruncated response element returns true, and Marker \n contains a value to include in the subsequent call that tells the service where to continue \n from.

" } } + }, + "traits": { + "smithy.api#input": {} } }, "com.amazonaws.iam#ListGroupsResponse": { @@ -8332,7 +8680,8 @@ } }, "traits": { - "smithy.api#documentation": "

Contains the response to a successful ListGroups request.

" + "smithy.api#documentation": "

Contains the response to a successful ListGroups request.

", + "smithy.api#output": {} } }, "com.amazonaws.iam#ListInstanceProfileTags": { @@ -8377,6 +8726,9 @@ "smithy.api#documentation": "

Use this only when paginating results to indicate the \n maximum number of items you want in the response. If additional items exist beyond the maximum \n you specify, the IsTruncated response element is true.

\n

If you do not include this parameter, the number of items defaults to 100. Note that\n IAM might return fewer results, even when there are more results available. In that case, the\n IsTruncated response element returns true, and Marker \n contains a value to include in the subsequent call that tells the service where to continue \n from.

" } } + }, + "traits": { + "smithy.api#input": {} } }, "com.amazonaws.iam#ListInstanceProfileTagsResponse": { @@ -8402,6 +8754,9 @@ "smithy.api#documentation": "

When IsTruncated is true, this element\n is present and contains the value to use for the Marker parameter in a subsequent \n pagination request.

" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.iam#ListInstanceProfiles": { @@ -8418,7 +8773,7 @@ } ], "traits": { - "smithy.api#documentation": "

Lists the instance profiles that have the specified path prefix. If there are none,\n the operation returns an empty list. For more information about instance profiles, see\n About\n instance profiles.

\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 an instance profile, see GetInstanceProfile.

\n
\n

You can paginate the results using the MaxItems and Marker\n parameters.

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

Lists the instance profiles that have the specified path prefix. If there are none,\n the operation returns an empty list. For more information about instance profiles, see\n About\n instance profiles.

\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 an instance profile, see GetInstanceProfile.

\n
\n

You can paginate the results using the MaxItems and Marker\n parameters.

", "smithy.api#paginated": { "inputToken": "Marker", "outputToken": "Marker", @@ -8444,7 +8799,7 @@ } ], "traits": { - "smithy.api#documentation": "

Lists the instance profiles that have the specified associated IAM role. If there\n are none, the operation returns an empty list. For more information about instance\n profiles, go to About instance\n profiles.

\n

You can paginate the results using the MaxItems and Marker\n parameters.

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

Lists the instance profiles that have the specified associated IAM role. If there\n are none, the operation returns an empty list. For more information about instance\n profiles, go to About instance\n profiles.

\n

You can paginate the results using the MaxItems and Marker\n parameters.

", "smithy.api#paginated": { "inputToken": "Marker", "outputToken": "Marker", @@ -8459,7 +8814,7 @@ "RoleName": { "target": "com.amazonaws.iam#roleNameType", "traits": { - "smithy.api#documentation": "

The name of the role to list instance profiles for.

\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 role to list instance profiles for.

\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": {} } }, @@ -8475,6 +8830,9 @@ "smithy.api#documentation": "

Use this only when paginating results to indicate the \n maximum number of items you want in the response. If additional items exist beyond the maximum \n you specify, the IsTruncated response element is true.

\n

If you do not include this parameter, the number of items defaults to 100. Note that\n IAM might return fewer results, even when there are more results available. In that case, the\n IsTruncated response element returns true, and Marker \n contains a value to include in the subsequent call that tells the service where to continue \n from.

" } } + }, + "traits": { + "smithy.api#input": {} } }, "com.amazonaws.iam#ListInstanceProfilesForRoleResponse": { @@ -8502,7 +8860,8 @@ } }, "traits": { - "smithy.api#documentation": "

Contains the response to a successful ListInstanceProfilesForRole\n request.

" + "smithy.api#documentation": "

Contains the response to a successful ListInstanceProfilesForRole\n request.

", + "smithy.api#output": {} } }, "com.amazonaws.iam#ListInstanceProfilesRequest": { @@ -8511,7 +8870,7 @@ "PathPrefix": { "target": "com.amazonaws.iam#pathPrefixType", "traits": { - "smithy.api#documentation": "

The path prefix for filtering the results. For example, the prefix\n /application_abc/component_xyz/ gets all instance profiles whose path\n starts with /application_abc/component_xyz/.

\n

This parameter is optional. If it is not included, it defaults to a slash (/), listing\n all instance profiles. This parameter allows (through its regex pattern) a string of characters consisting \n of either a forward slash (/) by itself or a string that must begin and end with forward slashes.\n In addition, it can contain any ASCII character from the ! (\\u0021) through the DEL character (\\u007F), including \n most punctuation characters, digits, and upper and lowercased letters.

" + "smithy.api#documentation": "

The path prefix for filtering the results. For example, the prefix\n /application_abc/component_xyz/ gets all instance profiles whose path\n starts with /application_abc/component_xyz/.

\n

This parameter is optional. If it is not included, it defaults to a slash (/), listing\n all instance profiles. This parameter allows (through its regex pattern) a string of characters consisting \n of either a forward slash (/) by itself or a string that must begin and end with forward slashes.\n In addition, it can contain any ASCII character from the ! (\\u0021) through the DEL character (\\u007F), including \n most punctuation characters, digits, and upper and lowercased letters.

" } }, "Marker": { @@ -8526,6 +8885,9 @@ "smithy.api#documentation": "

Use this only when paginating results to indicate the \n maximum number of items you want in the response. If additional items exist beyond the maximum \n you specify, the IsTruncated response element is true.

\n

If you do not include this parameter, the number of items defaults to 100. Note that\n IAM might return fewer results, even when there are more results available. In that case, the\n IsTruncated response element returns true, and Marker \n contains a value to include in the subsequent call that tells the service where to continue \n from.

" } } + }, + "traits": { + "smithy.api#input": {} } }, "com.amazonaws.iam#ListInstanceProfilesResponse": { @@ -8553,7 +8915,8 @@ } }, "traits": { - "smithy.api#documentation": "

Contains the response to a successful ListInstanceProfiles request.\n

" + "smithy.api#documentation": "

Contains the response to a successful ListInstanceProfiles request.\n

", + "smithy.api#output": {} } }, "com.amazonaws.iam#ListMFADeviceTags": { @@ -8601,6 +8964,9 @@ "smithy.api#documentation": "

Use this only when paginating results to indicate the \n maximum number of items you want in the response. If additional items exist beyond the maximum \n you specify, the IsTruncated response element is true.

\n

If you do not include this parameter, the number of items defaults to 100. Note that\n IAM might return fewer results, even when there are more results available. In that case, the\n IsTruncated response element returns true, and Marker \n contains a value to include in the subsequent call that tells the service where to continue \n from.

" } } + }, + "traits": { + "smithy.api#input": {} } }, "com.amazonaws.iam#ListMFADeviceTagsResponse": { @@ -8626,6 +8992,9 @@ "smithy.api#documentation": "

When IsTruncated is true, this element\n is present and contains the value to use for the Marker parameter in a subsequent \n pagination request.

" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.iam#ListMFADevices": { @@ -8645,7 +9014,7 @@ } ], "traits": { - "smithy.api#documentation": "

Lists the MFA devices for an IAM user. If the request includes a IAM user name,\n then this operation lists all the MFA devices associated with the specified user. If you\n do not specify a user name, IAM determines the user name implicitly based on the Amazon Web Services\n access key ID signing the request for this operation.

\n

You can paginate the results using the MaxItems and Marker\n parameters.

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

Lists the MFA devices for an IAM user. If the request includes a IAM user name,\n then this operation lists all the MFA devices associated with the specified user. If you\n do not specify a user name, IAM determines the user name implicitly based on the Amazon Web Services\n access key ID signing the request for this operation.

\n

You can paginate the results using the MaxItems and Marker\n parameters.

", "smithy.api#paginated": { "inputToken": "Marker", "outputToken": "Marker", @@ -8660,7 +9029,7 @@ "UserName": { "target": "com.amazonaws.iam#existingUserNameType", "traits": { - "smithy.api#documentation": "

The name of the user whose MFA devices you want to list.

\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 user whose MFA devices you want to list.

\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: _+=,.@-

" } }, "Marker": { @@ -8675,6 +9044,9 @@ "smithy.api#documentation": "

Use this only when paginating results to indicate the \n maximum number of items you want in the response. If additional items exist beyond the maximum \n you specify, the IsTruncated response element is true.

\n

If you do not include this parameter, the number of items defaults to 100. Note that\n IAM might return fewer results, even when there are more results available. In that case, the\n IsTruncated response element returns true, and Marker \n contains a value to include in the subsequent call that tells the service where to continue \n from.

" } } + }, + "traits": { + "smithy.api#input": {} } }, "com.amazonaws.iam#ListMFADevicesResponse": { @@ -8702,7 +9074,8 @@ } }, "traits": { - "smithy.api#documentation": "

Contains the response to a successful ListMFADevices request.\n

" + "smithy.api#documentation": "

Contains the response to a successful ListMFADevices request.\n

", + "smithy.api#output": {} } }, "com.amazonaws.iam#ListOpenIDConnectProviderTags": { @@ -8750,6 +9123,9 @@ "smithy.api#documentation": "

Use this only when paginating results to indicate the \n maximum number of items you want in the response. If additional items exist beyond the maximum \n you specify, the IsTruncated response element is true.

\n

If you do not include this parameter, the number of items defaults to 100. Note that\n IAM might return fewer results, even when there are more results available. In that case, the\n IsTruncated response element returns true, and Marker \n contains a value to include in the subsequent call that tells the service where to continue \n from.

" } } + }, + "traits": { + "smithy.api#input": {} } }, "com.amazonaws.iam#ListOpenIDConnectProviderTagsResponse": { @@ -8775,6 +9151,9 @@ "smithy.api#documentation": "

When IsTruncated is true, this element\n is present and contains the value to use for the Marker parameter in a subsequent \n pagination request.

" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.iam#ListOpenIDConnectProviders": { @@ -8791,12 +9170,15 @@ } ], "traits": { - "smithy.api#documentation": "

Lists information about the IAM OpenID Connect (OIDC) provider resource objects\n defined in the Amazon Web Services account.

\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 an OIDC provider, see GetOpenIDConnectProvider.

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

Lists information about the IAM OpenID Connect (OIDC) provider resource objects\n defined in the Amazon Web Services account.

\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 an OIDC provider, see GetOpenIDConnectProvider.

\n
" } }, "com.amazonaws.iam#ListOpenIDConnectProvidersRequest": { "type": "structure", - "members": {} + "members": {}, + "traits": { + "smithy.api#input": {} + } }, "com.amazonaws.iam#ListOpenIDConnectProvidersResponse": { "type": "structure", @@ -8809,7 +9191,8 @@ } }, "traits": { - "smithy.api#documentation": "

Contains the response to a successful ListOpenIDConnectProviders\n request.

" + "smithy.api#documentation": "

Contains the response to a successful ListOpenIDConnectProviders\n request.

", + "smithy.api#output": {} } }, "com.amazonaws.iam#ListPolicies": { @@ -8826,7 +9209,7 @@ } ], "traits": { - "smithy.api#documentation": "

Lists all the managed policies that are available in your Amazon Web Services account, including\n your own customer-defined managed policies and all Amazon Web Services managed policies.

\n

You can filter the list of policies that is returned using the optional\n OnlyAttached, Scope, and PathPrefix\n parameters. For example, to list only the customer managed policies in your Amazon Web Services\n account, set Scope to Local. To list only Amazon Web Services managed\n policies, set Scope to AWS.

\n

You can paginate the results using the MaxItems and Marker\n parameters.

\n

For more information about managed policies, see Managed policies and inline\n policies in the IAM User Guide.

\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 customer manged policy, see\n GetPolicy.

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

Lists all the managed policies that are available in your Amazon Web Services account, including\n your own customer-defined managed policies and all Amazon Web Services managed policies.

\n

You can filter the list of policies that is returned using the optional\n OnlyAttached, Scope, and PathPrefix\n parameters. For example, to list only the customer managed policies in your Amazon Web Services\n account, set Scope to Local. To list only Amazon Web Services managed\n policies, set Scope to AWS.

\n

You can paginate the results using the MaxItems and Marker\n parameters.

\n

For more information about managed policies, see Managed policies and inline\n policies in the IAM User Guide.

\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 customer manged policy, see\n GetPolicy.

\n
", "smithy.api#paginated": { "inputToken": "Marker", "outputToken": "Marker", @@ -8852,7 +9235,7 @@ } ], "traits": { - "smithy.api#documentation": "

Retrieves a list of policies that the IAM identity (user, group, or role) can use to\n access each specified service.

\n \n

This operation does not use other policy types when determining whether a resource\n could access a service. These other policy types include resource-based policies,\n access control lists, Organizations policies, IAM permissions boundaries, and STS\n assume role policies. It only applies permissions policy logic. For more about the\n evaluation of policy types, see Evaluating policies in the\n IAM User Guide.

\n
\n

The list of policies returned by the operation depends on the ARN of the identity that\n you provide.

\n
    \n
  • \n

    \n User – The list of policies includes\n the managed and inline policies that are attached to the user directly. The list\n also includes any additional managed and inline policies that are attached to\n the group to which the user belongs.

    \n
  • \n
  • \n

    \n Group – The list of policies includes\n only the managed and inline policies that are attached to the group directly.\n Policies that are attached to the group’s user are not included.

    \n
  • \n
  • \n

    \n Role – The list of policies includes\n only the managed and inline policies that are attached to the role.

    \n
  • \n
\n

For each managed policy, this operation returns the ARN and policy name. For each\n inline policy, it returns the policy name and the entity to which it is attached. Inline\n policies do not have an ARN. For more information about these policy types, see Managed policies and inline policies in the\n IAM User Guide.

\n

Policies that are attached to users and roles as permissions boundaries are not\n returned. To view which managed policy is currently used to set the permissions boundary\n for a user or role, use the GetUser or GetRole\n operations.

" + "smithy.api#documentation": "

Retrieves a list of policies that the IAM identity (user, group, or role) can use to\n access each specified service.

\n \n

This operation does not use other policy types when determining whether a resource\n could access a service. These other policy types include resource-based policies,\n access control lists, Organizations policies, IAM permissions boundaries, and STS\n assume role policies. It only applies permissions policy logic. For more about the\n evaluation of policy types, see Evaluating policies in the\n IAM User Guide.

\n
\n

The list of policies returned by the operation depends on the ARN of the identity that\n you provide.

\n
    \n
  • \n

    \n User – The list of policies includes\n the managed and inline policies that are attached to the user directly. The list\n also includes any additional managed and inline policies that are attached to\n the group to which the user belongs.

    \n
  • \n
  • \n

    \n Group – The list of policies includes\n only the managed and inline policies that are attached to the group directly.\n Policies that are attached to the group’s user are not included.

    \n
  • \n
  • \n

    \n Role – The list of policies includes\n only the managed and inline policies that are attached to the role.

    \n
  • \n
\n

For each managed policy, this operation returns the ARN and policy name. For each\n inline policy, it returns the policy name and the entity to which it is attached. Inline\n policies do not have an ARN. For more information about these policy types, see Managed policies and inline policies in the\n IAM User Guide.

\n

Policies that are attached to users and roles as permissions boundaries are not\n returned. To view which managed policy is currently used to set the permissions boundary\n for a user or role, use the GetUser or GetRole\n operations.

" } }, "com.amazonaws.iam#ListPoliciesGrantingServiceAccessEntry": { @@ -8894,10 +9277,13 @@ "ServiceNamespaces": { "target": "com.amazonaws.iam#serviceNamespaceListType", "traits": { - "smithy.api#documentation": "

The service namespace for the Amazon Web Services services whose policies you want to list.

\n

To learn the service namespace for a service, see Actions, resources, and condition keys for Amazon Web Services services in the\n IAM User Guide. Choose the name of the service to view\n details for that service. In the first paragraph, find the service prefix. For example,\n (service prefix: a4b). For more information about service namespaces,\n see Amazon Web Services\n service namespaces in the Amazon Web Services General Reference.

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

The service namespace for the Amazon Web Services services whose policies you want to list.

\n

To learn the service namespace for a service, see Actions, resources, and condition keys for Amazon Web Services services in the\n IAM User Guide. Choose the name of the service to view\n details for that service. In the first paragraph, find the service prefix. For example,\n (service prefix: a4b). For more information about service namespaces,\n see Amazon Web Services\n service namespaces in the Amazon Web Services General Reference.

", "smithy.api#required": {} } } + }, + "traits": { + "smithy.api#input": {} } }, "com.amazonaws.iam#ListPoliciesGrantingServiceAccessResponse": { @@ -8923,6 +9309,9 @@ "smithy.api#documentation": "

When IsTruncated is true, this element\n is present and contains the value to use for the Marker parameter in a subsequent \n pagination request.

" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.iam#ListPoliciesRequest": { @@ -8931,14 +9320,14 @@ "Scope": { "target": "com.amazonaws.iam#policyScopeType", "traits": { - "smithy.api#documentation": "

The scope to use for filtering the results.

\n

To list only Amazon Web Services managed policies, set Scope to AWS. To\n list only the customer managed policies in your Amazon Web Services account, set Scope to\n Local.

\n

This parameter is optional. If it is not included, or if it is set to\n All, all policies are returned.

" + "smithy.api#documentation": "

The scope to use for filtering the results.

\n

To list only Amazon Web Services managed policies, set Scope to AWS. To\n list only the customer managed policies in your Amazon Web Services account, set Scope to\n Local.

\n

This parameter is optional. If it is not included, or if it is set to\n All, all policies are returned.

" } }, "OnlyAttached": { "target": "com.amazonaws.iam#booleanType", "traits": { "smithy.api#default": false, - "smithy.api#documentation": "

A flag to filter the results to only the attached policies.

\n

When OnlyAttached is true, the returned list contains only\n the policies that are attached to an IAM user, group, or role. When\n OnlyAttached is false, or when the parameter is not\n included, all policies are returned.

" + "smithy.api#documentation": "

A flag to filter the results to only the attached policies.

\n

When OnlyAttached is true, the returned list contains only\n the policies that are attached to an IAM user, group, or role. When\n OnlyAttached is false, or when the parameter is not\n included, all policies are returned.

" } }, "PathPrefix": { @@ -8950,7 +9339,7 @@ "PolicyUsageFilter": { "target": "com.amazonaws.iam#PolicyUsageType", "traits": { - "smithy.api#documentation": "

The policy usage method to use for filtering the results.

\n

To list only permissions policies,\n set PolicyUsageFilter to PermissionsPolicy. To list only\n the policies used to set permissions boundaries, set the value\n to PermissionsBoundary.

\n

This parameter is optional. If it is not included, all policies are returned.

" + "smithy.api#documentation": "

The policy usage method to use for filtering the results.

\n

To list only permissions policies,\n set PolicyUsageFilter to PermissionsPolicy. To list only\n the policies used to set permissions boundaries, set the value\n to PermissionsBoundary.

\n

This parameter is optional. If it is not included, all policies are returned.

" } }, "Marker": { @@ -8965,6 +9354,9 @@ "smithy.api#documentation": "

Use this only when paginating results to indicate the \n maximum number of items you want in the response. If additional items exist beyond the maximum \n you specify, the IsTruncated response element is true.

\n

If you do not include this parameter, the number of items defaults to 100. Note that\n IAM might return fewer results, even when there are more results available. In that case, the\n IsTruncated response element returns true, and Marker \n contains a value to include in the subsequent call that tells the service where to continue \n from.

" } } + }, + "traits": { + "smithy.api#input": {} } }, "com.amazonaws.iam#ListPoliciesResponse": { @@ -8991,7 +9383,8 @@ } }, "traits": { - "smithy.api#documentation": "

Contains the response to a successful ListPolicies request.\n

" + "smithy.api#documentation": "

Contains the response to a successful ListPolicies request.\n

", + "smithy.api#output": {} } }, "com.amazonaws.iam#ListPolicyTags": { @@ -9039,6 +9432,9 @@ "smithy.api#documentation": "

Use this only when paginating results to indicate the \n maximum number of items you want in the response. If additional items exist beyond the maximum \n you specify, the IsTruncated response element is true.

\n

If you do not include this parameter, the number of items defaults to 100. Note that\n IAM might return fewer results, even when there are more results available. In that case, the\n IsTruncated response element returns true, and Marker \n contains a value to include in the subsequent call that tells the service where to continue \n from.

" } } + }, + "traits": { + "smithy.api#input": {} } }, "com.amazonaws.iam#ListPolicyTagsResponse": { @@ -9064,6 +9460,9 @@ "smithy.api#documentation": "

When IsTruncated is true, this element\n is present and contains the value to use for the Marker parameter in a subsequent \n pagination request.

" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.iam#ListPolicyVersions": { @@ -9086,7 +9485,7 @@ } ], "traits": { - "smithy.api#documentation": "

Lists information about the versions of the specified managed policy, including the\n version that is currently set as the policy's default version.

\n

For more information about managed policies, see Managed policies and inline\n policies in the IAM User Guide.

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

Lists information about the versions of the specified managed policy, including the\n version that is currently set as the policy's default version.

\n

For more information about managed policies, see Managed policies and inline\n policies in the IAM User Guide.

", "smithy.api#paginated": { "inputToken": "Marker", "outputToken": "Marker", @@ -9101,7 +9500,7 @@ "PolicyArn": { "target": "com.amazonaws.iam#arnType", "traits": { - "smithy.api#documentation": "

The Amazon Resource Name (ARN) of the IAM policy for which you want the\n versions.

\n

For more information about ARNs, see Amazon Resource Names (ARNs) in the Amazon Web Services General Reference.

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

The Amazon Resource Name (ARN) of the IAM policy for which you want the\n versions.

\n

For more information about ARNs, see Amazon Resource Names (ARNs) in the Amazon Web Services General Reference.

", "smithy.api#required": {} } }, @@ -9117,6 +9516,9 @@ "smithy.api#documentation": "

Use this only when paginating results to indicate the \n maximum number of items you want in the response. If additional items exist beyond the maximum \n you specify, the IsTruncated response element is true.

\n

If you do not include this parameter, the number of items defaults to 100. Note that\n IAM might return fewer results, even when there are more results available. In that case, the\n IsTruncated response element returns true, and Marker \n contains a value to include in the subsequent call that tells the service where to continue \n from.

" } } + }, + "traits": { + "smithy.api#input": {} } }, "com.amazonaws.iam#ListPolicyVersionsResponse": { @@ -9125,7 +9527,7 @@ "Versions": { "target": "com.amazonaws.iam#policyDocumentVersionListType", "traits": { - "smithy.api#documentation": "

A list of policy versions.

\n

For more information about managed policy versions, see Versioning for managed\n policies in the IAM User Guide.

" + "smithy.api#documentation": "

A list of policy versions.

\n

For more information about managed policy versions, see Versioning for managed\n policies in the IAM User Guide.

" } }, "IsTruncated": { @@ -9143,7 +9545,8 @@ } }, "traits": { - "smithy.api#documentation": "

Contains the response to a successful ListPolicyVersions request.\n

" + "smithy.api#documentation": "

Contains the response to a successful ListPolicyVersions request.\n

", + "smithy.api#output": {} } }, "com.amazonaws.iam#ListRolePolicies": { @@ -9163,7 +9566,7 @@ } ], "traits": { - "smithy.api#documentation": "

Lists the names of the inline policies that are embedded in the specified IAM\n role.

\n

An IAM role can also have managed policies attached to it. To list the managed\n policies that are attached to a role, use ListAttachedRolePolicies.\n For more information about policies, see Managed policies and inline\n policies in the IAM User Guide.

\n

You can paginate the results using the MaxItems and Marker\n parameters. If there are no inline policies embedded with the specified role, the\n operation returns an empty list.

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

Lists the names of the inline policies that are embedded in the specified IAM\n role.

\n

An IAM role can also have managed policies attached to it. To list the managed\n policies that are attached to a role, use ListAttachedRolePolicies.\n For more information about policies, see Managed policies and inline\n policies in the IAM User Guide.

\n

You can paginate the results using the MaxItems and Marker\n parameters. If there are no inline policies embedded with the specified role, the\n operation returns an empty list.

", "smithy.api#paginated": { "inputToken": "Marker", "outputToken": "Marker", @@ -9178,7 +9581,7 @@ "RoleName": { "target": "com.amazonaws.iam#roleNameType", "traits": { - "smithy.api#documentation": "

The name of the role to list policies for.

\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 role to list policies for.

\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": {} } }, @@ -9194,6 +9597,9 @@ "smithy.api#documentation": "

Use this only when paginating results to indicate the \n maximum number of items you want in the response. If additional items exist beyond the maximum \n you specify, the IsTruncated response element is true.

\n

If you do not include this parameter, the number of items defaults to 100. Note that\n IAM might return fewer results, even when there are more results available. In that case, the\n IsTruncated response element returns true, and Marker \n contains a value to include in the subsequent call that tells the service where to continue \n from.

" } } + }, + "traits": { + "smithy.api#input": {} } }, "com.amazonaws.iam#ListRolePoliciesResponse": { @@ -9221,7 +9627,8 @@ } }, "traits": { - "smithy.api#documentation": "

Contains the response to a successful ListRolePolicies request.\n

" + "smithy.api#documentation": "

Contains the response to a successful ListRolePolicies request.\n

", + "smithy.api#output": {} } }, "com.amazonaws.iam#ListRoleTags": { @@ -9266,6 +9673,9 @@ "smithy.api#documentation": "

Use this only when paginating results to indicate the \n maximum number of items you want in the response. If additional items exist beyond the maximum \n you specify, the IsTruncated response element is true.

\n

If you do not include this parameter, the number of items defaults to 100. Note that\n IAM might return fewer results, even when there are more results available. In that case, the\n IsTruncated response element returns true, and Marker \n contains a value to include in the subsequent call that tells the service where to continue \n from.

" } } + }, + "traits": { + "smithy.api#input": {} } }, "com.amazonaws.iam#ListRoleTagsResponse": { @@ -9291,6 +9701,9 @@ "smithy.api#documentation": "

When IsTruncated is true, this element\n is present and contains the value to use for the Marker parameter in a subsequent \n pagination request.

" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.iam#ListRoles": { @@ -9307,7 +9720,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. 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#paginated": { "inputToken": "Marker", "outputToken": "Marker", @@ -9322,7 +9735,7 @@ "PathPrefix": { "target": "com.amazonaws.iam#pathPrefixType", "traits": { - "smithy.api#documentation": "

The path prefix for filtering the results. For example, the prefix\n /application_abc/component_xyz/ gets all roles whose path starts with\n /application_abc/component_xyz/.

\n

This parameter is optional. If it is not included, it defaults to a slash (/), listing\n all roles. This parameter allows (through its regex pattern) a string of characters consisting \n of either a forward slash (/) by itself or a string that must begin and end with forward slashes.\n In addition, it can contain any ASCII character from the ! (\\u0021) through the DEL character (\\u007F), including \n most punctuation characters, digits, and upper and lowercased letters.

" + "smithy.api#documentation": "

The path prefix for filtering the results. For example, the prefix\n /application_abc/component_xyz/ gets all roles whose path starts with\n /application_abc/component_xyz/.

\n

This parameter is optional. If it is not included, it defaults to a slash (/), listing\n all roles. This parameter allows (through its regex pattern) a string of characters consisting \n of either a forward slash (/) by itself or a string that must begin and end with forward slashes.\n In addition, it can contain any ASCII character from the ! (\\u0021) through the DEL character (\\u007F), including \n most punctuation characters, digits, and upper and lowercased letters.

" } }, "Marker": { @@ -9337,6 +9750,9 @@ "smithy.api#documentation": "

Use this only when paginating results to indicate the \n maximum number of items you want in the response. If additional items exist beyond the maximum \n you specify, the IsTruncated response element is true.

\n

If you do not include this parameter, the number of items defaults to 100. Note that\n IAM might return fewer results, even when there are more results available. In that case, the\n IsTruncated response element returns true, and Marker \n contains a value to include in the subsequent call that tells the service where to continue \n from.

" } } + }, + "traits": { + "smithy.api#input": {} } }, "com.amazonaws.iam#ListRolesResponse": { @@ -9364,7 +9780,8 @@ } }, "traits": { - "smithy.api#documentation": "

Contains the response to a successful ListRoles request.

" + "smithy.api#documentation": "

Contains the response to a successful ListRoles request.

", + "smithy.api#output": {} } }, "com.amazonaws.iam#ListSAMLProviderTags": { @@ -9412,6 +9829,9 @@ "smithy.api#documentation": "

Use this only when paginating results to indicate the \n maximum number of items you want in the response. If additional items exist beyond the maximum \n you specify, the IsTruncated response element is true.

\n

If you do not include this parameter, the number of items defaults to 100. Note that\n IAM might return fewer results, even when there are more results available. In that case, the\n IsTruncated response element returns true, and Marker \n contains a value to include in the subsequent call that tells the service where to continue \n from.

" } } + }, + "traits": { + "smithy.api#input": {} } }, "com.amazonaws.iam#ListSAMLProviderTagsResponse": { @@ -9437,6 +9857,9 @@ "smithy.api#documentation": "

When IsTruncated is true, this element\n is present and contains the value to use for the Marker parameter in a subsequent \n pagination request.

" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.iam#ListSAMLProviders": { @@ -9453,12 +9876,15 @@ } ], "traits": { - "smithy.api#documentation": "

Lists the SAML provider resource objects defined in IAM in the account.\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 SAML provider, see GetSAMLProvider.

\n \n

This operation requires Signature Version 4.

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

Lists the SAML provider resource objects defined in IAM in the account.\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 SAML provider, see GetSAMLProvider.

\n \n

This operation requires Signature Version 4.

\n
" } }, "com.amazonaws.iam#ListSAMLProvidersRequest": { "type": "structure", - "members": {} + "members": {}, + "traits": { + "smithy.api#input": {} + } }, "com.amazonaws.iam#ListSAMLProvidersResponse": { "type": "structure", @@ -9471,7 +9897,8 @@ } }, "traits": { - "smithy.api#documentation": "

Contains the response to a successful ListSAMLProviders request.\n

" + "smithy.api#documentation": "

Contains the response to a successful ListSAMLProviders request.\n

", + "smithy.api#output": {} } }, "com.amazonaws.iam#ListSSHPublicKeys": { @@ -9488,7 +9915,7 @@ } ], "traits": { - "smithy.api#documentation": "

Returns information about the SSH public keys associated with the specified IAM\n user. If none exists, the operation returns an empty list.

\n

The SSH public keys returned by this operation are used only for authenticating the\n IAM user to an CodeCommit repository. For more information about using SSH keys to\n authenticate to an CodeCommit repository, see Set up CodeCommit for\n SSH connections in the CodeCommit User Guide.

\n

Although each user is limited to a small number of keys, you can still paginate the\n results using the MaxItems and Marker parameters.

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

Returns information about the SSH public keys associated with the specified IAM\n user. If none exists, the operation returns an empty list.

\n

The SSH public keys returned by this operation are used only for authenticating the\n IAM user to an CodeCommit repository. For more information about using SSH keys to\n authenticate to an CodeCommit repository, see Set up CodeCommit for\n SSH connections in the CodeCommit User Guide.

\n

Although each user is limited to a small number of keys, you can still paginate the\n results using the MaxItems and Marker parameters.

", "smithy.api#paginated": { "inputToken": "Marker", "outputToken": "Marker", @@ -9503,7 +9930,7 @@ "UserName": { "target": "com.amazonaws.iam#userNameType", "traits": { - "smithy.api#documentation": "

The name of the IAM user to list SSH public keys for. If none is specified, the\n UserName field is determined implicitly based on the Amazon Web Services access key\n used to sign the request.

\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 IAM user to list SSH public keys for. If none is specified, the\n UserName field is determined implicitly based on the Amazon Web Services access key\n used to sign the request.

\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: _+=,.@-

" } }, "Marker": { @@ -9518,6 +9945,9 @@ "smithy.api#documentation": "

Use this only when paginating results to indicate the \n maximum number of items you want in the response. If additional items exist beyond the maximum \n you specify, the IsTruncated response element is true.

\n

If you do not include this parameter, the number of items defaults to 100. Note that\n IAM might return fewer results, even when there are more results available. In that case, the\n IsTruncated response element returns true, and Marker \n contains a value to include in the subsequent call that tells the service where to continue \n from.

" } } + }, + "traits": { + "smithy.api#input": {} } }, "com.amazonaws.iam#ListSSHPublicKeysResponse": { @@ -9544,7 +9974,8 @@ } }, "traits": { - "smithy.api#documentation": "

Contains the response to a successful ListSSHPublicKeys\n request.

" + "smithy.api#documentation": "

Contains the response to a successful ListSSHPublicKeys\n request.

", + "smithy.api#output": {} } }, "com.amazonaws.iam#ListServerCertificateTags": { @@ -9589,6 +10020,9 @@ "smithy.api#documentation": "

Use this only when paginating results to indicate the \n maximum number of items you want in the response. If additional items exist beyond the maximum \n you specify, the IsTruncated response element is true.

\n

If you do not include this parameter, the number of items defaults to 100. Note that\n IAM might return fewer results, even when there are more results available. In that case, the\n IsTruncated response element returns true, and Marker \n contains a value to include in the subsequent call that tells the service where to continue \n from.

" } } + }, + "traits": { + "smithy.api#input": {} } }, "com.amazonaws.iam#ListServerCertificateTagsResponse": { @@ -9614,6 +10048,9 @@ "smithy.api#documentation": "

When IsTruncated is true, this element\n is present and contains the value to use for the Marker parameter in a subsequent \n pagination request.

" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.iam#ListServerCertificates": { @@ -9630,7 +10067,7 @@ } ], "traits": { - "smithy.api#documentation": "

Lists the server certificates stored in IAM that have the specified path prefix. If\n none exist, the operation returns an empty list.

\n

You can paginate the results using the MaxItems and Marker\n parameters.

\n

For more information about working with server certificates, see Working\n with server certificates in the IAM User Guide. This\n topic also includes a list of Amazon Web Services services that can use the server certificates that\n you manage with IAM.

\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 servercertificate, see GetServerCertificate.

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

Lists the server certificates stored in IAM that have the specified path prefix. If\n none exist, the operation returns an empty list.

\n

You can paginate the results using the MaxItems and Marker\n parameters.

\n

For more information about working with server certificates, see Working\n with server certificates in the IAM User Guide. This\n topic also includes a list of Amazon Web Services services that can use the server certificates that\n you manage with IAM.

\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 servercertificate, see GetServerCertificate.

\n
", "smithy.api#paginated": { "inputToken": "Marker", "outputToken": "Marker", @@ -9645,7 +10082,7 @@ "PathPrefix": { "target": "com.amazonaws.iam#pathPrefixType", "traits": { - "smithy.api#documentation": "

The path prefix for filtering the results. For example:\n /company/servercerts would get all server certificates for which the\n path starts with /company/servercerts.

\n

This parameter is optional. If it is not included, it defaults to a slash (/), listing\n all server certificates. This parameter allows (through its regex pattern) a string of characters consisting \n of either a forward slash (/) by itself or a string that must begin and end with forward slashes.\n In addition, it can contain any ASCII character from the ! (\\u0021) through the DEL character (\\u007F), including \n most punctuation characters, digits, and upper and lowercased letters.

" + "smithy.api#documentation": "

The path prefix for filtering the results. For example:\n /company/servercerts would get all server certificates for which the\n path starts with /company/servercerts.

\n

This parameter is optional. If it is not included, it defaults to a slash (/), listing\n all server certificates. This parameter allows (through its regex pattern) a string of characters consisting \n of either a forward slash (/) by itself or a string that must begin and end with forward slashes.\n In addition, it can contain any ASCII character from the ! (\\u0021) through the DEL character (\\u007F), including \n most punctuation characters, digits, and upper and lowercased letters.

" } }, "Marker": { @@ -9660,6 +10097,9 @@ "smithy.api#documentation": "

Use this only when paginating results to indicate the \n maximum number of items you want in the response. If additional items exist beyond the maximum \n you specify, the IsTruncated response element is true.

\n

If you do not include this parameter, the number of items defaults to 100. Note that\n IAM might return fewer results, even when there are more results available. In that case, the\n IsTruncated response element returns true, and Marker \n contains a value to include in the subsequent call that tells the service where to continue \n from.

" } } + }, + "traits": { + "smithy.api#input": {} } }, "com.amazonaws.iam#ListServerCertificatesResponse": { @@ -9687,7 +10127,8 @@ } }, "traits": { - "smithy.api#documentation": "

Contains the response to a successful ListServerCertificates request.\n

" + "smithy.api#documentation": "

Contains the response to a successful ListServerCertificates request.\n

", + "smithy.api#output": {} } }, "com.amazonaws.iam#ListServiceSpecificCredentials": { @@ -9716,7 +10157,7 @@ "UserName": { "target": "com.amazonaws.iam#userNameType", "traits": { - "smithy.api#documentation": "

The name of the user whose service-specific credentials you want information about. If\n this value is not specified, then the operation assumes the user whose credentials are\n used to call the operation.

\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 user whose service-specific credentials you want information about. If\n this value is not specified, then the operation assumes the user whose credentials are\n used to call the operation.

\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: _+=,.@-

" } }, "ServiceName": { @@ -9725,6 +10166,9 @@ "smithy.api#documentation": "

Filters the returned results to only those for the specified Amazon Web Services service. If not\n specified, then Amazon Web Services returns service-specific credentials for all services.

" } } + }, + "traits": { + "smithy.api#input": {} } }, "com.amazonaws.iam#ListServiceSpecificCredentialsResponse": { @@ -9736,6 +10180,9 @@ "smithy.api#documentation": "

A list of structures that each contain details about a service-specific\n credential.

" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.iam#ListSigningCertificates": { @@ -9755,7 +10202,7 @@ } ], "traits": { - "smithy.api#documentation": "

Returns information about the signing certificates associated with the specified IAM\n user. If none exists, the operation returns an empty list.

\n

Although each user is limited to a small number of signing certificates, you can still\n paginate the results using the MaxItems and Marker\n parameters.

\n

If the UserName field is not specified, the user name is determined\n implicitly based on the Amazon Web Services access key ID used to sign the request for this operation.\n This operation works for access keys under the Amazon Web Services account. Consequently, you can use\n this operation to manage Amazon Web Services account root user credentials even if the Amazon Web Services account\n has no associated users.

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

Returns information about the signing certificates associated with the specified IAM\n user. If none exists, the operation returns an empty list.

\n

Although each user is limited to a small number of signing certificates, you can still\n paginate the results using the MaxItems and Marker\n parameters.

\n

If the UserName field is not specified, the user name is determined\n implicitly based on the Amazon Web Services access key ID used to sign the request for this operation.\n This operation works for access keys under the Amazon Web Services account. Consequently, you can use\n this operation to manage Amazon Web Services account root user credentials even if the Amazon Web Services account has no\n associated users.

", "smithy.api#paginated": { "inputToken": "Marker", "outputToken": "Marker", @@ -9770,7 +10217,7 @@ "UserName": { "target": "com.amazonaws.iam#existingUserNameType", "traits": { - "smithy.api#documentation": "

The name of the IAM user whose signing certificates you want to examine.

\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 IAM user whose signing certificates you want to examine.

\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: _+=,.@-

" } }, "Marker": { @@ -9785,6 +10232,9 @@ "smithy.api#documentation": "

Use this only when paginating results to indicate the \n maximum number of items you want in the response. If additional items exist beyond the maximum \n you specify, the IsTruncated response element is true.

\n

If you do not include this parameter, the number of items defaults to 100. Note that\n IAM might return fewer results, even when there are more results available. In that case, the\n IsTruncated response element returns true, and Marker \n contains a value to include in the subsequent call that tells the service where to continue \n from.

" } } + }, + "traits": { + "smithy.api#input": {} } }, "com.amazonaws.iam#ListSigningCertificatesResponse": { @@ -9812,7 +10262,8 @@ } }, "traits": { - "smithy.api#documentation": "

Contains the response to a successful ListSigningCertificates\n request.

" + "smithy.api#documentation": "

Contains the response to a successful ListSigningCertificates\n request.

", + "smithy.api#output": {} } }, "com.amazonaws.iam#ListUserPolicies": { @@ -9832,7 +10283,7 @@ } ], "traits": { - "smithy.api#documentation": "

Lists the names of the inline policies embedded in the specified IAM user.

\n

An IAM user can also have managed policies attached to it. To list the managed\n policies that are attached to a user, use ListAttachedUserPolicies.\n For more information about policies, see Managed policies and inline\n policies in the IAM User Guide.

\n

You can paginate the results using the MaxItems and Marker\n parameters. If there are no inline policies embedded with the specified user, the\n operation returns an empty list.

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

Lists the names of the inline policies embedded in the specified IAM user.

\n

An IAM user can also have managed policies attached to it. To list the managed\n policies that are attached to a user, use ListAttachedUserPolicies.\n For more information about policies, see Managed policies and inline\n policies in the IAM User Guide.

\n

You can paginate the results using the MaxItems and Marker\n parameters. If there are no inline policies embedded with the specified user, the\n operation returns an empty list.

", "smithy.api#paginated": { "inputToken": "Marker", "outputToken": "Marker", @@ -9847,7 +10298,7 @@ "UserName": { "target": "com.amazonaws.iam#existingUserNameType", "traits": { - "smithy.api#documentation": "

The name of the user to list policies for.

\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 user to list policies for.

\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": {} } }, @@ -9863,6 +10314,9 @@ "smithy.api#documentation": "

Use this only when paginating results to indicate the \n maximum number of items you want in the response. If additional items exist beyond the maximum \n you specify, the IsTruncated response element is true.

\n

If you do not include this parameter, the number of items defaults to 100. Note that\n IAM might return fewer results, even when there are more results available. In that case, the\n IsTruncated response element returns true, and Marker \n contains a value to include in the subsequent call that tells the service where to continue \n from.

" } } + }, + "traits": { + "smithy.api#input": {} } }, "com.amazonaws.iam#ListUserPoliciesResponse": { @@ -9890,7 +10344,8 @@ } }, "traits": { - "smithy.api#documentation": "

Contains the response to a successful ListUserPolicies request.\n

" + "smithy.api#documentation": "

Contains the response to a successful ListUserPolicies request.\n

", + "smithy.api#output": {} } }, "com.amazonaws.iam#ListUserTags": { @@ -9941,6 +10396,9 @@ "smithy.api#documentation": "

Use this only when paginating results to indicate the \n maximum number of items you want in the response. If additional items exist beyond the maximum \n you specify, the IsTruncated response element is true.

\n

If you do not include this parameter, the number of items defaults to 100. Note that\n IAM might return fewer results, even when there are more results available. In that case, the\n IsTruncated response element returns true, and Marker \n contains a value to include in the subsequent call that tells the service where to continue \n from.

" } } + }, + "traits": { + "smithy.api#input": {} } }, "com.amazonaws.iam#ListUserTagsResponse": { @@ -9966,6 +10424,9 @@ "smithy.api#documentation": "

When IsTruncated is true, this element\n is present and contains the value to use for the Marker parameter in a subsequent \n pagination request.

" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.iam#ListUsers": { @@ -9982,7 +10443,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. 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#paginated": { "inputToken": "Marker", "outputToken": "Marker", @@ -9997,7 +10458,7 @@ "PathPrefix": { "target": "com.amazonaws.iam#pathPrefixType", "traits": { - "smithy.api#documentation": "

The path prefix for filtering the results. For example:\n /division_abc/subdivision_xyz/, which would get all user names whose\n path starts with /division_abc/subdivision_xyz/.

\n

This parameter is optional. If it is not included, it defaults to a slash (/), listing\n all user names. This parameter allows (through its regex pattern) a string of characters consisting \n of either a forward slash (/) by itself or a string that must begin and end with forward slashes.\n In addition, it can contain any ASCII character from the ! (\\u0021) through the DEL character (\\u007F), including \n most punctuation characters, digits, and upper and lowercased letters.

" + "smithy.api#documentation": "

The path prefix for filtering the results. For example:\n /division_abc/subdivision_xyz/, which would get all user names whose\n path starts with /division_abc/subdivision_xyz/.

\n

This parameter is optional. If it is not included, it defaults to a slash (/), listing\n all user names. This parameter allows (through its regex pattern) a string of characters consisting \n of either a forward slash (/) by itself or a string that must begin and end with forward slashes.\n In addition, it can contain any ASCII character from the ! (\\u0021) through the DEL character (\\u007F), including \n most punctuation characters, digits, and upper and lowercased letters.

" } }, "Marker": { @@ -10012,6 +10473,9 @@ "smithy.api#documentation": "

Use this only when paginating results to indicate the \n maximum number of items you want in the response. If additional items exist beyond the maximum \n you specify, the IsTruncated response element is true.

\n

If you do not include this parameter, the number of items defaults to 100. Note that\n IAM might return fewer results, even when there are more results available. In that case, the\n IsTruncated response element returns true, and Marker \n contains a value to include in the subsequent call that tells the service where to continue \n from.

" } } + }, + "traits": { + "smithy.api#input": {} } }, "com.amazonaws.iam#ListUsersResponse": { @@ -10039,7 +10503,8 @@ } }, "traits": { - "smithy.api#documentation": "

Contains the response to a successful ListUsers request.

" + "smithy.api#documentation": "

Contains the response to a successful ListUsers request.

", + "smithy.api#output": {} } }, "com.amazonaws.iam#ListVirtualMFADevices": { @@ -10051,7 +10516,7 @@ "target": "com.amazonaws.iam#ListVirtualMFADevicesResponse" }, "traits": { - "smithy.api#documentation": "

Lists the virtual MFA devices defined in the Amazon Web Services account by assignment status. If\n you do not specify an assignment status, the operation returns a list of all virtual MFA\n devices. Assignment status can be Assigned, Unassigned, or\n Any.

\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 tag information for a virtual MFA device, see ListMFADeviceTags.

\n
\n

You can paginate the results using the MaxItems and Marker\n parameters.

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

Lists the virtual MFA devices defined in the Amazon Web Services account by assignment status. If\n you do not specify an assignment status, the operation returns a list of all virtual MFA\n devices. Assignment status can be Assigned, Unassigned, or\n Any.

\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 tag information for a virtual MFA device, see ListMFADeviceTags.

\n
\n

You can paginate the results using the MaxItems and Marker\n parameters.

", "smithy.api#paginated": { "inputToken": "Marker", "outputToken": "Marker", @@ -10081,6 +10546,9 @@ "smithy.api#documentation": "

Use this only when paginating results to indicate the \n maximum number of items you want in the response. If additional items exist beyond the maximum \n you specify, the IsTruncated response element is true.

\n

If you do not include this parameter, the number of items defaults to 100. Note that\n IAM might return fewer results, even when there are more results available. In that case, the\n IsTruncated response element returns true, and Marker \n contains a value to include in the subsequent call that tells the service where to continue \n from.

" } } + }, + "traits": { + "smithy.api#input": {} } }, "com.amazonaws.iam#ListVirtualMFADevicesResponse": { @@ -10108,7 +10576,8 @@ } }, "traits": { - "smithy.api#documentation": "

Contains the response to a successful ListVirtualMFADevices request.\n

" + "smithy.api#documentation": "

Contains the response to a successful ListVirtualMFADevices request.\n

", + "smithy.api#output": {} } }, "com.amazonaws.iam#LoginProfile": { @@ -10884,7 +11353,7 @@ } ], "traits": { - "smithy.api#documentation": "

Adds or updates an inline policy document that is embedded in the specified IAM\n group.

\n

A user can also have managed policies attached to it. To attach a managed policy to a\n group, use AttachGroupPolicy. To create a new managed policy, use\n CreatePolicy. For information about policies, see Managed\n policies and inline policies in the\n IAM User Guide.

\n

For information about the maximum number of inline policies that you can embed in a\n group, see IAM and STS quotas in the IAM User Guide.

\n \n

Because policy documents can be large, you should use POST rather than GET when\n calling PutGroupPolicy. For general information about using the Query\n API with IAM, see Making query requests in the\n IAM User Guide.

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

Adds or updates an inline policy document that is embedded in the specified IAM\n group.

\n

A user can also have managed policies attached to it. To attach a managed policy to a\n group, use AttachGroupPolicy. To create a new managed policy, use\n CreatePolicy. For information about policies, see Managed\n policies and inline policies in the\n IAM User Guide.

\n

For information about the maximum number of inline policies that you can embed in a\n group, see IAM and STS quotas in the IAM User Guide.

\n \n

Because policy documents can be large, you should use POST rather than GET when\n calling PutGroupPolicy. For general information about using the Query\n API with IAM, see Making query requests in the\n IAM User Guide.

\n
" } }, "com.amazonaws.iam#PutGroupPolicyRequest": { @@ -10893,24 +11362,27 @@ "GroupName": { "target": "com.amazonaws.iam#groupNameType", "traits": { - "smithy.api#documentation": "

The name of the group to associate the policy with.

\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 group to associate the policy with.

\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": {} } }, "PolicyName": { "target": "com.amazonaws.iam#policyNameType", "traits": { - "smithy.api#documentation": "

The name of the policy document.

\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 policy document.

\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": {} } }, "PolicyDocument": { "target": "com.amazonaws.iam#policyDocumentType", "traits": { - "smithy.api#documentation": "

The policy document.

\n\n

You must provide policies in JSON format in IAM. However, for CloudFormation templates\n formatted in YAML, you can provide the policy in JSON or YAML format. CloudFormation always\n converts a YAML policy to JSON format before submitting it to = IAM.

\n

The regex pattern \n used to validate this parameter is a string of characters consisting of the following:

\n
    \n
  • \n

    Any printable ASCII \n character ranging from the space character (\\u0020) through the end of the ASCII character range

    \n
  • \n
  • \n

    The printable characters in the Basic Latin and Latin-1 Supplement character set \n (through \\u00FF)

    \n
  • \n
  • \n

    The special characters tab (\\u0009), line feed (\\u000A), and \n carriage return (\\u000D)

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

The policy document.

\n

You must provide policies in JSON format in IAM. However, for CloudFormation templates\n formatted in YAML, you can provide the policy in JSON or YAML format. CloudFormation always\n converts a YAML policy to JSON format before submitting it to = IAM.

\n

The regex pattern \n used to validate this parameter is a string of characters consisting of the following:

\n
    \n
  • \n

    Any printable ASCII \n character ranging from the space character (\\u0020) through the end of the ASCII character range

    \n
  • \n
  • \n

    The printable characters in the Basic Latin and Latin-1 Supplement character set \n (through \\u00FF)

    \n
  • \n
  • \n

    The special characters tab (\\u0009), line feed (\\u000A), and \n carriage return (\\u000D)

    \n
  • \n
", "smithy.api#required": {} } } + }, + "traits": { + "smithy.api#input": {} } }, "com.amazonaws.iam#PutRolePermissionsBoundary": { @@ -10939,7 +11411,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": { @@ -10955,10 +11427,13 @@ "PermissionsBoundary": { "target": "com.amazonaws.iam#arnType", "traits": { - "smithy.api#documentation": "

The ARN of the policy that is used to set the permissions boundary for the\n role.

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

The ARN of the managed policy that is used to set the permissions boundary for the\n role.

\n

A permissions boundary policy defines the maximum permissions that identity-based\n policies can grant to an entity, but does not grant permissions. Permissions boundaries\n do not define the maximum permissions that a resource-based policy can grant to an\n entity. To learn more, see Permissions boundaries\n for IAM entities in the IAM User Guide.

\n

For more information about policy types, see Policy types\n in the IAM User Guide.

", "smithy.api#required": {} } } + }, + "traits": { + "smithy.api#input": {} } }, "com.amazonaws.iam#PutRolePolicy": { @@ -10987,7 +11462,7 @@ } ], "traits": { - "smithy.api#documentation": "

Adds or updates an inline policy document that is embedded in the specified IAM\n role.

\n

When you embed an inline policy in a role, the inline policy is used as part of the\n role's access (permissions) policy. The role's trust policy is created at the same time\n as the role, using CreateRole. You can update a role's trust policy\n using UpdateAssumeRolePolicy. For more information about IAM roles,\n see Using roles to\n delegate permissions and federate identities.

\n

A role can also have a managed policy attached to it. To attach a managed policy to a\n role, use AttachRolePolicy. To create a new managed policy, use CreatePolicy. For information about policies, see Managed\n policies and inline policies in the\n IAM User Guide.

\n

For information about the maximum number of inline policies that you can embed with a\n role, see IAM and STS quotas in the IAM User Guide.

\n \n

Because policy documents can be large, you should use POST rather than GET when\n calling PutRolePolicy. For general information about using the Query\n API with IAM, see Making query requests in the\n IAM User Guide.

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

Adds or updates an inline policy document that is embedded in the specified IAM\n role.

\n

When you embed an inline policy in a role, the inline policy is used as part of the\n role's access (permissions) policy. The role's trust policy is created at the same time\n as the role, using CreateRole. You can update a role's trust policy\n using UpdateAssumeRolePolicy. For more information about IAM roles,\n see Using roles to\n delegate permissions and federate identities.

\n

A role can also have a managed policy attached to it. To attach a managed policy to a\n role, use AttachRolePolicy. To create a new managed policy, use CreatePolicy. For information about policies, see Managed\n policies and inline policies in the\n IAM User Guide.

\n

For information about the maximum number of inline policies that you can embed with a\n role, see IAM and STS quotas in the IAM User Guide.

\n \n

Because policy documents can be large, you should use POST rather than GET when\n calling PutRolePolicy. For general information about using the Query\n API with IAM, see Making query requests in the\n IAM User Guide.

\n
" } }, "com.amazonaws.iam#PutRolePolicyRequest": { @@ -10996,24 +11471,27 @@ "RoleName": { "target": "com.amazonaws.iam#roleNameType", "traits": { - "smithy.api#documentation": "

The name of the role to associate the policy with.

\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 role to associate the policy with.

\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": {} } }, "PolicyName": { "target": "com.amazonaws.iam#policyNameType", "traits": { - "smithy.api#documentation": "

The name of the policy document.

\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 policy document.

\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": {} } }, "PolicyDocument": { "target": "com.amazonaws.iam#policyDocumentType", "traits": { - "smithy.api#documentation": "

The policy document.

\n

You must provide policies in JSON format in IAM. However, for CloudFormation\n templates formatted in YAML, you can provide the policy in JSON or YAML format. CloudFormation always converts a YAML policy to JSON format before submitting it to\n IAM.

\n

The regex pattern \n used to validate this parameter is a string of characters consisting of the following:

\n
    \n
  • \n

    Any printable ASCII \n character ranging from the space character (\\u0020) through the end of the ASCII character range

    \n
  • \n
  • \n

    The printable characters in the Basic Latin and Latin-1 Supplement character set \n (through \\u00FF)

    \n
  • \n
  • \n

    The special characters tab (\\u0009), line feed (\\u000A), and \n carriage return (\\u000D)

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

The policy document.

\n

You must provide policies in JSON format in IAM. However, for CloudFormation\n templates formatted in YAML, you can provide the policy in JSON or YAML format. CloudFormation always converts a YAML policy to JSON format before submitting it to\n IAM.

\n

The regex pattern \n used to validate this parameter is a string of characters consisting of the following:

\n
    \n
  • \n

    Any printable ASCII \n character ranging from the space character (\\u0020) through the end of the ASCII character range

    \n
  • \n
  • \n

    The printable characters in the Basic Latin and Latin-1 Supplement character set \n (through \\u00FF)

    \n
  • \n
  • \n

    The special characters tab (\\u0009), line feed (\\u000A), and \n carriage return (\\u000D)

    \n
  • \n
", "smithy.api#required": {} } } + }, + "traits": { + "smithy.api#input": {} } }, "com.amazonaws.iam#PutUserPermissionsBoundary": { @@ -11039,7 +11517,7 @@ } ], "traits": { - "smithy.api#documentation": "

Adds or updates the policy that is specified as the IAM user'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 user. Use the boundary to control the maximum permissions that the user can have.\n Setting a permissions boundary is an advanced feature that can affect the permissions\n for the user.

\n \n

Policies that are used as permissions boundaries do not provide permissions. You\n must also attach a permissions policy to the user. To learn how the effective\n permissions for a user 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 user's permissions\n boundary. You can use an Amazon Web Services managed policy or a customer managed policy to set the\n boundary for a user. Use the boundary to control the maximum permissions that the user\n can have. Setting a permissions boundary is an advanced feature that can affect the\n permissions for the user.

\n \n

Policies that are used as permissions boundaries do not provide permissions. You\n must also attach a permissions policy to the user. To learn how the effective\n permissions for a user are evaluated, see IAM JSON policy\n evaluation logic in the IAM User Guide.

\n
" } }, "com.amazonaws.iam#PutUserPermissionsBoundaryRequest": { @@ -11055,10 +11533,13 @@ "PermissionsBoundary": { "target": "com.amazonaws.iam#arnType", "traits": { - "smithy.api#documentation": "

The ARN of the policy that is used to set the permissions boundary for the\n user.

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

The ARN of the managed policy that is used to set the permissions boundary for the\n user.

\n

A permissions boundary policy defines the maximum permissions that identity-based\n policies can grant to an entity, but does not grant permissions. Permissions boundaries\n do not define the maximum permissions that a resource-based policy can grant to an\n entity. To learn more, see Permissions boundaries\n for IAM entities in the IAM User Guide.

\n

For more information about policy types, see Policy types\n in the IAM User Guide.

", "smithy.api#required": {} } } + }, + "traits": { + "smithy.api#input": {} } }, "com.amazonaws.iam#PutUserPolicy": { @@ -11084,7 +11565,7 @@ } ], "traits": { - "smithy.api#documentation": "

Adds or updates an inline policy document that is embedded in the specified IAM\n user.

\n

An IAM user can also have a managed policy attached to it. To attach a managed\n policy to a user, use AttachUserPolicy. To create a new managed\n policy, use CreatePolicy. For information about policies, see Managed\n policies and inline policies in the\n IAM User Guide.

\n

For information about the maximum number of inline policies that you can embed in a\n user, see IAM and STS quotas in the IAM User Guide.

\n \n

Because policy documents can be large, you should use POST rather than GET when\n calling PutUserPolicy. For general information about using the Query\n API with IAM, see Making query requests in the\n IAM User Guide.

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

Adds or updates an inline policy document that is embedded in the specified IAM\n user.

\n

An IAM user can also have a managed policy attached to it. To attach a managed\n policy to a user, use AttachUserPolicy. To create a new managed\n policy, use CreatePolicy. For information about policies, see Managed\n policies and inline policies in the\n IAM User Guide.

\n

For information about the maximum number of inline policies that you can embed in a\n user, see IAM and STS quotas in the IAM User Guide.

\n \n

Because policy documents can be large, you should use POST rather than GET when\n calling PutUserPolicy. For general information about using the Query\n API with IAM, see Making query requests in the\n IAM User Guide.

\n
" } }, "com.amazonaws.iam#PutUserPolicyRequest": { @@ -11093,24 +11574,27 @@ "UserName": { "target": "com.amazonaws.iam#existingUserNameType", "traits": { - "smithy.api#documentation": "

The name of the user to associate the policy with.

\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 user to associate the policy with.

\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": {} } }, "PolicyName": { "target": "com.amazonaws.iam#policyNameType", "traits": { - "smithy.api#documentation": "

The name of the policy document.

\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 policy document.

\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": {} } }, "PolicyDocument": { "target": "com.amazonaws.iam#policyDocumentType", "traits": { - "smithy.api#documentation": "

The policy document.

\n\n

You must provide policies in JSON format in IAM. However, for CloudFormation\n templates formatted in YAML, you can provide the policy in JSON or YAML format. CloudFormation always converts a YAML policy to JSON format before submitting it to\n IAM.

\n

The regex pattern \n used to validate this parameter is a string of characters consisting of the following:

\n
    \n
  • \n

    Any printable ASCII \n character ranging from the space character (\\u0020) through the end of the ASCII character range

    \n
  • \n
  • \n

    The printable characters in the Basic Latin and Latin-1 Supplement character set \n (through \\u00FF)

    \n
  • \n
  • \n

    The special characters tab (\\u0009), line feed (\\u000A), and \n carriage return (\\u000D)

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

The policy document.

\n

You must provide policies in JSON format in IAM. However, for CloudFormation\n templates formatted in YAML, you can provide the policy in JSON or YAML format. CloudFormation always converts a YAML policy to JSON format before submitting it to\n IAM.

\n

The regex pattern \n used to validate this parameter is a string of characters consisting of the following:

\n
    \n
  • \n

    Any printable ASCII \n character ranging from the space character (\\u0020) through the end of the ASCII character range

    \n
  • \n
  • \n

    The printable characters in the Basic Latin and Latin-1 Supplement character set \n (through \\u00FF)

    \n
  • \n
  • \n

    The special characters tab (\\u0009), line feed (\\u000A), and \n carriage return (\\u000D)

    \n
  • \n
", "smithy.api#required": {} } } + }, + "traits": { + "smithy.api#input": {} } }, "com.amazonaws.iam#ReasonType": { @@ -11151,7 +11635,7 @@ } ], "traits": { - "smithy.api#documentation": "

Removes the specified client ID (also known as audience) from the list of client IDs\n registered for the specified IAM OpenID Connect (OIDC) provider resource\n object.

\n

This operation is idempotent; it does not fail or return an error if you try to remove\n a client ID that does not exist.

" + "smithy.api#documentation": "

Removes the specified client ID (also known as audience) from the list of client IDs\n registered for the specified IAM OpenID Connect (OIDC) provider resource\n object.

\n

This operation is idempotent; it does not fail or return an error if you try to remove\n a client ID that does not exist.

" } }, "com.amazonaws.iam#RemoveClientIDFromOpenIDConnectProviderRequest": { @@ -11160,7 +11644,7 @@ "OpenIDConnectProviderArn": { "target": "com.amazonaws.iam#arnType", "traits": { - "smithy.api#documentation": "

The Amazon Resource Name (ARN) of the IAM OIDC provider resource to remove the\n client ID from. You can get a list of OIDC provider ARNs by using the ListOpenIDConnectProviders operation.

\n

For more information about ARNs, see Amazon Resource Names (ARNs) in the Amazon Web Services General Reference.

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

The Amazon Resource Name (ARN) of the IAM OIDC provider resource to remove the\n client ID from. You can get a list of OIDC provider ARNs by using the ListOpenIDConnectProviders operation.

\n

For more information about ARNs, see Amazon Resource Names (ARNs) in the Amazon Web Services General Reference.

", "smithy.api#required": {} } }, @@ -11171,6 +11655,9 @@ "smithy.api#required": {} } } + }, + "traits": { + "smithy.api#input": {} } }, "com.amazonaws.iam#RemoveRoleFromInstanceProfile": { @@ -11196,7 +11683,7 @@ } ], "traits": { - "smithy.api#documentation": "

Removes the specified IAM role from the specified EC2 instance profile.

\n \n

Make sure that you do not have any Amazon EC2 instances running with the role you\n are about to remove from the instance profile. Removing a role from an instance\n profile that is associated with a running instance might break any applications\n running on the instance.

\n
\n

For more information about IAM roles, see Working with roles. For more\n information about instance profiles, see About instance\n profiles.

" + "smithy.api#documentation": "

Removes the specified IAM role from the specified EC2 instance profile.

\n \n

Make sure that you do not have any Amazon EC2 instances running with the role you\n are about to remove from the instance profile. Removing a role from an instance\n profile that is associated with a running instance might break any applications\n running on the instance.

\n
\n

For more information about IAM roles, see Working with roles. For more\n information about instance profiles, see About instance\n profiles.

" } }, "com.amazonaws.iam#RemoveRoleFromInstanceProfileRequest": { @@ -11205,17 +11692,20 @@ "InstanceProfileName": { "target": "com.amazonaws.iam#instanceProfileNameType", "traits": { - "smithy.api#documentation": "

The name of the instance profile to update.

\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 instance profile to update.

\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": {} } }, "RoleName": { "target": "com.amazonaws.iam#roleNameType", "traits": { - "smithy.api#documentation": "

The name of the role to remove.

\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 role to remove.

\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": {} } } + }, + "traits": { + "smithy.api#input": {} } }, "com.amazonaws.iam#RemoveUserFromGroup": { @@ -11247,17 +11737,20 @@ "GroupName": { "target": "com.amazonaws.iam#groupNameType", "traits": { - "smithy.api#documentation": "

The name of the group to update.

\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 group to update.

\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": {} } }, "UserName": { "target": "com.amazonaws.iam#existingUserNameType", "traits": { - "smithy.api#documentation": "

The name of the user to remove.

\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 user to remove.

\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": {} } } + }, + "traits": { + "smithy.api#input": {} } }, "com.amazonaws.iam#ReportContentType": { @@ -11340,16 +11833,19 @@ "UserName": { "target": "com.amazonaws.iam#userNameType", "traits": { - "smithy.api#documentation": "

The name of the IAM user associated with the service-specific credential. If this\n value is not specified, then the operation assumes the user whose credentials are used\n to call the operation.

\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 IAM user associated with the service-specific credential. If this\n value is not specified, then the operation assumes the user whose credentials are used\n to call the operation.

\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: _+=,.@-

" } }, "ServiceSpecificCredentialId": { "target": "com.amazonaws.iam#serviceSpecificCredentialId", "traits": { - "smithy.api#documentation": "

The unique identifier of the service-specific credential.

\n

This parameter allows (through its regex pattern) a string of characters that can \n consist of any upper or lowercased letter or digit.

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

The unique identifier of the service-specific credential.

\n

This parameter allows (through its regex pattern) a string of characters that can \n consist of any upper or lowercased letter or digit.

", "smithy.api#required": {} } } + }, + "traits": { + "smithy.api#input": {} } }, "com.amazonaws.iam#ResetServiceSpecificCredentialResponse": { @@ -11358,9 +11854,12 @@ "ServiceSpecificCredential": { "target": "com.amazonaws.iam#ServiceSpecificCredential", "traits": { - "smithy.api#documentation": "

A structure with details about the updated service-specific credential, including the\n new password.

\n \n

This is the only time that you can access the\n password. You cannot recover the password later, but you can reset it again.

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

A structure with details about the updated service-specific credential, including the\n new password.

\n \n

This is the only time that you can access the\n password. You cannot recover the password later, but you can reset it again.

\n
" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.iam#ResourceHandlingOptionType": { @@ -11462,7 +11961,7 @@ } ], "traits": { - "smithy.api#documentation": "

Synchronizes the specified MFA device with its IAM resource object on the Amazon Web Services\n servers.

\n

For more information about creating and working with virtual MFA devices, see Using a virtual MFA\n device in the IAM User Guide.

" + "smithy.api#documentation": "

Synchronizes the specified MFA device with its IAM resource object on the Amazon Web Services\n servers.

\n

For more information about creating and working with virtual MFA devices, see Using a virtual MFA\n device in the IAM User Guide.

" } }, "com.amazonaws.iam#ResyncMFADeviceRequest": { @@ -11471,31 +11970,34 @@ "UserName": { "target": "com.amazonaws.iam#existingUserNameType", "traits": { - "smithy.api#documentation": "

The name of the user whose MFA device you want to resynchronize.

\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 user whose MFA device you want to resynchronize.

\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": {} } }, "SerialNumber": { "target": "com.amazonaws.iam#serialNumberType", "traits": { - "smithy.api#documentation": "

Serial number that uniquely identifies the 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#documentation": "

Serial number that uniquely identifies the 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": {} } }, "AuthenticationCode1": { "target": "com.amazonaws.iam#authenticationCodeType", "traits": { - "smithy.api#documentation": "

An authentication code emitted by the device.

\n

The format for this parameter is a sequence of six digits.

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

An authentication code emitted by the device.

\n

The format for this parameter is a sequence of six digits.

", "smithy.api#required": {} } }, "AuthenticationCode2": { "target": "com.amazonaws.iam#authenticationCodeType", "traits": { - "smithy.api#documentation": "

A subsequent authentication code emitted by the device.

\n

The format for this parameter is a sequence of six digits.

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

A subsequent authentication code emitted by the device.

\n

The format for this parameter is a sequence of six digits.

", "smithy.api#required": {} } } + }, + "traits": { + "smithy.api#input": {} } }, "com.amazonaws.iam#Role": { @@ -11569,7 +12071,7 @@ "RoleLastUsed": { "target": "com.amazonaws.iam#RoleLastUsed", "traits": { - "smithy.api#documentation": "

Contains information about the last time that an IAM role was used. This includes the\n date and time and the Region in which the role was last used. Activity is only reported for\n the trailing 400 days. This period can be shorter if your Region began supporting these\n features within the last year. The role might have been used more than 400 days ago. For\n more information, see Regions where data is tracked in the IAM User\n Guide.

" + "smithy.api#documentation": "

Contains information about the last time that an IAM role was used. This includes the\n date and time and the Region in which the role was last used. Activity is only reported for\n the trailing 400 days. This period can be shorter if your Region began supporting these\n features within the last year. The role might have been used more than 400 days ago. For\n more information, see Regions where data is tracked in the IAM user\n Guide.

" } } }, @@ -11646,7 +12148,7 @@ "RoleLastUsed": { "target": "com.amazonaws.iam#RoleLastUsed", "traits": { - "smithy.api#documentation": "

Contains information about the last time that an IAM role was used. This includes the\n date and time and the Region in which the role was last used. Activity is only reported for\n the trailing 400 days. This period can be shorter if your Region began supporting these\n features within the last year. The role might have been used more than 400 days ago. For\n more information, see Regions where data is tracked in the IAM User\n Guide.

" + "smithy.api#documentation": "

Contains information about the last time that an IAM role was used. This includes the\n date and time and the Region in which the role was last used. Activity is only reported for\n the trailing 400 days. This period can be shorter if your Region began supporting these\n features within the last year. The role might have been used more than 400 days ago. For\n more information, see Regions where data is tracked in the IAM User Guide.

" } } }, @@ -11671,7 +12173,7 @@ } }, "traits": { - "smithy.api#documentation": "

Contains information about the last time that an IAM role was used. This includes the\n date and time and the Region in which the role was last used. Activity is only reported for\n the trailing 400 days. This period can be shorter if your Region began supporting these\n features within the last year. The role might have been used more than 400 days ago. For\n more information, see Regions where data is tracked in the IAM User\n Guide.

\n

This data type is returned as a response element in the GetRole and\n GetAccountAuthorizationDetails operations.

" + "smithy.api#documentation": "

Contains information about the last time that an IAM role was used. This includes the\n date and time and the Region in which the role was last used. Activity is only reported for\n the trailing 400 days. This period can be shorter if your Region began supporting these\n features within the last year. The role might have been used more than 400 days ago. For\n more information, see Regions where data is tracked in the IAM user\n Guide.

\n

This data type is returned as a response element in the GetRole and\n GetAccountAuthorizationDetails operations.

" } }, "com.amazonaws.iam#RoleUsageListType": { @@ -12152,7 +12654,7 @@ } ], "traits": { - "smithy.api#documentation": "

Sets the specified version of the specified policy as the policy's default (operative)\n version.

\n

This operation affects all users, groups, and roles that the policy is attached to. To\n list the users, groups, and roles that the policy is attached to, use ListEntitiesForPolicy.

\n

For information about managed policies, see Managed policies and inline\n policies in the IAM User Guide.

" + "smithy.api#documentation": "

Sets the specified version of the specified policy as the policy's default (operative)\n version.

\n

This operation affects all users, groups, and roles that the policy is attached to. To\n list the users, groups, and roles that the policy is attached to, use ListEntitiesForPolicy.

\n

For information about managed policies, see Managed policies and inline\n policies in the IAM User Guide.

" } }, "com.amazonaws.iam#SetDefaultPolicyVersionRequest": { @@ -12161,17 +12663,20 @@ "PolicyArn": { "target": "com.amazonaws.iam#arnType", "traits": { - "smithy.api#documentation": "

The Amazon Resource Name (ARN) of the IAM policy whose default version you want to\n set.

\n

For more information about ARNs, see Amazon Resource Names (ARNs) in the Amazon Web Services General Reference.

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

The Amazon Resource Name (ARN) of the IAM policy whose default version you want to\n set.

\n

For more information about ARNs, see Amazon Resource Names (ARNs) in the Amazon Web Services General Reference.

", "smithy.api#required": {} } }, "VersionId": { "target": "com.amazonaws.iam#policyVersionIdType", "traits": { - "smithy.api#documentation": "

The version of the policy to set as the default (operative) version.

\n

For more information about managed policy versions, see Versioning for managed\n policies in the IAM User Guide.

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

The version of the policy to set as the default (operative) version.

\n

For more information about managed policy versions, see Versioning for managed\n policies in the IAM User Guide.

", "smithy.api#required": {} } } + }, + "traits": { + "smithy.api#input": {} } }, "com.amazonaws.iam#SetSecurityTokenServicePreferences": { @@ -12188,7 +12693,7 @@ } ], "traits": { - "smithy.api#documentation": "

Sets the specified version of the global endpoint token as the token version used for\n the Amazon Web Services account.

\n

By default, Security Token Service (STS) is available as a global service, and all STS requests\n go to a single endpoint at https://sts.amazonaws.com. Amazon Web Services recommends\n using Regional STS endpoints to reduce latency, build in redundancy, and increase\n session token availability. For information about Regional endpoints for STS, see\n Security Token Service\n endpoints and quotas in the Amazon Web Services General Reference.

\n

If you make an STS call to the global endpoint, the resulting session tokens might\n be valid in some Regions but not others. It depends on the version that is set in this\n operation. Version 1 tokens are valid only in Amazon Web Services Regions that are\n available by default. These tokens do not work in manually enabled Regions, such as Asia\n Pacific (Hong Kong). Version 2 tokens are valid in all Regions. However, version 2\n tokens are longer and might affect systems where you temporarily store tokens. For\n information, see Activating and\n deactivating STS in an Amazon Web Services Region in the\n IAM User Guide.

\n

To view the current session token version, see the\n GlobalEndpointTokenVersion entry in the response of the GetAccountSummary operation.

" + "smithy.api#documentation": "

Sets the specified version of the global endpoint token as the token version used for\n the Amazon Web Services account.

\n

By default, Security Token Service (STS) is available as a global service, and all STS requests\n go to a single endpoint at https://sts.amazonaws.com. Amazon Web Services recommends\n using Regional STS endpoints to reduce latency, build in redundancy, and increase\n session token availability. For information about Regional endpoints for STS, see\n Security Token Service\n endpoints and quotas in the Amazon Web Services General Reference.

\n

If you make an STS call to the global endpoint, the resulting session tokens might\n be valid in some Regions but not others. It depends on the version that is set in this\n operation. Version 1 tokens are valid only in Amazon Web Services Regions that are\n available by default. These tokens do not work in manually enabled Regions, such as Asia\n Pacific (Hong Kong). Version 2 tokens are valid in all Regions. However, version 2\n tokens are longer and might affect systems where you temporarily store tokens. For\n information, see Activating and\n deactivating STS in an Amazon Web Services Region in the\n IAM User Guide.

\n

To view the current session token version, see the\n GlobalEndpointTokenVersion entry in the response of the GetAccountSummary operation.

" } }, "com.amazonaws.iam#SetSecurityTokenServicePreferencesRequest": { @@ -12197,10 +12702,13 @@ "GlobalEndpointTokenVersion": { "target": "com.amazonaws.iam#globalEndpointTokenVersion", "traits": { - "smithy.api#documentation": "

The version of the global endpoint token. Version 1 tokens are valid only in Amazon Web Services Regions that are available by default. These tokens do not work in\n manually enabled Regions, such as Asia Pacific (Hong Kong). Version 2 tokens are valid\n in all Regions. However, version 2 tokens are longer and might affect systems where you\n temporarily store tokens.

\n

For information, see Activating and\n deactivating STS in an Amazon Web Services Region in the\n IAM User Guide.

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

The version of the global endpoint token. Version 1 tokens are valid only in Amazon Web Services Regions that are available by default. These tokens do not work in\n manually enabled Regions, such as Asia Pacific (Hong Kong). Version 2 tokens are valid\n in all Regions. However, version 2 tokens are longer and might affect systems where you\n temporarily store tokens.

\n

For information, see Activating and\n deactivating STS in an Amazon Web Services Region in the\n IAM User Guide.

", "smithy.api#required": {} } } + }, + "traits": { + "smithy.api#input": {} } }, "com.amazonaws.iam#SigningCertificate": { @@ -12262,7 +12770,7 @@ } ], "traits": { - "smithy.api#documentation": "

Simulate how a set of IAM policies and optionally a resource-based policy works with\n a list of API operations and Amazon Web Services resources to determine the policies' effective\n permissions. The policies are provided as strings.

\n

The simulation does not perform the API operations; it only checks the authorization\n to determine if the simulated policies allow or deny the operations. You can simulate\n resources that don't exist in your account.

\n

If you want to simulate existing policies that are attached to an IAM user, group,\n or role, use SimulatePrincipalPolicy instead.

\n

Context keys are variables that are maintained by Amazon Web Services and its services and which\n provide details about the context of an API query request. You can use the\n Condition element of an IAM policy to evaluate context keys. To get\n the list of context keys that the policies require for correct simulation, use GetContextKeysForCustomPolicy.

\n

If the output is long, you can use MaxItems and Marker\n parameters to paginate the results.

\n

For more information about using the policy simulator, see Testing IAM policies\n with the IAM policy simulator in the\n IAM User Guide.

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

Simulate how a set of IAM policies and optionally a resource-based policy works with\n a list of API operations and Amazon Web Services resources to determine the policies' effective\n permissions. The policies are provided as strings.

\n

The simulation does not perform the API operations; it only checks the authorization\n to determine if the simulated policies allow or deny the operations. You can simulate\n resources that don't exist in your account.

\n

If you want to simulate existing policies that are attached to an IAM user, group,\n or role, use SimulatePrincipalPolicy instead.

\n

Context keys are variables that are maintained by Amazon Web Services and its services and which\n provide details about the context of an API query request. You can use the\n Condition element of an IAM policy to evaluate context keys. To get\n the list of context keys that the policies require for correct simulation, use GetContextKeysForCustomPolicy.

\n

If the output is long, you can use MaxItems and Marker\n parameters to paginate the results.

\n \n

The IAM policy simulator evaluates statements in the identity-based policy and\n the inputs that you provide during simulation. The policy simulator results can\n differ from your live Amazon Web Services environment. We recommend that you check your policies\n against your live Amazon Web Services environment after testing using the policy simulator to\n confirm that you have the desired results. For more information about using the\n policy simulator, see Testing IAM\n policies with the IAM policy simulator in the\n IAM User Guide.

\n
", "smithy.api#paginated": { "inputToken": "Marker", "outputToken": "Marker", @@ -12277,14 +12785,14 @@ "PolicyInputList": { "target": "com.amazonaws.iam#SimulationPolicyListType", "traits": { - "smithy.api#documentation": "

A list of policy documents to include in the simulation. Each document is specified as\n a string containing the complete, valid JSON text of an IAM policy. Do not include any\n resource-based policies in this parameter. Any resource-based policy must be submitted\n with the ResourcePolicy parameter. The policies cannot be \"scope-down\"\n policies, such as you could include in a call to GetFederationToken or one of\n the AssumeRole API operations. In other words, do not use policies designed to\n restrict what a user can do while using the temporary credentials.

\n

The maximum length of the policy document that you can pass in this operation,\n including whitespace, is listed below. To view the maximum character counts of a managed policy with no whitespaces, see IAM and STS character quotas.

\n

The regex pattern \n used to validate this parameter is a string of characters consisting of the following:

\n
    \n
  • \n

    Any printable ASCII \n character ranging from the space character (\\u0020) through the end of the ASCII character range

    \n
  • \n
  • \n

    The printable characters in the Basic Latin and Latin-1 Supplement character set \n (through \\u00FF)

    \n
  • \n
  • \n

    The special characters tab (\\u0009), line feed (\\u000A), and \n carriage return (\\u000D)

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

A list of policy documents to include in the simulation. Each document is specified as\n a string containing the complete, valid JSON text of an IAM policy. Do not include any\n resource-based policies in this parameter. Any resource-based policy must be submitted\n with the ResourcePolicy parameter. The policies cannot be \"scope-down\"\n policies, such as you could include in a call to GetFederationToken or one of\n the AssumeRole API operations. In other words, do not use policies designed to\n restrict what a user can do while using the temporary credentials.

\n

The maximum length of the policy document that you can pass in this operation,\n including whitespace, is listed below. To view the maximum character counts of a managed policy with no whitespaces, see IAM and STS character quotas.

\n

The regex pattern \n used to validate this parameter is a string of characters consisting of the following:

\n
    \n
  • \n

    Any printable ASCII \n character ranging from the space character (\\u0020) through the end of the ASCII character range

    \n
  • \n
  • \n

    The printable characters in the Basic Latin and Latin-1 Supplement character set \n (through \\u00FF)

    \n
  • \n
  • \n

    The special characters tab (\\u0009), line feed (\\u000A), and \n carriage return (\\u000D)

    \n
  • \n
", "smithy.api#required": {} } }, "PermissionsBoundaryPolicyInputList": { "target": "com.amazonaws.iam#SimulationPolicyListType", "traits": { - "smithy.api#documentation": "

The IAM permissions boundary policy to simulate. The permissions boundary sets the\n maximum permissions that an IAM entity can have. You can input only one permissions\n boundary when you pass a policy to this operation. For more information about\n permissions boundaries, see Permissions boundaries for IAM\n entities in the IAM User Guide. The policy input is\n specified as a string that contains the complete, valid JSON text of a permissions\n boundary policy.

\n

The maximum length of the policy document that you can pass in this operation,\n including whitespace, is listed below. To view the maximum character counts of a managed policy with no whitespaces, see IAM and STS character quotas.

\n

The regex pattern \n used to validate this parameter is a string of characters consisting of the following:

\n
    \n
  • \n

    Any printable ASCII \n character ranging from the space character (\\u0020) through the end of the ASCII character range

    \n
  • \n
  • \n

    The printable characters in the Basic Latin and Latin-1 Supplement character set \n (through \\u00FF)

    \n
  • \n
  • \n

    The special characters tab (\\u0009), line feed (\\u000A), and \n carriage return (\\u000D)

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

The IAM permissions boundary policy to simulate. The permissions boundary sets the\n maximum permissions that an IAM entity can have. You can input only one permissions\n boundary when you pass a policy to this operation. For more information about\n permissions boundaries, see Permissions boundaries for IAM\n entities in the IAM User Guide. The policy input is\n specified as a string that contains the complete, valid JSON text of a permissions\n boundary policy.

\n

The maximum length of the policy document that you can pass in this operation,\n including whitespace, is listed below. To view the maximum character counts of a managed policy with no whitespaces, see IAM and STS character quotas.

\n

The regex pattern \n used to validate this parameter is a string of characters consisting of the following:

\n
    \n
  • \n

    Any printable ASCII \n character ranging from the space character (\\u0020) through the end of the ASCII character range

    \n
  • \n
  • \n

    The printable characters in the Basic Latin and Latin-1 Supplement character set \n (through \\u00FF)

    \n
  • \n
  • \n

    The special characters tab (\\u0009), line feed (\\u000A), and \n carriage return (\\u000D)

    \n
  • \n
" } }, "ActionNames": { @@ -12297,25 +12805,25 @@ "ResourceArns": { "target": "com.amazonaws.iam#ResourceNameListType", "traits": { - "smithy.api#documentation": "

A list of ARNs of Amazon Web Services resources to include in the simulation. If this parameter is\n not provided, then the value defaults to * (all resources). Each API in the\n ActionNames parameter is evaluated for each resource in this list. The\n simulation determines the access result (allowed or denied) of each combination and\n reports it in the response. You can simulate resources that don't exist in your\n account.

\n

The simulation does not automatically retrieve policies for the specified resources.\n If you want to include a resource policy in the simulation, then you must include the\n policy as a string in the ResourcePolicy parameter.

\n

If you include a ResourcePolicy, then it must be applicable to all of the\n resources included in the simulation or you receive an invalid input error.

\n

For more information about ARNs, see Amazon Resource Names (ARNs) in the Amazon Web Services General Reference.

" + "smithy.api#documentation": "

A list of ARNs of Amazon Web Services resources to include in the simulation. If this parameter is\n not provided, then the value defaults to * (all resources). Each API in the\n ActionNames parameter is evaluated for each resource in this list. The\n simulation determines the access result (allowed or denied) of each combination and\n reports it in the response. You can simulate resources that don't exist in your\n account.

\n

The simulation does not automatically retrieve policies for the specified resources.\n If you want to include a resource policy in the simulation, then you must include the\n policy as a string in the ResourcePolicy parameter.

\n

If you include a ResourcePolicy, then it must be applicable to all of the\n resources included in the simulation or you receive an invalid input error.

\n

For more information about ARNs, see Amazon Resource Names (ARNs) in the Amazon Web Services General Reference.

\n \n

Simulation of resource-based policies isn't supported for IAM roles.

\n
" } }, "ResourcePolicy": { "target": "com.amazonaws.iam#policyDocumentType", "traits": { - "smithy.api#documentation": "

A resource-based policy to include in the simulation provided as a string. Each\n resource in the simulation is treated as if it had this policy attached. You can include\n only one resource-based policy in a simulation.

\n

The maximum length of the policy document that you can pass in this operation,\n including whitespace, is listed below. To view the maximum character counts of a managed policy with no whitespaces, see IAM and STS character quotas.

\n

The regex pattern \n used to validate this parameter is a string of characters consisting of the following:

\n
    \n
  • \n

    Any printable ASCII \n character ranging from the space character (\\u0020) through the end of the ASCII character range

    \n
  • \n
  • \n

    The printable characters in the Basic Latin and Latin-1 Supplement character set \n (through \\u00FF)

    \n
  • \n
  • \n

    The special characters tab (\\u0009), line feed (\\u000A), and \n carriage return (\\u000D)

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

A resource-based policy to include in the simulation provided as a string. Each\n resource in the simulation is treated as if it had this policy attached. You can include\n only one resource-based policy in a simulation.

\n

The maximum length of the policy document that you can pass in this operation,\n including whitespace, is listed below. To view the maximum character counts of a managed policy with no whitespaces, see IAM and STS character quotas.

\n

The regex pattern \n used to validate this parameter is a string of characters consisting of the following:

\n
    \n
  • \n

    Any printable ASCII \n character ranging from the space character (\\u0020) through the end of the ASCII character range

    \n
  • \n
  • \n

    The printable characters in the Basic Latin and Latin-1 Supplement character set \n (through \\u00FF)

    \n
  • \n
  • \n

    The special characters tab (\\u0009), line feed (\\u000A), and \n carriage return (\\u000D)

    \n
  • \n
\n \n

Simulation of resource-based policies isn't supported for IAM roles.

\n
" } }, "ResourceOwner": { "target": "com.amazonaws.iam#ResourceNameType", "traits": { - "smithy.api#documentation": "

An ARN representing the Amazon Web Services account ID that specifies the owner of any simulated\n resource that does not identify its owner in the resource ARN. Examples of resource ARNs\n include an S3 bucket or object. If ResourceOwner is specified, it is also\n used as the account owner of any ResourcePolicy included in the simulation.\n If the ResourceOwner parameter is not specified, then the owner of the\n resources and the resource policy defaults to the account of the identity provided in\n CallerArn. This parameter is required only if you specify a\n resource-based policy and account that owns the resource is different from the account\n that owns the simulated calling user CallerArn.

\n

The ARN for an account uses the following syntax:\n arn:aws:iam::AWS-account-ID:root. For example,\n to represent the account with the 112233445566 ID, use the following ARN:\n arn:aws:iam::112233445566-ID:root.

" + "smithy.api#documentation": "

An ARN representing the Amazon Web Services account ID that specifies the owner of any simulated\n resource that does not identify its owner in the resource ARN. Examples of resource ARNs\n include an S3 bucket or object. If ResourceOwner is specified, it is also\n used as the account owner of any ResourcePolicy included in the simulation.\n If the ResourceOwner parameter is not specified, then the owner of the\n resources and the resource policy defaults to the account of the identity provided in\n CallerArn. This parameter is required only if you specify a\n resource-based policy and account that owns the resource is different from the account\n that owns the simulated calling user CallerArn.

\n

The ARN for an account uses the following syntax:\n arn:aws:iam::AWS-account-ID:root. For example,\n to represent the account with the 112233445566 ID, use the following ARN:\n arn:aws:iam::112233445566-ID:root.

" } }, "CallerArn": { "target": "com.amazonaws.iam#ResourceNameType", "traits": { - "smithy.api#documentation": "

The ARN of the IAM user that you want to use as the simulated caller of the API\n operations. CallerArn is required if you include a\n ResourcePolicy so that the policy's Principal element has\n a value to use in evaluating the policy.

\n

You can specify only the ARN of an IAM user. You cannot specify the ARN of an\n assumed role, federated user, or a service principal.

" + "smithy.api#documentation": "

The ARN of the IAM user that you want to use as the simulated caller of the API\n operations. CallerArn is required if you include a\n ResourcePolicy so that the policy's Principal element has\n a value to use in evaluating the policy.

\n

You can specify only the ARN of an IAM user. You cannot specify the ARN of an\n assumed role, federated user, or a service principal.

" } }, "ContextEntries": { @@ -12327,7 +12835,7 @@ "ResourceHandlingOption": { "target": "com.amazonaws.iam#ResourceHandlingOptionType", "traits": { - "smithy.api#documentation": "

Specifies the type of simulation to run. Different API operations that support\n resource-based policies require different combinations of resources. By specifying the\n type of simulation to run, you enable the policy simulator to enforce the presence of\n the required resources to ensure reliable simulation results. If your simulation does\n not match one of the following scenarios, then you can omit this parameter. The\n following list shows each of the supported scenario values and the resources that you\n must define to run the simulation.

\n

Each of the EC2 scenarios requires that you specify instance, image, and security\n group resources. If your scenario includes an EBS volume, then you must specify that\n volume as a resource. If the EC2 scenario includes VPC, then you must supply the network\n interface resource. If it includes an IP subnet, then you must specify the subnet\n resource. For more information on the EC2 scenario options, see Supported platforms in the Amazon EC2 User\n Guide.

\n
    \n
  • \n

    \n EC2-VPC-InstanceStore\n

    \n

    instance, image, security group, network interface

    \n
  • \n
  • \n

    \n EC2-VPC-InstanceStore-Subnet\n

    \n

    instance, image, security group, network interface, subnet

    \n
  • \n
  • \n

    \n EC2-VPC-EBS\n

    \n

    instance, image, security group, network interface, volume

    \n
  • \n
  • \n

    \n EC2-VPC-EBS-Subnet\n

    \n

    instance, image, security group, network interface, subnet, volume

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

Specifies the type of simulation to run. Different API operations that support\n resource-based policies require different combinations of resources. By specifying the\n type of simulation to run, you enable the policy simulator to enforce the presence of\n the required resources to ensure reliable simulation results. If your simulation does\n not match one of the following scenarios, then you can omit this parameter. The\n following list shows each of the supported scenario values and the resources that you\n must define to run the simulation.

\n

Each of the EC2 scenarios requires that you specify instance, image, and security\n group resources. If your scenario includes an EBS volume, then you must specify that\n volume as a resource. If the EC2 scenario includes VPC, then you must supply the network\n interface resource. If it includes an IP subnet, then you must specify the subnet\n resource. For more information on the EC2 scenario options, see Supported platforms in the Amazon EC2 User\n Guide.

\n
    \n
  • \n

    \n EC2-VPC-InstanceStore\n

    \n

    instance, image, security group, network interface

    \n
  • \n
  • \n

    \n EC2-VPC-InstanceStore-Subnet\n

    \n

    instance, image, security group, network interface, subnet

    \n
  • \n
  • \n

    \n EC2-VPC-EBS\n

    \n

    instance, image, security group, network interface, volume

    \n
  • \n
  • \n

    \n EC2-VPC-EBS-Subnet\n

    \n

    instance, image, security group, network interface, subnet, volume

    \n
  • \n
" } }, "MaxItems": { @@ -12342,6 +12850,9 @@ "smithy.api#documentation": "

Use this parameter only when paginating results and only after \n you receive a response indicating that the results are truncated. Set it to the value of the\n Marker element in the response that you received to indicate where the next call \n should start.

" } } + }, + "traits": { + "smithy.api#input": {} } }, "com.amazonaws.iam#SimulatePolicyResponse": { @@ -12391,7 +12902,7 @@ } ], "traits": { - "smithy.api#documentation": "

Simulate how a set of IAM policies attached to an IAM entity works with a list of\n API operations and Amazon Web Services resources to determine the policies' effective permissions. The\n entity can be an IAM user, group, or role. If you specify a user, then the simulation\n also includes all of the policies that are attached to groups that the user belongs to.\n You can simulate resources that don't exist in your account.

\n

You can optionally include a list of one or more additional policies specified as\n strings to include in the simulation. If you want to simulate only policies specified as\n strings, use SimulateCustomPolicy instead.

\n

You can also optionally include one resource-based policy to be evaluated with each of\n the resources included in the simulation.

\n

The simulation does not perform the API operations; it only checks the authorization\n to determine if the simulated policies allow or deny the operations.

\n

\n Note: This operation discloses information about the\n permissions granted to other users. If you do not want users to see other user's\n permissions, then consider allowing them to use SimulateCustomPolicy\n instead.

\n

Context keys are variables maintained by Amazon Web Services and its services that provide details\n about the context of an API query request. You can use the Condition\n element of an IAM policy to evaluate context keys. To get the list of context keys\n that the policies require for correct simulation, use GetContextKeysForPrincipalPolicy.

\n

If the output is long, you can use the MaxItems and Marker\n parameters to paginate the results.

\n

For more information about using the policy simulator, see Testing IAM policies\n with the IAM policy simulator in the\n IAM User Guide.

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

Simulate how a set of IAM policies attached to an IAM entity works with a list of\n API operations and Amazon Web Services resources to determine the policies' effective permissions. The\n entity can be an IAM user, group, or role. If you specify a user, then the simulation\n also includes all of the policies that are attached to groups that the user belongs to.\n You can simulate resources that don't exist in your account.

\n

You can optionally include a list of one or more additional policies specified as\n strings to include in the simulation. If you want to simulate only policies specified as\n strings, use SimulateCustomPolicy instead.

\n

You can also optionally include one resource-based policy to be evaluated with each of\n the resources included in the simulation for IAM users only.

\n

The simulation does not perform the API operations; it only checks the authorization\n to determine if the simulated policies allow or deny the operations.

\n

\n Note: This operation discloses information about the\n permissions granted to other users. If you do not want users to see other user's\n permissions, then consider allowing them to use SimulateCustomPolicy\n instead.

\n

Context keys are variables maintained by Amazon Web Services and its services that provide details\n about the context of an API query request. You can use the Condition\n element of an IAM policy to evaluate context keys. To get the list of context keys\n that the policies require for correct simulation, use GetContextKeysForPrincipalPolicy.

\n

If the output is long, you can use the MaxItems and Marker\n parameters to paginate the results.

\n \n

The IAM policy simulator evaluates statements in the identity-based policy and\n the inputs that you provide during simulation. The policy simulator results can\n differ from your live Amazon Web Services environment. We recommend that you check your policies\n against your live Amazon Web Services environment after testing using the policy simulator to\n confirm that you have the desired results. For more information about using the\n policy simulator, see Testing IAM\n policies with the IAM policy simulator in the\n IAM User Guide.

\n
", "smithy.api#paginated": { "inputToken": "Marker", "outputToken": "Marker", @@ -12406,20 +12917,20 @@ "PolicySourceArn": { "target": "com.amazonaws.iam#arnType", "traits": { - "smithy.api#documentation": "

The Amazon Resource Name (ARN) of a user, group, or role whose policies you want to\n include in the simulation. If you specify a user, group, or role, the simulation\n includes all policies that are associated with that entity. If you specify a user, the\n simulation also includes all policies that are attached to any groups the user belongs\n to.

\n

The maximum length of the policy document that you can pass in this operation,\n including whitespace, is listed below. To view the maximum character counts of a managed policy with no whitespaces, see IAM and STS character quotas.

\n

For more information about ARNs, see Amazon Resource Names (ARNs) in the Amazon Web Services General Reference.

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

The Amazon Resource Name (ARN) of a user, group, or role whose policies you want to\n include in the simulation. If you specify a user, group, or role, the simulation\n includes all policies that are associated with that entity. If you specify a user, the\n simulation also includes all policies that are attached to any groups the user belongs\n to.

\n

The maximum length of the policy document that you can pass in this operation,\n including whitespace, is listed below. To view the maximum character counts of a managed policy with no whitespaces, see IAM and STS character quotas.

\n

For more information about ARNs, see Amazon Resource Names (ARNs) in the Amazon Web Services General Reference.

", "smithy.api#required": {} } }, "PolicyInputList": { "target": "com.amazonaws.iam#SimulationPolicyListType", "traits": { - "smithy.api#documentation": "

An optional list of additional policy documents to include in the simulation. Each\n document is specified as a string containing the complete, valid JSON text of an IAM\n policy.

\n

The regex pattern \n used to validate this parameter is a string of characters consisting of the following:

\n
    \n
  • \n

    Any printable ASCII \n character ranging from the space character (\\u0020) through the end of the ASCII character range

    \n
  • \n
  • \n

    The printable characters in the Basic Latin and Latin-1 Supplement character set \n (through \\u00FF)

    \n
  • \n
  • \n

    The special characters tab (\\u0009), line feed (\\u000A), and \n carriage return (\\u000D)

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

An optional list of additional policy documents to include in the simulation. Each\n document is specified as a string containing the complete, valid JSON text of an IAM\n policy.

\n

The regex pattern \n used to validate this parameter is a string of characters consisting of the following:

\n
    \n
  • \n

    Any printable ASCII \n character ranging from the space character (\\u0020) through the end of the ASCII character range

    \n
  • \n
  • \n

    The printable characters in the Basic Latin and Latin-1 Supplement character set \n (through \\u00FF)

    \n
  • \n
  • \n

    The special characters tab (\\u0009), line feed (\\u000A), and \n carriage return (\\u000D)

    \n
  • \n
" } }, "PermissionsBoundaryPolicyInputList": { "target": "com.amazonaws.iam#SimulationPolicyListType", "traits": { - "smithy.api#documentation": "

The IAM permissions boundary policy to simulate. The permissions boundary sets the\n maximum permissions that the entity can have. You can input only one permissions\n boundary when you pass a policy to this operation. An IAM entity can only have one\n permissions boundary in effect at a time. For example, if a permissions boundary is\n attached to an entity and you pass in a different permissions boundary policy using this\n parameter, then the new permissions boundary policy is used for the simulation. For more\n information about permissions boundaries, see Permissions boundaries for IAM\n entities in the IAM User Guide. The policy input is\n specified as a string containing the complete, valid JSON text of a permissions boundary\n policy.

\n

The maximum length of the policy document that you can pass in this operation,\n including whitespace, is listed below. To view the maximum character counts of a managed policy with no whitespaces, see IAM and STS character quotas.

\n

The regex pattern \n used to validate this parameter is a string of characters consisting of the following:

\n
    \n
  • \n

    Any printable ASCII \n character ranging from the space character (\\u0020) through the end of the ASCII character range

    \n
  • \n
  • \n

    The printable characters in the Basic Latin and Latin-1 Supplement character set \n (through \\u00FF)

    \n
  • \n
  • \n

    The special characters tab (\\u0009), line feed (\\u000A), and \n carriage return (\\u000D)

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

The IAM permissions boundary policy to simulate. The permissions boundary sets the\n maximum permissions that the entity can have. You can input only one permissions\n boundary when you pass a policy to this operation. An IAM entity can only have one\n permissions boundary in effect at a time. For example, if a permissions boundary is\n attached to an entity and you pass in a different permissions boundary policy using this\n parameter, then the new permissions boundary policy is used for the simulation. For more\n information about permissions boundaries, see Permissions boundaries for IAM\n entities in the IAM User Guide. The policy input is\n specified as a string containing the complete, valid JSON text of a permissions boundary\n policy.

\n

The maximum length of the policy document that you can pass in this operation,\n including whitespace, is listed below. To view the maximum character counts of a managed policy with no whitespaces, see IAM and STS character quotas.

\n

The regex pattern \n used to validate this parameter is a string of characters consisting of the following:

\n
    \n
  • \n

    Any printable ASCII \n character ranging from the space character (\\u0020) through the end of the ASCII character range

    \n
  • \n
  • \n

    The printable characters in the Basic Latin and Latin-1 Supplement character set \n (through \\u00FF)

    \n
  • \n
  • \n

    The special characters tab (\\u0009), line feed (\\u000A), and \n carriage return (\\u000D)

    \n
  • \n
" } }, "ActionNames": { @@ -12432,13 +12943,13 @@ "ResourceArns": { "target": "com.amazonaws.iam#ResourceNameListType", "traits": { - "smithy.api#documentation": "

A list of ARNs of Amazon Web Services resources to include in the simulation. If this parameter is\n not provided, then the value defaults to * (all resources). Each API in the\n ActionNames parameter is evaluated for each resource in this list. The\n simulation determines the access result (allowed or denied) of each combination and\n reports it in the response. You can simulate resources that don't exist in your\n account.

\n

The simulation does not automatically retrieve policies for the specified resources.\n If you want to include a resource policy in the simulation, then you must include the\n policy as a string in the ResourcePolicy parameter.

\n

For more information about ARNs, see Amazon Resource Names (ARNs) in the Amazon Web Services General Reference.

" + "smithy.api#documentation": "

A list of ARNs of Amazon Web Services resources to include in the simulation. If this parameter is\n not provided, then the value defaults to * (all resources). Each API in the\n ActionNames parameter is evaluated for each resource in this list. The\n simulation determines the access result (allowed or denied) of each combination and\n reports it in the response. You can simulate resources that don't exist in your\n account.

\n

The simulation does not automatically retrieve policies for the specified resources.\n If you want to include a resource policy in the simulation, then you must include the\n policy as a string in the ResourcePolicy parameter.

\n

For more information about ARNs, see Amazon Resource Names (ARNs) in the Amazon Web Services General Reference.

\n \n

Simulation of resource-based policies isn't supported for IAM roles.

\n
" } }, "ResourcePolicy": { "target": "com.amazonaws.iam#policyDocumentType", "traits": { - "smithy.api#documentation": "

A resource-based policy to include in the simulation provided as a string. Each\n resource in the simulation is treated as if it had this policy attached. You can include\n only one resource-based policy in a simulation.

\n

The maximum length of the policy document that you can pass in this operation,\n including whitespace, is listed below. To view the maximum character counts of a managed policy with no whitespaces, see IAM and STS character quotas.

\n

The regex pattern \n used to validate this parameter is a string of characters consisting of the following:

\n
    \n
  • \n

    Any printable ASCII \n character ranging from the space character (\\u0020) through the end of the ASCII character range

    \n
  • \n
  • \n

    The printable characters in the Basic Latin and Latin-1 Supplement character set \n (through \\u00FF)

    \n
  • \n
  • \n

    The special characters tab (\\u0009), line feed (\\u000A), and \n carriage return (\\u000D)

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

A resource-based policy to include in the simulation provided as a string. Each\n resource in the simulation is treated as if it had this policy attached. You can include\n only one resource-based policy in a simulation.

\n

The maximum length of the policy document that you can pass in this operation,\n including whitespace, is listed below. To view the maximum character counts of a managed policy with no whitespaces, see IAM and STS character quotas.

\n

The regex pattern \n used to validate this parameter is a string of characters consisting of the following:

\n
    \n
  • \n

    Any printable ASCII \n character ranging from the space character (\\u0020) through the end of the ASCII character range

    \n
  • \n
  • \n

    The printable characters in the Basic Latin and Latin-1 Supplement character set \n (through \\u00FF)

    \n
  • \n
  • \n

    The special characters tab (\\u0009), line feed (\\u000A), and \n carriage return (\\u000D)

    \n
  • \n
\n \n

Simulation of resource-based policies isn't supported for IAM roles.

\n
" } }, "ResourceOwner": { @@ -12450,7 +12961,7 @@ "CallerArn": { "target": "com.amazonaws.iam#ResourceNameType", "traits": { - "smithy.api#documentation": "

The ARN of the IAM user that you want to specify as the simulated caller of the API\n operations. If you do not specify a CallerArn, it defaults to the ARN of\n the user that you specify in PolicySourceArn, if you specified a user. If\n you include both a PolicySourceArn (for example,\n arn:aws:iam::123456789012:user/David) and a CallerArn (for\n example, arn:aws:iam::123456789012:user/Bob), the result is that you\n simulate calling the API operations as Bob, as if Bob had David's policies.

\n

You can specify only the ARN of an IAM user. You cannot specify the ARN of an\n assumed role, federated user, or a service principal.

\n

\n CallerArn is required if you include a ResourcePolicy and\n the PolicySourceArn is not the ARN for an IAM user. This is required so\n that the resource-based policy's Principal element has a value to use in\n evaluating the policy.

\n

For more information about ARNs, see Amazon Resource Names (ARNs) in the Amazon Web Services General Reference.

" + "smithy.api#documentation": "

The ARN of the IAM user that you want to specify as the simulated caller of the API\n operations. If you do not specify a CallerArn, it defaults to the ARN of\n the user that you specify in PolicySourceArn, if you specified a user. If\n you include both a PolicySourceArn (for example,\n arn:aws:iam::123456789012:user/David) and a CallerArn (for\n example, arn:aws:iam::123456789012:user/Bob), the result is that you\n simulate calling the API operations as Bob, as if Bob had David's policies.

\n

You can specify only the ARN of an IAM user. You cannot specify the ARN of an\n assumed role, federated user, or a service principal.

\n

\n CallerArn is required if you include a ResourcePolicy and\n the PolicySourceArn is not the ARN for an IAM user. This is required so\n that the resource-based policy's Principal element has a value to use in\n evaluating the policy.

\n

For more information about ARNs, see Amazon Resource Names (ARNs) in the Amazon Web Services General Reference.

" } }, "ContextEntries": { @@ -12462,7 +12973,7 @@ "ResourceHandlingOption": { "target": "com.amazonaws.iam#ResourceHandlingOptionType", "traits": { - "smithy.api#documentation": "

Specifies the type of simulation to run. Different API operations that support\n resource-based policies require different combinations of resources. By specifying the\n type of simulation to run, you enable the policy simulator to enforce the presence of\n the required resources to ensure reliable simulation results. If your simulation does\n not match one of the following scenarios, then you can omit this parameter. The\n following list shows each of the supported scenario values and the resources that you\n must define to run the simulation.

\n

Each of the EC2 scenarios requires that you specify instance, image, and security\n group resources. If your scenario includes an EBS volume, then you must specify that\n volume as a resource. If the EC2 scenario includes VPC, then you must supply the network\n interface resource. If it includes an IP subnet, then you must specify the subnet\n resource. For more information on the EC2 scenario options, see Supported platforms in the Amazon EC2 User\n Guide.

\n
    \n
  • \n

    \n EC2-VPC-InstanceStore\n

    \n

    instance, image, security group, network interface

    \n
  • \n
  • \n

    \n EC2-VPC-InstanceStore-Subnet\n

    \n

    instance, image, security group, network interface, subnet

    \n
  • \n
  • \n

    \n EC2-VPC-EBS\n

    \n

    instance, image, security group, network interface, volume

    \n
  • \n
  • \n

    \n EC2-VPC-EBS-Subnet\n

    \n

    instance, image, security group, network interface, subnet, volume

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

Specifies the type of simulation to run. Different API operations that support\n resource-based policies require different combinations of resources. By specifying the\n type of simulation to run, you enable the policy simulator to enforce the presence of\n the required resources to ensure reliable simulation results. If your simulation does\n not match one of the following scenarios, then you can omit this parameter. The\n following list shows each of the supported scenario values and the resources that you\n must define to run the simulation.

\n

Each of the EC2 scenarios requires that you specify instance, image, and security\n group resources. If your scenario includes an EBS volume, then you must specify that\n volume as a resource. If the EC2 scenario includes VPC, then you must supply the network\n interface resource. If it includes an IP subnet, then you must specify the subnet\n resource. For more information on the EC2 scenario options, see Supported platforms in the Amazon EC2 User\n Guide.

\n
    \n
  • \n

    \n EC2-VPC-InstanceStore\n

    \n

    instance, image, security group, network interface

    \n
  • \n
  • \n

    \n EC2-VPC-InstanceStore-Subnet\n

    \n

    instance, image, security group, network interface, subnet

    \n
  • \n
  • \n

    \n EC2-VPC-EBS\n

    \n

    instance, image, security group, network interface, volume

    \n
  • \n
  • \n

    \n EC2-VPC-EBS-Subnet\n

    \n

    instance, image, security group, network interface, subnet, volume

    \n
  • \n
" } }, "MaxItems": { @@ -12477,6 +12988,9 @@ "smithy.api#documentation": "

Use this parameter only when paginating results and only after \n you receive a response indicating that the results are truncated. Set it to the value of the\n Marker element in the response that you received to indicate where the next call \n should start.

" } } + }, + "traits": { + "smithy.api#input": {} } }, "com.amazonaws.iam#SimulationPolicyListType": { @@ -12591,6 +13105,9 @@ "smithy.api#required": {} } } + }, + "traits": { + "smithy.api#input": {} } }, "com.amazonaws.iam#TagMFADevice": { @@ -12639,6 +13156,9 @@ "smithy.api#required": {} } } + }, + "traits": { + "smithy.api#input": {} } }, "com.amazonaws.iam#TagOpenIDConnectProvider": { @@ -12667,7 +13187,7 @@ } ], "traits": { - "smithy.api#documentation": "

Adds one or more tags to an OpenID Connect (OIDC)-compatible identity provider. For\n more information about these providers, see About web identity federation. If\n a tag with the same key name already exists, then that tag is overwritten with the new\n value.

\n

A tag consists of a key name and an associated value. By assigning tags to your\n resources, you can do the following:

\n
    \n
  • \n

    \n Administrative grouping and discovery - Attach\n tags to resources to aid in organization and search. For example, you could search for all\n resources with the key name Project and the value\n MyImportantProject. Or search for all resources with the key name\n Cost Center and the value 41200.

    \n
  • \n
  • \n

    \n Access control - Include tags in IAM user-based\n and resource-based policies. You can use tags to restrict access to only an OIDC provider\n that has a specified tag attached. For examples of policies that show how to use tags to\n control access, see Control access using IAM tags in the\n IAM User Guide.

    \n
  • \n
\n \n
    \n
  • \n

    If any one of the tags is invalid or if you exceed the allowed maximum number of tags, then the entire request \n fails and the resource is not created. For more information about tagging, see Tagging IAM resources in the\n IAM User Guide.

    \n
  • \n
  • \n

    Amazon Web Services always interprets the tag Value as a single string. If you\n need to store an array, you can store comma-separated values in the string. However, you\n must interpret the value in your code.

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

Adds one or more tags to an OpenID Connect (OIDC)-compatible identity provider. For\n more information about these providers, see About web identity federation. If\n a tag with the same key name already exists, then that tag is overwritten with the new\n value.

\n

A tag consists of a key name and an associated value. By assigning tags to your\n resources, you can do the following:

\n
    \n
  • \n

    \n Administrative grouping and discovery - Attach\n tags to resources to aid in organization and search. For example, you could search for all\n resources with the key name Project and the value\n MyImportantProject. Or search for all resources with the key name\n Cost Center and the value 41200.

    \n
  • \n
  • \n

    \n Access control - Include tags in IAM identity-based\n and resource-based policies. You can use tags to restrict access to only an OIDC provider\n that has a specified tag attached. For examples of policies that show how to use tags to\n control access, see Control access using IAM tags in the\n IAM User Guide.

    \n
  • \n
\n \n
    \n
  • \n

    If any one of the tags is invalid or if you exceed the allowed maximum number of tags, then the entire request \n fails and the resource is not created. For more information about tagging, see Tagging IAM resources in the\n IAM User Guide.

    \n
  • \n
  • \n

    Amazon Web Services always interprets the tag Value as a single string. If you\n need to store an array, you can store comma-separated values in the string. However, you\n must interpret the value in your code.

    \n
  • \n
\n
" } }, "com.amazonaws.iam#TagOpenIDConnectProviderRequest": { @@ -12687,6 +13207,9 @@ "smithy.api#required": {} } } + }, + "traits": { + "smithy.api#input": {} } }, "com.amazonaws.iam#TagPolicy": { @@ -12735,6 +13258,9 @@ "smithy.api#required": {} } } + }, + "traits": { + "smithy.api#input": {} } }, "com.amazonaws.iam#TagRole": { @@ -12783,6 +13309,9 @@ "smithy.api#required": {} } } + }, + "traits": { + "smithy.api#input": {} } }, "com.amazonaws.iam#TagSAMLProvider": { @@ -12831,6 +13360,9 @@ "smithy.api#required": {} } } + }, + "traits": { + "smithy.api#input": {} } }, "com.amazonaws.iam#TagServerCertificate": { @@ -12879,6 +13411,9 @@ "smithy.api#required": {} } } + }, + "traits": { + "smithy.api#input": {} } }, "com.amazonaws.iam#TagUser": { @@ -12907,7 +13442,7 @@ } ], "traits": { - "smithy.api#documentation": "

Adds one or more tags to an IAM user. If a tag with the same key name already exists,\n then that tag is overwritten with the new value.

\n

A tag consists of a key name and an associated value. By assigning tags to your\n resources, you can do the following:

\n
    \n
  • \n

    \n Administrative grouping and discovery - Attach\n tags to resources to aid in organization and search. For example, you could search for all\n resources with the key name Project and the value\n MyImportantProject. Or search for all resources with the key name\n Cost Center and the value 41200.

    \n
  • \n
  • \n

    \n Access control - Include tags in IAM user-based\n and resource-based policies. You can use tags to restrict access to only an IAM\n requesting user that has a specified tag attached. You can also restrict access to only\n those resources that have a certain tag attached. For examples of policies that show how\n to use tags to control access, see Control access using IAM tags in the\n IAM User Guide.

    \n
  • \n
  • \n

    \n Cost allocation - Use tags to help track which\n individuals and teams are using which Amazon Web Services resources.

    \n
  • \n
\n \n
    \n
  • \n

    If any one of the tags is invalid or if you exceed the allowed maximum number of tags, then the entire request \n fails and the resource is not created. For more information about tagging, see Tagging IAM resources in the\n IAM User Guide.

    \n
  • \n
  • \n

    Amazon Web Services always interprets the tag Value as a single string. If you\n need to store an array, you can store comma-separated values in the string. However, you\n must interpret the value in your code.

    \n
  • \n
\n
\n

For more information about tagging, see Tagging IAM identities in the\n IAM User Guide.

" + "smithy.api#documentation": "

Adds one or more tags to an IAM user. If a tag with the same key name already exists,\n then that tag is overwritten with the new value.

\n

A tag consists of a key name and an associated value. By assigning tags to your\n resources, you can do the following:

\n
    \n
  • \n

    \n Administrative grouping and discovery - Attach\n tags to resources to aid in organization and search. For example, you could search for all\n resources with the key name Project and the value\n MyImportantProject. Or search for all resources with the key name\n Cost Center and the value 41200.

    \n
  • \n
  • \n

    \n Access control - Include tags in IAM identity-based\n and resource-based policies. You can use tags to restrict access to only an IAM\n requesting user that has a specified tag attached. You can also restrict access to only\n those resources that have a certain tag attached. For examples of policies that show how\n to use tags to control access, see Control access using IAM tags in the\n IAM User Guide.

    \n
  • \n
  • \n

    \n Cost allocation - Use tags to help track which\n individuals and teams are using which Amazon Web Services resources.

    \n
  • \n
\n \n
    \n
  • \n

    If any one of the tags is invalid or if you exceed the allowed maximum number of tags, then the entire request \n fails and the resource is not created. For more information about tagging, see Tagging IAM resources in the\n IAM User Guide.

    \n
  • \n
  • \n

    Amazon Web Services always interprets the tag Value as a single string. If you\n need to store an array, you can store comma-separated values in the string. However, you\n must interpret the value in your code.

    \n
  • \n
\n
\n

For more information about tagging, see Tagging IAM identities in the\n IAM User Guide.

" } }, "com.amazonaws.iam#TagUserRequest": { @@ -12927,6 +13462,9 @@ "smithy.api#required": {} } } + }, + "traits": { + "smithy.api#input": {} } }, "com.amazonaws.iam#TrackedActionLastAccessed": { @@ -13041,6 +13579,9 @@ "smithy.api#required": {} } } + }, + "traits": { + "smithy.api#input": {} } }, "com.amazonaws.iam#UntagMFADevice": { @@ -13086,6 +13627,9 @@ "smithy.api#required": {} } } + }, + "traits": { + "smithy.api#input": {} } }, "com.amazonaws.iam#UntagOpenIDConnectProvider": { @@ -13131,6 +13675,9 @@ "smithy.api#required": {} } } + }, + "traits": { + "smithy.api#input": {} } }, "com.amazonaws.iam#UntagPolicy": { @@ -13176,6 +13723,9 @@ "smithy.api#required": {} } } + }, + "traits": { + "smithy.api#input": {} } }, "com.amazonaws.iam#UntagRole": { @@ -13218,6 +13768,9 @@ "smithy.api#required": {} } } + }, + "traits": { + "smithy.api#input": {} } }, "com.amazonaws.iam#UntagSAMLProvider": { @@ -13263,6 +13816,9 @@ "smithy.api#required": {} } } + }, + "traits": { + "smithy.api#input": {} } }, "com.amazonaws.iam#UntagServerCertificate": { @@ -13308,6 +13864,9 @@ "smithy.api#required": {} } } + }, + "traits": { + "smithy.api#input": {} } }, "com.amazonaws.iam#UntagUser": { @@ -13350,6 +13909,9 @@ "smithy.api#required": {} } } + }, + "traits": { + "smithy.api#input": {} } }, "com.amazonaws.iam#UpdateAccessKey": { @@ -13372,7 +13934,7 @@ } ], "traits": { - "smithy.api#documentation": "

Changes the status of the specified access key from Active to Inactive, or vice versa.\n This operation can be used to disable a user's key as part of a key rotation\n workflow.

\n

If the UserName is not specified, the user name is determined implicitly\n based on the Amazon Web Services access key ID used to sign the request. If a temporary access key is\n used, then UserName is required. If a long-term key is assigned to the\n user, then UserName is not required. This operation works for access keys\n under the Amazon Web Services account. Consequently, you can use this operation to manage\n Amazon Web Services account root user credentials even if the Amazon Web Services account has no associated\n users.

\n

For information about rotating keys, see Managing keys and certificates\n in the IAM User Guide.

" + "smithy.api#documentation": "

Changes the status of the specified access key from Active to Inactive, or vice versa.\n This operation can be used to disable a user's key as part of a key rotation\n workflow.

\n

If the UserName is not specified, the user name is determined implicitly\n based on the Amazon Web Services access key ID used to sign the request. If a temporary access key is\n used, then UserName is required. If a long-term key is assigned to the\n user, then UserName is not required. This operation works for access keys\n under the Amazon Web Services account. Consequently, you can use this operation to manage Amazon Web Services account root user\n credentials even if the Amazon Web Services account has no associated users.

\n

For information about rotating keys, see Managing keys and certificates\n in the IAM User Guide.

" } }, "com.amazonaws.iam#UpdateAccessKeyRequest": { @@ -13381,13 +13943,13 @@ "UserName": { "target": "com.amazonaws.iam#existingUserNameType", "traits": { - "smithy.api#documentation": "

The name of the user whose key you want to update.

\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 user whose key you want to update.

\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: _+=,.@-

" } }, "AccessKeyId": { "target": "com.amazonaws.iam#accessKeyIdType", "traits": { - "smithy.api#documentation": "

The access key ID of the secret access key you want to update.

\n

This parameter allows (through its regex pattern) a string of characters that can \n consist of any upper or lowercased letter or digit.

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

The access key ID of the secret access key you want to update.

\n

This parameter allows (through its regex pattern) a string of characters that can \n consist of any upper or lowercased letter or digit.

", "smithy.api#required": {} } }, @@ -13398,6 +13960,9 @@ "smithy.api#required": {} } } + }, + "traits": { + "smithy.api#input": {} } }, "com.amazonaws.iam#UpdateAccountPasswordPolicy": { @@ -13423,7 +13988,7 @@ } ], "traits": { - "smithy.api#documentation": "

Updates the password policy settings for the Amazon Web Services account.

\n \n

This operation does not support partial updates. No parameters are required, but\n if you do not specify a parameter, that parameter's value reverts to its default\n value. See the Request Parameters section for each\n parameter's default value. Also note that some parameters do not allow the default\n parameter to be explicitly set. Instead, to invoke the default value, do not include\n that parameter when you invoke the operation.

\n
\n

For more information about using a password policy, see Managing an IAM password\n policy in the IAM User Guide.

" + "smithy.api#documentation": "

Updates the password policy settings for the Amazon Web Services account.

\n \n

This operation does not support partial updates. No parameters are required, but\n if you do not specify a parameter, that parameter's value reverts to its default\n value. See the Request Parameters section for each\n parameter's default value. Also note that some parameters do not allow the default\n parameter to be explicitly set. Instead, to invoke the default value, do not include\n that parameter when you invoke the operation.

\n
\n

For more information about using a password policy, see Managing an IAM password\n policy in the IAM User Guide.

" } }, "com.amazonaws.iam#UpdateAccountPasswordPolicyRequest": { @@ -13432,62 +13997,65 @@ "MinimumPasswordLength": { "target": "com.amazonaws.iam#minimumPasswordLengthType", "traits": { - "smithy.api#documentation": "

The minimum number of characters allowed in an IAM user password.

\n

If you do not specify a value for this parameter, then the operation uses the default\n value of 6.

" + "smithy.api#documentation": "

The minimum number of characters allowed in an IAM user password.

\n

If you do not specify a value for this parameter, then the operation uses the default\n value of 6.

" } }, "RequireSymbols": { "target": "com.amazonaws.iam#booleanType", "traits": { "smithy.api#default": false, - "smithy.api#documentation": "

Specifies whether IAM user passwords must contain at least one of the following\n non-alphanumeric characters:

\n

! @ # $ % ^ & * ( ) _ + - = [ ] { } | '

\n

If you do not specify a value for this parameter, then the operation uses the default\n value of false. The result is that passwords do not require at least one\n symbol character.

" + "smithy.api#documentation": "

Specifies whether IAM user passwords must contain at least one of the following\n non-alphanumeric characters:

\n

! @ # $ % ^ & * ( ) _ + - = [ ] { } | '

\n

If you do not specify a value for this parameter, then the operation uses the default\n value of false. The result is that passwords do not require at least one\n symbol character.

" } }, "RequireNumbers": { "target": "com.amazonaws.iam#booleanType", "traits": { "smithy.api#default": false, - "smithy.api#documentation": "

Specifies whether IAM user passwords must contain at least one numeric character (0\n to 9).

\n

If you do not specify a value for this parameter, then the operation uses the default\n value of false. The result is that passwords do not require at least one\n numeric character.

" + "smithy.api#documentation": "

Specifies whether IAM user passwords must contain at least one numeric character (0\n to 9).

\n

If you do not specify a value for this parameter, then the operation uses the default\n value of false. The result is that passwords do not require at least one\n numeric character.

" } }, "RequireUppercaseCharacters": { "target": "com.amazonaws.iam#booleanType", "traits": { "smithy.api#default": false, - "smithy.api#documentation": "

Specifies whether IAM user passwords must contain at least one uppercase character\n from the ISO basic Latin alphabet (A to Z).

\n

If you do not specify a value for this parameter, then the operation uses the default\n value of false. The result is that passwords do not require at least one\n uppercase character.

" + "smithy.api#documentation": "

Specifies whether IAM user passwords must contain at least one uppercase character\n from the ISO basic Latin alphabet (A to Z).

\n

If you do not specify a value for this parameter, then the operation uses the default\n value of false. The result is that passwords do not require at least one\n uppercase character.

" } }, "RequireLowercaseCharacters": { "target": "com.amazonaws.iam#booleanType", "traits": { "smithy.api#default": false, - "smithy.api#documentation": "

Specifies whether IAM user passwords must contain at least one lowercase character\n from the ISO basic Latin alphabet (a to z).

\n

If you do not specify a value for this parameter, then the operation uses the default\n value of false. The result is that passwords do not require at least one\n lowercase character.

" + "smithy.api#documentation": "

Specifies whether IAM user passwords must contain at least one lowercase character\n from the ISO basic Latin alphabet (a to z).

\n

If you do not specify a value for this parameter, then the operation uses the default\n value of false. The result is that passwords do not require at least one\n lowercase character.

" } }, "AllowUsersToChangePassword": { "target": "com.amazonaws.iam#booleanType", "traits": { "smithy.api#default": false, - "smithy.api#documentation": "

Allows all IAM users in your account to use the Amazon Web Services Management Console to change their own\n passwords. For more information, see Permitting\n IAM users to change their own passwords in the\n IAM User Guide.

\n

If you do not specify a value for this parameter, then the operation uses the default\n value of false. The result is that IAM users in the account do not\n automatically have permissions to change their own password.

" + "smithy.api#documentation": "

Allows all IAM users in your account to use the Amazon Web Services Management Console to change their own\n passwords. For more information, see Permitting\n IAM users to change their own passwords in the\n IAM User Guide.

\n

If you do not specify a value for this parameter, then the operation uses the default\n value of false. The result is that IAM users in the account do not\n automatically have permissions to change their own password.

" } }, "MaxPasswordAge": { "target": "com.amazonaws.iam#maxPasswordAgeType", "traits": { - "smithy.api#documentation": "

The number of days that an IAM user password is valid.

\n

If you do not specify a value for this parameter, then the operation uses the default\n value of 0. The result is that IAM user passwords never expire.

" + "smithy.api#documentation": "

The number of days that an IAM user password is valid.

\n

If you do not specify a value for this parameter, then the operation uses the default\n value of 0. The result is that IAM user passwords never expire.

" } }, "PasswordReusePrevention": { "target": "com.amazonaws.iam#passwordReusePreventionType", "traits": { - "smithy.api#documentation": "

Specifies the number of previous passwords that IAM users are prevented from\n reusing.

\n

If you do not specify a value for this parameter, then the operation uses the default\n value of 0. The result is that IAM users are not prevented from reusing\n previous passwords.

" + "smithy.api#documentation": "

Specifies the number of previous passwords that IAM users are prevented from\n reusing.

\n

If you do not specify a value for this parameter, then the operation uses the default\n value of 0. The result is that IAM users are not prevented from reusing\n previous passwords.

" } }, "HardExpiry": { "target": "com.amazonaws.iam#booleanObjectType", "traits": { - "smithy.api#documentation": "

Prevents IAM users who are accessing the account via the Amazon Web Services Management Console from setting a\n new console password after their password has expired. The IAM user cannot access the\n console until an administrator resets the password.

\n

If you do not specify a value for this parameter, then the operation uses the default\n value of false. The result is that IAM users can change their passwords\n after they expire and continue to sign in as the user.

\n \n

In the Amazon Web Services Management Console, the custom password policy option Allow\n users to change their own password gives IAM users permissions to\n iam:ChangePassword for only their user and to the\n iam:GetAccountPasswordPolicy action. This option does not attach a\n permissions policy to each user, rather the permissions are applied at the\n account-level for all users by IAM. IAM users with\n iam:ChangePassword permission and active access keys can reset\n their own expired console password using the CLI or API.

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

Prevents IAM users who are accessing the account via the Amazon Web Services Management Console from setting a\n new console password after their password has expired. The IAM user cannot access the\n console until an administrator resets the password.

\n

If you do not specify a value for this parameter, then the operation uses the default\n value of false. The result is that IAM users can change their passwords\n after they expire and continue to sign in as the user.

\n \n

In the Amazon Web Services Management Console, the custom password policy option Allow\n users to change their own password gives IAM users permissions to\n iam:ChangePassword for only their user and to the\n iam:GetAccountPasswordPolicy action. This option does not attach a\n permissions policy to each user, rather the permissions are applied at the\n account-level for all users by IAM. IAM users with\n iam:ChangePassword permission and active access keys can reset\n their own expired console password using the CLI or API.

\n
" } } + }, + "traits": { + "smithy.api#input": {} } }, "com.amazonaws.iam#UpdateAssumeRolePolicy": { @@ -13525,17 +14093,20 @@ "RoleName": { "target": "com.amazonaws.iam#roleNameType", "traits": { - "smithy.api#documentation": "

The name of the role to update with the new policy.

\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 role to update with the new policy.

\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": {} } }, "PolicyDocument": { "target": "com.amazonaws.iam#policyDocumentType", "traits": { - "smithy.api#documentation": "

The policy that grants an entity permission to assume the role.

\n

You must provide policies in JSON format in IAM. However, for CloudFormation\n templates formatted in YAML, you can provide the policy in JSON or YAML format. CloudFormation always converts a YAML policy to JSON format before submitting it to\n IAM.

\n

The regex pattern \n used to validate this parameter is a string of characters consisting of the following:

\n
    \n
  • \n

    Any printable ASCII \n character ranging from the space character (\\u0020) through the end of the ASCII character range

    \n
  • \n
  • \n

    The printable characters in the Basic Latin and Latin-1 Supplement character set \n (through \\u00FF)

    \n
  • \n
  • \n

    The special characters tab (\\u0009), line feed (\\u000A), and \n carriage return (\\u000D)

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

The policy that grants an entity permission to assume the role.

\n

You must provide policies in JSON format in IAM. However, for CloudFormation\n templates formatted in YAML, you can provide the policy in JSON or YAML format. CloudFormation always converts a YAML policy to JSON format before submitting it to\n IAM.

\n

The regex pattern \n used to validate this parameter is a string of characters consisting of the following:

\n
    \n
  • \n

    Any printable ASCII \n character ranging from the space character (\\u0020) through the end of the ASCII character range

    \n
  • \n
  • \n

    The printable characters in the Basic Latin and Latin-1 Supplement character set \n (through \\u00FF)

    \n
  • \n
  • \n

    The special characters tab (\\u0009), line feed (\\u000A), and \n carriage return (\\u000D)

    \n
  • \n
", "smithy.api#required": {} } } + }, + "traits": { + "smithy.api#input": {} } }, "com.amazonaws.iam#UpdateGroup": { @@ -13561,7 +14132,7 @@ } ], "traits": { - "smithy.api#documentation": "

Updates the name and/or the path of the specified IAM group.

\n \n

You should understand the implications of changing a group's path or name. For\n more information, see Renaming users and\n groups in the IAM User Guide.

\n
\n \n

The person making the request (the principal), must have permission to change the\n role group with the old name and the new name. For example, to change the group\n named Managers to MGRs, the principal must have a policy\n that allows them to update both groups. If the principal has permission to update\n the Managers group, but not the MGRs group, then the\n update fails. For more information about permissions, see Access management.\n

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

Updates the name and/or the path of the specified IAM group.

\n \n

You should understand the implications of changing a group's path or name. For\n more information, see Renaming users and\n groups in the IAM User Guide.

\n
\n \n

The person making the request (the principal), must have permission to change the\n role group with the old name and the new name. For example, to change the group\n named Managers to MGRs, the principal must have a policy\n that allows them to update both groups. If the principal has permission to update\n the Managers group, but not the MGRs group, then the\n update fails. For more information about permissions, see Access management.\n

\n
" } }, "com.amazonaws.iam#UpdateGroupRequest": { @@ -13570,22 +14141,25 @@ "GroupName": { "target": "com.amazonaws.iam#groupNameType", "traits": { - "smithy.api#documentation": "

Name of the IAM group to update. If you're changing the name of the group, this is\n the original name.

\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": "

Name of the IAM group to update. If you're changing the name of the group, this is\n the original name.

\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": {} } }, "NewPath": { "target": "com.amazonaws.iam#pathType", "traits": { - "smithy.api#documentation": "

New path for the IAM group. Only include this if changing the group's path.

\n

This parameter allows (through its regex pattern) a string of characters consisting \n of either a forward slash (/) by itself or a string that must begin and end with forward slashes.\n In addition, it can contain any ASCII character from the ! (\\u0021) through the DEL character (\\u007F), including \n most punctuation characters, digits, and upper and lowercased letters.

" + "smithy.api#documentation": "

New path for the IAM group. Only include this if changing the group's path.

\n

This parameter allows (through its regex pattern) a string of characters consisting \n of either a forward slash (/) by itself or a string that must begin and end with forward slashes.\n In addition, it can contain any ASCII character from the ! (\\u0021) through the DEL character (\\u007F), including \n most punctuation characters, digits, and upper and lowercased letters.

" } }, "NewGroupName": { "target": "com.amazonaws.iam#groupNameType", "traits": { - "smithy.api#documentation": "

New name for the IAM group. Only include this if changing the group's name.

\n

IAM user, group, role, and policy names must be unique within the account. Names are\n not distinguished by case. For example, you cannot create resources named both\n \"MyResource\" and \"myresource\".

" + "smithy.api#documentation": "

New name for the IAM group. Only include this if changing the group's name.

\n

IAM user, group, role, and policy names must be unique within the account. Names are\n not distinguished by case. For example, you cannot create resources named both\n \"MyResource\" and \"myresource\".

" } } + }, + "traits": { + "smithy.api#input": {} } }, "com.amazonaws.iam#UpdateLoginProfile": { @@ -13614,7 +14188,7 @@ } ], "traits": { - "smithy.api#documentation": "

Changes the password for the specified IAM user. You can use the CLI,\n the Amazon Web Services API, or the Users page in the IAM console\n to change the password for any IAM user. Use ChangePassword to\n change your own password in the My Security Credentials\n page in the Amazon Web Services Management Console.

\n

For more information about modifying passwords, see Managing passwords in the\n IAM User Guide.

" + "smithy.api#documentation": "

Changes the password for the specified IAM user. You can use the CLI, the Amazon Web Services\n API, or the Users page in the IAM console to change\n the password for any IAM user. Use ChangePassword to change your own\n password in the My Security Credentials page in the\n Amazon Web Services Management Console.

\n

For more information about modifying passwords, see Managing passwords in the\n IAM User Guide.

" } }, "com.amazonaws.iam#UpdateLoginProfileRequest": { @@ -13623,14 +14197,14 @@ "UserName": { "target": "com.amazonaws.iam#userNameType", "traits": { - "smithy.api#documentation": "

The name of the user whose password you want to update.

\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 user whose password you want to update.

\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": {} } }, "Password": { "target": "com.amazonaws.iam#passwordType", "traits": { - "smithy.api#documentation": "

The new password for the specified IAM user.

\n

The regex pattern \n used to validate this parameter is a string of characters consisting of the following:

\n
    \n
  • \n

    Any printable ASCII \n character ranging from the space character (\\u0020) through the end of the ASCII character range

    \n
  • \n
  • \n

    The printable characters in the Basic Latin and Latin-1 Supplement character set \n (through \\u00FF)

    \n
  • \n
  • \n

    The special characters tab (\\u0009), line feed (\\u000A), and \n carriage return (\\u000D)

    \n
  • \n
\n

However, the format can be further restricted by the account administrator by setting\n a password policy on the Amazon Web Services account. For more information, see UpdateAccountPasswordPolicy.

" + "smithy.api#documentation": "

The new password for the specified IAM user.

\n

The regex pattern \n used to validate this parameter is a string of characters consisting of the following:

\n
    \n
  • \n

    Any printable ASCII \n character ranging from the space character (\\u0020) through the end of the ASCII character range

    \n
  • \n
  • \n

    The printable characters in the Basic Latin and Latin-1 Supplement character set \n (through \\u00FF)

    \n
  • \n
  • \n

    The special characters tab (\\u0009), line feed (\\u000A), and \n carriage return (\\u000D)

    \n
  • \n
\n

However, the format can be further restricted by the account administrator by setting\n a password policy on the Amazon Web Services account. For more information, see UpdateAccountPasswordPolicy.

" } }, "PasswordResetRequired": { @@ -13639,6 +14213,9 @@ "smithy.api#documentation": "

Allows this new password to be used only once by requiring the specified IAM user to\n set a new password on next sign-in.

" } } + }, + "traits": { + "smithy.api#input": {} } }, "com.amazonaws.iam#UpdateOpenIDConnectProviderThumbprint": { @@ -13661,7 +14238,7 @@ } ], "traits": { - "smithy.api#documentation": "

Replaces the existing list of server certificate thumbprints associated with an OpenID\n Connect (OIDC) provider resource object with a new list of thumbprints.

\n

The list that you pass with this operation completely replaces the existing list of\n thumbprints. (The lists are not merged.)

\n

Typically, you need to update a thumbprint only when the identity provider certificate\n changes, which occurs rarely. However, if the provider's certificate\n does change, any attempt to assume an IAM role that specifies\n the OIDC provider as a principal fails until the certificate thumbprint is\n updated.

\n \n

Amazon Web Services secures communication with some OIDC identity providers (IdPs) through our\n library of trusted certificate authorities (CAs) instead of using a certificate\n thumbprint to verify your IdP server certificate. These OIDC IdPs include Google, and\n those that use an Amazon S3 bucket to host a JSON Web Key Set (JWKS) endpoint. In these\n cases, your legacy thumbprint remains in your configuration, but is no longer used for validation.

\n
\n \n

Trust for the OIDC provider is derived from the provider certificate and is\n validated by the thumbprint. Therefore, it is best to limit access to the\n UpdateOpenIDConnectProviderThumbprint operation to highly\n privileged users.

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

Replaces the existing list of server certificate thumbprints associated with an OpenID\n Connect (OIDC) provider resource object with a new list of thumbprints.

\n

The list that you pass with this operation completely replaces the existing list of\n thumbprints. (The lists are not merged.)

\n

Typically, you need to update a thumbprint only when the identity provider certificate\n changes, which occurs rarely. However, if the provider's certificate\n does change, any attempt to assume an IAM role that specifies\n the OIDC provider as a principal fails until the certificate thumbprint is\n updated.

\n \n

Amazon Web Services secures communication with some OIDC identity providers (IdPs) through our\n library of trusted certificate authorities (CAs) instead of using a certificate\n thumbprint to verify your IdP server certificate. These OIDC IdPs include Google, Auth0,\n and those that use an Amazon S3 bucket to host a JSON Web Key Set (JWKS) endpoint. In these\n cases, your legacy thumbprint remains in your configuration, but is no longer used for\n validation.

\n
\n \n

Trust for the OIDC provider is derived from the provider certificate and is\n validated by the thumbprint. Therefore, it is best to limit access to the\n UpdateOpenIDConnectProviderThumbprint operation to highly\n privileged users.

\n
" } }, "com.amazonaws.iam#UpdateOpenIDConnectProviderThumbprintRequest": { @@ -13670,7 +14247,7 @@ "OpenIDConnectProviderArn": { "target": "com.amazonaws.iam#arnType", "traits": { - "smithy.api#documentation": "

The Amazon Resource Name (ARN) of the IAM OIDC provider resource object for which\n you want to update the thumbprint. You can get a list of OIDC provider ARNs by using the\n ListOpenIDConnectProviders operation.

\n

For more information about ARNs, see Amazon Resource Names (ARNs) in the Amazon Web Services General Reference.

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

The Amazon Resource Name (ARN) of the IAM OIDC provider resource object for which\n you want to update the thumbprint. You can get a list of OIDC provider ARNs by using the\n ListOpenIDConnectProviders operation.

\n

For more information about ARNs, see Amazon Resource Names (ARNs) in the Amazon Web Services General Reference.

", "smithy.api#required": {} } }, @@ -13681,6 +14258,9 @@ "smithy.api#required": {} } } + }, + "traits": { + "smithy.api#input": {} } }, "com.amazonaws.iam#UpdateRole": { @@ -13726,7 +14306,7 @@ } ], "traits": { - "smithy.api#documentation": "

Use UpdateRole instead.

\n

Modifies only the description of a role. This operation performs the same function as\n the Description parameter in the UpdateRole operation.

" + "smithy.api#documentation": "

Use UpdateRole instead.

\n

Modifies only the description of a role. This operation performs the same function as\n the Description parameter in the UpdateRole operation.

" } }, "com.amazonaws.iam#UpdateRoleDescriptionRequest": { @@ -13746,6 +14326,9 @@ "smithy.api#required": {} } } + }, + "traits": { + "smithy.api#input": {} } }, "com.amazonaws.iam#UpdateRoleDescriptionResponse": { @@ -13757,6 +14340,9 @@ "smithy.api#documentation": "

A structure that contains details about the modified role.

" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.iam#UpdateRoleRequest": { @@ -13778,14 +14364,20 @@ "MaxSessionDuration": { "target": "com.amazonaws.iam#roleMaxSessionDurationType", "traits": { - "smithy.api#documentation": "

The maximum session duration (in seconds) that you want to set for the specified role.\n If you do not specify a value for this setting, the default value of one hour is\n applied. This setting can have a value from 1 hour to 12 hours.

\n

Anyone who assumes the role from the CLI or API can use the\n DurationSeconds API parameter or the duration-seconds CLI\n parameter to request a longer session. The MaxSessionDuration setting\n determines the maximum duration that can be requested using the\n DurationSeconds parameter. If users don't specify a value for the\n DurationSeconds parameter, their security credentials are valid for one\n hour by default. This applies when you use the AssumeRole* API operations\n or the assume-role* CLI operations but does not apply when you use those\n operations to create a console URL. For more information, see Using IAM\n roles in the IAM User Guide.

" + "smithy.api#documentation": "

The maximum session duration (in seconds) that you want to set for the specified role.\n If you do not specify a value for this setting, the default value of one hour is\n applied. This setting can have a value from 1 hour to 12 hours.

\n

Anyone who assumes the role from the CLI or API can use the\n DurationSeconds API parameter or the duration-seconds\n CLI parameter to request a longer session. The MaxSessionDuration setting\n determines the maximum duration that can be requested using the\n DurationSeconds parameter. If users don't specify a value for the\n DurationSeconds parameter, their security credentials are valid for one\n hour by default. This applies when you use the AssumeRole* API operations\n or the assume-role* CLI operations but does not apply when you use those\n operations to create a console URL. For more information, see Using IAM\n roles in the IAM User Guide.

" } } + }, + "traits": { + "smithy.api#input": {} } }, "com.amazonaws.iam#UpdateRoleResponse": { "type": "structure", - "members": {} + "members": {}, + "traits": { + "smithy.api#output": {} + } }, "com.amazonaws.iam#UpdateSAMLProvider": { "type": "operation", @@ -13810,7 +14402,7 @@ } ], "traits": { - "smithy.api#documentation": "

Updates the metadata document for an existing SAML provider resource object.

\n \n

This operation requires Signature Version 4.

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

Updates the metadata document for an existing SAML provider resource object.

\n \n

This operation requires Signature Version 4.

\n
" } }, "com.amazonaws.iam#UpdateSAMLProviderRequest": { @@ -13826,10 +14418,13 @@ "SAMLProviderArn": { "target": "com.amazonaws.iam#arnType", "traits": { - "smithy.api#documentation": "

The Amazon Resource Name (ARN) of the SAML provider to update.

\n

For more information about ARNs, see Amazon Resource Names (ARNs) in the Amazon Web Services General Reference.

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

The Amazon Resource Name (ARN) of the SAML provider to update.

\n

For more information about ARNs, see Amazon Resource Names (ARNs) in the Amazon Web Services General Reference.

", "smithy.api#required": {} } } + }, + "traits": { + "smithy.api#input": {} } }, "com.amazonaws.iam#UpdateSAMLProviderResponse": { @@ -13843,7 +14438,8 @@ } }, "traits": { - "smithy.api#documentation": "

Contains the response to a successful UpdateSAMLProvider request.\n

" + "smithy.api#documentation": "

Contains the response to a successful UpdateSAMLProvider request.\n

", + "smithy.api#output": {} } }, "com.amazonaws.iam#UpdateSSHPublicKey": { @@ -13860,7 +14456,7 @@ } ], "traits": { - "smithy.api#documentation": "

Sets the status of an IAM user's SSH public key to active or inactive. SSH public\n keys that are inactive cannot be used for authentication. This operation can be used to\n disable a user's SSH public key as part of a key rotation work flow.

\n

The SSH public key affected by this operation is used only for authenticating the\n associated IAM user to an CodeCommit repository. For more information about using SSH keys\n to authenticate to an CodeCommit repository, see Set up CodeCommit for\n SSH connections in the CodeCommit User Guide.

" + "smithy.api#documentation": "

Sets the status of an IAM user's SSH public key to active or inactive. SSH public\n keys that are inactive cannot be used for authentication. This operation can be used to\n disable a user's SSH public key as part of a key rotation work flow.

\n

The SSH public key affected by this operation is used only for authenticating the\n associated IAM user to an CodeCommit repository. For more information about using SSH keys\n to authenticate to an CodeCommit repository, see Set up CodeCommit for\n SSH connections in the CodeCommit User Guide.

" } }, "com.amazonaws.iam#UpdateSSHPublicKeyRequest": { @@ -13869,14 +14465,14 @@ "UserName": { "target": "com.amazonaws.iam#userNameType", "traits": { - "smithy.api#documentation": "

The name of the IAM user associated with the SSH public key.

\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 IAM user associated with the SSH public key.

\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": {} } }, "SSHPublicKeyId": { "target": "com.amazonaws.iam#publicKeyIdType", "traits": { - "smithy.api#documentation": "

The unique identifier for the SSH public key.

\n

This parameter allows (through its regex pattern) a string of characters that can \n consist of any upper or lowercased letter or digit.

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

The unique identifier for the SSH public key.

\n

This parameter allows (through its regex pattern) a string of characters that can \n consist of any upper or lowercased letter or digit.

", "smithy.api#required": {} } }, @@ -13887,6 +14483,9 @@ "smithy.api#required": {} } } + }, + "traits": { + "smithy.api#input": {} } }, "com.amazonaws.iam#UpdateServerCertificate": { @@ -13912,7 +14511,7 @@ } ], "traits": { - "smithy.api#documentation": "

Updates the name and/or the path of the specified server certificate stored in\n IAM.

\n

For more information about working with server certificates, see Working\n with server certificates in the IAM User Guide. This\n topic also includes a list of Amazon Web Services services that can use the server certificates that\n you manage with IAM.

\n \n

You should understand the implications of changing a server certificate's path or\n name. For more information, see Renaming a server certificate in the\n IAM User Guide.

\n
\n \n

The person making the request (the principal), must have permission to change the\n server certificate with the old name and the new name. For example, to change the\n certificate named ProductionCert to ProdCert, the\n principal must have a policy that allows them to update both certificates. If the\n principal has permission to update the ProductionCert group, but not\n the ProdCert certificate, then the update fails. For more information\n about permissions, see Access management in the IAM User Guide.

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

Updates the name and/or the path of the specified server certificate stored in\n IAM.

\n

For more information about working with server certificates, see Working\n with server certificates in the IAM User Guide. This\n topic also includes a list of Amazon Web Services services that can use the server certificates that\n you manage with IAM.

\n \n

You should understand the implications of changing a server certificate's path or\n name. For more information, see Renaming a server certificate in the\n IAM User Guide.

\n
\n \n

The person making the request (the principal), must have permission to change the\n server certificate with the old name and the new name. For example, to change the\n certificate named ProductionCert to ProdCert, the\n principal must have a policy that allows them to update both certificates. If the\n principal has permission to update the ProductionCert group, but not\n the ProdCert certificate, then the update fails. For more information\n about permissions, see Access management in the IAM User Guide.

\n
" } }, "com.amazonaws.iam#UpdateServerCertificateRequest": { @@ -13921,22 +14520,25 @@ "ServerCertificateName": { "target": "com.amazonaws.iam#serverCertificateNameType", "traits": { - "smithy.api#documentation": "

The name of the server certificate that you want to update.

\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 server certificate that you want to update.

\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": {} } }, "NewPath": { "target": "com.amazonaws.iam#pathType", "traits": { - "smithy.api#documentation": "

The new path for the server certificate. Include this only if you are updating the\n server certificate's path.

\n

This parameter allows (through its regex pattern) a string of characters consisting \n of either a forward slash (/) by itself or a string that must begin and end with forward slashes.\n In addition, it can contain any ASCII character from the ! (\\u0021) through the DEL character (\\u007F), including \n most punctuation characters, digits, and upper and lowercased letters.

" + "smithy.api#documentation": "

The new path for the server certificate. Include this only if you are updating the\n server certificate's path.

\n

This parameter allows (through its regex pattern) a string of characters consisting \n of either a forward slash (/) by itself or a string that must begin and end with forward slashes.\n In addition, it can contain any ASCII character from the ! (\\u0021) through the DEL character (\\u007F), including \n most punctuation characters, digits, and upper and lowercased letters.

" } }, "NewServerCertificateName": { "target": "com.amazonaws.iam#serverCertificateNameType", "traits": { - "smithy.api#documentation": "

The new name for the server certificate. Include this only if you are updating the\n server certificate's name. The name of the certificate cannot contain any spaces.

\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 new name for the server certificate. Include this only if you are updating the\n server certificate's name. The name of the certificate cannot contain any spaces.

\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: _+=,.@-

" } } + }, + "traits": { + "smithy.api#input": {} } }, "com.amazonaws.iam#UpdateServiceSpecificCredential": { @@ -13962,13 +14564,13 @@ "UserName": { "target": "com.amazonaws.iam#userNameType", "traits": { - "smithy.api#documentation": "

The name of the IAM user associated with the service-specific credential. If you do\n not specify this value, then the operation assumes the user whose credentials are used\n to call the operation.

\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 IAM user associated with the service-specific credential. If you do\n not specify this value, then the operation assumes the user whose credentials are used\n to call the operation.

\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: _+=,.@-

" } }, "ServiceSpecificCredentialId": { "target": "com.amazonaws.iam#serviceSpecificCredentialId", "traits": { - "smithy.api#documentation": "

The unique identifier of the service-specific credential.

\n

This parameter allows (through its regex pattern) a string of characters that can \n consist of any upper or lowercased letter or digit.

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

The unique identifier of the service-specific credential.

\n

This parameter allows (through its regex pattern) a string of characters that can \n consist of any upper or lowercased letter or digit.

", "smithy.api#required": {} } }, @@ -13979,6 +14581,9 @@ "smithy.api#required": {} } } + }, + "traits": { + "smithy.api#input": {} } }, "com.amazonaws.iam#UpdateSigningCertificate": { @@ -14001,7 +14606,7 @@ } ], "traits": { - "smithy.api#documentation": "

Changes the status of the specified user signing certificate from active to disabled,\n or vice versa. This operation can be used to disable an IAM user's signing certificate\n as part of a certificate rotation work flow.

\n

If the UserName field is not specified, the user name is determined\n implicitly based on the Amazon Web Services access key ID used to sign the request. This operation\n works for access keys under the Amazon Web Services account. Consequently, you can use this operation\n to manage Amazon Web Services account root user credentials even if the Amazon Web Services account has no\n associated users.

" + "smithy.api#documentation": "

Changes the status of the specified user signing certificate from active to disabled,\n or vice versa. This operation can be used to disable an IAM user's signing\n certificate as part of a certificate rotation work flow.

\n

If the UserName field is not specified, the user name is determined\n implicitly based on the Amazon Web Services access key ID used to sign the request. This operation\n works for access keys under the Amazon Web Services account. Consequently, you can use this operation\n to manage Amazon Web Services account root user credentials even if the Amazon Web Services account has no associated\n users.

" } }, "com.amazonaws.iam#UpdateSigningCertificateRequest": { @@ -14010,13 +14615,13 @@ "UserName": { "target": "com.amazonaws.iam#existingUserNameType", "traits": { - "smithy.api#documentation": "

The name of the IAM user the signing certificate belongs to.

\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 IAM user the signing certificate belongs to.

\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: _+=,.@-

" } }, "CertificateId": { "target": "com.amazonaws.iam#certificateIdType", "traits": { - "smithy.api#documentation": "

The ID of the signing certificate you want to update.

\n

This parameter allows (through its regex pattern) a string of characters that can \n consist of any upper or lowercased letter or digit.

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

The ID of the signing certificate you want to update.

\n

This parameter allows (through its regex pattern) a string of characters that can \n consist of any upper or lowercased letter or digit.

", "smithy.api#required": {} } }, @@ -14027,6 +14632,9 @@ "smithy.api#required": {} } } + }, + "traits": { + "smithy.api#input": {} } }, "com.amazonaws.iam#UpdateUser": { @@ -14058,7 +14666,7 @@ } ], "traits": { - "smithy.api#documentation": "

Updates the name and/or the path of the specified IAM user.

\n \n

You should understand the implications of changing an IAM user's path or name.\n For more information, see Renaming an IAM\n user and Renaming an IAM\n group in the IAM User Guide.

\n
\n \n

To change a user name, the requester must have appropriate permissions on both\n the source object and the target object. For example, to change Bob to Robert, the\n entity making the request must have permission on Bob and Robert, or must have\n permission on all (*). For more information about permissions, see Permissions and policies.

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

Updates the name and/or the path of the specified IAM user.

\n \n

You should understand the implications of changing an IAM user's path or\n name. For more information, see Renaming an IAM\n user and Renaming an IAM\n group in the IAM User Guide.

\n
\n \n

To change a user name, the requester must have appropriate permissions on both\n the source object and the target object. For example, to change Bob to Robert, the\n entity making the request must have permission on Bob and Robert, or must have\n permission on all (*). For more information about permissions, see Permissions and policies.

\n
" } }, "com.amazonaws.iam#UpdateUserRequest": { @@ -14067,22 +14675,25 @@ "UserName": { "target": "com.amazonaws.iam#existingUserNameType", "traits": { - "smithy.api#documentation": "

Name of the user to update. If you're changing the name of the user, this is the\n original user name.

\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": "

Name of the user to update. If you're changing the name of the user, this is the\n original user name.

\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": {} } }, "NewPath": { "target": "com.amazonaws.iam#pathType", "traits": { - "smithy.api#documentation": "

New path for the IAM user. Include this parameter only if you're changing the user's\n path.

\n

This parameter allows (through its regex pattern) a string of characters consisting \n of either a forward slash (/) by itself or a string that must begin and end with forward slashes.\n In addition, it can contain any ASCII character from the ! (\\u0021) through the DEL character (\\u007F), including \n most punctuation characters, digits, and upper and lowercased letters.

" + "smithy.api#documentation": "

New path for the IAM user. Include this parameter only if you're changing the user's\n path.

\n

This parameter allows (through its regex pattern) a string of characters consisting \n of either a forward slash (/) by itself or a string that must begin and end with forward slashes.\n In addition, it can contain any ASCII character from the ! (\\u0021) through the DEL character (\\u007F), including \n most punctuation characters, digits, and upper and lowercased letters.

" } }, "NewUserName": { "target": "com.amazonaws.iam#userNameType", "traits": { - "smithy.api#documentation": "

New name for the user. Include this parameter only if you're changing the user's\n name.

\n

IAM user, group, role, and policy names must be unique within the account. Names are\n not distinguished by case. For example, you cannot create resources named both\n \"MyResource\" and \"myresource\".

" + "smithy.api#documentation": "

New name for the user. Include this parameter only if you're changing the user's\n name.

\n

IAM user, group, role, and policy names must be unique within the account. Names are\n not distinguished by case. For example, you cannot create resources named both\n \"MyResource\" and \"myresource\".

" } } + }, + "traits": { + "smithy.api#input": {} } }, "com.amazonaws.iam#UploadSSHPublicKey": { @@ -14111,7 +14722,7 @@ } ], "traits": { - "smithy.api#documentation": "

Uploads an SSH public key and associates it with the specified IAM user.

\n

The SSH public key uploaded by this operation can be used only for authenticating the\n associated IAM user to an CodeCommit repository. For more information about using SSH keys\n to authenticate to an CodeCommit repository, see Set up CodeCommit for\n SSH connections in the CodeCommit User Guide.

" + "smithy.api#documentation": "

Uploads an SSH public key and associates it with the specified IAM user.

\n

The SSH public key uploaded by this operation can be used only for authenticating the\n associated IAM user to an CodeCommit repository. For more information about using SSH keys\n to authenticate to an CodeCommit repository, see Set up CodeCommit for\n SSH connections in the CodeCommit User Guide.

" } }, "com.amazonaws.iam#UploadSSHPublicKeyRequest": { @@ -14120,17 +14731,20 @@ "UserName": { "target": "com.amazonaws.iam#userNameType", "traits": { - "smithy.api#documentation": "

The name of the IAM user to associate the SSH public key with.

\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 IAM user to associate the SSH public key with.

\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": {} } }, "SSHPublicKeyBody": { "target": "com.amazonaws.iam#publicKeyMaterialType", "traits": { - "smithy.api#documentation": "

The SSH public key. The public key must be encoded in ssh-rsa format or PEM format.\n The minimum bit-length of the public key is 2048 bits. For example, you can generate a\n 2048-bit key, and the resulting PEM file is 1679 bytes long.

\n

The regex pattern \n used to validate this parameter is a string of characters consisting of the following:

\n
    \n
  • \n

    Any printable ASCII \n character ranging from the space character (\\u0020) through the end of the ASCII character range

    \n
  • \n
  • \n

    The printable characters in the Basic Latin and Latin-1 Supplement character set \n (through \\u00FF)

    \n
  • \n
  • \n

    The special characters tab (\\u0009), line feed (\\u000A), and \n carriage return (\\u000D)

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

The SSH public key. The public key must be encoded in ssh-rsa format or PEM format.\n The minimum bit-length of the public key is 2048 bits. For example, you can generate a\n 2048-bit key, and the resulting PEM file is 1679 bytes long.

\n

The regex pattern \n used to validate this parameter is a string of characters consisting of the following:

\n
    \n
  • \n

    Any printable ASCII \n character ranging from the space character (\\u0020) through the end of the ASCII character range

    \n
  • \n
  • \n

    The printable characters in the Basic Latin and Latin-1 Supplement character set \n (through \\u00FF)

    \n
  • \n
  • \n

    The special characters tab (\\u0009), line feed (\\u000A), and \n carriage return (\\u000D)

    \n
  • \n
", "smithy.api#required": {} } } + }, + "traits": { + "smithy.api#input": {} } }, "com.amazonaws.iam#UploadSSHPublicKeyResponse": { @@ -14144,7 +14758,8 @@ } }, "traits": { - "smithy.api#documentation": "

Contains the response to a successful UploadSSHPublicKey\n request.

" + "smithy.api#documentation": "

Contains the response to a successful UploadSSHPublicKey\n request.

", + "smithy.api#output": {} } }, "com.amazonaws.iam#UploadServerCertificate": { @@ -14179,7 +14794,7 @@ } ], "traits": { - "smithy.api#documentation": "

Uploads a server certificate entity for the Amazon Web Services account. The server certificate\n entity includes a public key certificate, a private key, and an optional certificate\n chain, which should all be PEM-encoded.

\n

We recommend that you use Certificate Manager to\n provision, manage, and deploy your server certificates. With ACM you can request a\n certificate, deploy it to Amazon Web Services resources, and let ACM handle certificate renewals for\n you. Certificates provided by ACM are free. For more information about using ACM,\n see the Certificate Manager User\n Guide.

\n

For more information about working with server certificates, see Working\n with server certificates in the IAM User Guide. This\n topic includes a list of Amazon Web Services services that can use the server certificates that you\n manage with IAM.

\n

For information about the number of server certificates you can upload, see IAM and STS\n quotas in the IAM User Guide.

\n \n

Because the body of the public key certificate, private key, and the certificate\n chain can be large, you should use POST rather than GET when calling\n UploadServerCertificate. For information about setting up\n signatures and authorization through the API, see Signing Amazon Web Services API\n requests in the Amazon Web Services General Reference. For general\n information about using the Query API with IAM, see Calling the API by making HTTP query\n requests in the IAM User Guide.

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

Uploads a server certificate entity for the Amazon Web Services account. The server certificate\n entity includes a public key certificate, a private key, and an optional certificate\n chain, which should all be PEM-encoded.

\n

We recommend that you use Certificate Manager to\n provision, manage, and deploy your server certificates. With ACM you can request a\n certificate, deploy it to Amazon Web Services resources, and let ACM handle certificate renewals for\n you. Certificates provided by ACM are free. For more information about using ACM,\n see the Certificate Manager User\n Guide.

\n

For more information about working with server certificates, see Working\n with server certificates in the IAM User Guide. This\n topic includes a list of Amazon Web Services services that can use the server certificates that you\n manage with IAM.

\n

For information about the number of server certificates you can upload, see IAM and STS\n quotas in the IAM User Guide.

\n \n

Because the body of the public key certificate, private key, and the certificate\n chain can be large, you should use POST rather than GET when calling\n UploadServerCertificate. For information about setting up\n signatures and authorization through the API, see Signing Amazon Web Services API\n requests in the Amazon Web Services General Reference. For general\n information about using the Query API with IAM, see Calling the API by making HTTP query\n requests in the IAM User Guide.

\n
" } }, "com.amazonaws.iam#UploadServerCertificateRequest": { @@ -14188,34 +14803,34 @@ "Path": { "target": "com.amazonaws.iam#pathType", "traits": { - "smithy.api#documentation": "

The path for the server certificate. For more information about paths, see IAM\n identifiers in the IAM User Guide.

\n

This parameter is optional. If it is not included, it defaults to a slash (/).\n This parameter allows (through its regex pattern) a string of characters consisting \n of either a forward slash (/) by itself or a string that must begin and end with forward slashes.\n In addition, it can contain any ASCII character from the ! (\\u0021) through the DEL character (\\u007F), including \n most punctuation characters, digits, and upper and lowercased letters.

\n \n

If you are uploading a server certificate specifically for use with Amazon\n CloudFront distributions, you must specify a path using the path\n parameter. The path must begin with /cloudfront and must include a\n trailing slash (for example, /cloudfront/test/).

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

The path for the server certificate. For more information about paths, see IAM\n identifiers in the IAM User Guide.

\n

This parameter is optional. If it is not included, it defaults to a slash (/).\n This parameter allows (through its regex pattern) a string of characters consisting \n of either a forward slash (/) by itself or a string that must begin and end with forward slashes.\n In addition, it can contain any ASCII character from the ! (\\u0021) through the DEL character (\\u007F), including \n most punctuation characters, digits, and upper and lowercased letters.

\n \n

If you are uploading a server certificate specifically for use with Amazon\n CloudFront distributions, you must specify a path using the path\n parameter. The path must begin with /cloudfront and must include a\n trailing slash (for example, /cloudfront/test/).

\n
" } }, "ServerCertificateName": { "target": "com.amazonaws.iam#serverCertificateNameType", "traits": { - "smithy.api#documentation": "

The name for the server certificate. Do not include the path in this value. The name\n of the certificate cannot contain any spaces.

\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 for the server certificate. Do not include the path in this value. The name\n of the certificate cannot contain any spaces.

\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": {} } }, "CertificateBody": { "target": "com.amazonaws.iam#certificateBodyType", "traits": { - "smithy.api#documentation": "

The contents of the public key certificate in PEM-encoded format.

\n

The regex pattern \n used to validate this parameter is a string of characters consisting of the following:

\n
    \n
  • \n

    Any printable ASCII \n character ranging from the space character (\\u0020) through the end of the ASCII character range

    \n
  • \n
  • \n

    The printable characters in the Basic Latin and Latin-1 Supplement character set \n (through \\u00FF)

    \n
  • \n
  • \n

    The special characters tab (\\u0009), line feed (\\u000A), and \n carriage return (\\u000D)

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

The contents of the public key certificate in PEM-encoded format.

\n

The regex pattern \n used to validate this parameter is a string of characters consisting of the following:

\n
    \n
  • \n

    Any printable ASCII \n character ranging from the space character (\\u0020) through the end of the ASCII character range

    \n
  • \n
  • \n

    The printable characters in the Basic Latin and Latin-1 Supplement character set \n (through \\u00FF)

    \n
  • \n
  • \n

    The special characters tab (\\u0009), line feed (\\u000A), and \n carriage return (\\u000D)

    \n
  • \n
", "smithy.api#required": {} } }, "PrivateKey": { "target": "com.amazonaws.iam#privateKeyType", "traits": { - "smithy.api#documentation": "

The contents of the private key in PEM-encoded format.

\n

The regex pattern \n used to validate this parameter is a string of characters consisting of the following:

\n
    \n
  • \n

    Any printable ASCII \n character ranging from the space character (\\u0020) through the end of the ASCII character range

    \n
  • \n
  • \n

    The printable characters in the Basic Latin and Latin-1 Supplement character set \n (through \\u00FF)

    \n
  • \n
  • \n

    The special characters tab (\\u0009), line feed (\\u000A), and \n carriage return (\\u000D)

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

The contents of the private key in PEM-encoded format.

\n

The regex pattern \n used to validate this parameter is a string of characters consisting of the following:

\n
    \n
  • \n

    Any printable ASCII \n character ranging from the space character (\\u0020) through the end of the ASCII character range

    \n
  • \n
  • \n

    The printable characters in the Basic Latin and Latin-1 Supplement character set \n (through \\u00FF)

    \n
  • \n
  • \n

    The special characters tab (\\u0009), line feed (\\u000A), and \n carriage return (\\u000D)

    \n
  • \n
", "smithy.api#required": {} } }, "CertificateChain": { "target": "com.amazonaws.iam#certificateChainType", "traits": { - "smithy.api#documentation": "

The contents of the certificate chain. This is typically a concatenation of the\n PEM-encoded public key certificates of the chain.

\n

The regex pattern \n used to validate this parameter is a string of characters consisting of the following:

\n
    \n
  • \n

    Any printable ASCII \n character ranging from the space character (\\u0020) through the end of the ASCII character range

    \n
  • \n
  • \n

    The printable characters in the Basic Latin and Latin-1 Supplement character set \n (through \\u00FF)

    \n
  • \n
  • \n

    The special characters tab (\\u0009), line feed (\\u000A), and \n carriage return (\\u000D)

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

The contents of the certificate chain. This is typically a concatenation of the\n PEM-encoded public key certificates of the chain.

\n

The regex pattern \n used to validate this parameter is a string of characters consisting of the following:

\n
    \n
  • \n

    Any printable ASCII \n character ranging from the space character (\\u0020) through the end of the ASCII character range

    \n
  • \n
  • \n

    The printable characters in the Basic Latin and Latin-1 Supplement character set \n (through \\u00FF)

    \n
  • \n
  • \n

    The special characters tab (\\u0009), line feed (\\u000A), and \n carriage return (\\u000D)

    \n
  • \n
" } }, "Tags": { @@ -14224,6 +14839,9 @@ "smithy.api#documentation": "

A list of tags that you want to attach to the new IAM server certificate resource.\n Each tag consists of a key name and an associated value. For more information about tagging, see Tagging IAM resources in the\n IAM User Guide.

\n \n

If any one of the tags is invalid or if you exceed the allowed maximum number of tags, then the entire request \n fails and the resource is not created.

\n
" } } + }, + "traits": { + "smithy.api#input": {} } }, "com.amazonaws.iam#UploadServerCertificateResponse": { @@ -14243,7 +14861,8 @@ } }, "traits": { - "smithy.api#documentation": "

Contains the response to a successful UploadServerCertificate\n request.

" + "smithy.api#documentation": "

Contains the response to a successful UploadServerCertificate\n request.

", + "smithy.api#output": {} } }, "com.amazonaws.iam#UploadSigningCertificate": { @@ -14278,7 +14897,7 @@ } ], "traits": { - "smithy.api#documentation": "

Uploads an X.509 signing certificate and associates it with the specified IAM user.\n Some Amazon Web Services services require you to use certificates to validate requests that are signed\n with a corresponding private key. When you upload the certificate, its default status is\n Active.

\n

For information about when you would use an X.509 signing certificate, see Managing\n server certificates in IAM in the\n IAM User Guide.

\n

If the UserName is not specified, the IAM user name is determined\n implicitly based on the Amazon Web Services access key ID used to sign the request. This operation\n works for access keys under the Amazon Web Services account. Consequently, you can use this operation\n to manage Amazon Web Services account root user credentials even if the Amazon Web Services account has no\n associated users.

\n \n

Because the body of an X.509 certificate can be large, you should use POST rather\n than GET when calling UploadSigningCertificate. For information about\n setting up signatures and authorization through the API, see Signing\n Amazon Web Services API requests in the Amazon Web Services General Reference. For\n general information about using the Query API with IAM, see Making query\n requests in the IAM User Guide.

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

Uploads an X.509 signing certificate and associates it with the specified IAM user.\n Some Amazon Web Services services require you to use certificates to validate requests that are signed\n with a corresponding private key. When you upload the certificate, its default status is\n Active.

\n

For information about when you would use an X.509 signing certificate, see Managing\n server certificates in IAM in the\n IAM User Guide.

\n

If the UserName is not specified, the IAM user name is determined\n implicitly based on the Amazon Web Services access key ID used to sign the request. This operation\n works for access keys under the Amazon Web Services account. Consequently, you can use this operation\n to manage Amazon Web Services account root user credentials even if the Amazon Web Services account has no associated\n users.

\n \n

Because the body of an X.509 certificate can be large, you should use POST rather\n than GET when calling UploadSigningCertificate. For information about\n setting up signatures and authorization through the API, see Signing\n Amazon Web Services API requests in the Amazon Web Services General Reference. For\n general information about using the Query API with IAM, see Making query\n requests in the IAM User Guide.

\n
" } }, "com.amazonaws.iam#UploadSigningCertificateRequest": { @@ -14287,16 +14906,19 @@ "UserName": { "target": "com.amazonaws.iam#existingUserNameType", "traits": { - "smithy.api#documentation": "

The name of the user the signing certificate is for.

\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 user the signing certificate is for.

\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: _+=,.@-

" } }, "CertificateBody": { "target": "com.amazonaws.iam#certificateBodyType", "traits": { - "smithy.api#documentation": "

The contents of the signing certificate.

\n

The regex pattern \n used to validate this parameter is a string of characters consisting of the following:

\n
    \n
  • \n

    Any printable ASCII \n character ranging from the space character (\\u0020) through the end of the ASCII character range

    \n
  • \n
  • \n

    The printable characters in the Basic Latin and Latin-1 Supplement character set \n (through \\u00FF)

    \n
  • \n
  • \n

    The special characters tab (\\u0009), line feed (\\u000A), and \n carriage return (\\u000D)

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

The contents of the signing certificate.

\n

The regex pattern \n used to validate this parameter is a string of characters consisting of the following:

\n
    \n
  • \n

    Any printable ASCII \n character ranging from the space character (\\u0020) through the end of the ASCII character range

    \n
  • \n
  • \n

    The printable characters in the Basic Latin and Latin-1 Supplement character set \n (through \\u00FF)

    \n
  • \n
  • \n

    The special characters tab (\\u0009), line feed (\\u000A), and \n carriage return (\\u000D)

    \n
  • \n
", "smithy.api#required": {} } } + }, + "traits": { + "smithy.api#input": {} } }, "com.amazonaws.iam#UploadSigningCertificateResponse": { @@ -14311,7 +14933,8 @@ } }, "traits": { - "smithy.api#documentation": "

Contains the response to a successful UploadSigningCertificate\n request.

" + "smithy.api#documentation": "

Contains the response to a successful UploadSigningCertificate\n request.

", + "smithy.api#output": {} } }, "com.amazonaws.iam#User": { diff --git a/aws/sdk/aws-models/kms.json b/aws/sdk/aws-models/kms.json index d4ce662672..cd4fd4edd6 100644 --- a/aws/sdk/aws-models/kms.json +++ b/aws/sdk/aws-models/kms.json @@ -176,10 +176,13 @@ "KeyId": { "target": "com.amazonaws.kms#KeyIdType", "traits": { - "smithy.api#documentation": "

Identifies the KMS key whose deletion is being canceled.

\n \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": "

Identifies the KMS key whose deletion is being canceled.

\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": {} } } + }, + "traits": { + "smithy.api#input": {} } }, "com.amazonaws.kms#CancelKeyDeletionResponse": { @@ -191,6 +194,9 @@ "smithy.api#documentation": "

The Amazon Resource Name (key ARN) of the KMS key whose deletion is canceled.

" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.kms#CiphertextType": { @@ -240,7 +246,7 @@ "code": "CloudHsmClusterInvalidConfigurationException", "httpResponseCode": 400 }, - "smithy.api#documentation": "

The request was rejected because the associated CloudHSM cluster did not meet the\n configuration requirements for an CloudHSM key store.

\n\n
    \n
  • \n

    The CloudHSM cluster must be configured with private subnets in at least two different\n Availability Zones in the Region.

    \n
  • \n
  • \n

    The security group for\n the cluster (cloudhsm-cluster--sg) must\n include inbound rules and outbound rules that allow TCP traffic on ports 2223-2225. The\n Source in the inbound rules and the Destination in the outbound rules must match the security group\n ID. These rules are set by default when you create the CloudHSM cluster. Do not delete or\n change them. To get information about a particular security group, use the DescribeSecurityGroups operation.

    \n
  • \n
  • \n

    The CloudHSM cluster must contain at least as many HSMs as the operation requires. To add\n HSMs, use the CloudHSM CreateHsm operation.

    \n

    For the CreateCustomKeyStore, UpdateCustomKeyStore, and CreateKey operations, the CloudHSM cluster must have at least two\n active HSMs, each in a different Availability Zone. For the ConnectCustomKeyStore operation, the CloudHSM must contain at least one active\n HSM.

    \n
  • \n
\n

For information about the requirements for an CloudHSM cluster that is associated with an\n CloudHSM key store, see Assemble the Prerequisites\n in the Key Management Service Developer Guide. For information about creating a private subnet for an CloudHSM cluster,\n see Create a Private\n Subnet in the CloudHSM User Guide. For information about cluster security groups, see\n Configure a Default Security\n Group in the \n CloudHSM User Guide\n .

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

The request was rejected because the associated CloudHSM cluster did not meet the\n configuration requirements for an CloudHSM key store.

\n
    \n
  • \n

    The CloudHSM cluster must be configured with private subnets in at least two different\n Availability Zones in the Region.

    \n
  • \n
  • \n

    The security group for\n the cluster (cloudhsm-cluster--sg) must\n include inbound rules and outbound rules that allow TCP traffic on ports 2223-2225. The\n Source in the inbound rules and the Destination in the outbound rules must match the security group\n ID. These rules are set by default when you create the CloudHSM cluster. Do not delete or\n change them. To get information about a particular security group, use the DescribeSecurityGroups operation.

    \n
  • \n
  • \n

    The CloudHSM cluster must contain at least as many HSMs as the operation requires. To add\n HSMs, use the CloudHSM CreateHsm operation.

    \n

    For the CreateCustomKeyStore, UpdateCustomKeyStore, and CreateKey operations, the CloudHSM cluster must have at least two\n active HSMs, each in a different Availability Zone. For the ConnectCustomKeyStore operation, the CloudHSM must contain at least one active\n HSM.

    \n
  • \n
\n

For information about the requirements for an CloudHSM cluster that is associated with an\n CloudHSM key store, see Assemble the Prerequisites\n in the Key Management Service Developer Guide. For information about creating a private subnet for an CloudHSM cluster,\n see Create a Private\n Subnet in the CloudHSM User Guide. For information about cluster security groups, see\n Configure a Default Security\n Group in the \n CloudHSM User Guide\n .

", "smithy.api#error": "client", "smithy.api#httpError": 400 } @@ -322,7 +328,7 @@ } ], "traits": { - "smithy.api#documentation": "

Connects or reconnects a custom key store to its backing key store. For an CloudHSM key\n store, ConnectCustomKeyStore connects the key store to its associated CloudHSM\n cluster. For an external key store, ConnectCustomKeyStore connects the key store\n to the external key store proxy that communicates with your external key manager.

\n

The custom key store must be connected before you can create KMS keys in the key store or\n use the KMS keys it contains. You can disconnect and reconnect a custom key store at any\n time.

\n

The connection process for a custom key store can take an extended amount of time to\n complete. This operation starts the connection process, but it does not wait for it to\n complete. When it succeeds, this operation quickly returns an HTTP 200 response and a JSON\n object with no properties. However, this response does not indicate that the custom key store\n is connected. To get the connection state of the custom key store, use the DescribeCustomKeyStores operation.

\n

This operation is part of the custom key stores feature in KMS, which\ncombines the convenience and extensive integration of KMS with the isolation and control of a\nkey store that you own and manage.

\n

The ConnectCustomKeyStore operation might fail for various reasons. To find\n the reason, use the DescribeCustomKeyStores operation and see the\n ConnectionErrorCode in the response. For help interpreting the\n ConnectionErrorCode, see CustomKeyStoresListEntry.

\n

To fix the failure, use the DisconnectCustomKeyStore operation to\n disconnect the custom key store, correct the error, use the UpdateCustomKeyStore operation if necessary, and then use\n ConnectCustomKeyStore again.

\n

\n CloudHSM key store\n

\n

During the connection process for an CloudHSM key store, KMS finds the CloudHSM cluster that\n is associated with the custom key store, creates the connection infrastructure, connects to\n the cluster, logs into the CloudHSM client as the kmsuser CU, and rotates its\n password.

\n

To connect an CloudHSM key store, its associated CloudHSM cluster must have at least one active\n HSM. To get the number of active HSMs in a cluster, use the DescribeClusters operation. To add HSMs\n to the cluster, use the CreateHsm operation. Also, the \n kmsuser crypto\n user (CU) must not be logged into the cluster. This prevents KMS from using this\n account to log in.

\n

If you are having trouble connecting or disconnecting a CloudHSM key store, see Troubleshooting an CloudHSM key\n store in the Key Management Service Developer Guide.

\n

\n External key store\n

\n

When you connect an external key store that uses public endpoint connectivity, KMS tests\n its ability to communicate with your external key manager by sending a request via the\n external key store proxy.

\n

When you connect to an external key store that uses VPC endpoint service connectivity,\n KMS establishes the networking elements that it needs to communicate with your external key\n manager via the external key store proxy. This includes creating an interface endpoint to the\n VPC endpoint service and a private hosted zone for traffic between KMS and the VPC endpoint\n service.

\n

To connect an external key store, KMS must be able to connect to the external key store\n proxy, the external key store proxy must be able to communicate with your external key\n manager, and the external key manager must be available for cryptographic operations.

\n

If you are having trouble connecting or disconnecting an external key store, see Troubleshooting an external\n key store in the Key Management Service Developer Guide.

\n

\n Cross-account use: No. You cannot perform this operation on a custom key store in a different Amazon Web Services account.

\n\n

\n Required permissions: kms:ConnectCustomKeyStore (IAM policy)

\n

\n Related operations\n

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

Connects or reconnects a custom key store to its backing key store. For an CloudHSM key\n store, ConnectCustomKeyStore connects the key store to its associated CloudHSM\n cluster. For an external key store, ConnectCustomKeyStore connects the key store\n to the external key store proxy that communicates with your external key manager.

\n

The custom key store must be connected before you can create KMS keys in the key store or\n use the KMS keys it contains. You can disconnect and reconnect a custom key store at any\n time.

\n

The connection process for a custom key store can take an extended amount of time to\n complete. This operation starts the connection process, but it does not wait for it to\n complete. When it succeeds, this operation quickly returns an HTTP 200 response and a JSON\n object with no properties. However, this response does not indicate that the custom key store\n is connected. To get the connection state of the custom key store, use the DescribeCustomKeyStores operation.

\n

This operation is part of the custom key stores feature in KMS, which\ncombines the convenience and extensive integration of KMS with the isolation and control of a\nkey store that you own and manage.

\n

The ConnectCustomKeyStore operation might fail for various reasons. To find\n the reason, use the DescribeCustomKeyStores operation and see the\n ConnectionErrorCode in the response. For help interpreting the\n ConnectionErrorCode, see CustomKeyStoresListEntry.

\n

To fix the failure, use the DisconnectCustomKeyStore operation to\n disconnect the custom key store, correct the error, use the UpdateCustomKeyStore operation if necessary, and then use\n ConnectCustomKeyStore again.

\n

\n CloudHSM key store\n

\n

During the connection process for an CloudHSM key store, KMS finds the CloudHSM cluster that\n is associated with the custom key store, creates the connection infrastructure, connects to\n the cluster, logs into the CloudHSM client as the kmsuser CU, and rotates its\n password.

\n

To connect an CloudHSM key store, its associated CloudHSM cluster must have at least one active\n HSM. To get the number of active HSMs in a cluster, use the DescribeClusters operation. To add HSMs\n to the cluster, use the CreateHsm operation. Also, the \n kmsuser crypto\n user (CU) must not be logged into the cluster. This prevents KMS from using this\n account to log in.

\n

If you are having trouble connecting or disconnecting a CloudHSM key store, see Troubleshooting an CloudHSM key\n store in the Key Management Service Developer Guide.

\n

\n External key store\n

\n

When you connect an external key store that uses public endpoint connectivity, KMS tests\n its ability to communicate with your external key manager by sending a request via the\n external key store proxy.

\n

When you connect to an external key store that uses VPC endpoint service connectivity,\n KMS establishes the networking elements that it needs to communicate with your external key\n manager via the external key store proxy. This includes creating an interface endpoint to the\n VPC endpoint service and a private hosted zone for traffic between KMS and the VPC endpoint\n service.

\n

To connect an external key store, KMS must be able to connect to the external key store\n proxy, the external key store proxy must be able to communicate with your external key\n manager, and the external key manager must be available for cryptographic operations.

\n

If you are having trouble connecting or disconnecting an external key store, see Troubleshooting an external\n key store in the Key Management Service Developer Guide.

\n

\n Cross-account use: No. You cannot perform this operation on a custom key store in a different Amazon Web Services account.

\n

\n Required permissions: kms:ConnectCustomKeyStore (IAM policy)

\n

\n Related operations\n

\n " } }, "com.amazonaws.kms#ConnectCustomKeyStoreRequest": { @@ -335,11 +341,17 @@ "smithy.api#required": {} } } + }, + "traits": { + "smithy.api#input": {} } }, "com.amazonaws.kms#ConnectCustomKeyStoreResponse": { "type": "structure", - "members": {} + "members": {}, + "traits": { + "smithy.api#output": {} + } }, "com.amazonaws.kms#ConnectionErrorCodeType": { "type": "enum", @@ -521,7 +533,7 @@ } ], "traits": { - "smithy.api#documentation": "

Creates a friendly name for a KMS key.

\n \n

Adding, deleting, or updating an alias can allow or deny permission to the KMS key. For details, see ABAC for KMS in the Key Management Service Developer Guide.

\n
\n

You can use an alias to identify a KMS key in the KMS console, in the DescribeKey operation and in cryptographic operations, such as Encrypt and\n GenerateDataKey. You can also change the KMS key that's associated with\n the alias (UpdateAlias) or delete the alias (DeleteAlias)\n at any time. These operations don't affect the underlying KMS key.

\n

You can associate the alias with any customer managed key in the same Amazon Web Services Region. Each\n alias is associated with only one KMS key at a time, but a KMS key can have multiple aliases.\n A valid KMS key is required. You can't create an alias without a KMS key.

\n

The alias must be unique in the account and Region, but you can have aliases with the same\n name in different Regions. For detailed information about aliases, see Using aliases in the\n Key Management Service Developer Guide.

\n

This operation does not return a response. To get the alias that you created, use the\n ListAliases operation.

\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 an alias in a different Amazon Web Services account.

\n\n

\n Required permissions\n

\n \n

For details, see Controlling access to aliases in the\n Key Management Service Developer Guide.

\n

\n Related operations:\n

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

Creates a friendly name for a KMS key.

\n \n

Adding, deleting, or updating an alias can allow or deny permission to the KMS key. For details, see ABAC for KMS in the Key Management Service Developer Guide.

\n
\n

You can use an alias to identify a KMS key in the KMS console, in the DescribeKey operation and in cryptographic operations, such as Encrypt and\n GenerateDataKey. You can also change the KMS key that's associated with\n the alias (UpdateAlias) or delete the alias (DeleteAlias)\n at any time. These operations don't affect the underlying KMS key.

\n

You can associate the alias with any customer managed key in the same Amazon Web Services Region. Each\n alias is associated with only one KMS key at a time, but a KMS key can have multiple aliases.\n A valid KMS key is required. You can't create an alias without a KMS key.

\n

The alias must be unique in the account and Region, but you can have aliases with the same\n name in different Regions. For detailed information about aliases, see Using aliases in the\n Key Management Service Developer Guide.

\n

This operation does not return a response. To get the alias that you created, use the\n ListAliases operation.

\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 an alias in a different Amazon Web Services account.

\n

\n Required permissions\n

\n \n

For details, see Controlling access to aliases in the\n Key Management Service Developer Guide.

\n

\n Related operations:\n

\n " } }, "com.amazonaws.kms#CreateAliasRequest": { @@ -537,10 +549,13 @@ "TargetKeyId": { "target": "com.amazonaws.kms#KeyIdType", "traits": { - "smithy.api#documentation": "

Associates the alias with the specified customer managed key. The KMS key must\n be in the same Amazon Web Services Region.

\n

A valid key ID is required. If you supply a null or empty string value, this operation\n returns an error.

\n

For help finding the key ID and ARN, see Finding the Key ID and\n ARN in the \n Key Management Service Developer Guide\n .

\n \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": "

Associates the alias with the specified customer managed key. The KMS key must\n be in the same Amazon Web Services Region.

\n

A valid key ID is required. If you supply a null or empty string value, this operation\n returns an error.

\n

For help finding the key ID and ARN, see Finding the Key ID and\n ARN in the \n Key Management Service Developer Guide\n .

\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": {} } } + }, + "traits": { + "smithy.api#input": {} } }, "com.amazonaws.kms#CreateCustomKeyStore": { @@ -605,7 +620,7 @@ } ], "traits": { - "smithy.api#documentation": "

Creates a custom key store backed by a key store that you own and manage. When you use a\n KMS key in a custom key store for a cryptographic operation, the cryptographic operation is\n actually performed in your key store using your keys. KMS supports CloudHSM key stores\n backed by an CloudHSM cluster\n and external key stores backed by an external key store proxy and\n external key manager outside of Amazon Web Services.

\n

This operation is part of the custom key stores feature in KMS, which\ncombines the convenience and extensive integration of KMS with the isolation and control of a\nkey store that you own and manage.

\n

Before you create the custom key store, the required elements must be in place and\n operational. We recommend that you use the test tools that KMS provides to verify the\n configuration your external key store proxy. For details about the required elements and\n verification tests, see Assemble the prerequisites (for\n CloudHSM key stores) or Assemble the prerequisites (for\n external key stores) in the Key Management Service Developer Guide.

\n

To create a custom key store, use the following parameters.

\n
    \n
  • \n

    To create an CloudHSM key store, specify the CustomKeyStoreName,\n CloudHsmClusterId, KeyStorePassword, and\n TrustAnchorCertificate. The CustomKeyStoreType parameter is\n optional for CloudHSM key stores. If you include it, set it to the default value,\n AWS_CLOUDHSM. For help with failures, see Troubleshooting an CloudHSM key store in the\n Key Management Service Developer Guide.

    \n
  • \n
  • \n

    To create an external key store, specify the CustomKeyStoreName and a\n CustomKeyStoreType of EXTERNAL_KEY_STORE. Also, specify values\n for XksProxyConnectivity, XksProxyAuthenticationCredential,\n XksProxyUriEndpoint, and XksProxyUriPath. If your\n XksProxyConnectivity value is VPC_ENDPOINT_SERVICE, specify\n the XksProxyVpcEndpointServiceName parameter. For help with failures, see\n Troubleshooting\n an external key store in the Key Management Service Developer Guide.

    \n
  • \n
\n \n

For external key stores:

\n

Some external key managers provide a simpler method for creating an external key store.\n For details, see your external key manager documentation.

\n

When creating an external key store in the KMS console, you can upload a JSON-based\n proxy configuration file with the desired values. You cannot use a proxy configuration\n with the CreateCustomKeyStore operation. However, you can use the values in\n the file to help you determine the correct values for the CreateCustomKeyStore\n parameters.

\n
\n

When the operation completes successfully, it returns the ID of the new custom key store.\n Before you can use your new custom key store, you need to use the ConnectCustomKeyStore operation to connect a new CloudHSM key store to its CloudHSM\n cluster, or to connect a new external key store to the external key store proxy for your\n external key manager. Even if you are not going to use your custom key store immediately, you\n might want to connect it to verify that all settings are correct and then disconnect it until\n you are ready to use it.

\n

For help with failures, see Troubleshooting a custom key store in the\n Key Management Service Developer Guide.

\n

\n Cross-account use: No. You cannot perform this operation on a custom key store in a different Amazon Web Services account.

\n

\n Required permissions: kms:CreateCustomKeyStore (IAM policy).

\n

\n Related operations:\n

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

Creates a custom key store backed by a key store that you own and manage. When you use a\n KMS key in a custom key store for a cryptographic operation, the cryptographic operation is\n actually performed in your key store using your keys. KMS supports CloudHSM key stores\n backed by an CloudHSM cluster\n and external key stores backed by an external key store proxy and\n external key manager outside of Amazon Web Services.

\n

This operation is part of the custom key stores feature in KMS, which\ncombines the convenience and extensive integration of KMS with the isolation and control of a\nkey store that you own and manage.

\n

Before you create the custom key store, the required elements must be in place and\n operational. We recommend that you use the test tools that KMS provides to verify the\n configuration your external key store proxy. For details about the required elements and\n verification tests, see Assemble the prerequisites (for\n CloudHSM key stores) or Assemble the prerequisites (for\n external key stores) in the Key Management Service Developer Guide.

\n

To create a custom key store, use the following parameters.

\n
    \n
  • \n

    To create an CloudHSM key store, specify the CustomKeyStoreName,\n CloudHsmClusterId, KeyStorePassword, and\n TrustAnchorCertificate. The CustomKeyStoreType parameter is\n optional for CloudHSM key stores. If you include it, set it to the default value,\n AWS_CLOUDHSM. For help with failures, see Troubleshooting an CloudHSM key store in the\n Key Management Service Developer Guide.

    \n
  • \n
  • \n

    To create an external key store, specify the CustomKeyStoreName and a\n CustomKeyStoreType of EXTERNAL_KEY_STORE. Also, specify values\n for XksProxyConnectivity, XksProxyAuthenticationCredential,\n XksProxyUriEndpoint, and XksProxyUriPath. If your\n XksProxyConnectivity value is VPC_ENDPOINT_SERVICE, specify\n the XksProxyVpcEndpointServiceName parameter. For help with failures, see\n Troubleshooting\n an external key store in the Key Management Service Developer Guide.

    \n
  • \n
\n \n

For external key stores:

\n

Some external key managers provide a simpler method for creating an external key store.\n For details, see your external key manager documentation.

\n

When creating an external key store in the KMS console, you can upload a JSON-based\n proxy configuration file with the desired values. You cannot use a proxy configuration\n with the CreateCustomKeyStore operation. However, you can use the values in\n the file to help you determine the correct values for the CreateCustomKeyStore\n parameters.

\n
\n

When the operation completes successfully, it returns the ID of the new custom key store.\n Before you can use your new custom key store, you need to use the ConnectCustomKeyStore operation to connect a new CloudHSM key store to its CloudHSM\n cluster, or to connect a new external key store to the external key store proxy for your\n external key manager. Even if you are not going to use your custom key store immediately, you\n might want to connect it to verify that all settings are correct and then disconnect it until\n you are ready to use it.

\n

For help with failures, see Troubleshooting a custom key store in the\n Key Management Service Developer Guide.

\n

\n Cross-account use: No. You cannot perform this operation on a custom key store in a different Amazon Web Services account.

\n

\n Required permissions: kms:CreateCustomKeyStore (IAM policy).

\n

\n Related operations:\n

\n " } }, "com.amazonaws.kms#CreateCustomKeyStoreRequest": { @@ -672,6 +687,9 @@ "smithy.api#documentation": "

Indicates how KMS communicates with the external key store proxy. This parameter is\n required for custom key stores with a CustomKeyStoreType of\n EXTERNAL_KEY_STORE.

\n

If the external key store proxy uses a public endpoint, specify\n PUBLIC_ENDPOINT. If the external key store proxy uses a Amazon VPC\n endpoint service for communication with KMS, specify VPC_ENDPOINT_SERVICE. For\n help making this choice, see Choosing a connectivity option in the Key Management Service Developer Guide.

\n

An Amazon VPC endpoint service keeps your communication with KMS in a private address space\n entirely within Amazon Web Services, but it requires more configuration, including establishing a Amazon VPC with multiple subnets, a VPC endpoint service, a network load balancer, and a\n verified private DNS name. A public endpoint is simpler to set up, but it might be slower and\n might not fulfill your security requirements. You might consider testing with a public\n endpoint, and then establishing a VPC endpoint service for production tasks. Note that this\n choice does not determine the location of the external key store proxy. Even if you choose a\n VPC endpoint service, the proxy can be hosted within the VPC or outside of Amazon Web Services such as in\n your corporate data center.

" } } + }, + "traits": { + "smithy.api#input": {} } }, "com.amazonaws.kms#CreateCustomKeyStoreResponse": { @@ -683,6 +701,9 @@ "smithy.api#documentation": "

A unique identifier for the new custom key store.

" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.kms#CreateGrant": { @@ -729,21 +750,21 @@ "KeyId": { "target": "com.amazonaws.kms#KeyIdType", "traits": { - "smithy.api#documentation": "

Identifies the KMS key for the grant. The grant gives principals permission to use this\n KMS key.

\n \n

Specify the key ID or key ARN of the KMS key. To specify a KMS key in a\ndifferent Amazon Web Services account, you must use the key ARN.

\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": "

Identifies the KMS key for the grant. The grant gives principals permission to use this\n KMS key.

\n

Specify the key ID or key ARN of the KMS key. To specify a KMS key in a\ndifferent Amazon Web Services account, you must use the key ARN.

\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": {} } }, "GranteePrincipal": { "target": "com.amazonaws.kms#PrincipalIdType", "traits": { - "smithy.api#documentation": "

The identity that gets the permissions specified in the grant.

\n

To specify the principal, use the Amazon Resource Name (ARN) of an\n Amazon Web Services principal. Valid Amazon Web Services principals include Amazon Web Services accounts (root), IAM users, IAM roles,\n federated users, and assumed role users. For examples of the ARN syntax to use for specifying\n a principal, see Amazon Web Services Identity and Access\n Management (IAM) in the Example ARNs section of the Amazon Web Services General\n Reference.

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

The identity that gets the permissions specified in the grant.

\n

To specify the grantee principal, use the Amazon Resource Name (ARN) of an\n Amazon Web Services principal. Valid principals include Amazon Web Services accounts, IAM users, IAM roles,\n federated users, and assumed role users. For help with the ARN syntax for a principal, see\n IAM ARNs in the \n Identity and Access Management User Guide\n .

", "smithy.api#required": {} } }, "RetiringPrincipal": { "target": "com.amazonaws.kms#PrincipalIdType", "traits": { - "smithy.api#documentation": "

The principal that has permission to use the RetireGrant operation to\n retire the grant.

\n

To specify the principal, use the Amazon Resource Name (ARN) of an\n Amazon Web Services principal. Valid Amazon Web Services principals include Amazon Web Services accounts (root), IAM users, federated\n users, and assumed role users. For examples of the ARN syntax to use for specifying a\n principal, see Amazon Web Services Identity and Access\n Management (IAM) in the Example ARNs section of the Amazon Web Services General\n Reference.

\n

The grant determines the retiring principal. Other principals might have permission to\n retire the grant or revoke the grant. For details, see RevokeGrant and\n Retiring and\n revoking grants in the Key Management Service Developer Guide.

" + "smithy.api#documentation": "

The principal that has permission to use the RetireGrant operation to\n retire the grant.

\n

To specify the principal, use the Amazon Resource Name (ARN) of an\n Amazon Web Services principal. Valid principals include Amazon Web Services accounts, IAM users, IAM roles,\n federated users, and assumed role users. For help with the ARN syntax for a principal, see\n IAM ARNs in the \n Identity and Access Management User Guide\n .

\n

The grant determines the retiring principal. Other principals might have permission to\n retire the grant or revoke the grant. For details, see RevokeGrant and\n Retiring and\n revoking grants in the Key Management Service Developer Guide.

" } }, "Operations": { @@ -771,6 +792,9 @@ "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.

" } } + }, + "traits": { + "smithy.api#input": {} } }, "com.amazonaws.kms#CreateGrantResponse": { @@ -788,6 +812,9 @@ "smithy.api#documentation": "

The unique identifier for the grant.

\n

You can use the GrantId in a ListGrants, RetireGrant, or RevokeGrant operation.

" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.kms#CreateKey": { @@ -840,7 +867,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\n\n

To create different types of KMS keys, use the following guidance:

\n\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

\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

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 " } }, "com.amazonaws.kms#CreateKeyRequest": { @@ -849,7 +876,7 @@ "Policy": { "target": "com.amazonaws.kms#PolicyType", "traits": { - "smithy.api#documentation": "

The key policy to attach to the KMS key.

\n

If you provide a key policy, it must meet the following criteria:

\n
    \n
  • \n

    If you don't set BypassPolicyLockoutSafetyCheck to true, the key policy\n must allow the principal that is making the CreateKey request to make a\n subsequent PutKeyPolicy request on the KMS key. This reduces the risk\n that the KMS key becomes unmanageable. For more information, refer to the scenario in the\n Default Key Policy section of the \n Key Management Service Developer Guide\n .

    \n
  • \n
  • \n

    Each statement in the key policy must contain one or more principals. The principals\n in the key policy must exist and be visible to KMS. When you create a new Amazon Web Services\n principal (for example, an IAM user or role), you might need to enforce a delay before\n including the new principal in a key policy because the new principal might not be\n immediately visible to KMS. For more information, see Changes that I make are not always immediately visible in the Amazon Web Services\n Identity and Access Management User Guide.

    \n
  • \n
\n

If you do not provide a key policy, KMS attaches a default key policy to the KMS key.\n For more information, see Default Key Policy in the\n Key Management Service Developer Guide.

\n

The key policy size quota is 32 kilobytes (32768 bytes).

\n

For help writing and formatting a JSON policy document, see the IAM JSON Policy Reference in the \n Identity and Access Management User Guide\n .

" + "smithy.api#documentation": "

The key policy to attach to the KMS key.

\n

If you provide a key policy, it must meet the following criteria:

\n
    \n
  • \n

    The key policy must allow the calling principal to make a\n subsequent PutKeyPolicy request on the KMS key. This reduces the risk that\n the KMS key becomes unmanageable. For more information, see Default key policy in the Key Management Service Developer Guide. (To omit\n this condition, set BypassPolicyLockoutSafetyCheck to true.)

    \n
  • \n
  • \n

    Each statement in the key policy must contain one or more principals. The principals\n in the key policy must exist and be visible to KMS. When you create a new Amazon Web Services\n principal, you might need to enforce a delay before including the new principal in a key\n policy because the new principal might not be immediately visible to KMS. For more\n information, see Changes that I make are not always immediately visible in the Amazon Web Services\n Identity and Access Management User Guide.

    \n
  • \n
\n

If you do not provide a key policy, KMS attaches a default key policy to the KMS key.\n For more information, see Default key policy in the\n Key Management Service Developer Guide.

\n

The key policy size quota is 32 kilobytes (32768 bytes).

\n

For help writing and formatting a JSON policy document, see the IAM JSON Policy Reference in the \n Identity and Access Management User Guide\n .

" } }, "Description": { @@ -876,7 +903,7 @@ "KeySpec": { "target": "com.amazonaws.kms#KeySpec", "traits": { - "smithy.api#documentation": "

Specifies the type of KMS key to create. The default value,\n SYMMETRIC_DEFAULT, creates a KMS key with a 256-bit AES-GCM key that is used for encryption and decryption, except in China Regions, \n where it creates a 128-bit symmetric key that uses SM4 encryption. For help choosing a key spec for your KMS key, see Choosing a KMS key type in the \n Key Management Service Developer Guide\n .

\n

The KeySpec determines whether the KMS key contains a symmetric key or an\n asymmetric key pair. It also determines the algorithms that the KMS key supports. You can't\n change the KeySpec after the KMS key is created. To further restrict the\n algorithms that can be used with the KMS key, use a condition key in its key policy or IAM\n policy. For more information, see kms:EncryptionAlgorithm, kms:MacAlgorithm or kms:Signing Algorithm in the \n Key Management Service Developer Guide\n .

\n \n

\n Amazon Web Services services that\n are integrated with KMS use symmetric encryption KMS keys to protect your data.\n These services do not support asymmetric KMS keys or HMAC KMS keys.

\n
\n

KMS supports the following key specs for KMS keys:

\n
    \n
  • \n

    Symmetric encryption key (default)

    \n
      \n
    • \n

      \n SYMMETRIC_DEFAULT\n

      \n
    • \n
    \n
  • \n
  • \n

    HMAC keys (symmetric)

    \n
      \n
    • \n

      \n HMAC_224\n

      \n
    • \n
    • \n

      \n HMAC_256\n

      \n
    • \n
    • \n

      \n HMAC_384\n

      \n
    • \n
    • \n

      \n HMAC_512\n

      \n
    • \n
    \n
  • \n
  • \n

    Asymmetric RSA key pairs

    \n
      \n
    • \n

      \n RSA_2048\n

      \n
    • \n
    • \n

      \n RSA_3072\n

      \n
    • \n
    • \n

      \n RSA_4096\n

      \n
    • \n
    \n
  • \n
  • \n

    Asymmetric NIST-recommended elliptic curve key pairs

    \n
      \n
    • \n

      \n ECC_NIST_P256 (secp256r1)

      \n
    • \n
    • \n

      \n ECC_NIST_P384 (secp384r1)

      \n
    • \n
    • \n

      \n ECC_NIST_P521 (secp521r1)

      \n
    • \n
    \n
  • \n
  • \n

    Other asymmetric elliptic curve key pairs

    \n
      \n
    • \n

      \n ECC_SECG_P256K1 (secp256k1), commonly used for\n cryptocurrencies.

      \n
    • \n
    \n
  • \n
  • \n

    SM2 key pairs (China Regions only)

    \n
      \n
    • \n

      \n SM2\n

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

Specifies the type of KMS key to create. The default value,\n SYMMETRIC_DEFAULT, creates a KMS key with a 256-bit AES-GCM key that is used for encryption and decryption, except in China Regions, \n where it creates a 128-bit symmetric key that uses SM4 encryption. For help choosing a key spec for your KMS key, see Choosing a KMS key type in the \n Key Management Service Developer Guide\n .

\n

The KeySpec determines whether the KMS key contains a symmetric key or an\n asymmetric key pair. It also determines the algorithms that the KMS key supports. You can't\n change the KeySpec after the KMS key is created. To further restrict the\n algorithms that can be used with the KMS key, use a condition key in its key policy or IAM\n policy. For more information, see kms:EncryptionAlgorithm, kms:MacAlgorithm or kms:Signing Algorithm in the \n Key Management Service Developer Guide\n .

\n \n

\n Amazon Web Services services that\n are integrated with KMS use symmetric encryption KMS keys to protect your data.\n These services do not support asymmetric KMS keys or HMAC KMS keys.

\n
\n

KMS supports the following key specs for KMS keys:

\n
    \n
  • \n

    Symmetric encryption key (default)

    \n
      \n
    • \n

      \n SYMMETRIC_DEFAULT\n

      \n
    • \n
    \n
  • \n
  • \n

    HMAC keys (symmetric)

    \n
      \n
    • \n

      \n HMAC_224\n

      \n
    • \n
    • \n

      \n HMAC_256\n

      \n
    • \n
    • \n

      \n HMAC_384\n

      \n
    • \n
    • \n

      \n HMAC_512\n

      \n
    • \n
    \n
  • \n
  • \n

    Asymmetric RSA key pairs

    \n
      \n
    • \n

      \n RSA_2048\n

      \n
    • \n
    • \n

      \n RSA_3072\n

      \n
    • \n
    • \n

      \n RSA_4096\n

      \n
    • \n
    \n
  • \n
  • \n

    Asymmetric NIST-recommended elliptic curve key pairs

    \n
      \n
    • \n

      \n ECC_NIST_P256 (secp256r1)

      \n
    • \n
    • \n

      \n ECC_NIST_P384 (secp384r1)

      \n
    • \n
    • \n

      \n ECC_NIST_P521 (secp521r1)

      \n
    • \n
    \n
  • \n
  • \n

    Other asymmetric elliptic curve key pairs

    \n
      \n
    • \n

      \n ECC_SECG_P256K1 (secp256k1), commonly used for\n cryptocurrencies.

      \n
    • \n
    \n
  • \n
  • \n

    SM2 key pairs (China Regions only)

    \n
      \n
    • \n

      \n SM2\n

      \n
    • \n
    \n
  • \n
" } }, "Origin": { @@ -895,7 +922,7 @@ "target": "com.amazonaws.kms#BooleanType", "traits": { "smithy.api#default": false, - "smithy.api#documentation": "

A flag to indicate whether to bypass the key policy lockout safety check.

\n \n

Setting this value to true increases the risk that the KMS key becomes unmanageable. Do\n not set this value to true indiscriminately.

\n

For more information, refer to the scenario in the Default Key Policy section in the \n Key Management Service Developer Guide\n .

\n
\n

Use this parameter only when you include a policy in the request and you intend to prevent\n the principal that is making the request from making a subsequent PutKeyPolicy request on the KMS key.

\n

The default value is false.

" + "smithy.api#documentation": "

Skips (\"bypasses\") the key policy lockout safety check. The default value is false.

\n \n

Setting this value to true increases the risk that the KMS key becomes unmanageable. Do\n not set this value to true indiscriminately.

\n

For more information, see Default key policy in the Key Management Service Developer Guide.

\n
\n

Use this parameter only when you intend to prevent the principal that is making the\n request from making a subsequent PutKeyPolicy request on the KMS key.

" } }, "Tags": { @@ -916,6 +943,9 @@ "smithy.api#documentation": "

Identifies the external key that\n serves as key material for the KMS key in an external key store. Specify the ID that\n the external key store proxy uses to refer to the external key. For help, see the\n documentation for your external key store proxy.

\n

This parameter is required for a KMS key with an Origin value of\n EXTERNAL_KEY_STORE. It is not valid for KMS keys with any other\n Origin value.

\n

The external key must be an existing 256-bit AES symmetric encryption key hosted outside\n of Amazon Web Services in an external key manager associated with the external key store specified by the\n CustomKeyStoreId parameter. This key must be enabled and configured to perform\n encryption and decryption. Each KMS key in an external key store must use a different external\n key. For details, see Requirements for a KMS key in an external\n key store in the Key Management Service Developer Guide.

\n

Each KMS key in an external key store is associated two backing keys. One is key material\n that KMS generates. The other is the external key specified by this parameter. When you use\n the KMS key in an external key store to encrypt data, the encryption operation is performed\n first by KMS using the KMS key material, and then by the external key manager using the\n specified external key, a process known as double encryption. For\n details, see Double\n encryption in the Key Management Service Developer Guide.

" } } + }, + "traits": { + "smithy.api#input": {} } }, "com.amazonaws.kms#CreateKeyResponse": { @@ -927,6 +957,9 @@ "smithy.api#documentation": "

Metadata associated with the KMS key.

" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.kms#CustomKeyStoreHasCMKsException": { @@ -1074,7 +1107,7 @@ "ConnectionErrorCode": { "target": "com.amazonaws.kms#ConnectionErrorCodeType", "traits": { - "smithy.api#documentation": "

Describes the connection error. This field appears in the response only when the\n ConnectionState is FAILED.

\n

Many failures can be resolved by updating the properties of the custom key store. To\n update a custom key store, disconnect it (DisconnectCustomKeyStore), correct\n the errors (UpdateCustomKeyStore), and try to connect again (ConnectCustomKeyStore). For additional help resolving these errors, see How to Fix a\n Connection Failure in Key Management Service Developer Guide.

\n

\n All custom key stores:\n

\n
    \n
  • \n

    \n INTERNAL_ERROR — KMS could not complete the request due to an\n internal error. Retry the request. For ConnectCustomKeyStore requests,\n disconnect the custom key store before trying to connect again.

    \n
  • \n
  • \n

    \n NETWORK_ERRORS — Network errors are preventing KMS from\n connecting the custom key store to its backing key store.

    \n
  • \n
\n\n

\n CloudHSM key stores:\n

\n
    \n
  • \n

    \n CLUSTER_NOT_FOUND — KMS cannot find the CloudHSM cluster with the\n specified cluster ID.

    \n
  • \n
  • \n

    \n INSUFFICIENT_CLOUDHSM_HSMS — The associated CloudHSM cluster does not\n contain any active HSMs. To connect a custom key store to its CloudHSM cluster, the cluster\n must contain at least one active HSM.

    \n
  • \n
  • \n

    \n INSUFFICIENT_FREE_ADDRESSES_IN_SUBNET — At least one private subnet\n associated with the CloudHSM cluster doesn't have any available IP addresses. A CloudHSM key\n store connection requires one free IP address in each of the associated private subnets,\n although two are preferable. For details, see How to Fix a Connection\n Failure in the Key Management Service Developer Guide.

    \n
  • \n
  • \n

    \n INVALID_CREDENTIALS — The KeyStorePassword for the\n custom key store doesn't match the current password of the kmsuser crypto\n user in the CloudHSM cluster. Before you can connect your custom key store to its CloudHSM\n cluster, you must change the kmsuser account password and update the\n KeyStorePassword value for the custom key store.

    \n
  • \n
  • \n

    \n SUBNET_NOT_FOUND — A subnet in the CloudHSM cluster configuration was\n deleted. If KMS cannot find all of the subnets in the cluster configuration, attempts to\n connect the custom key store to the CloudHSM cluster fail. To fix this error, create a\n cluster from a recent backup and associate it with your custom key store. (This process\n creates a new cluster configuration with a VPC and private subnets.) For details, see\n How\n to Fix a Connection Failure in the Key Management Service Developer Guide.

    \n
  • \n
  • \n

    \n USER_LOCKED_OUT — The kmsuser CU account is locked\n out of the associated CloudHSM cluster due to too many failed password attempts. Before you\n can connect your custom key store to its CloudHSM cluster, you must change the\n kmsuser account password and update the key store password value for the\n custom key store.

    \n
  • \n
  • \n

    \n USER_LOGGED_IN — The kmsuser CU account is logged\n into the associated CloudHSM cluster. This prevents KMS from rotating the\n kmsuser account password and logging into the cluster. Before you can\n connect your custom key store to its CloudHSM cluster, you must log the kmsuser\n CU out of the cluster. If you changed the kmsuser password to log into the\n cluster, you must also and update the key store password value for the custom key store.\n For help, see How to Log Out and\n Reconnect in the Key Management Service Developer Guide.

    \n
  • \n
  • \n

    \n USER_NOT_FOUND — KMS cannot find a kmsuser CU\n account in the associated CloudHSM cluster. Before you can connect your custom key store to\n its CloudHSM cluster, you must create a kmsuser CU account in the cluster, and\n then update the key store password value for the custom key store.

    \n
  • \n
\n\n

\n External key stores:\n

\n
    \n
  • \n

    \n INVALID_CREDENTIALS — One or both of the\n XksProxyAuthenticationCredential values is not valid on the specified\n external key store proxy.

    \n
  • \n
  • \n

    \n XKS_PROXY_ACCESS_DENIED — KMS requests are denied access to the\n external key store proxy. If the external key store proxy has authorization rules, verify\n that they permit KMS to communicate with the proxy on your behalf.

    \n
  • \n
  • \n

    \n XKS_PROXY_INVALID_CONFIGURATION — A configuration error is\n preventing the external key store from connecting to its proxy. Verify the value of the\n XksProxyUriPath.

    \n
  • \n
  • \n

    \n XKS_PROXY_INVALID_RESPONSE — KMS cannot interpret the response\n from the external key store proxy. If you see this connection error code repeatedly,\n notify your external key store proxy vendor.

    \n
  • \n
  • \n

    \n XKS_PROXY_INVALID_TLS_CONFIGURATION — KMS cannot connect to the\n external key store proxy because the TLS configuration is invalid. Verify that the XKS\n proxy supports TLS 1.2 or 1.3. Also, verify that the TLS certificate is not expired, and\n that it matches the hostname in the XksProxyUriEndpoint value, and that it is\n signed by a certificate authority included in the Trusted Certificate Authorities\n list.

    \n
  • \n
  • \n

    \n XKS_PROXY_NOT_REACHABLE — KMS can't communicate with your\n external key store proxy. Verify that the XksProxyUriEndpoint and\n XksProxyUriPath are correct. Use the tools for your external key store\n proxy to verify that the proxy is active and available on its network. Also, verify that\n your external key manager instances are operating properly. Connection attempts fail with\n this connection error code if the proxy reports that all external key manager instances\n are unavailable.

    \n
  • \n
  • \n

    \n XKS_PROXY_TIMED_OUT — KMS can connect to the external key store\n proxy, but the proxy does not respond to KMS in the time allotted. If you see this\n connection error code repeatedly, notify your external key store proxy vendor.

    \n
  • \n
  • \n

    \n XKS_VPC_ENDPOINT_SERVICE_INVALID_CONFIGURATION — The Amazon VPC\n endpoint service configuration doesn't conform to the requirements for an KMS external\n key store.

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

      The VPC endpoint service must be an endpoint service for interface endpoints in the caller's Amazon Web Services account.

      \n
    • \n
    • \n

      It must have a network load balancer (NLB) connected to at least two subnets, each in a different Availability Zone.

      \n
    • \n
    • \n

      The Allow principals list must include \n\t the KMS service principal for the Region, cks.kms..amazonaws.com, \n\t such as cks.kms.us-east-1.amazonaws.com.

      \n
    • \n
    • \n

      It must not require acceptance of connection requests.

      \n
    • \n
    • \n

      It must have a private DNS name. The private DNS name for an external key store with VPC_ENDPOINT_SERVICE connectivity\n\t must be unique in its Amazon Web Services Region.

      \n
    • \n
    • \n

      The domain of the private DNS name must have a verification status of\n\t verified.

      \n
    • \n
    • \n

      The TLS certificate specifies the private DNS hostname at which the endpoint is reachable.

      \n
    • \n
    \n
  • \n
  • \n

    \n XKS_VPC_ENDPOINT_SERVICE_NOT_FOUND — KMS can't find the VPC\n endpoint service that it uses to communicate with the external key store proxy. Verify\n that the XksProxyVpcEndpointServiceName is correct and the KMS service\n principal has service consumer permissions on the Amazon VPC endpoint service.

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

Describes the connection error. This field appears in the response only when the\n ConnectionState is FAILED.

\n

Many failures can be resolved by updating the properties of the custom key store. To\n update a custom key store, disconnect it (DisconnectCustomKeyStore), correct\n the errors (UpdateCustomKeyStore), and try to connect again (ConnectCustomKeyStore). For additional help resolving these errors, see How to Fix a\n Connection Failure in Key Management Service Developer Guide.

\n

\n All custom key stores:\n

\n
    \n
  • \n

    \n INTERNAL_ERROR — KMS could not complete the request due to an\n internal error. Retry the request. For ConnectCustomKeyStore requests,\n disconnect the custom key store before trying to connect again.

    \n
  • \n
  • \n

    \n NETWORK_ERRORS — Network errors are preventing KMS from\n connecting the custom key store to its backing key store.

    \n
  • \n
\n

\n CloudHSM key stores:\n

\n
    \n
  • \n

    \n CLUSTER_NOT_FOUND — KMS cannot find the CloudHSM cluster with the\n specified cluster ID.

    \n
  • \n
  • \n

    \n INSUFFICIENT_CLOUDHSM_HSMS — The associated CloudHSM cluster does not\n contain any active HSMs. To connect a custom key store to its CloudHSM cluster, the cluster\n must contain at least one active HSM.

    \n
  • \n
  • \n

    \n INSUFFICIENT_FREE_ADDRESSES_IN_SUBNET — At least one private subnet\n associated with the CloudHSM cluster doesn't have any available IP addresses. A CloudHSM key\n store connection requires one free IP address in each of the associated private subnets,\n although two are preferable. For details, see How to Fix a Connection\n Failure in the Key Management Service Developer Guide.

    \n
  • \n
  • \n

    \n INVALID_CREDENTIALS — The KeyStorePassword for the\n custom key store doesn't match the current password of the kmsuser crypto\n user in the CloudHSM cluster. Before you can connect your custom key store to its CloudHSM\n cluster, you must change the kmsuser account password and update the\n KeyStorePassword value for the custom key store.

    \n
  • \n
  • \n

    \n SUBNET_NOT_FOUND — A subnet in the CloudHSM cluster configuration was\n deleted. If KMS cannot find all of the subnets in the cluster configuration, attempts to\n connect the custom key store to the CloudHSM cluster fail. To fix this error, create a\n cluster from a recent backup and associate it with your custom key store. (This process\n creates a new cluster configuration with a VPC and private subnets.) For details, see\n How\n to Fix a Connection Failure in the Key Management Service Developer Guide.

    \n
  • \n
  • \n

    \n USER_LOCKED_OUT — The kmsuser CU account is locked\n out of the associated CloudHSM cluster due to too many failed password attempts. Before you\n can connect your custom key store to its CloudHSM cluster, you must change the\n kmsuser account password and update the key store password value for the\n custom key store.

    \n
  • \n
  • \n

    \n USER_LOGGED_IN — The kmsuser CU account is logged\n into the associated CloudHSM cluster. This prevents KMS from rotating the\n kmsuser account password and logging into the cluster. Before you can\n connect your custom key store to its CloudHSM cluster, you must log the kmsuser\n CU out of the cluster. If you changed the kmsuser password to log into the\n cluster, you must also and update the key store password value for the custom key store.\n For help, see How to Log Out and\n Reconnect in the Key Management Service Developer Guide.

    \n
  • \n
  • \n

    \n USER_NOT_FOUND — KMS cannot find a kmsuser CU\n account in the associated CloudHSM cluster. Before you can connect your custom key store to\n its CloudHSM cluster, you must create a kmsuser CU account in the cluster, and\n then update the key store password value for the custom key store.

    \n
  • \n
\n

\n External key stores:\n

\n
    \n
  • \n

    \n INVALID_CREDENTIALS — One or both of the\n XksProxyAuthenticationCredential values is not valid on the specified\n external key store proxy.

    \n
  • \n
  • \n

    \n XKS_PROXY_ACCESS_DENIED — KMS requests are denied access to the\n external key store proxy. If the external key store proxy has authorization rules, verify\n that they permit KMS to communicate with the proxy on your behalf.

    \n
  • \n
  • \n

    \n XKS_PROXY_INVALID_CONFIGURATION — A configuration error is\n preventing the external key store from connecting to its proxy. Verify the value of the\n XksProxyUriPath.

    \n
  • \n
  • \n

    \n XKS_PROXY_INVALID_RESPONSE — KMS cannot interpret the response\n from the external key store proxy. If you see this connection error code repeatedly,\n notify your external key store proxy vendor.

    \n
  • \n
  • \n

    \n XKS_PROXY_INVALID_TLS_CONFIGURATION — KMS cannot connect to the\n external key store proxy because the TLS configuration is invalid. Verify that the XKS\n proxy supports TLS 1.2 or 1.3. Also, verify that the TLS certificate is not expired, and\n that it matches the hostname in the XksProxyUriEndpoint value, and that it is\n signed by a certificate authority included in the Trusted Certificate Authorities\n list.

    \n
  • \n
  • \n

    \n XKS_PROXY_NOT_REACHABLE — KMS can't communicate with your\n external key store proxy. Verify that the XksProxyUriEndpoint and\n XksProxyUriPath are correct. Use the tools for your external key store\n proxy to verify that the proxy is active and available on its network. Also, verify that\n your external key manager instances are operating properly. Connection attempts fail with\n this connection error code if the proxy reports that all external key manager instances\n are unavailable.

    \n
  • \n
  • \n

    \n XKS_PROXY_TIMED_OUT — KMS can connect to the external key store\n proxy, but the proxy does not respond to KMS in the time allotted. If you see this\n connection error code repeatedly, notify your external key store proxy vendor.

    \n
  • \n
  • \n

    \n XKS_VPC_ENDPOINT_SERVICE_INVALID_CONFIGURATION — The Amazon VPC\n endpoint service configuration doesn't conform to the requirements for an KMS external\n key store.

    \n
      \n
    • \n

      The VPC endpoint service must be an endpoint service for interface endpoints in the caller's Amazon Web Services account.

      \n
    • \n
    • \n

      It must have a network load balancer (NLB) connected to at least two subnets, each in a different Availability Zone.

      \n
    • \n
    • \n

      The Allow principals list must include \n\t the KMS service principal for the Region, cks.kms..amazonaws.com, \n\t such as cks.kms.us-east-1.amazonaws.com.

      \n
    • \n
    • \n

      It must not require acceptance of connection requests.

      \n
    • \n
    • \n

      It must have a private DNS name. The private DNS name for an external key store with VPC_ENDPOINT_SERVICE connectivity\n\t must be unique in its Amazon Web Services Region.

      \n
    • \n
    • \n

      The domain of the private DNS name must have a verification status of\n\t verified.

      \n
    • \n
    • \n

      The TLS certificate specifies the private DNS hostname at which the endpoint is reachable.

      \n
    • \n
    \n
  • \n
  • \n

    \n XKS_VPC_ENDPOINT_SERVICE_NOT_FOUND — KMS can't find the VPC\n endpoint service that it uses to communicate with the external key store proxy. Verify\n that the XksProxyVpcEndpointServiceName is correct and the KMS service\n principal has service consumer permissions on the Amazon VPC endpoint service.

    \n
  • \n
" } }, "CreationDate": { @@ -1302,7 +1335,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 user 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. 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

\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

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 " } }, "com.amazonaws.kms#DecryptRequest": { @@ -1330,7 +1363,7 @@ "KeyId": { "target": "com.amazonaws.kms#KeyIdType", "traits": { - "smithy.api#documentation": "

Specifies the KMS key that KMS uses to decrypt the ciphertext.

\n\n

Enter a key ID of the KMS key that was used to encrypt the ciphertext. If you identify a\n different KMS key, the Decrypt operation throws an\n IncorrectKeyException.

\n\n

This parameter is required only when the ciphertext was encrypted under an asymmetric KMS\n key. If you used a symmetric encryption KMS key, KMS can get the KMS key from metadata that\n it adds to the symmetric ciphertext blob. However, it is always recommended as a best\n practice. This practice ensures that you use the KMS key that you intend.

\n \n

To specify a KMS key, use its key ID, key ARN, alias name, or alias ARN. When using an alias name, prefix it with \"alias/\". To specify a KMS key in a different Amazon Web Services account, you must use the key ARN or alias ARN.

\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

    Alias name: alias/ExampleAlias\n

    \n
  • \n
  • \n

    Alias ARN: arn:aws:kms:us-east-2:111122223333:alias/ExampleAlias\n

    \n
  • \n
\n

To get the key ID and key ARN for a KMS key, use ListKeys or DescribeKey. To get the alias name and alias ARN, use ListAliases.

" + "smithy.api#documentation": "

Specifies the KMS key that KMS uses to decrypt the ciphertext.

\n

Enter a key ID of the KMS key that was used to encrypt the ciphertext. If you identify a\n different KMS key, the Decrypt operation throws an\n IncorrectKeyException.

\n

This parameter is required only when the ciphertext was encrypted under an asymmetric KMS\n key. If you used a symmetric encryption KMS key, KMS can get the KMS key from metadata that\n it adds to the symmetric ciphertext blob. However, it is always recommended as a best\n practice. This practice ensures that you use the KMS key that you intend.

\n

To specify a KMS key, use its key ID, key ARN, alias name, or alias ARN. When using an alias name, prefix it with \"alias/\". To specify a KMS key in a different Amazon Web Services account, you must use the key ARN or alias ARN.

\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

    Alias name: alias/ExampleAlias\n

    \n
  • \n
  • \n

    Alias ARN: arn:aws:kms:us-east-2:111122223333:alias/ExampleAlias\n

    \n
  • \n
\n

To get the key ID and key ARN for a KMS key, use ListKeys or DescribeKey. To get the alias name and alias ARN, use ListAliases.

" } }, "EncryptionAlgorithm": { @@ -1339,6 +1372,9 @@ "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.

" } } + }, + "traits": { + "smithy.api#input": {} } }, "com.amazonaws.kms#DecryptResponse": { @@ -1362,6 +1398,9 @@ "smithy.api#documentation": "

The encryption algorithm that was used to decrypt the ciphertext.

" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.kms#DeleteAlias": { @@ -1400,6 +1439,9 @@ "smithy.api#required": {} } } + }, + "traits": { + "smithy.api#input": {} } }, "com.amazonaws.kms#DeleteCustomKeyStore": { @@ -1425,7 +1467,7 @@ } ], "traits": { - "smithy.api#documentation": "

Deletes a custom key store. This operation does not affect any backing elements of the\n custom key store. It does not delete the CloudHSM cluster that is associated with an CloudHSM key\n store, or affect any users or keys in the cluster. For an external key store, it does not\n affect the external key store proxy, external key manager, or any external keys.

\n

This operation is part of the custom key stores feature in KMS, which\ncombines the convenience and extensive integration of KMS with the isolation and control of a\nkey store that you own and manage.

\n

The custom key store that you delete cannot contain any KMS keys. Before deleting the key store,\n verify that you will never need to use any of the KMS keys in the key store for any\n cryptographic operations. Then, use ScheduleKeyDeletion to delete the KMS keys from the\n key store. After the required waiting period expires and all KMS keys are deleted from the\n custom key store, use DisconnectCustomKeyStore to disconnect the key store\n from KMS. Then, you can delete the custom key store.

\n

For keys in an CloudHSM key store, the ScheduleKeyDeletion operation makes a\n best effort to delete the key material from the associated cluster. However, you might need to\n manually delete the orphaned key\n material from the cluster and its backups. KMS never creates, manages, or deletes\n cryptographic keys in the external key manager associated with an external key store. You must\n manage them using your external key manager tools.

\n

Instead of deleting the custom key store, consider using the DisconnectCustomKeyStore operation to disconnect the custom key store from its\n backing key store. While the key store is disconnected, you cannot create or use the KMS keys\n in the key store. But, you do not need to delete KMS keys and you can reconnect a disconnected\n custom key store at any time.

\n

If the operation succeeds, it returns a JSON object with no\nproperties.

\n

\n Cross-account use: No. You cannot perform this operation on a custom key store in a different Amazon Web Services account.

\n\n

\n Required permissions: kms:DeleteCustomKeyStore (IAM policy)

\n

\n Related operations:\n

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

Deletes a custom key store. This operation does not affect any backing elements of the\n custom key store. It does not delete the CloudHSM cluster that is associated with an CloudHSM key\n store, or affect any users or keys in the cluster. For an external key store, it does not\n affect the external key store proxy, external key manager, or any external keys.

\n

This operation is part of the custom key stores feature in KMS, which\ncombines the convenience and extensive integration of KMS with the isolation and control of a\nkey store that you own and manage.

\n

The custom key store that you delete cannot contain any KMS keys. Before deleting the key store,\n verify that you will never need to use any of the KMS keys in the key store for any\n cryptographic operations. Then, use ScheduleKeyDeletion to delete the KMS keys from the\n key store. After the required waiting period expires and all KMS keys are deleted from the\n custom key store, use DisconnectCustomKeyStore to disconnect the key store\n from KMS. Then, you can delete the custom key store.

\n

For keys in an CloudHSM key store, the ScheduleKeyDeletion operation makes a\n best effort to delete the key material from the associated cluster. However, you might need to\n manually delete the orphaned key\n material from the cluster and its backups. KMS never creates, manages, or deletes\n cryptographic keys in the external key manager associated with an external key store. You must\n manage them using your external key manager tools.

\n

Instead of deleting the custom key store, consider using the DisconnectCustomKeyStore operation to disconnect the custom key store from its\n backing key store. While the key store is disconnected, you cannot create or use the KMS keys\n in the key store. But, you do not need to delete KMS keys and you can reconnect a disconnected\n custom key store at any time.

\n

If the operation succeeds, it returns a JSON object with no\nproperties.

\n

\n Cross-account use: No. You cannot perform this operation on a custom key store in a different Amazon Web Services account.

\n

\n Required permissions: kms:DeleteCustomKeyStore (IAM policy)

\n

\n Related operations:\n

\n " } }, "com.amazonaws.kms#DeleteCustomKeyStoreRequest": { @@ -1438,11 +1480,17 @@ "smithy.api#required": {} } } + }, + "traits": { + "smithy.api#input": {} } }, "com.amazonaws.kms#DeleteCustomKeyStoreResponse": { "type": "structure", - "members": {} + "members": {}, + "traits": { + "smithy.api#output": {} + } }, "com.amazonaws.kms#DeleteImportedKeyMaterial": { "type": "operation", @@ -1473,7 +1521,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

\n Required permissions: kms:DeleteImportedKeyMaterial (key policy)

\n

\n Related operations:\n

\n " + "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 " } }, "com.amazonaws.kms#DeleteImportedKeyMaterialRequest": { @@ -1482,10 +1530,13 @@ "KeyId": { "target": "com.amazonaws.kms#KeyIdType", "traits": { - "smithy.api#documentation": "

Identifies the KMS key from which you are deleting imported key material. The\n Origin of the KMS key must be EXTERNAL.

\n \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": "

Identifies the KMS key from which you are deleting imported key material. The\n 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#required": {} } } + }, + "traits": { + "smithy.api#input": {} } }, "com.amazonaws.kms#DependencyTimeoutException": { @@ -1561,6 +1612,9 @@ "smithy.api#documentation": "

Use this parameter in a subsequent request after you receive a response with\n truncated results. Set it to the value of NextMarker from the truncated response\n you just received.

" } } + }, + "traits": { + "smithy.api#input": {} } }, "com.amazonaws.kms#DescribeCustomKeyStoresResponse": { @@ -1585,6 +1639,9 @@ "smithy.api#documentation": "

A flag that indicates whether there are more items in the list. When this\n value is true, the list in this response is truncated. To get more items, pass the value of\n the NextMarker element in thisresponse to the Marker parameter in a\n subsequent request.

" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.kms#DescribeKey": { @@ -1610,7 +1667,7 @@ } ], "traits": { - "smithy.api#documentation": "

Provides detailed information about a KMS key. You can run DescribeKey on a\n customer managed\n key or an Amazon Web Services managed key.

\n

This detailed information includes the key ARN, creation date (and deletion date, if\n applicable), the key state, and the origin and expiration date (if any) of the key material.\n It includes fields, like KeySpec, that help you distinguish different types of\n KMS keys. It also displays the key usage (encryption, signing, or generating and verifying\n MACs) and the algorithms that the KMS key supports.

\n

For multi-Region keys,\n DescribeKey displays the primary key and all related replica keys. For KMS keys\n in CloudHSM key stores, it includes\n information about the key store, such as the key store ID and the CloudHSM cluster ID. For KMS\n keys in external key stores, it\n includes the custom key store ID and the ID of the external key.

\n

\n DescribeKey does not return the following information:

\n
    \n
  • \n

    Aliases associated with the KMS key. To get this information, use ListAliases.

    \n
  • \n
  • \n

    Whether automatic key rotation is enabled on the KMS key. To get this information, use\n GetKeyRotationStatus. Also, some key states prevent a KMS key from\n being automatically rotated. For details, see How Automatic Key Rotation\n Works in the Key Management Service Developer Guide.

    \n
  • \n
  • \n

    Tags on the KMS key. To get this information, use ListResourceTags.

    \n
  • \n
  • \n

    Key policies and grants on the KMS key. To get this information, use GetKeyPolicy and ListGrants.

    \n
  • \n
\n

In general, DescribeKey is a non-mutating operation. It returns data about\n KMS keys, but doesn't change them. However, Amazon Web Services services use DescribeKey to\n create Amazon Web Services\n managed keys from a predefined Amazon Web Services alias with no key\n ID.

\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

\n Required permissions: kms:DescribeKey (key policy)

\n

\n Related operations:\n

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

Provides detailed information about a KMS key. You can run DescribeKey on a\n customer managed\n key or an Amazon Web Services managed key.

\n

This detailed information includes the key ARN, creation date (and deletion date, if\n applicable), the key state, and the origin and expiration date (if any) of the key material.\n It includes fields, like KeySpec, that help you distinguish different types of\n KMS keys. It also displays the key usage (encryption, signing, or generating and verifying\n MACs) and the algorithms that the KMS key supports.

\n

For multi-Region keys,\n DescribeKey displays the primary key and all related replica keys. For KMS keys\n in CloudHSM key stores, it includes\n information about the key store, such as the key store ID and the CloudHSM cluster ID. For KMS\n keys in external key stores, it\n includes the custom key store ID and the ID of the external key.

\n

\n DescribeKey does not return the following information:

\n
    \n
  • \n

    Aliases associated with the KMS key. To get this information, use ListAliases.

    \n
  • \n
  • \n

    Whether automatic key rotation is enabled on the KMS key. To get this information, use\n GetKeyRotationStatus. Also, some key states prevent a KMS key from\n being automatically rotated. For details, see How Automatic Key Rotation\n Works in the Key Management Service Developer Guide.

    \n
  • \n
  • \n

    Tags on the KMS key. To get this information, use ListResourceTags.

    \n
  • \n
  • \n

    Key policies and grants on the KMS key. To get this information, use GetKeyPolicy and ListGrants.

    \n
  • \n
\n

In general, DescribeKey is a non-mutating operation. It returns data about\n KMS keys, but doesn't change them. However, Amazon Web Services services use DescribeKey to\n create Amazon Web Services\n managed keys from a predefined Amazon Web Services alias with no key\n ID.

\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:DescribeKey (key policy)

\n

\n Related operations:\n

\n " } }, "com.amazonaws.kms#DescribeKeyRequest": { @@ -1619,7 +1676,7 @@ "KeyId": { "target": "com.amazonaws.kms#KeyIdType", "traits": { - "smithy.api#documentation": "

Describes the specified KMS key.

\n

If you specify a predefined Amazon Web Services alias (an Amazon Web Services alias with no key ID), KMS associates\n the alias with an Amazon Web Services managed key and returns its\n KeyId and Arn in the response.

\n \n

To specify a KMS key, use its key ID, key ARN, alias name, or alias ARN. When using an alias name, prefix it with \"alias/\". To specify a KMS key in a different Amazon Web Services account, you must use the key ARN or alias ARN.

\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

    Alias name: alias/ExampleAlias\n

    \n
  • \n
  • \n

    Alias ARN: arn:aws:kms:us-east-2:111122223333:alias/ExampleAlias\n

    \n
  • \n
\n

To get the key ID and key ARN for a KMS key, use ListKeys or DescribeKey. To get the alias name and alias ARN, use ListAliases.

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

Describes the specified KMS key.

\n

If you specify a predefined Amazon Web Services alias (an Amazon Web Services alias with no key ID), KMS associates\n the alias with an Amazon Web Services managed key and returns its\n KeyId and Arn in the response.

\n

To specify a KMS key, use its key ID, key ARN, alias name, or alias ARN. When using an alias name, prefix it with \"alias/\". To specify a KMS key in a different Amazon Web Services account, you must use the key ARN or alias ARN.

\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

    Alias name: alias/ExampleAlias\n

    \n
  • \n
  • \n

    Alias ARN: arn:aws:kms:us-east-2:111122223333:alias/ExampleAlias\n

    \n
  • \n
\n

To get the key ID and key ARN for a KMS key, use ListKeys or DescribeKey. To get the alias name and alias ARN, use ListAliases.

", "smithy.api#required": {} } }, @@ -1629,6 +1686,9 @@ "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.

" } } + }, + "traits": { + "smithy.api#input": {} } }, "com.amazonaws.kms#DescribeKeyResponse": { @@ -1640,6 +1700,9 @@ "smithy.api#documentation": "

Metadata associated with the key.

" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.kms#DescriptionType": { @@ -1677,7 +1740,7 @@ } ], "traits": { - "smithy.api#documentation": "

Sets the state of a KMS key to disabled. This change temporarily prevents use of the KMS\n key for cryptographic operations.

\n

For more information about how key state affects the use of a KMS key, see\n Key states of KMS keys in the \n Key Management Service Developer Guide\n .

\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

\n Required permissions: kms:DisableKey (key policy)

\n

\n Related operations: EnableKey\n

" + "smithy.api#documentation": "

Sets the state of a KMS key to disabled. This change temporarily prevents use of the KMS\n key for cryptographic operations.

\n

For more information about how key state affects the use of a KMS key, see\n Key states of KMS keys in the \n Key Management Service Developer Guide\n .

\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:DisableKey (key policy)

\n

\n Related operations: EnableKey\n

" } }, "com.amazonaws.kms#DisableKeyRequest": { @@ -1686,10 +1749,13 @@ "KeyId": { "target": "com.amazonaws.kms#KeyIdType", "traits": { - "smithy.api#documentation": "

Identifies the KMS key to disable.

\n \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": "

Identifies the KMS key to disable.

\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": {} } } + }, + "traits": { + "smithy.api#input": {} } }, "com.amazonaws.kms#DisableKeyRotation": { @@ -1724,7 +1790,7 @@ } ], "traits": { - "smithy.api#documentation": "

Disables automatic\n rotation of the key material of the specified symmetric encryption KMS key.

\n

Automatic key rotation is supported only on symmetric encryption KMS keys.\n You cannot enable automatic rotation of asymmetric KMS keys, HMAC KMS keys, KMS keys with imported key material, or KMS keys in a custom key store. To enable or disable automatic rotation of a set of related multi-Region keys, set the property on the primary key.

\n

You can enable (EnableKeyRotation) and disable automatic rotation of the\n key material in customer managed KMS keys. Key material rotation of Amazon Web Services managed KMS keys is not\n configurable. KMS always rotates the key material for every year. Rotation of Amazon Web Services owned KMS\n keys varies.

\n \n

In May 2022, KMS changed the rotation schedule for Amazon Web Services managed keys from every three\n years to every year. For details, see EnableKeyRotation.

\n
\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

\n Required permissions: kms:DisableKeyRotation (key policy)

\n

\n Related operations:\n

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

Disables automatic\n rotation of the key material of the specified symmetric encryption KMS key.

\n

Automatic key rotation is supported only on symmetric encryption KMS keys.\n You cannot enable automatic rotation of asymmetric KMS keys, HMAC KMS keys, KMS keys with imported key material, or KMS keys in a custom key store. To enable or disable automatic rotation of a set of related multi-Region keys, set the property on the primary key.

\n

You can enable (EnableKeyRotation) and disable automatic rotation of the\n key material in customer managed KMS keys. Key material rotation of Amazon Web Services managed KMS keys is not\n configurable. KMS always rotates the key material for every year. Rotation of Amazon Web Services owned KMS\n keys varies.

\n \n

In May 2022, KMS changed the rotation schedule for Amazon Web Services managed keys from every three\n years to every year. For details, see EnableKeyRotation.

\n
\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:DisableKeyRotation (key policy)

\n

\n Related operations:\n

\n " } }, "com.amazonaws.kms#DisableKeyRotationRequest": { @@ -1733,10 +1799,13 @@ "KeyId": { "target": "com.amazonaws.kms#KeyIdType", "traits": { - "smithy.api#documentation": "

Identifies a symmetric encryption KMS key. You cannot enable or disable automatic rotation\n of asymmetric KMS keys, HMAC\n KMS keys, KMS keys with imported key material, or KMS keys in a\n custom key store.

\n \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": "

Identifies a symmetric encryption KMS key. You cannot enable or disable automatic rotation\n of asymmetric KMS keys, HMAC\n KMS keys, KMS keys with imported key material, or KMS keys in a\n 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": {} } } + }, + "traits": { + "smithy.api#input": {} } }, "com.amazonaws.kms#DisabledException": { @@ -1776,7 +1845,7 @@ } ], "traits": { - "smithy.api#documentation": "

Disconnects the custom key store from its backing key store. This operation disconnects an\n CloudHSM key store from its associated CloudHSM cluster or disconnects an external key store from\n the external key store proxy that communicates with your external key manager.

\n

This operation is part of the custom key stores feature in KMS, which\ncombines the convenience and extensive integration of KMS with the isolation and control of a\nkey store that you own and manage.

\n

While a custom key store is disconnected, you can manage the custom key store and its KMS\n keys, but you cannot create or use its KMS keys. You can reconnect the custom key store at any\n time.

\n \n

While a custom key store is disconnected, all attempts to create KMS keys in the custom key store or to use existing KMS keys in cryptographic operations will\n fail. This action can prevent users from storing and accessing sensitive data.

\n
\n

When you disconnect a custom key store, its ConnectionState changes to\n Disconnected. To find the connection state of a custom key store, use the DescribeCustomKeyStores operation. To reconnect a custom key store, use the\n ConnectCustomKeyStore operation.

\n

If the operation succeeds, it returns a JSON object with no\nproperties.

\n

\n Cross-account use: No. You cannot perform this operation on a custom key store in a different Amazon Web Services account.

\n\n

\n Required permissions: kms:DisconnectCustomKeyStore (IAM policy)

\n

\n Related operations:\n

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

Disconnects the custom key store from its backing key store. This operation disconnects an\n CloudHSM key store from its associated CloudHSM cluster or disconnects an external key store from\n the external key store proxy that communicates with your external key manager.

\n

This operation is part of the custom key stores feature in KMS, which\ncombines the convenience and extensive integration of KMS with the isolation and control of a\nkey store that you own and manage.

\n

While a custom key store is disconnected, you can manage the custom key store and its KMS\n keys, but you cannot create or use its KMS keys. You can reconnect the custom key store at any\n time.

\n \n

While a custom key store is disconnected, all attempts to create KMS keys in the custom key store or to use existing KMS keys in cryptographic operations will\n fail. This action can prevent users from storing and accessing sensitive data.

\n
\n

When you disconnect a custom key store, its ConnectionState changes to\n Disconnected. To find the connection state of a custom key store, use the DescribeCustomKeyStores operation. To reconnect a custom key store, use the\n ConnectCustomKeyStore operation.

\n

If the operation succeeds, it returns a JSON object with no\nproperties.

\n

\n Cross-account use: No. You cannot perform this operation on a custom key store in a different Amazon Web Services account.

\n

\n Required permissions: kms:DisconnectCustomKeyStore (IAM policy)

\n

\n Related operations:\n

\n " } }, "com.amazonaws.kms#DisconnectCustomKeyStoreRequest": { @@ -1789,11 +1858,17 @@ "smithy.api#required": {} } } + }, + "traits": { + "smithy.api#input": {} } }, "com.amazonaws.kms#DisconnectCustomKeyStoreResponse": { "type": "structure", - "members": {} + "members": {}, + "traits": { + "smithy.api#output": {} + } }, "com.amazonaws.kms#EnableKey": { "type": "operation", @@ -1824,7 +1899,7 @@ } ], "traits": { - "smithy.api#documentation": "

Sets the key state of a KMS key to enabled. This allows you to use the KMS key for\n cryptographic operations.

\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

\n Required permissions: kms:EnableKey (key policy)

\n

\n Related operations: DisableKey\n

" + "smithy.api#documentation": "

Sets the key state of a KMS key to enabled. This allows you to use the KMS key for\n cryptographic operations.

\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:EnableKey (key policy)

\n

\n Related operations: DisableKey\n

" } }, "com.amazonaws.kms#EnableKeyRequest": { @@ -1833,10 +1908,13 @@ "KeyId": { "target": "com.amazonaws.kms#KeyIdType", "traits": { - "smithy.api#documentation": "

Identifies the KMS key to enable.

\n \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": "

Identifies the KMS key to enable.

\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": {} } } + }, + "traits": { + "smithy.api#input": {} } }, "com.amazonaws.kms#EnableKeyRotation": { @@ -1871,7 +1949,7 @@ } ], "traits": { - "smithy.api#documentation": "

Enables automatic rotation\n of the key material of the specified symmetric encryption KMS key.

\n

When you enable automatic rotation of acustomer managed KMS key, KMS\n rotates the key material of the KMS key one year (approximately 365 days) from the enable date\n and every year thereafter. You can monitor rotation of the key material for your KMS keys in\n CloudTrail and Amazon CloudWatch. To disable rotation of the key material in a customer\n managed KMS key, use the DisableKeyRotation operation.

\n

Automatic key rotation is supported only on symmetric encryption KMS keys.\n You cannot enable automatic rotation of asymmetric KMS keys, HMAC KMS keys, KMS keys with imported key material, or KMS keys in a custom key store. To enable or disable automatic rotation of a set of related multi-Region keys, set the property on the primary key.

\n

You cannot enable or disable automatic rotation Amazon Web Services managed KMS keys. KMS\n always rotates the key material of Amazon Web Services managed keys every year. Rotation of Amazon Web Services owned KMS\n keys varies.

\n \n

In May 2022, KMS changed the rotation schedule for Amazon Web Services managed keys from every three\n years (approximately 1,095 days) to every year (approximately 365 days).

\n

New Amazon Web Services managed keys are automatically rotated one year after they are created, and\n approximately every year thereafter.

\n

Existing Amazon Web Services managed keys are automatically rotated one year after their most recent\n rotation, and every year thereafter.

\n
\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

\n Required permissions: kms:EnableKeyRotation (key policy)

\n

\n Related operations:\n

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

Enables automatic rotation\n of the key material of the specified symmetric encryption KMS key.

\n

When you enable automatic rotation of acustomer managed KMS key, KMS\n rotates the key material of the KMS key one year (approximately 365 days) from the enable date\n and every year thereafter. You can monitor rotation of the key material for your KMS keys in\n CloudTrail and Amazon CloudWatch. To disable rotation of the key material in a customer\n managed KMS key, use the DisableKeyRotation operation.

\n

Automatic key rotation is supported only on symmetric encryption KMS keys.\n You cannot enable automatic rotation of asymmetric KMS keys, HMAC KMS keys, KMS keys with imported key material, or KMS keys in a custom key store. To enable or disable automatic rotation of a set of related multi-Region keys, set the property on the primary key.

\n

You cannot enable or disable automatic rotation Amazon Web Services managed KMS keys. KMS\n always rotates the key material of Amazon Web Services managed keys every year. Rotation of Amazon Web Services owned KMS\n keys varies.

\n \n

In May 2022, KMS changed the rotation schedule for Amazon Web Services managed keys from every three\n years (approximately 1,095 days) to every year (approximately 365 days).

\n

New Amazon Web Services managed keys are automatically rotated one year after they are created, and\n approximately every year thereafter.

\n

Existing Amazon Web Services managed keys are automatically rotated one year after their most recent\n rotation, and every year thereafter.

\n
\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:EnableKeyRotation (key policy)

\n

\n Related operations:\n

\n " } }, "com.amazonaws.kms#EnableKeyRotationRequest": { @@ -1880,10 +1958,13 @@ "KeyId": { "target": "com.amazonaws.kms#KeyIdType", "traits": { - "smithy.api#documentation": "

Identifies a symmetric encryption KMS key. You cannot enable automatic rotation of asymmetric KMS keys, HMAC KMS keys, KMS keys with imported key material, or KMS keys in a custom key store. To enable or disable automatic rotation of a set of related multi-Region keys, set the property on the primary key.

\n \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": "

Identifies a symmetric encryption KMS key. You cannot enable automatic rotation of asymmetric KMS keys, HMAC KMS keys, KMS keys with imported key material, or KMS keys in a custom key store. To enable or disable automatic rotation of a set of related multi-Region keys, set the property on the primary key.

\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": {} } } + }, + "traits": { + "smithy.api#input": {} } }, "com.amazonaws.kms#Encrypt": { @@ -1921,7 +2002,7 @@ } ], "traits": { - "smithy.api#documentation": "

Encrypts plaintext of up to 4,096 bytes using a KMS key. You can use a symmetric or\n asymmetric KMS key with a KeyUsage of ENCRYPT_DECRYPT.

\n

You can use this operation to encrypt small amounts of arbitrary data, such as a personal\n identifier or database password, or other sensitive information. You don't need to use the\n Encrypt operation to encrypt a data key. The GenerateDataKey\n and GenerateDataKeyPair operations return a plaintext data key and an\n encrypted copy of that data key.

\n

If you use a symmetric encryption KMS key, you can use an encryption context to add\n additional security to your encryption operation. If you specify an\n EncryptionContext when encrypting data, you must specify the same encryption\n context (a case-sensitive exact match) when decrypting the data. Otherwise, the request to\n decrypt fails with an InvalidCiphertextException. For more information, see\n Encryption\n Context in the Key Management Service Developer Guide.

\n

If you specify an asymmetric KMS key, you must also specify the encryption algorithm. The\n algorithm must be compatible with the KMS key spec.

\n \n

When you use an asymmetric KMS key to encrypt or reencrypt data, be sure to record the KMS key and encryption algorithm that you choose. You will be required to provide the same KMS key and encryption algorithm when you decrypt the data. If the KMS key and algorithm do not match the values used to encrypt the data, the decrypt operation fails.

\n

You are not required to supply the key ID and encryption algorithm when you decrypt with symmetric encryption KMS keys because KMS stores this information in the ciphertext blob. KMS cannot store metadata in ciphertext generated with asymmetric keys. The standard format for asymmetric key ciphertext does not include configurable fields.

\n
\n\n\n

The maximum size of the data that you can encrypt varies with the type of KMS key and the\n encryption algorithm that you choose.

\n
    \n
  • \n

    Symmetric encryption KMS keys

    \n
      \n
    • \n

      \n SYMMETRIC_DEFAULT: 4096 bytes

      \n
    • \n
    \n
  • \n
  • \n

    \n RSA_2048\n

    \n
      \n
    • \n

      \n RSAES_OAEP_SHA_1: 214 bytes

      \n
    • \n
    • \n

      \n RSAES_OAEP_SHA_256: 190 bytes

      \n
    • \n
    \n
  • \n
  • \n

    \n RSA_3072\n

    \n
      \n
    • \n

      \n RSAES_OAEP_SHA_1: 342 bytes

      \n
    • \n
    • \n

      \n RSAES_OAEP_SHA_256: 318 bytes

      \n
    • \n
    \n
  • \n
  • \n

    \n RSA_4096\n

    \n
      \n
    • \n

      \n RSAES_OAEP_SHA_1: 470 bytes

      \n
    • \n
    • \n

      \n RSAES_OAEP_SHA_256: 446 bytes

      \n
    • \n
    \n
  • \n
  • \n

    \n SM2PKE: 1024 bytes (China Regions only)

    \n
  • \n
\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.\n 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

\n Required permissions: kms:Encrypt (key policy)

\n

\n Related operations:\n

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

Encrypts plaintext of up to 4,096 bytes using a KMS key. You can use a symmetric or\n asymmetric KMS key with a KeyUsage of ENCRYPT_DECRYPT.

\n

You can use this operation to encrypt small amounts of arbitrary data, such as a personal\n identifier or database password, or other sensitive information. You don't need to use the\n Encrypt operation to encrypt a data key. The GenerateDataKey\n and GenerateDataKeyPair operations return a plaintext data key and an\n encrypted copy of that data key.

\n

If you use a symmetric encryption KMS key, you can use an encryption context to add\n additional security to your encryption operation. If you specify an\n EncryptionContext when encrypting data, you must specify the same encryption\n context (a case-sensitive exact match) when decrypting the data. Otherwise, the request to\n decrypt fails with an InvalidCiphertextException. For more information, see\n Encryption\n Context in the Key Management Service Developer Guide.

\n

If you specify an asymmetric KMS key, you must also specify the encryption algorithm. The\n algorithm must be compatible with the KMS key spec.

\n \n

When you use an asymmetric KMS key to encrypt or reencrypt data, be sure to record the KMS key and encryption algorithm that you choose. You will be required to provide the same KMS key and encryption algorithm when you decrypt the data. If the KMS key and algorithm do not match the values used to encrypt the data, the decrypt operation fails.

\n

You are not required to supply the key ID and encryption algorithm when you decrypt with symmetric encryption KMS keys because KMS stores this information in the ciphertext blob. KMS cannot store metadata in ciphertext generated with asymmetric keys. The standard format for asymmetric key ciphertext does not include configurable fields.

\n
\n

The maximum size of the data that you can encrypt varies with the type of KMS key and the\n encryption algorithm that you choose.

\n
    \n
  • \n

    Symmetric encryption KMS keys

    \n
      \n
    • \n

      \n SYMMETRIC_DEFAULT: 4096 bytes

      \n
    • \n
    \n
  • \n
  • \n

    \n RSA_2048\n

    \n
      \n
    • \n

      \n RSAES_OAEP_SHA_1: 214 bytes

      \n
    • \n
    • \n

      \n RSAES_OAEP_SHA_256: 190 bytes

      \n
    • \n
    \n
  • \n
  • \n

    \n RSA_3072\n

    \n
      \n
    • \n

      \n RSAES_OAEP_SHA_1: 342 bytes

      \n
    • \n
    • \n

      \n RSAES_OAEP_SHA_256: 318 bytes

      \n
    • \n
    \n
  • \n
  • \n

    \n RSA_4096\n

    \n
      \n
    • \n

      \n RSAES_OAEP_SHA_1: 470 bytes

      \n
    • \n
    • \n

      \n RSAES_OAEP_SHA_256: 446 bytes

      \n
    • \n
    \n
  • \n
  • \n

    \n SM2PKE: 1024 bytes (China Regions only)

    \n
  • \n
\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.\n 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:Encrypt (key policy)

\n

\n Related operations:\n

\n " } }, "com.amazonaws.kms#EncryptRequest": { @@ -1930,7 +2011,7 @@ "KeyId": { "target": "com.amazonaws.kms#KeyIdType", "traits": { - "smithy.api#documentation": "

Identifies the KMS key to use in the encryption operation. The KMS key must have a\n KeyUsage of ENCRYPT_DECRYPT. To find the KeyUsage of\n a KMS key, use the DescribeKey operation.

\n \n

To specify a KMS key, use its key ID, key ARN, alias name, or alias ARN. When using an alias name, prefix it with \"alias/\". To specify a KMS key in a different Amazon Web Services account, you must use the key ARN or alias ARN.

\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

    Alias name: alias/ExampleAlias\n

    \n
  • \n
  • \n

    Alias ARN: arn:aws:kms:us-east-2:111122223333:alias/ExampleAlias\n

    \n
  • \n
\n

To get the key ID and key ARN for a KMS key, use ListKeys or DescribeKey. To get the alias name and alias ARN, use ListAliases.

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

Identifies the KMS key to use in the encryption operation. The KMS key must have a\n KeyUsage of ENCRYPT_DECRYPT. To find the KeyUsage of\n a KMS key, use the DescribeKey operation.

\n

To specify a KMS key, use its key ID, key ARN, alias name, or alias ARN. When using an alias name, prefix it with \"alias/\". To specify a KMS key in a different Amazon Web Services account, you must use the key ARN or alias ARN.

\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

    Alias name: alias/ExampleAlias\n

    \n
  • \n
  • \n

    Alias ARN: arn:aws:kms:us-east-2:111122223333:alias/ExampleAlias\n

    \n
  • \n
\n

To get the key ID and key ARN for a KMS key, use ListKeys or DescribeKey. To get the alias name and alias ARN, use ListAliases.

", "smithy.api#required": {} } }, @@ -1959,6 +2040,9 @@ "smithy.api#documentation": "

Specifies the encryption algorithm that KMS will use to encrypt the plaintext message.\n The algorithm must be compatible with the KMS key that you specify.

\n

This parameter is required only for asymmetric KMS keys. The default value,\n SYMMETRIC_DEFAULT, is the algorithm used for symmetric encryption KMS keys. If you are\n using an asymmetric KMS key, we recommend RSAES_OAEP_SHA_256.

\n

The SM2PKE algorithm is only available in China Regions.

" } } + }, + "traits": { + "smithy.api#input": {} } }, "com.amazonaws.kms#EncryptResponse": { @@ -1982,6 +2066,9 @@ "smithy.api#documentation": "

The encryption algorithm that was used to encrypt the plaintext.

" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.kms#EncryptionAlgorithmSpec": { @@ -2106,7 +2193,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\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 \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 \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\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\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

\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

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 " } }, "com.amazonaws.kms#GenerateDataKeyPair": { @@ -2147,7 +2234,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\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\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\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

\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

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

\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

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": { @@ -2162,7 +2249,7 @@ "KeyId": { "target": "com.amazonaws.kms#KeyIdType", "traits": { - "smithy.api#documentation": "

Specifies the symmetric encryption KMS key that encrypts the private key in the data key\n pair. You cannot specify an asymmetric KMS key or a KMS key in a custom key store. To get the\n type and origin of your KMS key, use the DescribeKey operation.

\n \n

To specify a KMS key, use its key ID, key ARN, alias name, or alias ARN. When using an alias name, prefix it with \"alias/\". To specify a KMS key in a different Amazon Web Services account, you must use the key ARN or alias ARN.

\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

    Alias name: alias/ExampleAlias\n

    \n
  • \n
  • \n

    Alias ARN: arn:aws:kms:us-east-2:111122223333:alias/ExampleAlias\n

    \n
  • \n
\n

To get the key ID and key ARN for a KMS key, use ListKeys or DescribeKey. To get the alias name and alias ARN, use ListAliases.

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

Specifies the symmetric encryption KMS key that encrypts the private key in the data key\n pair. You cannot specify an asymmetric KMS key or a KMS key in a custom key store. To get the\n type and origin of your KMS key, use the DescribeKey operation.

\n

To specify a KMS key, use its key ID, key ARN, alias name, or alias ARN. When using an alias name, prefix it with \"alias/\". To specify a KMS key in a different Amazon Web Services account, you must use the key ARN or alias ARN.

\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

    Alias name: alias/ExampleAlias\n

    \n
  • \n
  • \n

    Alias ARN: arn:aws:kms:us-east-2:111122223333:alias/ExampleAlias\n

    \n
  • \n
\n

To get the key ID and key ARN for a KMS key, use ListKeys or DescribeKey. To get the alias name and alias ARN, use ListAliases.

", "smithy.api#required": {} } }, @@ -2179,6 +2266,9 @@ "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.

" } } + }, + "traits": { + "smithy.api#input": {} } }, "com.amazonaws.kms#GenerateDataKeyPairResponse": { @@ -2214,6 +2304,9 @@ "smithy.api#documentation": "

The type of data key pair that was generated.

" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.kms#GenerateDataKeyPairWithoutPlaintext": { @@ -2254,7 +2347,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 and a copy of the private key that is encrypted under the symmetric\n encryption KMS key you specify. Unlike GenerateDataKeyPair, this operation\n does not return a plaintext private key. The bytes in the keys are random; they are not\n related to the caller or to the KMS key that is used to encrypt the private key.

\n

You can use the public key that GenerateDataKeyPairWithoutPlaintext returns\n to encrypt data or verify a signature outside of KMS. Then, store the encrypted private key\n with the data. 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 \n use ECC key pairs for signing, and use RSA and SM2 key pairs for either encryption or signing, but not\n both. However, KMS cannot enforce any restrictions on the use of data key pairs outside of KMS.

\n

\n GenerateDataKeyPairWithoutPlaintext returns a unique data key pair for each\n request. The bytes in the key are not related to the caller or KMS key that is used to encrypt\n the private key. The public key is a DER-encoded X.509 SubjectPublicKeyInfo, as specified in\n RFC 5280.

\n\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

\n Required permissions: kms:GenerateDataKeyPairWithoutPlaintext (key\n 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 and a copy of the private key that is encrypted under the symmetric\n encryption KMS key you specify. Unlike GenerateDataKeyPair, this operation\n does not return a plaintext private key. The bytes in the keys are random; they are not\n related to the caller or to the KMS key that is used to encrypt the private key.

\n

You can use the public key that GenerateDataKeyPairWithoutPlaintext returns\n to encrypt data or verify a signature outside of KMS. Then, store the encrypted private key\n with the data. 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 \n use ECC key pairs for signing, and use RSA and SM2 key pairs for either encryption or signing, but not\n both. However, KMS cannot enforce any restrictions on the use of data key pairs outside of KMS.

\n

\n GenerateDataKeyPairWithoutPlaintext returns a unique data key pair for each\n request. The bytes in the key are not related to the caller or KMS key that is used to encrypt\n the private key. The public key is a DER-encoded X.509 SubjectPublicKeyInfo, as specified in\n RFC 5280.

\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:GenerateDataKeyPairWithoutPlaintext (key\n policy)

\n

\n Related operations:\n

\n " } }, "com.amazonaws.kms#GenerateDataKeyPairWithoutPlaintextRequest": { @@ -2269,7 +2362,7 @@ "KeyId": { "target": "com.amazonaws.kms#KeyIdType", "traits": { - "smithy.api#documentation": "

Specifies the symmetric encryption KMS key that encrypts the private key in the data key\n pair. You cannot specify an asymmetric KMS key or a KMS key in a custom key store. To get the\n type and origin of your KMS key, use the DescribeKey operation.

\n \n

To specify a KMS key, use its key ID, key ARN, alias name, or alias ARN. When using an alias name, prefix it with \"alias/\". To specify a KMS key in a different Amazon Web Services account, you must use the key ARN or alias ARN.

\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

    Alias name: alias/ExampleAlias\n

    \n
  • \n
  • \n

    Alias ARN: arn:aws:kms:us-east-2:111122223333:alias/ExampleAlias\n

    \n
  • \n
\n

To get the key ID and key ARN for a KMS key, use ListKeys or DescribeKey. To get the alias name and alias ARN, use ListAliases.

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

Specifies the symmetric encryption KMS key that encrypts the private key in the data key\n pair. You cannot specify an asymmetric KMS key or a KMS key in a custom key store. To get the\n type and origin of your KMS key, use the DescribeKey operation.

\n

To specify a KMS key, use its key ID, key ARN, alias name, or alias ARN. When using an alias name, prefix it with \"alias/\". To specify a KMS key in a different Amazon Web Services account, you must use the key ARN or alias ARN.

\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

    Alias name: alias/ExampleAlias\n

    \n
  • \n
  • \n

    Alias ARN: arn:aws:kms:us-east-2:111122223333:alias/ExampleAlias\n

    \n
  • \n
\n

To get the key ID and key ARN for a KMS key, use ListKeys or DescribeKey. To get the alias name and alias ARN, use ListAliases.

", "smithy.api#required": {} } }, @@ -2286,6 +2379,9 @@ "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.

" } } + }, + "traits": { + "smithy.api#input": {} } }, "com.amazonaws.kms#GenerateDataKeyPairWithoutPlaintextResponse": { @@ -2315,6 +2411,9 @@ "smithy.api#documentation": "

The type of data key pair that was generated.

" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.kms#GenerateDataKeyRequest": { @@ -2323,7 +2422,7 @@ "KeyId": { "target": "com.amazonaws.kms#KeyIdType", "traits": { - "smithy.api#documentation": "

Specifies the symmetric encryption KMS key that encrypts the data key. You cannot specify\n an asymmetric KMS key or a KMS key in a custom key store. To get the type and origin of your\n KMS key, use the DescribeKey operation.

\n \n

To specify a KMS key, use its key ID, key ARN, alias name, or alias ARN. When using an alias name, prefix it with \"alias/\". To specify a KMS key in a different Amazon Web Services account, you must use the key ARN or alias ARN.

\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

    Alias name: alias/ExampleAlias\n

    \n
  • \n
  • \n

    Alias ARN: arn:aws:kms:us-east-2:111122223333:alias/ExampleAlias\n

    \n
  • \n
\n

To get the key ID and key ARN for a KMS key, use ListKeys or DescribeKey. To get the alias name and alias ARN, use ListAliases.

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

Specifies the symmetric encryption KMS key that encrypts the data key. You cannot specify\n an asymmetric KMS key or a KMS key in a custom key store. To get the type and origin of your\n KMS key, use the DescribeKey operation.

\n

To specify a KMS key, use its key ID, key ARN, alias name, or alias ARN. When using an alias name, prefix it with \"alias/\". To specify a KMS key in a different Amazon Web Services account, you must use the key ARN or alias ARN.

\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

    Alias name: alias/ExampleAlias\n

    \n
  • \n
  • \n

    Alias ARN: arn:aws:kms:us-east-2:111122223333:alias/ExampleAlias\n

    \n
  • \n
\n

To get the key ID and key ARN for a KMS key, use ListKeys or DescribeKey. To get the alias name and alias ARN, use ListAliases.

", "smithy.api#required": {} } }, @@ -2351,6 +2450,9 @@ "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.

" } } + }, + "traits": { + "smithy.api#input": {} } }, "com.amazonaws.kms#GenerateDataKeyResponse": { @@ -2374,6 +2476,9 @@ "smithy.api#documentation": "

The Amazon Resource Name (key ARN) of the KMS key that encrypted the data key.

" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.kms#GenerateDataKeyWithoutPlaintext": { @@ -2411,7 +2516,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\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 \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 \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\n

If the operation succeeds, you will find the encrypted copy of the data key in the\n CiphertextBlob field.

\n\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

\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 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 " } }, "com.amazonaws.kms#GenerateDataKeyWithoutPlaintextRequest": { @@ -2420,7 +2525,7 @@ "KeyId": { "target": "com.amazonaws.kms#KeyIdType", "traits": { - "smithy.api#documentation": "

Specifies the symmetric encryption KMS key that encrypts the data key. You cannot specify\n an asymmetric KMS key or a KMS key in a custom key store. To get the type and origin of your\n KMS key, use the DescribeKey operation.

\n \n

To specify a KMS key, use its key ID, key ARN, alias name, or alias ARN. When using an alias name, prefix it with \"alias/\". To specify a KMS key in a different Amazon Web Services account, you must use the key ARN or alias ARN.

\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

    Alias name: alias/ExampleAlias\n

    \n
  • \n
  • \n

    Alias ARN: arn:aws:kms:us-east-2:111122223333:alias/ExampleAlias\n

    \n
  • \n
\n

To get the key ID and key ARN for a KMS key, use ListKeys or DescribeKey. To get the alias name and alias ARN, use ListAliases.

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

Specifies the symmetric encryption KMS key that encrypts the data key. You cannot specify\n an asymmetric KMS key or a KMS key in a custom key store. To get the type and origin of your\n KMS key, use the DescribeKey operation.

\n

To specify a KMS key, use its key ID, key ARN, alias name, or alias ARN. When using an alias name, prefix it with \"alias/\". To specify a KMS key in a different Amazon Web Services account, you must use the key ARN or alias ARN.

\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

    Alias name: alias/ExampleAlias\n

    \n
  • \n
  • \n

    Alias ARN: arn:aws:kms:us-east-2:111122223333:alias/ExampleAlias\n

    \n
  • \n
\n

To get the key ID and key ARN for a KMS key, use ListKeys or DescribeKey. To get the alias name and alias ARN, use ListAliases.

", "smithy.api#required": {} } }, @@ -2448,6 +2553,9 @@ "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.

" } } + }, + "traits": { + "smithy.api#input": {} } }, "com.amazonaws.kms#GenerateDataKeyWithoutPlaintextResponse": { @@ -2465,6 +2573,9 @@ "smithy.api#documentation": "

The Amazon Resource Name (key ARN) of the KMS key that encrypted the data key.

" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.kms#GenerateMac": { @@ -2532,6 +2643,9 @@ "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.

" } } + }, + "traits": { + "smithy.api#input": {} } }, "com.amazonaws.kms#GenerateMacResponse": { @@ -2540,7 +2654,7 @@ "Mac": { "target": "com.amazonaws.kms#CiphertextType", "traits": { - "smithy.api#documentation": "

The hash-based message authentication code (HMAC) that was generated for the\n specified message, HMAC KMS key, and MAC algorithm.

\n

This is the standard, raw HMAC defined in RFC 2104.

" + "smithy.api#documentation": "

The hash-based message authentication code (HMAC) that was generated for the\n specified message, HMAC KMS key, and MAC algorithm.

\n

This is the standard, raw HMAC defined in RFC 2104.

" } }, "MacAlgorithm": { @@ -2555,6 +2669,9 @@ "smithy.api#documentation": "

The HMAC KMS key used in the operation.

" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.kms#GenerateRandom": { @@ -2583,7 +2700,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

\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

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)

" } }, "com.amazonaws.kms#GenerateRandomRequest": { @@ -2601,6 +2718,9 @@ "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.

" } } + }, + "traits": { + "smithy.api#input": {} } }, "com.amazonaws.kms#GenerateRandomResponse": { @@ -2612,6 +2732,9 @@ "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.

" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.kms#GetKeyPolicy": { @@ -2640,7 +2763,7 @@ } ], "traits": { - "smithy.api#documentation": "

Gets a key policy attached to the specified KMS key.

\n

\n Cross-account use: No. You cannot perform this operation on a KMS key in a different Amazon Web Services account.

\n\n

\n Required permissions: kms:GetKeyPolicy (key policy)

\n

\n Related operations: PutKeyPolicy\n

" + "smithy.api#documentation": "

Gets a key policy attached to the specified KMS key.

\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:GetKeyPolicy (key policy)

\n

\n Related operations: PutKeyPolicy\n

" } }, "com.amazonaws.kms#GetKeyPolicyRequest": { @@ -2649,7 +2772,7 @@ "KeyId": { "target": "com.amazonaws.kms#KeyIdType", "traits": { - "smithy.api#documentation": "

Gets the key policy for the specified KMS key.

\n \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": "

Gets the key policy for the specified KMS key.

\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": {} } }, @@ -2660,6 +2783,9 @@ "smithy.api#required": {} } } + }, + "traits": { + "smithy.api#input": {} } }, "com.amazonaws.kms#GetKeyPolicyResponse": { @@ -2671,6 +2797,9 @@ "smithy.api#documentation": "

A key policy document in JSON format.

" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.kms#GetKeyRotationStatus": { @@ -2702,7 +2831,7 @@ } ], "traits": { - "smithy.api#documentation": "

Gets a Boolean value that indicates whether automatic rotation of the key material is\n enabled for the specified KMS key.

\n

When you enable automatic rotation for customer managed KMS keys, KMS\n rotates the key material of the KMS key one year (approximately 365 days) from the enable date\n and every year thereafter. You can monitor rotation of the key material for your KMS keys in\n CloudTrail and Amazon CloudWatch.

\n

Automatic key rotation is supported only on symmetric encryption KMS keys.\n You cannot enable automatic rotation of asymmetric KMS keys, HMAC KMS keys, KMS keys with imported key material, or KMS keys in a custom key store. To enable or disable automatic rotation of a set of related multi-Region keys, set the property on the primary key..

\n

You can enable (EnableKeyRotation) and disable automatic rotation (DisableKeyRotation) of the key material in customer managed KMS keys. Key\n material rotation of Amazon Web Services managed KMS keys is not\n configurable. KMS always rotates the key material in Amazon Web Services managed KMS keys every year. The\n key rotation status for Amazon Web Services managed KMS keys is always true.

\n \n

In May 2022, KMS changed the rotation schedule for Amazon Web Services managed keys from every three\n years to every year. For details, see EnableKeyRotation.

\n
\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
  • \n

    Disabled: The key rotation status does not change when you disable a KMS key. However,\n while the KMS key is disabled, KMS does not rotate the key material. When you re-enable\n the KMS key, rotation resumes. If the key material in the re-enabled KMS key hasn't been\n rotated in one year, KMS rotates it immediately, and every year thereafter. If it's been\n less than a year since the key material in the re-enabled KMS key was rotated, the KMS key\n resumes its prior rotation schedule.

    \n
  • \n
  • \n

    Pending deletion: While a KMS key is pending deletion, its key rotation status is\n false and KMS does not rotate the key material. If you cancel the\n deletion, the original key rotation status returns to true.

    \n
  • \n
\n

\n Cross-account use: Yes. To perform this operation on a KMS key in a different Amazon Web Services account, specify the key\n ARN in the value of the KeyId parameter.

\n\n

\n Required permissions: kms:GetKeyRotationStatus (key policy)

\n

\n Related operations:\n

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

Gets a Boolean value that indicates whether automatic rotation of the key material is\n enabled for the specified KMS key.

\n

When you enable automatic rotation for customer managed KMS keys, KMS\n rotates the key material of the KMS key one year (approximately 365 days) from the enable date\n and every year thereafter. You can monitor rotation of the key material for your KMS keys in\n CloudTrail and Amazon CloudWatch.

\n

Automatic key rotation is supported only on symmetric encryption KMS keys.\n You cannot enable automatic rotation of asymmetric KMS keys, HMAC KMS keys, KMS keys with imported key material, or KMS keys in a custom key store. To enable or disable automatic rotation of a set of related multi-Region keys, set the property on the primary key..

\n

You can enable (EnableKeyRotation) and disable automatic rotation (DisableKeyRotation) of the key material in customer managed KMS keys. Key\n material rotation of Amazon Web Services managed KMS keys is not\n configurable. KMS always rotates the key material in Amazon Web Services managed KMS keys every year. The\n key rotation status for Amazon Web Services managed KMS keys is always true.

\n \n

In May 2022, KMS changed the rotation schedule for Amazon Web Services managed keys from every three\n years to every year. For details, see EnableKeyRotation.

\n
\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
  • \n

    Disabled: The key rotation status does not change when you disable a KMS key. However,\n while the KMS key is disabled, KMS does not rotate the key material. When you re-enable\n the KMS key, rotation resumes. If the key material in the re-enabled KMS key hasn't been\n rotated in one year, KMS rotates it immediately, and every year thereafter. If it's been\n less than a year since the key material in the re-enabled KMS key was rotated, the KMS key\n resumes its prior rotation schedule.

    \n
  • \n
  • \n

    Pending deletion: While a KMS key is pending deletion, its key rotation status is\n false and KMS does not rotate the key material. If you cancel the\n deletion, the original key rotation status returns to true.

    \n
  • \n
\n

\n Cross-account use: Yes. To perform this operation on a KMS key in a different Amazon Web Services account, specify the key\n ARN in the value of the KeyId parameter.

\n

\n Required permissions: kms:GetKeyRotationStatus (key policy)

\n

\n Related operations:\n

\n " } }, "com.amazonaws.kms#GetKeyRotationStatusRequest": { @@ -2711,10 +2840,13 @@ "KeyId": { "target": "com.amazonaws.kms#KeyIdType", "traits": { - "smithy.api#documentation": "

Gets the rotation status for the specified KMS key.

\n \n

Specify the key ID or key ARN of the KMS key. To specify a KMS key in a\ndifferent Amazon Web Services account, you must use the key ARN.

\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": "

Gets the rotation status for the specified KMS key.

\n

Specify the key ID or key ARN of the KMS key. To specify a KMS key in a\ndifferent Amazon Web Services account, you must use the key ARN.

\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": {} } } + }, + "traits": { + "smithy.api#input": {} } }, "com.amazonaws.kms#GetKeyRotationStatusResponse": { @@ -2727,6 +2859,9 @@ "smithy.api#documentation": "

A Boolean value that specifies whether key rotation is enabled.

" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.kms#GetParametersForImport": { @@ -2758,7 +2893,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

\n Required permissions: kms:GetParametersForImport (key policy)

\n

\n Related operations:\n

\n " + "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 " } }, "com.amazonaws.kms#GetParametersForImportRequest": { @@ -2767,14 +2902,14 @@ "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 \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 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#required": {} } }, "WrappingAlgorithm": { "target": "com.amazonaws.kms#AlgorithmSpec", "traits": { - "smithy.api#documentation": "

The algorithm you will use to encrypt the key material before importing it with ImportKeyMaterial. For more information, see Encrypt the Key Material\n in the Key Management Service Developer Guide.

", + "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#required": {} } }, @@ -2785,6 +2920,9 @@ "smithy.api#required": {} } } + }, + "traits": { + "smithy.api#input": {} } }, "com.amazonaws.kms#GetParametersForImportResponse": { @@ -2814,6 +2952,9 @@ "smithy.api#documentation": "

The time at which the import token and public key are no longer valid. After this time,\n you cannot use them to make an ImportKeyMaterial request and you must send\n another GetParametersForImport request to get new ones.

" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.kms#GetPublicKey": { @@ -2857,7 +2998,7 @@ } ], "traits": { - "smithy.api#documentation": "

Returns the public key of an asymmetric KMS key. Unlike the private key of a asymmetric\n KMS key, which never leaves KMS unencrypted, callers with kms:GetPublicKey\n permission can download the public key of an asymmetric KMS key. You can share the public key\n to allow others to encrypt messages and verify signatures outside of KMS.\n For information about asymmetric KMS keys, see Asymmetric KMS keys in the Key Management Service Developer Guide.

\n

You do not need to download the public key. Instead, you can use the public key within\n KMS by calling the Encrypt, ReEncrypt, or Verify operations with the identifier of an asymmetric KMS key. When you use the\n public key within KMS, you benefit from the authentication, authorization, and logging that\n are part of every KMS operation. You also reduce of risk of encrypting data that cannot be\n decrypted. These features are not effective outside of KMS.

\n \n

To help you use the public key safely outside of KMS, GetPublicKey returns\n important information about the public key in the response, including:

\n
    \n
  • \n

    \n KeySpec: The type of key material in the public key, such as\n RSA_4096 or ECC_NIST_P521.

    \n
  • \n
  • \n

    \n KeyUsage: Whether the key is used for encryption or signing.

    \n
  • \n
  • \n

    \n EncryptionAlgorithms or SigningAlgorithms: A list of the encryption algorithms or the signing\n algorithms for the key.

    \n
  • \n
\n

Although KMS cannot enforce these restrictions on external operations, it is crucial\n that you use this information to prevent the public key from being used improperly. For\n example, you can prevent a public signing key from being used encrypt data, or prevent a\n public key from being used with an encryption algorithm that is not supported by KMS. You\n can also avoid errors, such as using the wrong signing algorithm in a verification\n operation.

\n

To verify a signature outside of KMS with an SM2 public key (China Regions only), you must \n specify the distinguishing ID. By default, KMS uses 1234567812345678 as the \n distinguishing ID. For more information, see Offline verification\n with SM2 key pairs.

\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:\n 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

\n Required permissions: kms:GetPublicKey (key policy)

\n

\n Related operations: CreateKey\n

" + "smithy.api#documentation": "

Returns the public key of an asymmetric KMS key. Unlike the private key of a asymmetric\n KMS key, which never leaves KMS unencrypted, callers with kms:GetPublicKey\n permission can download the public key of an asymmetric KMS key. You can share the public key\n to allow others to encrypt messages and verify signatures outside of KMS.\n For information about asymmetric KMS keys, see Asymmetric KMS keys in the Key Management Service Developer Guide.

\n

You do not need to download the public key. Instead, you can use the public key within\n KMS by calling the Encrypt, ReEncrypt, or Verify operations with the identifier of an asymmetric KMS key. When you use the\n public key within KMS, you benefit from the authentication, authorization, and logging that\n are part of every KMS operation. You also reduce of risk of encrypting data that cannot be\n decrypted. These features are not effective outside of KMS.

\n

To help you use the public key safely outside of KMS, GetPublicKey returns\n important information about the public key in the response, including:

\n
    \n
  • \n

    \n KeySpec: The type of key material in the public key, such as\n RSA_4096 or ECC_NIST_P521.

    \n
  • \n
  • \n

    \n KeyUsage: Whether the key is used for encryption or signing.

    \n
  • \n
  • \n

    \n EncryptionAlgorithms or SigningAlgorithms: A list of the encryption algorithms or the signing\n algorithms for the key.

    \n
  • \n
\n

Although KMS cannot enforce these restrictions on external operations, it is crucial\n that you use this information to prevent the public key from being used improperly. For\n example, you can prevent a public signing key from being used encrypt data, or prevent a\n public key from being used with an encryption algorithm that is not supported by KMS. You\n can also avoid errors, such as using the wrong signing algorithm in a verification\n operation.

\n

To verify a signature outside of KMS with an SM2 public key (China Regions only), you must \n specify the distinguishing ID. By default, KMS uses 1234567812345678 as the \n distinguishing ID. For more information, see Offline verification\n with SM2 key pairs.

\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:\n 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:GetPublicKey (key policy)

\n

\n Related operations: CreateKey\n

" } }, "com.amazonaws.kms#GetPublicKeyRequest": { @@ -2866,7 +3007,7 @@ "KeyId": { "target": "com.amazonaws.kms#KeyIdType", "traits": { - "smithy.api#documentation": "

Identifies the asymmetric KMS key that includes the public key.

\n \n

To specify a KMS key, use its key ID, key ARN, alias name, or alias ARN. When using an alias name, prefix it with \"alias/\". To specify a KMS key in a different Amazon Web Services account, you must use the key ARN or alias ARN.

\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

    Alias name: alias/ExampleAlias\n

    \n
  • \n
  • \n

    Alias ARN: arn:aws:kms:us-east-2:111122223333:alias/ExampleAlias\n

    \n
  • \n
\n

To get the key ID and key ARN for a KMS key, use ListKeys or DescribeKey. To get the alias name and alias ARN, use ListAliases.

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

Identifies the asymmetric KMS key that includes the public key.

\n

To specify a KMS key, use its key ID, key ARN, alias name, or alias ARN. When using an alias name, prefix it with \"alias/\". To specify a KMS key in a different Amazon Web Services account, you must use the key ARN or alias ARN.

\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

    Alias name: alias/ExampleAlias\n

    \n
  • \n
  • \n

    Alias ARN: arn:aws:kms:us-east-2:111122223333:alias/ExampleAlias\n

    \n
  • \n
\n

To get the key ID and key ARN for a KMS key, use ListKeys or DescribeKey. To get the alias name and alias ARN, use ListAliases.

", "smithy.api#required": {} } }, @@ -2876,6 +3017,9 @@ "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.

" } } + }, + "traits": { + "smithy.api#input": {} } }, "com.amazonaws.kms#GetPublicKeyResponse": { @@ -2926,6 +3070,9 @@ "smithy.api#documentation": "

The signing algorithms that KMS supports for this key.

\n

This field appears in the response only when the KeyUsage of the public key\n is SIGN_VERIFY.

" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.kms#GrantConstraints": { @@ -3204,7 +3351,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

\n Required permissions: kms:ImportKeyMaterial (key policy)

\n

\n Related operations:\n

\n " + "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 " } }, "com.amazonaws.kms#ImportKeyMaterialRequest": { @@ -3213,7 +3360,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 \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 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#required": {} } }, @@ -3243,11 +3390,17 @@ "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.

" } } + }, + "traits": { + "smithy.api#input": {} } }, "com.amazonaws.kms#ImportKeyMaterialResponse": { "type": "structure", - "members": {} + "members": {}, + "traits": { + "smithy.api#output": {} + } }, "com.amazonaws.kms#IncorrectKeyException": { "type": "structure", @@ -3951,7 +4104,7 @@ } ], "traits": { - "smithy.api#documentation": "

Gets a list of aliases in the caller's Amazon Web Services account and region. For more information\n about aliases, see CreateAlias.

\n

By default, the ListAliases operation returns all aliases in the account and\n region. To get only the aliases associated with a particular KMS key, use the\n KeyId parameter.

\n

The ListAliases response can include aliases that you created and associated\n with your customer managed keys, and aliases that Amazon Web Services created and associated with Amazon Web Services\n managed keys in your account. You can recognize Amazon Web Services aliases because their names have the\n format aws/, such as aws/dynamodb.

\n

The response might also include aliases that have no TargetKeyId field. These\n are predefined aliases that Amazon Web Services has created but has not yet associated with a KMS key.\n Aliases that Amazon Web Services creates in your account, including predefined aliases, do not count against\n your KMS aliases\n quota.

\n

\n Cross-account use: No. ListAliases does not\n return aliases in other Amazon Web Services accounts.

\n \n\n

\n Required permissions: kms:ListAliases (IAM policy)

\n

For details, see Controlling access to aliases in the\n Key Management Service Developer Guide.

\n

\n Related operations:\n

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

Gets a list of aliases in the caller's Amazon Web Services account and region. For more information\n about aliases, see CreateAlias.

\n

By default, the ListAliases operation returns all aliases in the account and\n region. To get only the aliases associated with a particular KMS key, use the\n KeyId parameter.

\n

The ListAliases response can include aliases that you created and associated\n with your customer managed keys, and aliases that Amazon Web Services created and associated with Amazon Web Services\n managed keys in your account. You can recognize Amazon Web Services aliases because their names have the\n format aws/, such as aws/dynamodb.

\n

The response might also include aliases that have no TargetKeyId field. These\n are predefined aliases that Amazon Web Services has created but has not yet associated with a KMS key.\n Aliases that Amazon Web Services creates in your account, including predefined aliases, do not count against\n your KMS aliases\n quota.

\n

\n Cross-account use: No. ListAliases does not\n return aliases in other Amazon Web Services accounts.

\n

\n Required permissions: kms:ListAliases (IAM policy)

\n

For details, see Controlling access to aliases in the\n Key Management Service Developer Guide.

\n

\n Related operations:\n

\n ", "smithy.api#paginated": { "inputToken": "Marker", "outputToken": "NextMarker", @@ -3966,7 +4119,7 @@ "KeyId": { "target": "com.amazonaws.kms#KeyIdType", "traits": { - "smithy.api#documentation": "

Lists only aliases that are associated with the specified KMS key. Enter a KMS key in your\n Amazon Web Services account.

\n

This parameter is optional. If you omit it, ListAliases returns all aliases\n in the account and Region.

\n \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": "

Lists only aliases that are associated with the specified KMS key. Enter a KMS key in your\n Amazon Web Services account.

\n

This parameter is optional. If you omit it, ListAliases returns all aliases\n in the account and Region.

\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.

" } }, "Limit": { @@ -3981,6 +4134,9 @@ "smithy.api#documentation": "

Use this parameter in a subsequent request after you receive a response with\n truncated results. Set it to the value of NextMarker from the truncated response\n you just received.

" } } + }, + "traits": { + "smithy.api#input": {} } }, "com.amazonaws.kms#ListAliasesResponse": { @@ -4005,6 +4161,9 @@ "smithy.api#documentation": "

A flag that indicates whether there are more items in the list. When this\n value is true, the list in this response is truncated. To get more items, pass the value of\n the NextMarker element in thisresponse to the Marker parameter in a\n subsequent request.

" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.kms#ListGrants": { @@ -4039,7 +4198,7 @@ } ], "traits": { - "smithy.api#documentation": "

Gets a list of all grants for the specified KMS key.

\n

You must specify the KMS key in all requests. You can filter the grant list by grant ID or\n grantee principal.

\n

For detailed information about grants, including grant terminology, see Grants in KMS in the\n \n Key Management Service Developer Guide\n . For examples of working with grants in several\n programming languages, see Programming grants.

\n \n

The GranteePrincipal field in the ListGrants response usually contains the\n user or role designated as the grantee principal in the grant. However, when the grantee\n principal in the grant is an Amazon Web Services service, the GranteePrincipal field contains\n the service\n principal, which might represent several different grantee principals.

\n
\n

\n Cross-account use: Yes. To perform this operation on a KMS key in a different Amazon Web Services account, specify the key\n ARN in the value of the KeyId parameter.

\n\n

\n Required permissions: kms:ListGrants (key policy)

\n

\n Related operations:\n

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

Gets a list of all grants for the specified KMS key.

\n

You must specify the KMS key in all requests. You can filter the grant list by grant ID or\n grantee principal.

\n

For detailed information about grants, including grant terminology, see Grants in KMS in the\n \n Key Management Service Developer Guide\n . For examples of working with grants in several\n programming languages, see Programming grants.

\n \n

The GranteePrincipal field in the ListGrants response usually contains the\n user or role designated as the grantee principal in the grant. However, when the grantee\n principal in the grant is an Amazon Web Services service, the GranteePrincipal field contains\n the service\n principal, which might represent several different grantee principals.

\n
\n

\n Cross-account use: Yes. To perform this operation on a KMS key in a different Amazon Web Services account, specify the key\n ARN in the value of the KeyId parameter.

\n

\n Required permissions: kms:ListGrants (key policy)

\n

\n Related operations:\n

\n ", "smithy.api#paginated": { "inputToken": "Marker", "outputToken": "NextMarker", @@ -4066,7 +4225,7 @@ "KeyId": { "target": "com.amazonaws.kms#KeyIdType", "traits": { - "smithy.api#documentation": "

Returns only grants for the specified KMS key. This parameter is required.

\n \n

Specify the key ID or key ARN of the KMS key. To specify a KMS key in a\ndifferent Amazon Web Services account, you must use the key ARN.

\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": "

Returns only grants for the specified KMS key. This parameter is required.

\n

Specify the key ID or key ARN of the KMS key. To specify a KMS key in a\ndifferent Amazon Web Services account, you must use the key ARN.

\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": {} } }, @@ -4082,6 +4241,9 @@ "smithy.api#documentation": "

Returns only grants where the specified principal is the grantee principal for the\n grant.

" } } + }, + "traits": { + "smithy.api#input": {} } }, "com.amazonaws.kms#ListGrantsResponse": { @@ -4134,7 +4296,7 @@ } ], "traits": { - "smithy.api#documentation": "

Gets the names of the key policies that are attached to a KMS key. This operation is\n designed to get policy names that you can use in a GetKeyPolicy operation.\n However, the only valid policy name is default.

\n

\n Cross-account use: No. You cannot perform this operation on a KMS key in a different Amazon Web Services account.

\n\n

\n Required permissions: kms:ListKeyPolicies (key policy)

\n

\n Related operations:\n

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

Gets the names of the key policies that are attached to a KMS key. This operation is\n designed to get policy names that you can use in a GetKeyPolicy operation.\n However, the only valid policy name is default.

\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:ListKeyPolicies (key policy)

\n

\n Related operations:\n

\n ", "smithy.api#paginated": { "inputToken": "Marker", "outputToken": "NextMarker", @@ -4149,7 +4311,7 @@ "KeyId": { "target": "com.amazonaws.kms#KeyIdType", "traits": { - "smithy.api#documentation": "

Gets the names of key policies for the specified KMS key.

\n \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": "

Gets the names of key policies for the specified KMS key.

\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": {} } }, @@ -4165,6 +4327,9 @@ "smithy.api#documentation": "

Use this parameter in a subsequent request after you receive a response with\n truncated results. Set it to the value of NextMarker from the truncated response\n you just received.

" } } + }, + "traits": { + "smithy.api#input": {} } }, "com.amazonaws.kms#ListKeyPoliciesResponse": { @@ -4189,6 +4354,9 @@ "smithy.api#documentation": "

A flag that indicates whether there are more items in the list. When this\n value is true, the list in this response is truncated. To get more items, pass the value of\n the NextMarker element in thisresponse to the Marker parameter in a\n subsequent request.

" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.kms#ListKeys": { @@ -4211,7 +4379,7 @@ } ], "traits": { - "smithy.api#documentation": "

Gets a list of all KMS keys in the caller's Amazon Web Services account and Region.

\n

\n Cross-account use: No. You cannot perform this operation on a KMS key in a different Amazon Web Services account.

\n\n

\n Required permissions: kms:ListKeys (IAM policy)

\n

\n Related operations:\n

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

Gets a list of all KMS keys in the caller's Amazon Web Services account and Region.

\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:ListKeys (IAM policy)

\n

\n Related operations:\n

\n ", "smithy.api#paginated": { "inputToken": "Marker", "outputToken": "NextMarker", @@ -4235,6 +4403,9 @@ "smithy.api#documentation": "

Use this parameter in a subsequent request after you receive a response with\n truncated results. Set it to the value of NextMarker from the truncated response\n you just received.

" } } + }, + "traits": { + "smithy.api#input": {} } }, "com.amazonaws.kms#ListKeysResponse": { @@ -4259,6 +4430,9 @@ "smithy.api#documentation": "

A flag that indicates whether there are more items in the list. When this\n value is true, the list in this response is truncated. To get more items, pass the value of\n the NextMarker element in thisresponse to the Marker parameter in a\n subsequent request.

" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.kms#ListResourceTags": { @@ -4284,7 +4458,7 @@ } ], "traits": { - "smithy.api#documentation": "

Returns all tags on the specified KMS key.

\n

For general information about tags, including the format and syntax, see Tagging Amazon Web Services resources in\n the Amazon Web Services General Reference. For information about using\n tags in KMS, see Tagging\n keys.

\n

\n Cross-account use: No. You cannot perform this operation on a KMS key in a different Amazon Web Services account.

\n\n

\n Required permissions: kms:ListResourceTags (key policy)

\n

\n Related operations:\n

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

Returns all tags on the specified KMS key.

\n

For general information about tags, including the format and syntax, see Tagging Amazon Web Services resources in\n the Amazon Web Services General Reference. For information about using\n tags in KMS, see Tagging\n keys.

\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:ListResourceTags (key policy)

\n

\n Related operations:\n

\n ", "smithy.api#paginated": { "inputToken": "Marker", "outputToken": "NextMarker", @@ -4299,7 +4473,7 @@ "KeyId": { "target": "com.amazonaws.kms#KeyIdType", "traits": { - "smithy.api#documentation": "

Gets tags on the specified KMS key.

\n \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": "

Gets tags on the specified KMS key.

\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": {} } }, @@ -4315,6 +4489,9 @@ "smithy.api#documentation": "

Use this parameter in a subsequent request after you receive a response with\n truncated results. Set it to the value of NextMarker from the truncated response\n you just received.

\n

Do not attempt to construct this value. Use only the value of NextMarker from\n the truncated response you just received.

" } } + }, + "traits": { + "smithy.api#input": {} } }, "com.amazonaws.kms#ListResourceTagsResponse": { @@ -4339,6 +4516,9 @@ "smithy.api#documentation": "

A flag that indicates whether there are more items in the list. When this\n value is true, the list in this response is truncated. To get more items, pass the value of\n the NextMarker element in thisresponse to the Marker parameter in a\n subsequent request.

" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.kms#ListRetirableGrants": { @@ -4367,7 +4547,7 @@ } ], "traits": { - "smithy.api#documentation": "

Returns information about all grants in the Amazon Web Services account and Region that have the\n specified retiring principal.

\n

You can specify any principal in your Amazon Web Services account. The grants that are returned include\n grants for KMS keys in your Amazon Web Services account and other Amazon Web Services accounts. You might use this\n operation to determine which grants you may retire. To retire a grant, use the RetireGrant operation.

\n

For detailed information about grants, including grant terminology, see Grants in KMS in the\n \n Key Management Service Developer Guide\n . For examples of working with grants in several\n programming languages, see Programming grants.

\n

\n Cross-account use: You must specify a principal in your\n Amazon Web Services account. However, this operation can return grants in any Amazon Web Services account. You do not need\n kms:ListRetirableGrants permission (or any other additional permission) in any\n Amazon Web Services account other than your own.

\n\n

\n Required permissions: kms:ListRetirableGrants (IAM policy) in your\n Amazon Web Services account.

\n

\n Related operations:\n

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

Returns information about all grants in the Amazon Web Services account and Region that have the\n specified retiring principal.

\n

You can specify any principal in your Amazon Web Services account. The grants that are returned include\n grants for KMS keys in your Amazon Web Services account and other Amazon Web Services accounts. You might use this\n operation to determine which grants you may retire. To retire a grant, use the RetireGrant operation.

\n

For detailed information about grants, including grant terminology, see Grants in KMS in the\n \n Key Management Service Developer Guide\n . For examples of working with grants in several\n programming languages, see Programming grants.

\n

\n Cross-account use: You must specify a principal in your\n Amazon Web Services account. However, this operation can return grants in any Amazon Web Services account. You do not need\n kms:ListRetirableGrants permission (or any other additional permission) in any\n Amazon Web Services account other than your own.

\n

\n Required permissions: kms:ListRetirableGrants (IAM policy) in your\n Amazon Web Services account.

\n

\n Related operations:\n

\n ", "smithy.api#paginated": { "inputToken": "Marker", "outputToken": "NextMarker", @@ -4394,10 +4574,13 @@ "RetiringPrincipal": { "target": "com.amazonaws.kms#PrincipalIdType", "traits": { - "smithy.api#documentation": "

The retiring principal for which to list grants. Enter a principal in your\n Amazon Web Services account.

\n

To specify the retiring principal, use the Amazon Resource Name (ARN) of an\n Amazon Web Services principal. Valid Amazon Web Services principals include Amazon Web Services accounts (root), IAM users, federated\n users, and assumed role users. For examples of the ARN syntax for specifying a principal, see\n Amazon Web Services Identity and Access Management (IAM) in the Example ARNs section of the\n Amazon Web Services General Reference.

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

The retiring principal for which to list grants. Enter a principal in your\n Amazon Web Services account.

\n

To specify the retiring principal, use the Amazon Resource Name (ARN) of an\n Amazon Web Services principal. Valid principals include Amazon Web Services accounts, IAM users, IAM roles,\n federated users, and assumed role users. For help with the ARN syntax for a principal, see\n IAM ARNs in the \n Identity and Access Management User Guide\n .

", "smithy.api#required": {} } } + }, + "traits": { + "smithy.api#input": {} } }, "com.amazonaws.kms#MacAlgorithmSpec": { @@ -4705,7 +4888,7 @@ } ], "traits": { - "smithy.api#documentation": "

Attaches a key policy to the specified KMS key.

\n

For more information about key policies, see Key Policies in the Key Management Service Developer Guide.\n For help writing and formatting a JSON policy document, see the IAM JSON Policy Reference in the \n Identity and Access Management User Guide\n . For examples of adding a key policy in multiple programming languages,\n see Setting a key policy 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

\n Required permissions: kms:PutKeyPolicy (key policy)

\n

\n Related operations: GetKeyPolicy\n

" + "smithy.api#documentation": "

Attaches a key policy to the specified KMS key.

\n

For more information about key policies, see Key Policies in the Key Management Service Developer Guide.\n For help writing and formatting a JSON policy document, see the IAM JSON Policy Reference in the \n Identity and Access Management User Guide\n . For examples of adding a key policy in multiple programming languages,\n see Setting a key policy 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:PutKeyPolicy (key policy)

\n

\n Related operations: GetKeyPolicy\n

" } }, "com.amazonaws.kms#PutKeyPolicyRequest": { @@ -4714,7 +4897,7 @@ "KeyId": { "target": "com.amazonaws.kms#KeyIdType", "traits": { - "smithy.api#documentation": "

Sets the key policy on the specified KMS key.

\n \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": "

Sets the key policy on the specified KMS key.

\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": {} } }, @@ -4728,7 +4911,7 @@ "Policy": { "target": "com.amazonaws.kms#PolicyType", "traits": { - "smithy.api#documentation": "

The key policy to attach to the KMS key.

\n

The key policy must meet the following criteria:

\n
    \n
  • \n

    If you don't set BypassPolicyLockoutSafetyCheck to true, the key policy\n must allow the principal that is making the PutKeyPolicy request to make a\n subsequent PutKeyPolicy request on the KMS key. This reduces the risk that\n the KMS key becomes unmanageable. For more information, refer to the scenario in the\n Default Key Policy section of the Key Management Service Developer Guide.

    \n
  • \n
  • \n

    Each statement in the key policy must contain one or more principals. The principals\n in the key policy must exist and be visible to KMS. When you create a new Amazon Web Services\n principal (for example, an IAM user or role), you might need to enforce a delay before\n including the new principal in a key policy because the new principal might not be\n immediately visible to KMS. For more information, see Changes that I make are not always immediately visible in the Amazon Web Services\n Identity and Access Management User Guide.

    \n
  • \n
\n \n

A key policy document can include only the following characters:

\n
    \n
  • \n

    Printable ASCII characters from the space character (\\u0020) through the end of the ASCII character range.

    \n
  • \n
  • \n

    Printable characters in the Basic Latin and Latin-1 Supplement character set (through \\u00FF).

    \n
  • \n
  • \n

    The tab (\\u0009), line feed (\\u000A), and carriage return (\\u000D) special characters

    \n
  • \n
\n

For information about key policies, see Key policies in KMS in the\n Key Management Service Developer Guide.For help writing and formatting a JSON policy document, see the IAM JSON Policy Reference in the \n Identity and Access Management User Guide\n .

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

The key policy to attach to the KMS key.

\n

The key policy must meet the following criteria:

\n
    \n
  • \n

    The key policy must allow the calling principal to make a\n subsequent PutKeyPolicy request on the KMS key. This reduces the risk that\n the KMS key becomes unmanageable. For more information, see Default key policy in the Key Management Service Developer Guide. (To omit\n this condition, set BypassPolicyLockoutSafetyCheck to true.)

    \n
  • \n
  • \n

    Each statement in the key policy must contain one or more principals. The principals\n in the key policy must exist and be visible to KMS. When you create a new Amazon Web Services\n principal, you might need to enforce a delay before including the new principal in a key\n policy because the new principal might not be immediately visible to KMS. For more\n information, see Changes that I make are not always immediately visible in the Amazon Web Services\n Identity and Access Management User Guide.

    \n
  • \n
\n

A key policy document can include only the following characters:

\n
    \n
  • \n

    Printable ASCII characters from the space character (\\u0020) through the end of the ASCII character range.

    \n
  • \n
  • \n

    Printable characters in the Basic Latin and Latin-1 Supplement character set (through \\u00FF).

    \n
  • \n
  • \n

    The tab (\\u0009), line feed (\\u000A), and carriage return (\\u000D) special characters

    \n
  • \n
\n

For information about key policies, see Key policies in KMS in the\n Key Management Service Developer Guide.For help writing and formatting a JSON policy document, see the IAM JSON Policy Reference in the \n Identity and Access Management User Guide\n .

", "smithy.api#required": {} } }, @@ -4736,9 +4919,12 @@ "target": "com.amazonaws.kms#BooleanType", "traits": { "smithy.api#default": false, - "smithy.api#documentation": "

A flag to indicate whether to bypass the key policy lockout safety check.

\n \n

Setting this value to true increases the risk that the KMS key becomes unmanageable. Do\n not set this value to true indiscriminately.

\n

For more information, refer to the scenario in the Default Key Policy section in the Key Management Service Developer Guide.

\n
\n

Use this parameter only when you intend to prevent the principal that is making the\n request from making a subsequent PutKeyPolicy request on the KMS key.

\n

The default value is false.

" + "smithy.api#documentation": "

Skips (\"bypasses\") the key policy lockout safety check. The default value is false.

\n \n

Setting this value to true increases the risk that the KMS key becomes unmanageable. Do\n not set this value to true indiscriminately.

\n

For more information, see Default key policy in the Key Management Service Developer Guide.

\n
\n

Use this parameter only when you intend to prevent the principal that is making the\n request from making a subsequent PutKeyPolicy request on the KMS key.

" } } + }, + "traits": { + "smithy.api#input": {} } }, "com.amazonaws.kms#ReEncrypt": { @@ -4782,7 +4968,7 @@ } ], "traits": { - "smithy.api#documentation": "

Decrypts ciphertext and then reencrypts it entirely within KMS. You can use this\n operation to change the KMS key under which data is encrypted, such as when you manually\n rotate a KMS key or change the KMS key that protects a ciphertext. You can also use\n it to reencrypt ciphertext under the same KMS key, such as to change the encryption\n context of a ciphertext.

\n

The ReEncrypt operation can decrypt ciphertext that was encrypted by using a\n KMS key in an KMS operation, such as Encrypt or GenerateDataKey. It can also decrypt ciphertext that was encrypted by using the\n public key of an asymmetric KMS key\n outside of KMS. However, it cannot decrypt ciphertext produced by other libraries, such as\n the Amazon Web Services Encryption SDK or\n Amazon S3\n client-side encryption. These libraries return a ciphertext format that is\n incompatible with KMS.

\n

When you use the ReEncrypt operation, you need to provide information for the\n decrypt operation and the subsequent encrypt operation.

\n
    \n
  • \n

    If your ciphertext was encrypted under an asymmetric KMS key, you must use the\n SourceKeyId parameter to identify the KMS key that encrypted the\n ciphertext. You must also supply the encryption algorithm that was used. This information\n is required to decrypt the data.

    \n
  • \n
  • \n

    If your ciphertext was encrypted under a symmetric encryption KMS key, the\n SourceKeyId parameter is optional. KMS can get this information from\n metadata that it adds to the symmetric ciphertext blob. This feature adds durability to\n your implementation by ensuring that authorized users can decrypt ciphertext decades after\n it was encrypted, even if they've lost track of the key ID. However, specifying the source\n KMS key is always recommended as a best practice. When you use the\n SourceKeyId parameter to specify a KMS key, KMS uses only the KMS key you\n specify. If the ciphertext was encrypted under a different KMS key, the\n ReEncrypt operation fails. This practice ensures that you use the KMS key\n that you intend.

    \n
  • \n
  • \n

    To reencrypt the data, you must use the DestinationKeyId parameter to\n specify the KMS key that re-encrypts the data after it is decrypted. If the destination\n KMS key is an asymmetric KMS key, you must also provide the encryption algorithm. The\n algorithm that you choose must be compatible with the KMS key.

    \n\n \n

    When you use an asymmetric KMS key to encrypt or reencrypt data, be sure to record the KMS key and encryption algorithm that you choose. You will be required to provide the same KMS key and encryption algorithm when you decrypt the data. If the KMS key and algorithm do not match the values used to encrypt the data, the decrypt operation fails.

    \n

    You are not required to supply the key ID and encryption algorithm when you decrypt with symmetric encryption KMS keys because KMS stores this information in the ciphertext blob. KMS cannot store metadata in ciphertext generated with asymmetric keys. The standard format for asymmetric key ciphertext does not include configurable fields.

    \n
    \n
  • \n
\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. The source KMS key and\n destination KMS key can be in different Amazon Web Services accounts. Either or both KMS keys can be in a\n different account than the caller. To specify a KMS key in a different account, you must use\n its key ARN or alias ARN.

\n\n

\n Required permissions:

\n
    \n
  • \n

    \n kms:ReEncryptFrom\n permission on the source KMS key (key policy)

    \n
  • \n
  • \n

    \n kms:ReEncryptTo\n permission on the destination KMS key (key policy)

    \n
  • \n
\n

To permit reencryption from or to a KMS key, include the \"kms:ReEncrypt*\"\n permission in your key policy. This permission is\n automatically included in the key policy when you use the console to create a KMS key. But you\n must include it manually when you create a KMS key programmatically or when you use the PutKeyPolicy operation to set a key policy.

\n\n

\n Related operations:\n

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

Decrypts ciphertext and then reencrypts it entirely within KMS. You can use this\n operation to change the KMS key under which data is encrypted, such as when you manually\n rotate a KMS key or change the KMS key that protects a ciphertext. You can also use\n it to reencrypt ciphertext under the same KMS key, such as to change the encryption\n context of a ciphertext.

\n

The ReEncrypt operation can decrypt ciphertext that was encrypted by using a\n KMS key in an KMS operation, such as Encrypt or GenerateDataKey. It can also decrypt ciphertext that was encrypted by using the\n public key of an asymmetric KMS key\n outside of KMS. However, it cannot decrypt ciphertext produced by other libraries, such as\n the Amazon Web Services Encryption SDK or\n Amazon S3\n client-side encryption. These libraries return a ciphertext format that is\n incompatible with KMS.

\n

When you use the ReEncrypt operation, you need to provide information for the\n decrypt operation and the subsequent encrypt operation.

\n
    \n
  • \n

    If your ciphertext was encrypted under an asymmetric KMS key, you must use the\n SourceKeyId parameter to identify the KMS key that encrypted the\n ciphertext. You must also supply the encryption algorithm that was used. This information\n is required to decrypt the data.

    \n
  • \n
  • \n

    If your ciphertext was encrypted under a symmetric encryption KMS key, the\n SourceKeyId parameter is optional. KMS can get this information from\n metadata that it adds to the symmetric ciphertext blob. This feature adds durability to\n your implementation by ensuring that authorized users can decrypt ciphertext decades after\n it was encrypted, even if they've lost track of the key ID. However, specifying the source\n KMS key is always recommended as a best practice. When you use the\n SourceKeyId parameter to specify a KMS key, KMS uses only the KMS key you\n specify. If the ciphertext was encrypted under a different KMS key, the\n ReEncrypt operation fails. This practice ensures that you use the KMS key\n that you intend.

    \n
  • \n
  • \n

    To reencrypt the data, you must use the DestinationKeyId parameter to\n specify the KMS key that re-encrypts the data after it is decrypted. If the destination\n KMS key is an asymmetric KMS key, you must also provide the encryption algorithm. The\n algorithm that you choose must be compatible with the KMS key.

    \n \n

    When you use an asymmetric KMS key to encrypt or reencrypt data, be sure to record the KMS key and encryption algorithm that you choose. You will be required to provide the same KMS key and encryption algorithm when you decrypt the data. If the KMS key and algorithm do not match the values used to encrypt the data, the decrypt operation fails.

    \n

    You are not required to supply the key ID and encryption algorithm when you decrypt with symmetric encryption KMS keys because KMS stores this information in the ciphertext blob. KMS cannot store metadata in ciphertext generated with asymmetric keys. The standard format for asymmetric key ciphertext does not include configurable fields.

    \n
    \n
  • \n
\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. The source KMS key and\n destination KMS key can be in different Amazon Web Services accounts. Either or both KMS keys can be in a\n different account than the caller. To specify a KMS key in a different account, you must use\n its key ARN or alias ARN.

\n

\n Required permissions:

\n
    \n
  • \n

    \n kms:ReEncryptFrom\n permission on the source KMS key (key policy)

    \n
  • \n
  • \n

    \n kms:ReEncryptTo\n permission on the destination KMS key (key policy)

    \n
  • \n
\n

To permit reencryption from or to a KMS key, include the \"kms:ReEncrypt*\"\n permission in your key policy. This permission is\n automatically included in the key policy when you use the console to create a KMS key. But you\n must include it manually when you create a KMS key programmatically or when you use the PutKeyPolicy operation to set a key policy.

\n

\n Related operations:\n

\n " } }, "com.amazonaws.kms#ReEncryptRequest": { @@ -4804,13 +4990,13 @@ "SourceKeyId": { "target": "com.amazonaws.kms#KeyIdType", "traits": { - "smithy.api#documentation": "

Specifies the KMS key that KMS will use to decrypt the ciphertext before it is\n re-encrypted.

\n

Enter a key ID of the KMS key that was used to encrypt the ciphertext. If you identify a\n different KMS key, the ReEncrypt operation throws an\n IncorrectKeyException.

\n

This parameter is required only when the ciphertext was encrypted under an asymmetric KMS\n key. If you used a symmetric encryption KMS key, KMS can get the KMS key from metadata that\n it adds to the symmetric ciphertext blob. However, it is always recommended as a best\n practice. This practice ensures that you use the KMS key that you intend.

\n \n

To specify a KMS key, use its key ID, key ARN, alias name, or alias ARN. When using an alias name, prefix it with \"alias/\". To specify a KMS key in a different Amazon Web Services account, you must use the key ARN or alias ARN.

\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

    Alias name: alias/ExampleAlias\n

    \n
  • \n
  • \n

    Alias ARN: arn:aws:kms:us-east-2:111122223333:alias/ExampleAlias\n

    \n
  • \n
\n

To get the key ID and key ARN for a KMS key, use ListKeys or DescribeKey. To get the alias name and alias ARN, use ListAliases.

" + "smithy.api#documentation": "

Specifies the KMS key that KMS will use to decrypt the ciphertext before it is\n re-encrypted.

\n

Enter a key ID of the KMS key that was used to encrypt the ciphertext. If you identify a\n different KMS key, the ReEncrypt operation throws an\n IncorrectKeyException.

\n

This parameter is required only when the ciphertext was encrypted under an asymmetric KMS\n key. If you used a symmetric encryption KMS key, KMS can get the KMS key from metadata that\n it adds to the symmetric ciphertext blob. However, it is always recommended as a best\n practice. This practice ensures that you use the KMS key that you intend.

\n

To specify a KMS key, use its key ID, key ARN, alias name, or alias ARN. When using an alias name, prefix it with \"alias/\". To specify a KMS key in a different Amazon Web Services account, you must use the key ARN or alias ARN.

\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

    Alias name: alias/ExampleAlias\n

    \n
  • \n
  • \n

    Alias ARN: arn:aws:kms:us-east-2:111122223333:alias/ExampleAlias\n

    \n
  • \n
\n

To get the key ID and key ARN for a KMS key, use ListKeys or DescribeKey. To get the alias name and alias ARN, use ListAliases.

" } }, "DestinationKeyId": { "target": "com.amazonaws.kms#KeyIdType", "traits": { - "smithy.api#documentation": "

A unique identifier for the KMS key that is used to reencrypt the data. Specify a\n symmetric encryption KMS key or an asymmetric KMS key with a KeyUsage value of\n ENCRYPT_DECRYPT. To find the KeyUsage value of a KMS key, use the\n DescribeKey operation.

\n \n

To specify a KMS key, use its key ID, key ARN, alias name, or alias ARN. When using an alias name, prefix it with \"alias/\". To specify a KMS key in a different Amazon Web Services account, you must use the key ARN or alias ARN.

\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

    Alias name: alias/ExampleAlias\n

    \n
  • \n
  • \n

    Alias ARN: arn:aws:kms:us-east-2:111122223333:alias/ExampleAlias\n

    \n
  • \n
\n

To get the key ID and key ARN for a KMS key, use ListKeys or DescribeKey. To get the alias name and alias ARN, use ListAliases.

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

A unique identifier for the KMS key that is used to reencrypt the data. Specify a\n symmetric encryption KMS key or an asymmetric KMS key with a KeyUsage value of\n ENCRYPT_DECRYPT. To find the KeyUsage value of a KMS key, use the\n DescribeKey operation.

\n

To specify a KMS key, use its key ID, key ARN, alias name, or alias ARN. When using an alias name, prefix it with \"alias/\". To specify a KMS key in a different Amazon Web Services account, you must use the key ARN or alias ARN.

\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

    Alias name: alias/ExampleAlias\n

    \n
  • \n
  • \n

    Alias ARN: arn:aws:kms:us-east-2:111122223333:alias/ExampleAlias\n

    \n
  • \n
\n

To get the key ID and key ARN for a KMS key, use ListKeys or DescribeKey. To get the alias name and alias ARN, use ListAliases.

", "smithy.api#required": {} } }, @@ -4838,6 +5024,9 @@ "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.

" } } + }, + "traits": { + "smithy.api#input": {} } }, "com.amazonaws.kms#ReEncryptResponse": { @@ -4873,6 +5062,9 @@ "smithy.api#documentation": "

The encryption algorithm that was used to reencrypt the data.

" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.kms#RegionType": { @@ -4935,7 +5127,7 @@ "KeyId": { "target": "com.amazonaws.kms#KeyIdType", "traits": { - "smithy.api#documentation": "

Identifies the multi-Region primary key that is being replicated. To determine whether a\n KMS key is a multi-Region primary key, use the DescribeKey operation to\n check the value of the MultiRegionKeyType property.

\n \n

Specify the key ID or key ARN of a multi-Region primary key.

\n

For example:

\n
    \n
  • \n

    Key ID: mrk-1234abcd12ab34cd56ef1234567890ab\n

    \n
  • \n
  • \n

    Key ARN: arn:aws:kms:us-east-2:111122223333:key/mrk-1234abcd12ab34cd56ef1234567890ab\n

    \n
  • \n
\n

To get the key ID and key ARN for a KMS key, use ListKeys or DescribeKey.

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

Identifies the multi-Region primary key that is being replicated. To determine whether a\n KMS key is a multi-Region primary key, use the DescribeKey operation to\n check the value of the MultiRegionKeyType property.

\n

Specify the key ID or key ARN of a multi-Region primary key.

\n

For example:

\n
    \n
  • \n

    Key ID: mrk-1234abcd12ab34cd56ef1234567890ab\n

    \n
  • \n
  • \n

    Key ARN: arn:aws:kms:us-east-2:111122223333:key/mrk-1234abcd12ab34cd56ef1234567890ab\n

    \n
  • \n
\n

To get the key ID and key ARN for a KMS key, use ListKeys or DescribeKey.

", "smithy.api#required": {} } }, @@ -4949,14 +5141,14 @@ "Policy": { "target": "com.amazonaws.kms#PolicyType", "traits": { - "smithy.api#documentation": "

The key policy to attach to the KMS key. This parameter is optional. If you do not provide\n a key policy, KMS attaches the default key policy to the\n KMS key.

\n

The key policy is not a shared property of multi-Region keys. You can specify the same key\n policy or a different key policy for each key in a set of related multi-Region keys. KMS\n does not synchronize this property.

\n

If you provide a key policy, it must meet the following criteria:

\n
    \n
  • \n

    If you don't set BypassPolicyLockoutSafetyCheck to true, the key policy\n must give the caller kms:PutKeyPolicy permission on the replica key. This\n reduces the risk that the KMS key becomes unmanageable. For more information, refer to the\n scenario in the Default Key Policy section of the \n Key Management Service Developer Guide\n .

    \n
  • \n
  • \n

    Each statement in the key policy must contain one or more principals. The principals\n in the key policy must exist and be visible to KMS. When you create a new Amazon Web Services\n principal (for example, an IAM user or role), you might need to enforce a delay before\n including the new principal in a key policy because the new principal might not be\n immediately visible to KMS. For more information, see Changes that I make are not always immediately visible in the\n \n Identity and Access Management User Guide\n .

    \n
  • \n
\n \n

A key policy document can include only the following characters:

\n
    \n
  • \n

    Printable ASCII characters from the space character (\\u0020) through the end of the ASCII character range.

    \n
  • \n
  • \n

    Printable characters in the Basic Latin and Latin-1 Supplement character set (through \\u00FF).

    \n
  • \n
  • \n

    The tab (\\u0009), line feed (\\u000A), and carriage return (\\u000D) special characters

    \n
  • \n
\n

For information about key policies, see Key policies in KMS in the Key Management Service Developer Guide.\n For help writing and formatting a JSON policy document, see the IAM JSON Policy Reference in the \n Identity and Access Management User Guide\n .

" + "smithy.api#documentation": "

The key policy to attach to the KMS key. This parameter is optional. If you do not provide\n a key policy, KMS attaches the default key policy to the\n KMS key.

\n

The key policy is not a shared property of multi-Region keys. You can specify the same key\n policy or a different key policy for each key in a set of related multi-Region keys. KMS\n does not synchronize this property.

\n

If you provide a key policy, it must meet the following criteria:

\n
    \n
  • \n

    The key policy must allow the calling principal to make a\n subsequent PutKeyPolicy request on the KMS key. This reduces the risk that\n the KMS key becomes unmanageable. For more information, see Default key policy in the Key Management Service Developer Guide. (To omit\n this condition, set BypassPolicyLockoutSafetyCheck to true.)

    \n
  • \n
  • \n

    Each statement in the key policy must contain one or more principals. The principals\n in the key policy must exist and be visible to KMS. When you create a new Amazon Web Services\n principal, you might need to enforce a delay before including the new principal in a key\n policy because the new principal might not be immediately visible to KMS. For more\n information, see Changes that I make are not always immediately visible in the Amazon Web Services\n Identity and Access Management User Guide.

    \n
  • \n
\n

A key policy document can include only the following characters:

\n
    \n
  • \n

    Printable ASCII characters from the space character (\\u0020) through the end of the ASCII character range.

    \n
  • \n
  • \n

    Printable characters in the Basic Latin and Latin-1 Supplement character set (through \\u00FF).

    \n
  • \n
  • \n

    The tab (\\u0009), line feed (\\u000A), and carriage return (\\u000D) special characters

    \n
  • \n
\n

For information about key policies, see Key policies in KMS in the Key Management Service Developer Guide.\n For help writing and formatting a JSON policy document, see the IAM JSON Policy Reference in the \n Identity and Access Management User Guide\n .

" } }, "BypassPolicyLockoutSafetyCheck": { "target": "com.amazonaws.kms#BooleanType", "traits": { "smithy.api#default": false, - "smithy.api#documentation": "

A flag to indicate whether to bypass the key policy lockout safety check.

\n \n

Setting this value to true increases the risk that the KMS key becomes unmanageable. Do\n not set this value to true indiscriminately.

\n

For more information, refer to the scenario in the Default Key Policy section in the Key Management Service Developer Guide.

\n
\n

Use this parameter only when you intend to prevent the principal that is making the\n request from making a subsequent PutKeyPolicy request on the KMS key.

\n

The default value is false.

" + "smithy.api#documentation": "

Skips (\"bypasses\") the key policy lockout safety check. The default value is false.

\n \n

Setting this value to true increases the risk that the KMS key becomes unmanageable. Do\n not set this value to true indiscriminately.

\n

For more information, see Default key policy in the Key Management Service Developer Guide.

\n
\n

Use this parameter only when you intend to prevent the principal that is making the\n request from making a subsequent PutKeyPolicy request on the KMS key.

" } }, "Description": { @@ -4971,6 +5163,9 @@ "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.

" } } + }, + "traits": { + "smithy.api#input": {} } }, "com.amazonaws.kms#ReplicateKeyResponse": { @@ -4994,6 +5189,9 @@ "smithy.api#documentation": "

The tags on the new replica key. The value is a list of tag key and tag value\n pairs.

" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.kms#RetireGrant": { @@ -5052,6 +5250,9 @@ "smithy.api#documentation": "

Identifies the grant to retire. To get the grant ID, use CreateGrant,\n ListGrants, or ListRetirableGrants.

\n
    \n
  • \n

    Grant ID Example -\n 0123456789012345678901234567890123456789012345678901234567890123

    \n
  • \n
" } } + }, + "traits": { + "smithy.api#input": {} } }, "com.amazonaws.kms#RevokeGrant": { @@ -5092,7 +5293,7 @@ "KeyId": { "target": "com.amazonaws.kms#KeyIdType", "traits": { - "smithy.api#documentation": "

A unique identifier for the KMS key associated with the grant. To get the key ID and key\n ARN for a KMS key, use ListKeys or DescribeKey.

\n \n

Specify the key ID or key ARN of the KMS key. To specify a KMS key in a\ndifferent Amazon Web Services account, you must use the key ARN.

\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": "

A unique identifier for the KMS key associated with the grant. To get the key ID and key\n ARN for a KMS key, use ListKeys or DescribeKey.

\n

Specify the key ID or key ARN of the KMS key. To specify a KMS key in a\ndifferent Amazon Web Services account, you must use the key ARN.

\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": {} } }, @@ -5103,6 +5304,9 @@ "smithy.api#required": {} } } + }, + "traits": { + "smithy.api#input": {} } }, "com.amazonaws.kms#ScheduleKeyDeletion": { @@ -5131,7 +5335,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\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 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 " } }, "com.amazonaws.kms#ScheduleKeyDeletionRequest": { @@ -5140,7 +5344,7 @@ "KeyId": { "target": "com.amazonaws.kms#KeyIdType", "traits": { - "smithy.api#documentation": "

The unique identifier of the KMS key to delete.

\n \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 unique identifier of the KMS key to delete.

\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": {} } }, @@ -5150,6 +5354,9 @@ "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.

" } } + }, + "traits": { + "smithy.api#input": {} } }, "com.amazonaws.kms#ScheduleKeyDeletionResponse": { @@ -5179,6 +5386,9 @@ "smithy.api#documentation": "

The waiting period before the KMS key is deleted.

\n

If the KMS key is a multi-Region primary key with replicas, the waiting period begins when\n the last of its replica keys is deleted. Otherwise, the waiting period begins\n immediately.

" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.kms#Sign": { @@ -5216,7 +5426,7 @@ } ], "traits": { - "smithy.api#documentation": "

Creates a digital\n signature for a message or message digest by using the private key in an asymmetric\n signing KMS key. To verify the signature, use the Verify operation, or use\n the public key in the same asymmetric KMS key outside of KMS. For information about asymmetric KMS keys, see Asymmetric KMS keys in the Key Management Service Developer Guide.

\n

Digital signatures are generated and verified by using asymmetric key pair, such as an RSA\n or ECC pair that is represented by an asymmetric KMS key. The key owner (or an authorized\n user) uses their private key to sign a message. Anyone with the public key can verify that the\n message was signed with that particular private key and that the message hasn't changed since\n it was signed.

\n

To use the Sign operation, provide the following information:

\n
    \n
  • \n

    Use the KeyId parameter to identify an asymmetric KMS key with a\n KeyUsage value of SIGN_VERIFY. To get the\n KeyUsage value of a KMS key, use the DescribeKey\n operation. The caller must have kms:Sign permission on the KMS key.

    \n
  • \n
  • \n

    Use the Message parameter to specify the message or message digest to\n sign. You can submit messages of up to 4096 bytes. To sign a larger message, generate a\n hash digest of the message, and then provide the hash digest in the Message\n parameter. To indicate whether the message is a full message or a digest, use the\n MessageType parameter.

    \n
  • \n
  • \n

    Choose a signing algorithm that is compatible with the KMS key.

    \n
  • \n
\n \n

When signing a message, be sure to record the KMS key and the signing algorithm. This\n information is required to verify the signature.

\n
\n \n

Best practices recommend that you limit the time during which any signature is\n effective. This deters an attack where the actor uses a signed message to establish validity\n repeatedly or long after the message is superseded. Signatures do not include a timestamp,\n but you can include a timestamp in the signed message to help you detect when its time to\n refresh the signature.

\n
\n

To verify the signature that this operation generates, use the Verify\n operation. Or use the GetPublicKey operation to download the public key and\n then use the public key to verify the signature outside of KMS.

\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

\n Required permissions: kms:Sign (key policy)

\n

\n Related operations: Verify\n

" + "smithy.api#documentation": "

Creates a digital\n signature for a message or message digest by using the private key in an asymmetric\n signing KMS key. To verify the signature, use the Verify operation, or use\n the public key in the same asymmetric KMS key outside of KMS. For information about asymmetric KMS keys, see Asymmetric KMS keys in the Key Management Service Developer Guide.

\n

Digital signatures are generated and verified by using asymmetric key pair, such as an RSA\n or ECC pair that is represented by an asymmetric KMS key. The key owner (or an authorized\n user) uses their private key to sign a message. Anyone with the public key can verify that the\n message was signed with that particular private key and that the message hasn't changed since\n it was signed.

\n

To use the Sign operation, provide the following information:

\n
    \n
  • \n

    Use the KeyId parameter to identify an asymmetric KMS key with a\n KeyUsage value of SIGN_VERIFY. To get the\n KeyUsage value of a KMS key, use the DescribeKey\n operation. The caller must have kms:Sign permission on the KMS key.

    \n
  • \n
  • \n

    Use the Message parameter to specify the message or message digest to\n sign. You can submit messages of up to 4096 bytes. To sign a larger message, generate a\n hash digest of the message, and then provide the hash digest in the Message\n parameter. To indicate whether the message is a full message or a digest, use the\n MessageType parameter.

    \n
  • \n
  • \n

    Choose a signing algorithm that is compatible with the KMS key.

    \n
  • \n
\n \n

When signing a message, be sure to record the KMS key and the signing algorithm. This\n information is required to verify the signature.

\n
\n \n

Best practices recommend that you limit the time during which any signature is\n effective. This deters an attack where the actor uses a signed message to establish validity\n repeatedly or long after the message is superseded. Signatures do not include a timestamp,\n but you can include a timestamp in the signed message to help you detect when its time to\n refresh the signature.

\n
\n

To verify the signature that this operation generates, use the Verify\n operation. Or use the GetPublicKey operation to download the public key and\n then use the public key to verify the signature outside of KMS.

\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:Sign (key policy)

\n

\n Related operations: Verify\n

" } }, "com.amazonaws.kms#SignRequest": { @@ -5225,21 +5435,21 @@ "KeyId": { "target": "com.amazonaws.kms#KeyIdType", "traits": { - "smithy.api#documentation": "

Identifies an asymmetric KMS key. KMS uses the private key in the asymmetric KMS key to\n sign the message. The KeyUsage type of the KMS key must be\n SIGN_VERIFY. To find the KeyUsage of a KMS key, use the DescribeKey operation.

\n \n

To specify a KMS key, use its key ID, key ARN, alias name, or alias ARN. When using an alias name, prefix it with \"alias/\". To specify a KMS key in a different Amazon Web Services account, you must use the key ARN or alias ARN.

\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

    Alias name: alias/ExampleAlias\n

    \n
  • \n
  • \n

    Alias ARN: arn:aws:kms:us-east-2:111122223333:alias/ExampleAlias\n

    \n
  • \n
\n

To get the key ID and key ARN for a KMS key, use ListKeys or DescribeKey. To get the alias name and alias ARN, use ListAliases.

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

Identifies an asymmetric KMS key. KMS uses the private key in the asymmetric KMS key to\n sign the message. The KeyUsage type of the KMS key must be\n SIGN_VERIFY. To find the KeyUsage of a KMS key, use the DescribeKey operation.

\n

To specify a KMS key, use its key ID, key ARN, alias name, or alias ARN. When using an alias name, prefix it with \"alias/\". To specify a KMS key in a different Amazon Web Services account, you must use the key ARN or alias ARN.

\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

    Alias name: alias/ExampleAlias\n

    \n
  • \n
  • \n

    Alias ARN: arn:aws:kms:us-east-2:111122223333:alias/ExampleAlias\n

    \n
  • \n
\n

To get the key ID and key ARN for a KMS key, use ListKeys or DescribeKey. To get the alias name and alias ARN, use ListAliases.

", "smithy.api#required": {} } }, "Message": { "target": "com.amazonaws.kms#PlaintextType", "traits": { - "smithy.api#documentation": "

Specifies the message or message digest to sign. Messages can be 0-4096 bytes. To sign a\n larger message, provide the message digest.

\n

If you provide a message, KMS generates a hash digest of the message and then signs\n it.

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

Specifies the message or message digest to sign. Messages can be 0-4096 bytes. To sign a\n larger message, provide a message digest.

\n

If you provide a message digest, use the DIGEST value of MessageType to\n prevent the digest from being hashed again while signing.

", "smithy.api#required": {} } }, "MessageType": { "target": "com.amazonaws.kms#MessageType", "traits": { - "smithy.api#documentation": "

Tells KMS whether the value of the Message parameter is a message or\n message digest. The default value, RAW, indicates a message. To indicate a message digest,\n enter DIGEST.

" + "smithy.api#documentation": "

Tells KMS whether the value of the Message parameter should be hashed\n as part of the signing algorithm. Use RAW for unhashed messages; use DIGEST\n for message digests, which are already hashed.

\n

When the value of MessageType is RAW, KMS uses the standard\n signing algorithm, which begins with a hash function. When the value is DIGEST, KMS skips\n the hashing step in the signing algorithm.

\n \n

Use the DIGEST value only when the value of the Message\n parameter is a message digest. If you use the DIGEST value with an unhashed message,\n the security of the signing operation can be compromised.

\n
\n

When the value of MessageTypeis DIGEST, the length\n of the Message value must match the length of hashed messages for the specified signing algorithm.

\n

You can submit a message digest and omit the MessageType or specify\n RAW so the digest is hashed again while signing. However, this can cause verification failures when \n verifying with a system that assumes a single hash.

\n

The hashing algorithm in that Sign uses is based on the SigningAlgorithm value.

\n
    \n
  • \n

    Signing algorithms that end in SHA_256 use the SHA_256 hashing algorithm.

    \n
  • \n
  • \n

    Signing algorithms that end in SHA_384 use the SHA_384 hashing algorithm.

    \n
  • \n
  • \n

    Signing algorithms that end in SHA_512 use the SHA_512 hashing algorithm.

    \n
  • \n
  • \n

    SM2DSA uses the SM3 hashing algorithm. For details, see Offline verification with SM2 key pairs.

    \n
  • \n
" } }, "GrantTokens": { @@ -5251,10 +5461,13 @@ "SigningAlgorithm": { "target": "com.amazonaws.kms#SigningAlgorithmSpec", "traits": { - "smithy.api#documentation": "

Specifies the signing algorithm to use when signing the message.

\n

Choose an algorithm that is compatible with the type and size of the specified asymmetric\n KMS key.

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

Specifies the signing algorithm to use when signing the message.

\n

Choose an algorithm that is compatible with the type and size of the specified asymmetric\n KMS key. When signing with RSA key pairs, RSASSA-PSS algorithms are preferred. We include\n RSASSA-PKCS1-v1_5 algorithms for compatibility with existing applications.

", "smithy.api#required": {} } } + }, + "traits": { + "smithy.api#input": {} } }, "com.amazonaws.kms#SignResponse": { @@ -5278,6 +5491,9 @@ "smithy.api#documentation": "

The signing algorithm that was used to sign the message.

" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.kms#SigningAlgorithmSpec": { @@ -5440,7 +5656,7 @@ } ], "traits": { - "smithy.api#documentation": "

Adds or edits tags on a customer managed key.

\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

Each tag consists of a tag key and a tag value, both of which are case-sensitive strings.\n The tag value can be an empty (null) string. To add a tag, specify a new tag key and a tag\n value. To edit a tag, specify an existing tag key and a new tag value.

\n

You can use this operation to tag a customer managed key, but you cannot\n tag an Amazon Web Services\n managed key, an Amazon Web Services owned key, a custom key\n store, or an alias.

\n

You can also add tags to a KMS key while creating it (CreateKey) or\n replicating it (ReplicateKey).

\n

For information about using tags in KMS, see Tagging keys. For general information about\n tags, including the format and syntax, see Tagging Amazon Web Services resources in the Amazon\n Web Services General Reference.

\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

\n Required permissions: kms:TagResource (key policy)

\n

\n Related operations\n

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

Adds or edits tags on a customer managed key.

\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

Each tag consists of a tag key and a tag value, both of which are case-sensitive strings.\n The tag value can be an empty (null) string. To add a tag, specify a new tag key and a tag\n value. To edit a tag, specify an existing tag key and a new tag value.

\n

You can use this operation to tag a customer managed key, but you cannot\n tag an Amazon Web Services\n managed key, an Amazon Web Services owned key, a custom key\n store, or an alias.

\n

You can also add tags to a KMS key while creating it (CreateKey) or\n replicating it (ReplicateKey).

\n

For information about using tags in KMS, see Tagging keys. For general information about\n tags, including the format and syntax, see Tagging Amazon Web Services resources in the Amazon\n Web Services General Reference.

\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:TagResource (key policy)

\n

\n Related operations\n

\n " } }, "com.amazonaws.kms#TagResourceRequest": { @@ -5449,7 +5665,7 @@ "KeyId": { "target": "com.amazonaws.kms#KeyIdType", "traits": { - "smithy.api#documentation": "

Identifies a customer managed key in the account and Region.

\n \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": "

Identifies a customer managed key in the account and Region.

\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": {} } }, @@ -5460,6 +5676,9 @@ "smithy.api#required": {} } } + }, + "traits": { + "smithy.api#input": {} } }, "com.amazonaws.kms#TagValueType": { @@ -5638,7 +5857,7 @@ "name": "kms" }, "aws.protocols#awsJson1_1": {}, - "smithy.api#documentation": "Key Management Service\n

Key Management Service (KMS) is an encryption and key management web service. This guide describes\n the KMS operations that you can call programmatically. For general information about KMS,\n see the \n Key Management Service Developer Guide\n .

\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

Amazon Web Services provides SDKs that consist of libraries and sample code for various programming\n languages and platforms (Java, Ruby, .Net, macOS, Android, etc.). The SDKs provide a\n convenient way to create programmatic access to KMS and other Amazon Web Services services. For example,\n the SDKs take care of tasks such as signing requests (see below), managing errors, and\n retrying requests automatically. For more information about the Amazon Web Services SDKs, including how to\n download and install them, see Tools for Amazon Web\n Services.

\n
\n

We recommend that you use the Amazon Web Services SDKs to make programmatic API calls to KMS.

\n

If you need to use FIPS 140-2 validated cryptographic modules when communicating with\n Amazon Web Services, use the FIPS endpoint in your preferred Amazon Web Services Region. For more information about the\n available FIPS endpoints, see Service endpoints in the Key Management Service topic of\n the Amazon Web Services General Reference.

\n

All KMS API calls must be signed and be transmitted using Transport Layer Security\n (TLS). KMS recommends you always use the latest supported TLS version. Clients must also\n support cipher suites with Perfect Forward Secrecy (PFS) such as Ephemeral Diffie-Hellman\n (DHE) or Elliptic Curve Ephemeral Diffie-Hellman (ECDHE). Most modern systems such as Java 7\n and later support these modes.

\n

\n Signing Requests\n

\n

Requests must be signed by using an access key ID and a secret access key. We strongly\n recommend that you do not use your Amazon Web Services account (root) access key ID and\n secret access key for everyday work with KMS. Instead, use the access key ID and secret\n access key for an IAM user. You can also use the Amazon Web Services Security Token Service to generate\n temporary security credentials that you can use to sign requests.

\n

All KMS operations require Signature Version 4.

\n

\n Logging API Requests\n

\n

KMS supports CloudTrail, a service that logs Amazon Web Services API calls and related events for your\n Amazon Web Services account and delivers them to an Amazon S3 bucket that you specify. By using the\n information collected by CloudTrail, you can determine what requests were made to KMS, who made\n the request, when it was made, and so on. To learn more about CloudTrail, including how to turn it\n on and find your log files, see the CloudTrail User Guide.

\n

\n Additional Resources\n

\n

For more information about credentials and request signing, see the following:

\n \n

\n Commonly Used API Operations\n

\n

Of the API operations discussed in this guide, the following will prove the most useful\n for most applications. You will likely perform operations other than these, such as creating\n keys and assigning policies, by using the console.

\n ", + "smithy.api#documentation": "Key Management Service\n

Key Management Service (KMS) is an encryption and key management web service. This guide describes\n the KMS operations that you can call programmatically. For general information about KMS,\n see the \n Key Management Service Developer Guide\n .

\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

Amazon Web Services provides SDKs that consist of libraries and sample code for various programming\n languages and platforms (Java, Ruby, .Net, macOS, Android, etc.). The SDKs provide a\n convenient way to create programmatic access to KMS and other Amazon Web Services services. For example,\n the SDKs take care of tasks such as signing requests (see below), managing errors, and\n retrying requests automatically. For more information about the Amazon Web Services SDKs, including how to\n download and install them, see Tools for Amazon Web\n Services.

\n
\n

We recommend that you use the Amazon Web Services SDKs to make programmatic API calls to KMS.

\n

If you need to use FIPS 140-2 validated cryptographic modules when communicating with\n Amazon Web Services, use the FIPS endpoint in your preferred Amazon Web Services Region. For more information about the\n available FIPS endpoints, see Service endpoints in the Key Management Service topic of\n the Amazon Web Services General Reference.

\n

All KMS API calls must be signed and be transmitted using Transport Layer Security\n (TLS). KMS recommends you always use the latest supported TLS version. Clients must also\n support cipher suites with Perfect Forward Secrecy (PFS) such as Ephemeral Diffie-Hellman\n (DHE) or Elliptic Curve Ephemeral Diffie-Hellman (ECDHE). Most modern systems such as Java 7\n and later support these modes.

\n

\n Signing Requests\n

\n

Requests must be signed using an access key ID and a secret access key. We strongly\n recommend that you do not use your Amazon Web Services account root access key ID and secret access key for\n everyday work. You can use the access key ID and secret access key for an IAM user or you\n can use the Security Token Service (STS) to generate temporary security credentials and use those to sign\n requests.

\n

All KMS requests must be signed with Signature Version 4.

\n

\n Logging API Requests\n

\n

KMS supports CloudTrail, a service that logs Amazon Web Services API calls and related events for your\n Amazon Web Services account and delivers them to an Amazon S3 bucket that you specify. By using the\n information collected by CloudTrail, you can determine what requests were made to KMS, who made\n the request, when it was made, and so on. To learn more about CloudTrail, including how to turn it\n on and find your log files, see the CloudTrail User Guide.

\n

\n Additional Resources\n

\n

For more information about credentials and request signing, see the following:

\n \n

\n Commonly Used API Operations\n

\n

Of the API operations discussed in this guide, the following will prove the most useful\n for most applications. You will likely perform operations other than these, such as creating\n keys and assigning policies, by using the console.

\n ", "smithy.api#title": "AWS Key Management Service", "smithy.api#xmlNamespace": { "uri": "https://trent.amazonaws.com/doc/2014-11-01/" @@ -5648,7 +5867,7 @@ "parameters": { "Region": { "builtIn": "AWS::Region", - "required": true, + "required": false, "documentation": "The AWS region used to dispatch the request.", "type": "String" }, @@ -5677,13 +5896,12 @@ { "conditions": [ { - "fn": "aws.partition", + "fn": "isSet", "argv": [ { - "ref": "Region" + "ref": "Endpoint" } - ], - "assign": "PartitionResult" + ] } ], "type": "tree", @@ -5691,14 +5909,20 @@ { "conditions": [ { - "fn": "isSet", + "fn": "booleanEquals", "argv": [ { - "ref": "Endpoint" - } + "ref": "UseFIPS" + }, + true ] } ], + "error": "Invalid Configuration: FIPS and custom endpoint are not supported", + "type": "error" + }, + { + "conditions": [], "type": "tree", "rules": [ { @@ -5707,67 +5931,42 @@ "fn": "booleanEquals", "argv": [ { - "ref": "UseFIPS" + "ref": "UseDualStack" }, true ] } ], - "error": "Invalid Configuration: FIPS and custom endpoint are not supported", + "error": "Invalid Configuration: Dualstack 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" + "endpoint": { + "url": { + "ref": "Endpoint" }, - { - "conditions": [], - "endpoint": { - "url": { - "ref": "Endpoint" - }, - "properties": {}, - "headers": {} - }, - "type": "endpoint" - } - ] + "properties": {}, + "headers": {} + }, + "type": "endpoint" } ] - }, + } + ] + }, + { + "conditions": [], + "type": "tree", + "rules": [ { "conditions": [ { - "fn": "booleanEquals", - "argv": [ - { - "ref": "UseFIPS" - }, - true - ] - }, - { - "fn": "booleanEquals", + "fn": "isSet", "argv": [ { - "ref": "UseDualStack" - }, - true + "ref": "Region" + } ] } ], @@ -5776,154 +5975,215 @@ { "conditions": [ { - "fn": "booleanEquals", + "fn": "aws.partition", "argv": [ - true, { - "fn": "getAttr", + "ref": "Region" + } + ], + "assign": "PartitionResult" + } + ], + "type": "tree", + "rules": [ + { + "conditions": [ + { + "fn": "booleanEquals", + "argv": [ + { + "ref": "UseFIPS" + }, + true + ] + }, + { + "fn": "booleanEquals", "argv": [ { - "ref": "PartitionResult" + "ref": "UseDualStack" + }, + true + ] + } + ], + "type": "tree", + "rules": [ + { + "conditions": [ + { + "fn": "booleanEquals", + "argv": [ + true, + { + "fn": "getAttr", + "argv": [ + { + "ref": "PartitionResult" + }, + "supportsFIPS" + ] + } + ] }, - "supportsFIPS" + { + "fn": "booleanEquals", + "argv": [ + true, + { + "fn": "getAttr", + "argv": [ + { + "ref": "PartitionResult" + }, + "supportsDualStack" + ] + } + ] + } + ], + "type": "tree", + "rules": [ + { + "conditions": [], + "type": "tree", + "rules": [ + { + "conditions": [], + "endpoint": { + "url": "https://kms-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" } ] }, { - "fn": "booleanEquals", - "argv": [ - true, + "conditions": [ { - "fn": "getAttr", + "fn": "booleanEquals", "argv": [ { - "ref": "PartitionResult" + "ref": "UseFIPS" }, - "supportsDualStack" + true ] } - ] - } - ], - "type": "tree", - "rules": [ - { - "conditions": [], + ], "type": "tree", "rules": [ { - "conditions": [], - "endpoint": { - "url": "https://kms-fips.{Region}.{PartitionResult#dualStackDnsSuffix}", - "properties": {}, - "headers": {} - }, - "type": "endpoint" + "conditions": [ + { + "fn": "booleanEquals", + "argv": [ + true, + { + "fn": "getAttr", + "argv": [ + { + "ref": "PartitionResult" + }, + "supportsFIPS" + ] + } + ] + } + ], + "type": "tree", + "rules": [ + { + "conditions": [], + "type": "tree", + "rules": [ + { + "conditions": [], + "endpoint": { + "url": "https://kms-fips.{Region}.{PartitionResult#dnsSuffix}", + "properties": {}, + "headers": {} + }, + "type": "endpoint" + } + ] + } + ] + }, + { + "conditions": [], + "error": "FIPS is enabled but this partition does not support FIPS", + "type": "error" } ] - } - ] - }, - { - "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, + "conditions": [ { - "fn": "getAttr", + "fn": "booleanEquals", "argv": [ { - "ref": "PartitionResult" + "ref": "UseDualStack" }, - "supportsFIPS" + true ] } - ] - } - ], - "type": "tree", - "rules": [ - { - "conditions": [], + ], "type": "tree", "rules": [ { - "conditions": [], - "endpoint": { - "url": "https://kms-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": [ + "conditions": [ { - "ref": "PartitionResult" - }, - "supportsDualStack" + "fn": "booleanEquals", + "argv": [ + true, + { + "fn": "getAttr", + "argv": [ + { + "ref": "PartitionResult" + }, + "supportsDualStack" + ] + } + ] + } + ], + "type": "tree", + "rules": [ + { + "conditions": [], + "type": "tree", + "rules": [ + { + "conditions": [], + "endpoint": { + "url": "https://kms.{Region}.{PartitionResult#dualStackDnsSuffix}", + "properties": {}, + "headers": {} + }, + "type": "endpoint" + } + ] + } ] + }, + { + "conditions": [], + "error": "DualStack is enabled but this partition does not support DualStack", + "type": "error" } ] - } - ], - "type": "tree", - "rules": [ + }, { "conditions": [], "type": "tree", @@ -5931,7 +6191,7 @@ { "conditions": [], "endpoint": { - "url": "https://kms.{Region}.{PartitionResult#dualStackDnsSuffix}", + "url": "https://kms.{Region}.{PartitionResult#dnsSuffix}", "properties": {}, "headers": {} }, @@ -5940,28 +6200,13 @@ ] } ] - }, - { - "conditions": [], - "error": "DualStack is enabled but this partition does not support DualStack", - "type": "error" } ] }, { "conditions": [], - "type": "tree", - "rules": [ - { - "conditions": [], - "endpoint": { - "url": "https://kms.{Region}.{PartitionResult#dnsSuffix}", - "properties": {}, - "headers": {} - }, - "type": "endpoint" - } - ] + "error": "Invalid Configuration: Missing Region", + "type": "error" } ] } @@ -5970,29 +6215,29 @@ "smithy.rules#endpointTests": { "testCases": [ { - "documentation": "For region sa-east-1 with FIPS disabled and DualStack disabled", + "documentation": "For region af-south-1 with FIPS disabled and DualStack disabled", "expect": { "endpoint": { - "url": "https://kms.sa-east-1.amazonaws.com" + "url": "https://kms.af-south-1.amazonaws.com" } }, "params": { - "UseFIPS": false, "UseDualStack": false, - "Region": "sa-east-1" + "UseFIPS": false, + "Region": "af-south-1" } }, { - "documentation": "For region sa-east-1 with FIPS enabled and DualStack disabled", + "documentation": "For region af-south-1 with FIPS enabled and DualStack disabled", "expect": { "endpoint": { - "url": "https://kms-fips.sa-east-1.amazonaws.com" + "url": "https://kms-fips.af-south-1.amazonaws.com" } }, "params": { - "UseFIPS": true, "UseDualStack": false, - "Region": "sa-east-1" + "UseFIPS": true, + "Region": "af-south-1" } }, { @@ -6003,8 +6248,8 @@ } }, "params": { - "UseFIPS": false, "UseDualStack": false, + "UseFIPS": false, "Region": "ap-east-1" } }, @@ -6016,529 +6261,529 @@ } }, "params": { - "UseFIPS": true, "UseDualStack": false, + "UseFIPS": true, "Region": "ap-east-1" } }, { - "documentation": "For region eu-south-1 with FIPS disabled and DualStack disabled", + "documentation": "For region ap-northeast-1 with FIPS disabled and DualStack disabled", "expect": { "endpoint": { - "url": "https://kms.eu-south-1.amazonaws.com" + "url": "https://kms.ap-northeast-1.amazonaws.com" } }, "params": { - "UseFIPS": false, "UseDualStack": false, - "Region": "eu-south-1" + "UseFIPS": false, + "Region": "ap-northeast-1" } }, { - "documentation": "For region eu-south-1 with FIPS enabled and DualStack disabled", + "documentation": "For region ap-northeast-1 with FIPS enabled and DualStack disabled", "expect": { "endpoint": { - "url": "https://kms-fips.eu-south-1.amazonaws.com" + "url": "https://kms-fips.ap-northeast-1.amazonaws.com" } }, "params": { - "UseFIPS": true, "UseDualStack": false, - "Region": "eu-south-1" + "UseFIPS": true, + "Region": "ap-northeast-1" } }, { - "documentation": "For region eu-central-1 with FIPS disabled and DualStack disabled", + "documentation": "For region ap-northeast-2 with FIPS disabled and DualStack disabled", "expect": { "endpoint": { - "url": "https://kms.eu-central-1.amazonaws.com" + "url": "https://kms.ap-northeast-2.amazonaws.com" } }, "params": { - "UseFIPS": false, "UseDualStack": false, - "Region": "eu-central-1" + "UseFIPS": false, + "Region": "ap-northeast-2" } }, { - "documentation": "For region eu-central-1 with FIPS enabled and DualStack disabled", + "documentation": "For region ap-northeast-2 with FIPS enabled and DualStack disabled", "expect": { "endpoint": { - "url": "https://kms-fips.eu-central-1.amazonaws.com" + "url": "https://kms-fips.ap-northeast-2.amazonaws.com" } }, "params": { - "UseFIPS": true, "UseDualStack": false, - "Region": "eu-central-1" + "UseFIPS": true, + "Region": "ap-northeast-2" } }, { - "documentation": "For region ap-southeast-1 with FIPS disabled and DualStack disabled", + "documentation": "For region ap-northeast-3 with FIPS disabled and DualStack disabled", "expect": { "endpoint": { - "url": "https://kms.ap-southeast-1.amazonaws.com" + "url": "https://kms.ap-northeast-3.amazonaws.com" } }, "params": { - "UseFIPS": false, "UseDualStack": false, - "Region": "ap-southeast-1" + "UseFIPS": false, + "Region": "ap-northeast-3" } }, { - "documentation": "For region ap-southeast-1 with FIPS enabled and DualStack disabled", + "documentation": "For region ap-northeast-3 with FIPS enabled and DualStack disabled", "expect": { "endpoint": { - "url": "https://kms-fips.ap-southeast-1.amazonaws.com" + "url": "https://kms-fips.ap-northeast-3.amazonaws.com" } }, "params": { - "UseFIPS": true, "UseDualStack": false, - "Region": "ap-southeast-1" + "UseFIPS": true, + "Region": "ap-northeast-3" } }, { - "documentation": "For region ap-southeast-2 with FIPS disabled and DualStack disabled", + "documentation": "For region ap-south-1 with FIPS disabled and DualStack disabled", "expect": { "endpoint": { - "url": "https://kms.ap-southeast-2.amazonaws.com" + "url": "https://kms.ap-south-1.amazonaws.com" } }, "params": { - "UseFIPS": false, "UseDualStack": false, - "Region": "ap-southeast-2" + "UseFIPS": false, + "Region": "ap-south-1" } }, { - "documentation": "For region ap-southeast-2 with FIPS enabled and DualStack disabled", + "documentation": "For region ap-south-1 with FIPS enabled and DualStack disabled", "expect": { "endpoint": { - "url": "https://kms-fips.ap-southeast-2.amazonaws.com" + "url": "https://kms-fips.ap-south-1.amazonaws.com" } }, "params": { - "UseFIPS": true, "UseDualStack": false, - "Region": "ap-southeast-2" + "UseFIPS": true, + "Region": "ap-south-1" } }, { - "documentation": "For region ap-southeast-3 with FIPS disabled and DualStack disabled", + "documentation": "For region ap-southeast-1 with FIPS disabled and DualStack disabled", "expect": { "endpoint": { - "url": "https://kms.ap-southeast-3.amazonaws.com" + "url": "https://kms.ap-southeast-1.amazonaws.com" } }, "params": { - "UseFIPS": false, "UseDualStack": false, - "Region": "ap-southeast-3" + "UseFIPS": false, + "Region": "ap-southeast-1" } }, { - "documentation": "For region ap-southeast-3 with FIPS enabled and DualStack disabled", + "documentation": "For region ap-southeast-1 with FIPS enabled and DualStack disabled", "expect": { "endpoint": { - "url": "https://kms-fips.ap-southeast-3.amazonaws.com" + "url": "https://kms-fips.ap-southeast-1.amazonaws.com" } }, "params": { - "UseFIPS": true, "UseDualStack": false, - "Region": "ap-southeast-3" + "UseFIPS": true, + "Region": "ap-southeast-1" } }, { - "documentation": "For region ca-central-1 with FIPS disabled and DualStack disabled", + "documentation": "For region ap-southeast-2 with FIPS disabled and DualStack disabled", "expect": { "endpoint": { - "url": "https://kms.ca-central-1.amazonaws.com" + "url": "https://kms.ap-southeast-2.amazonaws.com" } }, "params": { - "UseFIPS": false, "UseDualStack": false, - "Region": "ca-central-1" + "UseFIPS": false, + "Region": "ap-southeast-2" } }, { - "documentation": "For region ca-central-1 with FIPS enabled and DualStack disabled", + "documentation": "For region ap-southeast-2 with FIPS enabled and DualStack disabled", "expect": { "endpoint": { - "url": "https://kms-fips.ca-central-1.amazonaws.com" + "url": "https://kms-fips.ap-southeast-2.amazonaws.com" } }, "params": { - "UseFIPS": true, "UseDualStack": false, - "Region": "ca-central-1" + "UseFIPS": true, + "Region": "ap-southeast-2" } }, { - "documentation": "For region us-west-1 with FIPS disabled and DualStack disabled", + "documentation": "For region ap-southeast-3 with FIPS disabled and DualStack disabled", "expect": { "endpoint": { - "url": "https://kms.us-west-1.amazonaws.com" + "url": "https://kms.ap-southeast-3.amazonaws.com" } }, "params": { - "UseFIPS": false, "UseDualStack": false, - "Region": "us-west-1" + "UseFIPS": false, + "Region": "ap-southeast-3" } }, { - "documentation": "For region us-west-1 with FIPS enabled and DualStack disabled", + "documentation": "For region ap-southeast-3 with FIPS enabled and DualStack disabled", "expect": { "endpoint": { - "url": "https://kms-fips.us-west-1.amazonaws.com" + "url": "https://kms-fips.ap-southeast-3.amazonaws.com" } }, "params": { - "UseFIPS": true, "UseDualStack": false, - "Region": "us-west-1" + "UseFIPS": true, + "Region": "ap-southeast-3" } }, { - "documentation": "For region us-west-2 with FIPS disabled and DualStack disabled", + "documentation": "For region ca-central-1 with FIPS disabled and DualStack disabled", "expect": { "endpoint": { - "url": "https://kms.us-west-2.amazonaws.com" + "url": "https://kms.ca-central-1.amazonaws.com" } }, "params": { - "UseFIPS": false, "UseDualStack": false, - "Region": "us-west-2" + "UseFIPS": false, + "Region": "ca-central-1" } }, { - "documentation": "For region us-west-2 with FIPS enabled and DualStack disabled", + "documentation": "For region ca-central-1 with FIPS enabled and DualStack disabled", "expect": { "endpoint": { - "url": "https://kms-fips.us-west-2.amazonaws.com" + "url": "https://kms-fips.ca-central-1.amazonaws.com" } }, "params": { - "UseFIPS": true, "UseDualStack": false, - "Region": "us-west-2" + "UseFIPS": true, + "Region": "ca-central-1" } }, { - "documentation": "For region af-south-1 with FIPS disabled and DualStack disabled", + "documentation": "For region eu-central-1 with FIPS disabled and DualStack disabled", "expect": { "endpoint": { - "url": "https://kms.af-south-1.amazonaws.com" + "url": "https://kms.eu-central-1.amazonaws.com" } }, "params": { - "UseFIPS": false, "UseDualStack": false, - "Region": "af-south-1" + "UseFIPS": false, + "Region": "eu-central-1" } }, { - "documentation": "For region af-south-1 with FIPS enabled and DualStack disabled", + "documentation": "For region eu-central-1 with FIPS enabled and DualStack disabled", "expect": { "endpoint": { - "url": "https://kms-fips.af-south-1.amazonaws.com" + "url": "https://kms-fips.eu-central-1.amazonaws.com" } }, "params": { - "UseFIPS": true, "UseDualStack": false, - "Region": "af-south-1" + "UseFIPS": true, + "Region": "eu-central-1" } }, { - "documentation": "For region ap-south-1 with FIPS disabled and DualStack disabled", + "documentation": "For region eu-north-1 with FIPS disabled and DualStack disabled", "expect": { "endpoint": { - "url": "https://kms.ap-south-1.amazonaws.com" + "url": "https://kms.eu-north-1.amazonaws.com" } }, "params": { - "UseFIPS": false, "UseDualStack": false, - "Region": "ap-south-1" + "UseFIPS": false, + "Region": "eu-north-1" } }, { - "documentation": "For region ap-south-1 with FIPS enabled and DualStack disabled", + "documentation": "For region eu-north-1 with FIPS enabled and DualStack disabled", "expect": { "endpoint": { - "url": "https://kms-fips.ap-south-1.amazonaws.com" + "url": "https://kms-fips.eu-north-1.amazonaws.com" } }, "params": { - "UseFIPS": true, "UseDualStack": false, - "Region": "ap-south-1" + "UseFIPS": true, + "Region": "eu-north-1" } }, { - "documentation": "For region ap-northeast-1 with FIPS disabled and DualStack disabled", + "documentation": "For region eu-south-1 with FIPS disabled and DualStack disabled", "expect": { "endpoint": { - "url": "https://kms.ap-northeast-1.amazonaws.com" + "url": "https://kms.eu-south-1.amazonaws.com" } }, "params": { - "UseFIPS": false, "UseDualStack": false, - "Region": "ap-northeast-1" + "UseFIPS": false, + "Region": "eu-south-1" } }, { - "documentation": "For region ap-northeast-1 with FIPS enabled and DualStack disabled", + "documentation": "For region eu-south-1 with FIPS enabled and DualStack disabled", "expect": { "endpoint": { - "url": "https://kms-fips.ap-northeast-1.amazonaws.com" + "url": "https://kms-fips.eu-south-1.amazonaws.com" } }, "params": { - "UseFIPS": true, "UseDualStack": false, - "Region": "ap-northeast-1" + "UseFIPS": true, + "Region": "eu-south-1" } }, { - "documentation": "For region ap-northeast-2 with FIPS disabled and DualStack disabled", + "documentation": "For region eu-west-1 with FIPS disabled and DualStack disabled", "expect": { "endpoint": { - "url": "https://kms.ap-northeast-2.amazonaws.com" + "url": "https://kms.eu-west-1.amazonaws.com" } }, "params": { - "UseFIPS": false, "UseDualStack": false, - "Region": "ap-northeast-2" + "UseFIPS": false, + "Region": "eu-west-1" } }, { - "documentation": "For region ap-northeast-2 with FIPS enabled and DualStack disabled", + "documentation": "For region eu-west-1 with FIPS enabled and DualStack disabled", "expect": { "endpoint": { - "url": "https://kms-fips.ap-northeast-2.amazonaws.com" + "url": "https://kms-fips.eu-west-1.amazonaws.com" } }, "params": { - "UseFIPS": true, "UseDualStack": false, - "Region": "ap-northeast-2" + "UseFIPS": true, + "Region": "eu-west-1" } }, { - "documentation": "For region ap-northeast-3 with FIPS disabled and DualStack disabled", + "documentation": "For region eu-west-2 with FIPS disabled and DualStack disabled", "expect": { "endpoint": { - "url": "https://kms.ap-northeast-3.amazonaws.com" + "url": "https://kms.eu-west-2.amazonaws.com" } }, "params": { - "UseFIPS": false, "UseDualStack": false, - "Region": "ap-northeast-3" + "UseFIPS": false, + "Region": "eu-west-2" } }, { - "documentation": "For region ap-northeast-3 with FIPS enabled and DualStack disabled", + "documentation": "For region eu-west-2 with FIPS enabled and DualStack disabled", "expect": { "endpoint": { - "url": "https://kms-fips.ap-northeast-3.amazonaws.com" + "url": "https://kms-fips.eu-west-2.amazonaws.com" } }, "params": { - "UseFIPS": true, "UseDualStack": false, - "Region": "ap-northeast-3" + "UseFIPS": true, + "Region": "eu-west-2" } }, { - "documentation": "For region us-east-1 with FIPS disabled and DualStack disabled", + "documentation": "For region eu-west-3 with FIPS disabled and DualStack disabled", "expect": { "endpoint": { - "url": "https://kms.us-east-1.amazonaws.com" + "url": "https://kms.eu-west-3.amazonaws.com" } }, "params": { - "UseFIPS": false, "UseDualStack": false, - "Region": "us-east-1" + "UseFIPS": false, + "Region": "eu-west-3" } }, { - "documentation": "For region us-east-1 with FIPS enabled and DualStack disabled", + "documentation": "For region eu-west-3 with FIPS enabled and DualStack disabled", "expect": { "endpoint": { - "url": "https://kms-fips.us-east-1.amazonaws.com" + "url": "https://kms-fips.eu-west-3.amazonaws.com" } }, "params": { - "UseFIPS": true, "UseDualStack": false, - "Region": "us-east-1" + "UseFIPS": true, + "Region": "eu-west-3" } }, { - "documentation": "For region eu-west-1 with FIPS disabled and DualStack disabled", + "documentation": "For region me-south-1 with FIPS disabled and DualStack disabled", "expect": { "endpoint": { - "url": "https://kms.eu-west-1.amazonaws.com" + "url": "https://kms.me-south-1.amazonaws.com" } }, "params": { - "UseFIPS": false, "UseDualStack": false, - "Region": "eu-west-1" + "UseFIPS": false, + "Region": "me-south-1" } }, { - "documentation": "For region eu-west-1 with FIPS enabled and DualStack disabled", + "documentation": "For region me-south-1 with FIPS enabled and DualStack disabled", "expect": { "endpoint": { - "url": "https://kms-fips.eu-west-1.amazonaws.com" + "url": "https://kms-fips.me-south-1.amazonaws.com" } }, "params": { - "UseFIPS": true, "UseDualStack": false, - "Region": "eu-west-1" + "UseFIPS": true, + "Region": "me-south-1" } }, { - "documentation": "For region eu-west-2 with FIPS disabled and DualStack disabled", + "documentation": "For region sa-east-1 with FIPS disabled and DualStack disabled", "expect": { "endpoint": { - "url": "https://kms.eu-west-2.amazonaws.com" + "url": "https://kms.sa-east-1.amazonaws.com" } }, "params": { - "UseFIPS": false, "UseDualStack": false, - "Region": "eu-west-2" + "UseFIPS": false, + "Region": "sa-east-1" } }, { - "documentation": "For region eu-west-2 with FIPS enabled and DualStack disabled", + "documentation": "For region sa-east-1 with FIPS enabled and DualStack disabled", "expect": { "endpoint": { - "url": "https://kms-fips.eu-west-2.amazonaws.com" + "url": "https://kms-fips.sa-east-1.amazonaws.com" } }, "params": { - "UseFIPS": true, "UseDualStack": false, - "Region": "eu-west-2" + "UseFIPS": true, + "Region": "sa-east-1" } }, { - "documentation": "For region eu-west-3 with FIPS disabled and DualStack disabled", + "documentation": "For region us-east-1 with FIPS disabled and DualStack disabled", "expect": { "endpoint": { - "url": "https://kms.eu-west-3.amazonaws.com" + "url": "https://kms.us-east-1.amazonaws.com" } }, "params": { - "UseFIPS": false, "UseDualStack": false, - "Region": "eu-west-3" + "UseFIPS": false, + "Region": "us-east-1" } }, { - "documentation": "For region eu-west-3 with FIPS enabled and DualStack disabled", + "documentation": "For region us-east-1 with FIPS enabled and DualStack disabled", "expect": { "endpoint": { - "url": "https://kms-fips.eu-west-3.amazonaws.com" + "url": "https://kms-fips.us-east-1.amazonaws.com" } }, "params": { - "UseFIPS": true, "UseDualStack": false, - "Region": "eu-west-3" + "UseFIPS": true, + "Region": "us-east-1" } }, { - "documentation": "For region me-south-1 with FIPS disabled and DualStack disabled", + "documentation": "For region us-east-2 with FIPS disabled and DualStack disabled", "expect": { "endpoint": { - "url": "https://kms.me-south-1.amazonaws.com" + "url": "https://kms.us-east-2.amazonaws.com" } }, "params": { - "UseFIPS": false, "UseDualStack": false, - "Region": "me-south-1" + "UseFIPS": false, + "Region": "us-east-2" } }, { - "documentation": "For region me-south-1 with FIPS enabled and DualStack disabled", + "documentation": "For region us-east-2 with FIPS enabled and DualStack disabled", "expect": { "endpoint": { - "url": "https://kms-fips.me-south-1.amazonaws.com" + "url": "https://kms-fips.us-east-2.amazonaws.com" } }, "params": { - "UseFIPS": true, "UseDualStack": false, - "Region": "me-south-1" + "UseFIPS": true, + "Region": "us-east-2" } }, { - "documentation": "For region eu-north-1 with FIPS disabled and DualStack disabled", + "documentation": "For region us-west-1 with FIPS disabled and DualStack disabled", "expect": { "endpoint": { - "url": "https://kms.eu-north-1.amazonaws.com" + "url": "https://kms.us-west-1.amazonaws.com" } }, "params": { - "UseFIPS": false, "UseDualStack": false, - "Region": "eu-north-1" + "UseFIPS": false, + "Region": "us-west-1" } }, { - "documentation": "For region eu-north-1 with FIPS enabled and DualStack disabled", + "documentation": "For region us-west-1 with FIPS enabled and DualStack disabled", "expect": { "endpoint": { - "url": "https://kms-fips.eu-north-1.amazonaws.com" + "url": "https://kms-fips.us-west-1.amazonaws.com" } }, "params": { - "UseFIPS": true, "UseDualStack": false, - "Region": "eu-north-1" + "UseFIPS": true, + "Region": "us-west-1" } }, { - "documentation": "For region us-east-2 with FIPS disabled and DualStack disabled", + "documentation": "For region us-west-2 with FIPS disabled and DualStack disabled", "expect": { "endpoint": { - "url": "https://kms.us-east-2.amazonaws.com" + "url": "https://kms.us-west-2.amazonaws.com" } }, "params": { - "UseFIPS": false, "UseDualStack": false, - "Region": "us-east-2" + "UseFIPS": false, + "Region": "us-west-2" } }, { - "documentation": "For region us-east-2 with FIPS enabled and DualStack disabled", + "documentation": "For region us-west-2 with FIPS enabled and DualStack disabled", "expect": { "endpoint": { - "url": "https://kms-fips.us-east-2.amazonaws.com" + "url": "https://kms-fips.us-west-2.amazonaws.com" } }, "params": { - "UseFIPS": true, "UseDualStack": false, - "Region": "us-east-2" + "UseFIPS": true, + "Region": "us-west-2" } }, { @@ -6549,8 +6794,8 @@ } }, "params": { - "UseFIPS": true, "UseDualStack": true, + "UseFIPS": true, "Region": "us-east-1" } }, @@ -6562,178 +6807,178 @@ } }, "params": { - "UseFIPS": false, "UseDualStack": true, + "UseFIPS": false, "Region": "us-east-1" } }, { - "documentation": "For region us-gov-west-1 with FIPS disabled and DualStack disabled", + "documentation": "For region cn-north-1 with FIPS disabled and DualStack disabled", "expect": { "endpoint": { - "url": "https://kms.us-gov-west-1.amazonaws.com" + "url": "https://kms.cn-north-1.amazonaws.com.cn" } }, "params": { - "UseFIPS": false, "UseDualStack": false, - "Region": "us-gov-west-1" + "UseFIPS": false, + "Region": "cn-north-1" } }, { - "documentation": "For region us-gov-west-1 with FIPS enabled and DualStack disabled", + "documentation": "For region cn-northwest-1 with FIPS disabled and DualStack disabled", "expect": { "endpoint": { - "url": "https://kms-fips.us-gov-west-1.amazonaws.com" + "url": "https://kms.cn-northwest-1.amazonaws.com.cn" } }, "params": { - "UseFIPS": true, "UseDualStack": false, - "Region": "us-gov-west-1" + "UseFIPS": false, + "Region": "cn-northwest-1" } }, { - "documentation": "For region us-gov-east-1 with FIPS disabled and DualStack disabled", + "documentation": "For region cn-north-1 with FIPS enabled and DualStack enabled", "expect": { "endpoint": { - "url": "https://kms.us-gov-east-1.amazonaws.com" + "url": "https://kms-fips.cn-north-1.api.amazonwebservices.com.cn" } }, "params": { - "UseFIPS": false, - "UseDualStack": false, - "Region": "us-gov-east-1" + "UseDualStack": true, + "UseFIPS": true, + "Region": "cn-north-1" } }, { - "documentation": "For region us-gov-east-1 with FIPS enabled and DualStack disabled", + "documentation": "For region cn-north-1 with FIPS enabled and DualStack disabled", "expect": { "endpoint": { - "url": "https://kms-fips.us-gov-east-1.amazonaws.com" + "url": "https://kms-fips.cn-north-1.amazonaws.com.cn" } }, "params": { - "UseFIPS": true, "UseDualStack": false, - "Region": "us-gov-east-1" + "UseFIPS": true, + "Region": "cn-north-1" } }, { - "documentation": "For region us-gov-east-1 with FIPS enabled and DualStack enabled", + "documentation": "For region cn-north-1 with FIPS disabled and DualStack enabled", "expect": { "endpoint": { - "url": "https://kms-fips.us-gov-east-1.api.aws" + "url": "https://kms.cn-north-1.api.amazonwebservices.com.cn" } }, "params": { - "UseFIPS": true, "UseDualStack": true, - "Region": "us-gov-east-1" + "UseFIPS": false, + "Region": "cn-north-1" } }, { - "documentation": "For region us-gov-east-1 with FIPS disabled and DualStack enabled", + "documentation": "For region us-gov-east-1 with FIPS disabled and DualStack disabled", "expect": { "endpoint": { - "url": "https://kms.us-gov-east-1.api.aws" + "url": "https://kms.us-gov-east-1.amazonaws.com" } }, "params": { + "UseDualStack": false, "UseFIPS": false, - "UseDualStack": true, "Region": "us-gov-east-1" } }, { - "documentation": "For region us-isob-east-1 with FIPS disabled and DualStack disabled", + "documentation": "For region us-gov-east-1 with FIPS enabled and DualStack disabled", "expect": { "endpoint": { - "url": "https://kms.us-isob-east-1.sc2s.sgov.gov" + "url": "https://kms-fips.us-gov-east-1.amazonaws.com" } }, "params": { - "UseFIPS": false, "UseDualStack": false, - "Region": "us-isob-east-1" + "UseFIPS": true, + "Region": "us-gov-east-1" } }, { - "documentation": "For region us-isob-east-1 with FIPS enabled and DualStack disabled", + "documentation": "For region us-gov-west-1 with FIPS disabled and DualStack disabled", "expect": { "endpoint": { - "url": "https://kms-fips.us-isob-east-1.sc2s.sgov.gov" + "url": "https://kms.us-gov-west-1.amazonaws.com" } }, "params": { - "UseFIPS": true, "UseDualStack": false, - "Region": "us-isob-east-1" + "UseFIPS": false, + "Region": "us-gov-west-1" } }, { - "documentation": "For region cn-northwest-1 with FIPS disabled and DualStack disabled", + "documentation": "For region us-gov-west-1 with FIPS enabled and DualStack disabled", "expect": { "endpoint": { - "url": "https://kms.cn-northwest-1.amazonaws.com.cn" + "url": "https://kms-fips.us-gov-west-1.amazonaws.com" } }, "params": { - "UseFIPS": false, "UseDualStack": false, - "Region": "cn-northwest-1" + "UseFIPS": true, + "Region": "us-gov-west-1" } }, { - "documentation": "For region cn-north-1 with FIPS disabled and DualStack disabled", + "documentation": "For region us-gov-east-1 with FIPS enabled and DualStack enabled", "expect": { "endpoint": { - "url": "https://kms.cn-north-1.amazonaws.com.cn" + "url": "https://kms-fips.us-gov-east-1.api.aws" } }, "params": { - "UseFIPS": false, - "UseDualStack": false, - "Region": "cn-north-1" + "UseDualStack": true, + "UseFIPS": true, + "Region": "us-gov-east-1" } }, { - "documentation": "For region cn-north-1 with FIPS enabled and DualStack enabled", + "documentation": "For region us-gov-east-1 with FIPS disabled and DualStack enabled", "expect": { "endpoint": { - "url": "https://kms-fips.cn-north-1.api.amazonwebservices.com.cn" + "url": "https://kms.us-gov-east-1.api.aws" } }, "params": { - "UseFIPS": true, "UseDualStack": true, - "Region": "cn-north-1" + "UseFIPS": false, + "Region": "us-gov-east-1" } }, { - "documentation": "For region cn-north-1 with FIPS enabled and DualStack disabled", + "documentation": "For region us-iso-east-1 with FIPS disabled and DualStack disabled", "expect": { "endpoint": { - "url": "https://kms-fips.cn-north-1.amazonaws.com.cn" + "url": "https://kms.us-iso-east-1.c2s.ic.gov" } }, "params": { - "UseFIPS": true, "UseDualStack": false, - "Region": "cn-north-1" + "UseFIPS": false, + "Region": "us-iso-east-1" } }, { - "documentation": "For region cn-north-1 with FIPS disabled and DualStack enabled", + "documentation": "For region us-iso-east-1 with FIPS enabled and DualStack disabled", "expect": { "endpoint": { - "url": "https://kms.cn-north-1.api.amazonwebservices.com.cn" + "url": "https://kms-fips.us-iso-east-1.c2s.ic.gov" } }, "params": { - "UseFIPS": false, - "UseDualStack": true, - "Region": "cn-north-1" + "UseDualStack": false, + "UseFIPS": true, + "Region": "us-iso-east-1" } }, { @@ -6744,8 +6989,8 @@ } }, "params": { - "UseFIPS": false, "UseDualStack": false, + "UseFIPS": false, "Region": "us-iso-west-1" } }, @@ -6757,59 +7002,72 @@ } }, "params": { - "UseFIPS": true, "UseDualStack": false, + "UseFIPS": true, "Region": "us-iso-west-1" } }, { - "documentation": "For region us-iso-east-1 with FIPS disabled and DualStack disabled", + "documentation": "For region us-isob-east-1 with FIPS disabled and DualStack disabled", "expect": { "endpoint": { - "url": "https://kms.us-iso-east-1.c2s.ic.gov" + "url": "https://kms.us-isob-east-1.sc2s.sgov.gov" } }, "params": { - "UseFIPS": false, "UseDualStack": false, - "Region": "us-iso-east-1" + "UseFIPS": false, + "Region": "us-isob-east-1" } }, { - "documentation": "For region us-iso-east-1 with FIPS enabled and DualStack disabled", + "documentation": "For region us-isob-east-1 with FIPS enabled and DualStack disabled", "expect": { "endpoint": { - "url": "https://kms-fips.us-iso-east-1.c2s.ic.gov" + "url": "https://kms-fips.us-isob-east-1.sc2s.sgov.gov" } }, "params": { - "UseFIPS": true, "UseDualStack": false, - "Region": "us-iso-east-1" + "UseFIPS": true, + "Region": "us-isob-east-1" } }, { - "documentation": "For custom endpoint with fips disabled and dualstack disabled", + "documentation": "For custom endpoint with region set and fips disabled and dualstack disabled", "expect": { "endpoint": { "url": "https://example.com" } }, "params": { - "UseFIPS": false, "UseDualStack": false, + "UseFIPS": false, "Region": "us-east-1", "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": { + "UseDualStack": false, + "UseFIPS": 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": { - "UseFIPS": true, "UseDualStack": false, + "UseFIPS": true, "Region": "us-east-1", "Endpoint": "https://example.com" } @@ -6820,8 +7078,8 @@ "error": "Invalid Configuration: Dualstack and custom endpoint are not supported" }, "params": { - "UseFIPS": false, "UseDualStack": true, + "UseFIPS": false, "Region": "us-east-1", "Endpoint": "https://example.com" } @@ -6883,7 +7141,7 @@ } ], "traits": { - "smithy.api#documentation": "

Deletes tags from a customer managed key. To delete a tag,\n specify the tag key and the KMS key.

\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

When it succeeds, the UntagResource operation doesn't return any output.\n Also, if the specified tag key isn't found on the KMS key, it doesn't throw an exception or\n return a response. To confirm that the operation worked, use the ListResourceTags operation.

\n\n

For information about using tags in KMS, see Tagging keys. For general information about\n tags, including the format and syntax, see Tagging Amazon Web Services resources in the Amazon\n Web Services General Reference.

\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

\n Required permissions: kms:UntagResource (key policy)

\n

\n Related operations\n

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

Deletes tags from a customer managed key. To delete a tag,\n specify the tag key and the KMS key.

\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

When it succeeds, the UntagResource operation doesn't return any output.\n Also, if the specified tag key isn't found on the KMS key, it doesn't throw an exception or\n return a response. To confirm that the operation worked, use the ListResourceTags operation.

\n

For information about using tags in KMS, see Tagging keys. For general information about\n tags, including the format and syntax, see Tagging Amazon Web Services resources in the Amazon\n Web Services General Reference.

\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:UntagResource (key policy)

\n

\n Related operations\n

\n " } }, "com.amazonaws.kms#UntagResourceRequest": { @@ -6892,7 +7150,7 @@ "KeyId": { "target": "com.amazonaws.kms#KeyIdType", "traits": { - "smithy.api#documentation": "

Identifies the KMS key from which you are removing tags.

\n \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": "

Identifies the KMS key from which you are removing tags.

\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": {} } }, @@ -6903,6 +7161,9 @@ "smithy.api#required": {} } } + }, + "traits": { + "smithy.api#input": {} } }, "com.amazonaws.kms#UpdateAlias": { @@ -6947,10 +7208,13 @@ "TargetKeyId": { "target": "com.amazonaws.kms#KeyIdType", "traits": { - "smithy.api#documentation": "

Identifies the customer managed key to associate with the alias. You don't have permission to\n associate an alias with an Amazon Web Services managed key.

\n

The KMS key must be in the same Amazon Web Services account and Region as the alias. Also, the new\n target KMS key must be the same type as the current target KMS key (both symmetric or both\n asymmetric or both HMAC) and they must have the same key usage.

\n \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.

\n\n

To verify that the alias is mapped to the correct KMS key, use ListAliases.

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

Identifies the customer managed key to associate with the alias. You don't have permission to\n associate an alias with an Amazon Web Services managed key.

\n

The KMS key must be in the same Amazon Web Services account and Region as the alias. Also, the new\n target KMS key must be the same type as the current target KMS key (both symmetric or both\n asymmetric or both HMAC) and they must have the same key usage.

\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.

\n

To verify that the alias is mapped to the correct KMS key, use ListAliases.

", "smithy.api#required": {} } } + }, + "traits": { + "smithy.api#input": {} } }, "com.amazonaws.kms#UpdateCustomKeyStore": { @@ -7076,11 +7340,17 @@ "smithy.api#documentation": "

Changes the connectivity setting for the external key store. To indicate that the external\n key store proxy uses a Amazon VPC endpoint service to communicate with KMS, specify\n VPC_ENDPOINT_SERVICE. Otherwise, specify PUBLIC_ENDPOINT.

\n

If you change the XksProxyConnectivity to VPC_ENDPOINT_SERVICE,\n you must also change the XksProxyUriEndpoint and add an\n XksProxyVpcEndpointServiceName value.

\n

If you change the XksProxyConnectivity to PUBLIC_ENDPOINT, you\n must also change the XksProxyUriEndpoint and specify a null or empty string for\n the XksProxyVpcEndpointServiceName value.

\n

To change this value, the external key store must be disconnected.

" } } + }, + "traits": { + "smithy.api#input": {} } }, "com.amazonaws.kms#UpdateCustomKeyStoreResponse": { "type": "structure", - "members": {} + "members": {}, + "traits": { + "smithy.api#output": {} + } }, "com.amazonaws.kms#UpdateKeyDescription": { "type": "operation", @@ -7108,7 +7378,7 @@ } ], "traits": { - "smithy.api#documentation": "

Updates the description of a KMS key. To see the description of a KMS key, use DescribeKey.

\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

\n Required permissions: kms:UpdateKeyDescription (key policy)

\n

\n Related operations\n

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

Updates the description of a KMS key. To see the description of a KMS key, use DescribeKey.

\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:UpdateKeyDescription (key policy)

\n

\n Related operations\n

\n " } }, "com.amazonaws.kms#UpdateKeyDescriptionRequest": { @@ -7117,7 +7387,7 @@ "KeyId": { "target": "com.amazonaws.kms#KeyIdType", "traits": { - "smithy.api#documentation": "

Updates the description of the specified KMS key.

\n \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": "

Updates the description of the specified KMS key.

\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": {} } }, @@ -7128,6 +7398,9 @@ "smithy.api#required": {} } } + }, + "traits": { + "smithy.api#input": {} } }, "com.amazonaws.kms#UpdatePrimaryRegion": { @@ -7168,7 +7441,7 @@ "KeyId": { "target": "com.amazonaws.kms#KeyIdType", "traits": { - "smithy.api#documentation": "

Identifies the current primary key. When the operation completes, this KMS key will be a\n replica key.

\n \n

Specify the key ID or key ARN of a multi-Region primary key.

\n

For example:

\n
    \n
  • \n

    Key ID: mrk-1234abcd12ab34cd56ef1234567890ab\n

    \n
  • \n
  • \n

    Key ARN: arn:aws:kms:us-east-2:111122223333:key/mrk-1234abcd12ab34cd56ef1234567890ab\n

    \n
  • \n
\n

To get the key ID and key ARN for a KMS key, use ListKeys or DescribeKey.

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

Identifies the current primary key. When the operation completes, this KMS key will be a\n replica key.

\n

Specify the key ID or key ARN of a multi-Region primary key.

\n

For example:

\n
    \n
  • \n

    Key ID: mrk-1234abcd12ab34cd56ef1234567890ab\n

    \n
  • \n
  • \n

    Key ARN: arn:aws:kms:us-east-2:111122223333:key/mrk-1234abcd12ab34cd56ef1234567890ab\n

    \n
  • \n
\n

To get the key ID and key ARN for a KMS key, use ListKeys or DescribeKey.

", "smithy.api#required": {} } }, @@ -7179,6 +7452,9 @@ "smithy.api#required": {} } } + }, + "traits": { + "smithy.api#input": {} } }, "com.amazonaws.kms#Verify": { @@ -7219,7 +7495,7 @@ } ], "traits": { - "smithy.api#documentation": "

Verifies a digital signature that was generated by the Sign operation.

\n

\n

Verification confirms that an authorized user signed the message with the specified KMS\n key and signing algorithm, and the message hasn't changed since it was signed. If the\n signature is verified, the value of the SignatureValid field in the response is\n True. If the signature verification fails, the Verify operation\n fails with an KMSInvalidSignatureException exception.

\n

A digital signature is generated by using the private key in an asymmetric KMS key. The\n signature is verified by using the public key in the same asymmetric KMS key.\n For information about asymmetric KMS keys, see Asymmetric KMS keys in the Key Management Service Developer Guide.

\n

To verify a digital signature, you can use the Verify operation. Specify the\n same asymmetric KMS key, message, and signing algorithm that were used to produce the\n signature.

\n

You can also verify the digital signature by using the public key of the KMS key outside\n of KMS. Use the GetPublicKey operation to download the public key in the\n asymmetric KMS key and then use the public key to verify the signature outside of KMS. The\n advantage of using the Verify operation is that it is performed within KMS. As\n a result, it's easy to call, the operation is performed within the FIPS boundary, it is logged\n in CloudTrail, and you can use key policy and IAM policy to determine who is authorized to use\n the KMS key to verify signatures.

\n

To verify a signature outside of KMS with an SM2 public key (China Regions only), you must \n specify the distinguishing ID. By default, KMS uses 1234567812345678 as the \n distinguishing ID. For more information, see Offline verification\n with SM2 key pairs.

\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:Verify (key policy)

\n

\n Related operations: Sign\n

" + "smithy.api#documentation": "

Verifies a digital signature that was generated by the Sign operation.

\n

\n

Verification confirms that an authorized user signed the message with the specified KMS\n key and signing algorithm, and the message hasn't changed since it was signed. If the\n signature is verified, the value of the SignatureValid field in the response is\n True. If the signature verification fails, the Verify operation\n fails with an KMSInvalidSignatureException exception.

\n

A digital signature is generated by using the private key in an asymmetric KMS key. The\n signature is verified by using the public key in the same asymmetric KMS key.\n For information about asymmetric KMS keys, see Asymmetric KMS keys in the Key Management Service Developer Guide.

\n

To use the Verify operation, specify the\n same asymmetric KMS key, message, and signing algorithm that were used to produce the\n signature. The message type does not need to be the same as the one used for signing, but it must \n indicate whether the value of the Message parameter should be\n hashed as part of the verification process.

\n

You can also verify the digital signature by using the public key of the KMS key outside\n of KMS. Use the GetPublicKey operation to download the public key in the\n asymmetric KMS key and then use the public key to verify the signature outside of KMS. The\n advantage of using the Verify operation is that it is performed within KMS. As\n a result, it's easy to call, the operation is performed within the FIPS boundary, it is logged\n in CloudTrail, and you can use key policy and IAM policy to determine who is authorized to use\n the KMS key to verify signatures.

\n

To verify a signature outside of KMS with an SM2 public key (China Regions only), you must \n specify the distinguishing ID. By default, KMS uses 1234567812345678 as the \n distinguishing ID. For more information, see Offline verification\n with SM2 key pairs.

\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:Verify (key policy)

\n

\n Related operations: Sign\n

" } }, "com.amazonaws.kms#VerifyMac": { @@ -7257,7 +7533,7 @@ } ], "traits": { - "smithy.api#documentation": "

Verifies the hash-based message authentication code (HMAC) for a specified message, HMAC\n KMS key, and MAC algorithm. To verify the HMAC, VerifyMac computes an HMAC using\n the message, HMAC KMS key, and MAC algorithm that you specify, and compares the computed HMAC\n to the HMAC that you specify. If the HMACs are identical, the verification succeeds;\n otherwise, it fails. Verification indicates that the message hasn't changed since the HMAC was\n calculated, and the specified key was used to generate and verify the HMAC.

\n

HMAC KMS keys and the HMAC algorithms that KMS uses conform to industry standards\n defined in RFC 2104.

\n

This operation is part of KMS support for HMAC KMS keys. For details, see\n HMAC keys in KMS 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

\n Required permissions: kms:VerifyMac (key policy)

\n

\n Related operations: GenerateMac\n

" + "smithy.api#documentation": "

Verifies the hash-based message authentication code (HMAC) for a specified message, HMAC\n KMS key, and MAC algorithm. To verify the HMAC, VerifyMac computes an HMAC using\n the message, HMAC KMS key, and MAC algorithm that you specify, and compares the computed HMAC\n to the HMAC that you specify. If the HMACs are identical, the verification succeeds;\n otherwise, it fails. Verification indicates that the message hasn't changed since the HMAC was\n calculated, and the specified key was used to generate and verify the HMAC.

\n

HMAC KMS keys and the HMAC algorithms that KMS uses conform to industry standards\n defined in RFC 2104.

\n

This operation is part of KMS support for HMAC KMS keys. For details, see\n HMAC keys in KMS 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:VerifyMac (key policy)

\n

\n Related operations: GenerateMac\n

" } }, "com.amazonaws.kms#VerifyMacRequest": { @@ -7273,7 +7549,7 @@ "KeyId": { "target": "com.amazonaws.kms#KeyIdType", "traits": { - "smithy.api#documentation": "

The KMS key that will be used in the verification.

\n\n

Enter a key ID of the KMS key that was used to generate the HMAC. If you identify a\n different KMS key, the VerifyMac operation fails.

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

The KMS key that will be used in the verification.

\n

Enter a key ID of the KMS key that was used to generate the HMAC. If you identify a\n different KMS key, the VerifyMac operation fails.

", "smithy.api#required": {} } }, @@ -7297,6 +7573,9 @@ "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.

" } } + }, + "traits": { + "smithy.api#input": {} } }, "com.amazonaws.kms#VerifyMacResponse": { @@ -7321,6 +7600,9 @@ "smithy.api#documentation": "

The MAC algorithm used in the verification.

" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.kms#VerifyRequest": { @@ -7329,21 +7611,21 @@ "KeyId": { "target": "com.amazonaws.kms#KeyIdType", "traits": { - "smithy.api#documentation": "

Identifies the asymmetric KMS key that will be used to verify the signature. This must be\n the same KMS key that was used to generate the signature. If you specify a different KMS key,\n the signature verification fails.

\n \n

To specify a KMS key, use its key ID, key ARN, alias name, or alias ARN. When using an alias name, prefix it with \"alias/\". To specify a KMS key in a different Amazon Web Services account, you must use the key ARN or alias ARN.

\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

    Alias name: alias/ExampleAlias\n

    \n
  • \n
  • \n

    Alias ARN: arn:aws:kms:us-east-2:111122223333:alias/ExampleAlias\n

    \n
  • \n
\n

To get the key ID and key ARN for a KMS key, use ListKeys or DescribeKey. To get the alias name and alias ARN, use ListAliases.

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

Identifies the asymmetric KMS key that will be used to verify the signature. This must be\n the same KMS key that was used to generate the signature. If you specify a different KMS key,\n the signature verification fails.

\n

To specify a KMS key, use its key ID, key ARN, alias name, or alias ARN. When using an alias name, prefix it with \"alias/\". To specify a KMS key in a different Amazon Web Services account, you must use the key ARN or alias ARN.

\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

    Alias name: alias/ExampleAlias\n

    \n
  • \n
  • \n

    Alias ARN: arn:aws:kms:us-east-2:111122223333:alias/ExampleAlias\n

    \n
  • \n
\n

To get the key ID and key ARN for a KMS key, use ListKeys or DescribeKey. To get the alias name and alias ARN, use ListAliases.

", "smithy.api#required": {} } }, "Message": { "target": "com.amazonaws.kms#PlaintextType", "traits": { - "smithy.api#documentation": "

Specifies the message that was signed. You can submit a raw message of up to 4096 bytes,\n or a hash digest of the message. If you submit a digest, use the MessageType\n parameter with a value of DIGEST.

\n

If the message specified here is different from the message that was signed, the signature\n verification fails. A message and its hash digest are considered to be the same\n message.

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

Specifies the message that was signed. You can submit a raw message of up to 4096 bytes,\n or a hash digest of the message. If you submit a digest, use the MessageType parameter\n with a value of DIGEST.

\n

If the message specified here is different from the message that was signed, the signature\n verification fails. A message and its hash digest are considered to be the same\n message.

", "smithy.api#required": {} } }, "MessageType": { "target": "com.amazonaws.kms#MessageType", "traits": { - "smithy.api#documentation": "

Tells KMS whether the value of the Message parameter is a message or\n message digest. The default value, RAW, indicates a message. To indicate a message digest,\n enter DIGEST.

\n \n

Use the DIGEST value only when the value of the Message\n parameter is a message digest. If you use the DIGEST value with a raw message,\n the security of the verification operation can be compromised.

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

Tells KMS whether the value of the Message parameter should be hashed\n as part of the signing algorithm. Use RAW for unhashed messages; use DIGEST\n for message digests, which are already hashed.

\n

When the value of MessageType is RAW, KMS uses the standard\n signing algorithm, which begins with a hash function. When the value is DIGEST, KMS \n skips the hashing step in the signing algorithm.

\n \n

Use the DIGEST value only when the value of the Message\n parameter is a message digest. If you use the DIGEST value with an unhashed message,\n the security of the verification operation can be compromised.

\n
\n

When the value of MessageTypeis DIGEST, the length\n of the Message value must match the length of hashed messages for the specified signing algorithm.

\n

You can submit a message digest and omit the MessageType or specify\n RAW so the digest is hashed again while signing. However, if the signed message is hashed once\n while signing, but twice while verifying, verification fails, even when the message hasn't changed.

\n

The hashing algorithm in that Verify uses is based on the SigningAlgorithm value.

\n
    \n
  • \n

    Signing algorithms that end in SHA_256 use the SHA_256 hashing algorithm.

    \n
  • \n
  • \n

    Signing algorithms that end in SHA_384 use the SHA_384 hashing algorithm.

    \n
  • \n
  • \n

    Signing algorithms that end in SHA_512 use the SHA_512 hashing algorithm.

    \n
  • \n
  • \n

    SM2DSA uses the SM3 hashing algorithm. For details, see Offline verification with SM2 key pairs.

    \n
  • \n
" } }, "Signature": { @@ -7366,6 +7648,9 @@ "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.

" } } + }, + "traits": { + "smithy.api#input": {} } }, "com.amazonaws.kms#VerifyResponse": { @@ -7390,6 +7675,9 @@ "smithy.api#documentation": "

The signing algorithm that was used to verify the signature.

" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.kms#WrappingKeySpec": { diff --git a/aws/sdk/aws-models/lambda.json b/aws/sdk/aws-models/lambda.json index bc75d111c7..18759e228b 100644 --- a/aws/sdk/aws-models/lambda.json +++ b/aws/sdk/aws-models/lambda.json @@ -248,7 +248,7 @@ "parameters": { "Region": { "builtIn": "AWS::Region", - "required": true, + "required": false, "documentation": "The AWS region used to dispatch the request.", "type": "String" }, @@ -277,13 +277,12 @@ { "conditions": [ { - "fn": "aws.partition", + "fn": "isSet", "argv": [ { - "ref": "Region" + "ref": "Endpoint" } - ], - "assign": "PartitionResult" + ] } ], "type": "tree", @@ -291,14 +290,20 @@ { "conditions": [ { - "fn": "isSet", + "fn": "booleanEquals", "argv": [ { - "ref": "Endpoint" - } + "ref": "UseFIPS" + }, + true ] } ], + "error": "Invalid Configuration: FIPS and custom endpoint are not supported", + "type": "error" + }, + { + "conditions": [], "type": "tree", "rules": [ { @@ -307,67 +312,42 @@ "fn": "booleanEquals", "argv": [ { - "ref": "UseFIPS" + "ref": "UseDualStack" }, true ] } ], - "error": "Invalid Configuration: FIPS and custom endpoint are not supported", + "error": "Invalid Configuration: Dualstack 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" + "endpoint": { + "url": { + "ref": "Endpoint" }, - { - "conditions": [], - "endpoint": { - "url": { - "ref": "Endpoint" - }, - "properties": {}, - "headers": {} - }, - "type": "endpoint" - } - ] + "properties": {}, + "headers": {} + }, + "type": "endpoint" } ] - }, + } + ] + }, + { + "conditions": [], + "type": "tree", + "rules": [ { "conditions": [ { - "fn": "booleanEquals", - "argv": [ - { - "ref": "UseFIPS" - }, - true - ] - }, - { - "fn": "booleanEquals", + "fn": "isSet", "argv": [ { - "ref": "UseDualStack" - }, - true + "ref": "Region" + } ] } ], @@ -376,90 +356,215 @@ { "conditions": [ { - "fn": "booleanEquals", + "fn": "aws.partition", "argv": [ - true, { - "fn": "getAttr", + "ref": "Region" + } + ], + "assign": "PartitionResult" + } + ], + "type": "tree", + "rules": [ + { + "conditions": [ + { + "fn": "booleanEquals", + "argv": [ + { + "ref": "UseFIPS" + }, + true + ] + }, + { + "fn": "booleanEquals", "argv": [ { - "ref": "PartitionResult" + "ref": "UseDualStack" + }, + true + ] + } + ], + "type": "tree", + "rules": [ + { + "conditions": [ + { + "fn": "booleanEquals", + "argv": [ + true, + { + "fn": "getAttr", + "argv": [ + { + "ref": "PartitionResult" + }, + "supportsFIPS" + ] + } + ] }, - "supportsFIPS" + { + "fn": "booleanEquals", + "argv": [ + true, + { + "fn": "getAttr", + "argv": [ + { + "ref": "PartitionResult" + }, + "supportsDualStack" + ] + } + ] + } + ], + "type": "tree", + "rules": [ + { + "conditions": [], + "type": "tree", + "rules": [ + { + "conditions": [], + "endpoint": { + "url": "https://lambda-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" } ] }, { - "fn": "booleanEquals", - "argv": [ - true, + "conditions": [ { - "fn": "getAttr", + "fn": "booleanEquals", "argv": [ { - "ref": "PartitionResult" + "ref": "UseFIPS" }, - "supportsDualStack" + 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://lambda-fips.{Region}.{PartitionResult#dnsSuffix}", + "properties": {}, + "headers": {} + }, + "type": "endpoint" + } + ] + } ] + }, + { + "conditions": [], + "error": "FIPS is enabled but this partition does not support FIPS", + "type": "error" } ] - } - ], - "type": "tree", - "rules": [ - { - "conditions": [], - "endpoint": { - "url": "https://lambda-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, + "conditions": [ { - "fn": "getAttr", + "fn": "booleanEquals", "argv": [ { - "ref": "PartitionResult" + "ref": "UseDualStack" }, - "supportsFIPS" + 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://lambda.{Region}.{PartitionResult#dualStackDnsSuffix}", + "properties": {}, + "headers": {} + }, + "type": "endpoint" + } + ] + } ] + }, + { + "conditions": [], + "error": "DualStack is enabled but this partition does not support DualStack", + "type": "error" } ] - } - ], - "type": "tree", - "rules": [ + }, { "conditions": [], "type": "tree", @@ -467,7 +572,7 @@ { "conditions": [], "endpoint": { - "url": "https://lambda-fips.{Region}.{PartitionResult#dnsSuffix}", + "url": "https://lambda.{Region}.{PartitionResult#dnsSuffix}", "properties": {}, "headers": {} }, @@ -476,1036 +581,108 @@ ] } ] - }, - { - "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": [], - "endpoint": { - "url": "https://lambda.{Region}.{PartitionResult#dualStackDnsSuffix}", - "properties": {}, - "headers": {} - }, - "type": "endpoint" - } - ] - }, - { - "conditions": [], - "error": "DualStack is enabled but this partition does not support DualStack", - "type": "error" } ] }, { - "conditions": [], - "endpoint": { - "url": "https://lambda.{Region}.{PartitionResult#dnsSuffix}", - "properties": {}, - "headers": {} - }, - "type": "endpoint" - } - ] - } - ] - }, - "smithy.rules#endpointTests": { - "testCases": [ - { - "documentation": "For region ap-south-2 with FIPS enabled and DualStack enabled", - "expect": { - "endpoint": { - "url": "https://lambda-fips.ap-south-2.api.aws" - } - }, - "params": { - "UseDualStack": true, - "UseFIPS": true, - "Region": "ap-south-2" - } - }, - { - "documentation": "For region ap-south-2 with FIPS enabled and DualStack disabled", - "expect": { - "endpoint": { - "url": "https://lambda-fips.ap-south-2.amazonaws.com" - } - }, - "params": { - "UseDualStack": false, - "UseFIPS": true, - "Region": "ap-south-2" - } - }, - { - "documentation": "For region ap-south-2 with FIPS disabled and DualStack enabled", - "expect": { - "endpoint": { - "url": "https://lambda.ap-south-2.api.aws" - } - }, - "params": { - "UseDualStack": true, - "UseFIPS": false, - "Region": "ap-south-2" - } - }, - { - "documentation": "For region ap-south-2 with FIPS disabled and DualStack disabled", - "expect": { - "endpoint": { - "url": "https://lambda.ap-south-2.amazonaws.com" - } - }, - "params": { - "UseDualStack": false, - "UseFIPS": false, - "Region": "ap-south-2" - } - }, - { - "documentation": "For region ap-south-1 with FIPS enabled and DualStack enabled", - "expect": { - "endpoint": { - "url": "https://lambda-fips.ap-south-1.api.aws" - } - }, - "params": { - "UseDualStack": true, - "UseFIPS": true, - "Region": "ap-south-1" - } - }, - { - "documentation": "For region ap-south-1 with FIPS enabled and DualStack disabled", - "expect": { - "endpoint": { - "url": "https://lambda-fips.ap-south-1.amazonaws.com" - } - }, - "params": { - "UseDualStack": false, - "UseFIPS": true, - "Region": "ap-south-1" - } - }, - { - "documentation": "For region ap-south-1 with FIPS disabled and DualStack enabled", - "expect": { - "endpoint": { - "url": "https://lambda.ap-south-1.api.aws" - } - }, - "params": { - "UseDualStack": true, - "UseFIPS": false, - "Region": "ap-south-1" - } - }, - { - "documentation": "For region ap-south-1 with FIPS disabled and DualStack disabled", - "expect": { - "endpoint": { - "url": "https://lambda.ap-south-1.amazonaws.com" - } - }, - "params": { - "UseDualStack": false, - "UseFIPS": false, - "Region": "ap-south-1" - } - }, - { - "documentation": "For region eu-south-1 with FIPS enabled and DualStack enabled", - "expect": { - "endpoint": { - "url": "https://lambda-fips.eu-south-1.api.aws" - } - }, - "params": { - "UseDualStack": true, - "UseFIPS": true, - "Region": "eu-south-1" - } - }, - { - "documentation": "For region eu-south-1 with FIPS enabled and DualStack disabled", - "expect": { - "endpoint": { - "url": "https://lambda-fips.eu-south-1.amazonaws.com" - } - }, - "params": { - "UseDualStack": false, - "UseFIPS": true, - "Region": "eu-south-1" - } - }, - { - "documentation": "For region eu-south-1 with FIPS disabled and DualStack enabled", - "expect": { - "endpoint": { - "url": "https://lambda.eu-south-1.api.aws" - } - }, - "params": { - "UseDualStack": true, - "UseFIPS": false, - "Region": "eu-south-1" - } - }, - { - "documentation": "For region eu-south-1 with FIPS disabled and DualStack disabled", - "expect": { - "endpoint": { - "url": "https://lambda.eu-south-1.amazonaws.com" - } - }, - "params": { - "UseDualStack": false, - "UseFIPS": false, - "Region": "eu-south-1" - } - }, - { - "documentation": "For region eu-south-2 with FIPS enabled and DualStack enabled", - "expect": { - "endpoint": { - "url": "https://lambda-fips.eu-south-2.api.aws" - } - }, - "params": { - "UseDualStack": true, - "UseFIPS": true, - "Region": "eu-south-2" - } - }, - { - "documentation": "For region eu-south-2 with FIPS enabled and DualStack disabled", - "expect": { - "endpoint": { - "url": "https://lambda-fips.eu-south-2.amazonaws.com" - } - }, - "params": { - "UseDualStack": false, - "UseFIPS": true, - "Region": "eu-south-2" - } - }, - { - "documentation": "For region eu-south-2 with FIPS disabled and DualStack enabled", - "expect": { - "endpoint": { - "url": "https://lambda.eu-south-2.api.aws" - } - }, - "params": { - "UseDualStack": true, - "UseFIPS": false, - "Region": "eu-south-2" - } - }, - { - "documentation": "For region eu-south-2 with FIPS disabled and DualStack disabled", - "expect": { - "endpoint": { - "url": "https://lambda.eu-south-2.amazonaws.com" - } - }, - "params": { - "UseDualStack": false, - "UseFIPS": false, - "Region": "eu-south-2" - } - }, - { - "documentation": "For region us-gov-east-1 with FIPS enabled and DualStack enabled", - "expect": { - "endpoint": { - "url": "https://lambda-fips.us-gov-east-1.api.aws" - } - }, - "params": { - "UseDualStack": true, - "UseFIPS": true, - "Region": "us-gov-east-1" - } - }, - { - "documentation": "For region us-gov-east-1 with FIPS enabled and DualStack disabled", - "expect": { - "endpoint": { - "url": "https://lambda-fips.us-gov-east-1.amazonaws.com" - } - }, - "params": { - "UseDualStack": false, - "UseFIPS": true, - "Region": "us-gov-east-1" - } - }, - { - "documentation": "For region us-gov-east-1 with FIPS disabled and DualStack enabled", - "expect": { - "endpoint": { - "url": "https://lambda.us-gov-east-1.api.aws" - } - }, - "params": { - "UseDualStack": true, - "UseFIPS": false, - "Region": "us-gov-east-1" - } - }, - { - "documentation": "For region us-gov-east-1 with FIPS disabled and DualStack disabled", - "expect": { - "endpoint": { - "url": "https://lambda.us-gov-east-1.amazonaws.com" - } - }, - "params": { - "UseDualStack": false, - "UseFIPS": false, - "Region": "us-gov-east-1" - } - }, - { - "documentation": "For region me-central-1 with FIPS enabled and DualStack enabled", - "expect": { - "endpoint": { - "url": "https://lambda-fips.me-central-1.api.aws" - } - }, - "params": { - "UseDualStack": true, - "UseFIPS": true, - "Region": "me-central-1" - } - }, - { - "documentation": "For region me-central-1 with FIPS enabled and DualStack disabled", - "expect": { - "endpoint": { - "url": "https://lambda-fips.me-central-1.amazonaws.com" - } - }, - "params": { - "UseDualStack": false, - "UseFIPS": true, - "Region": "me-central-1" - } - }, - { - "documentation": "For region me-central-1 with FIPS disabled and DualStack enabled", - "expect": { - "endpoint": { - "url": "https://lambda.me-central-1.api.aws" - } - }, - "params": { - "UseDualStack": true, - "UseFIPS": false, - "Region": "me-central-1" - } - }, - { - "documentation": "For region me-central-1 with FIPS disabled and DualStack disabled", - "expect": { - "endpoint": { - "url": "https://lambda.me-central-1.amazonaws.com" - } - }, - "params": { - "UseDualStack": false, - "UseFIPS": false, - "Region": "me-central-1" - } - }, - { - "documentation": "For region ca-central-1 with FIPS enabled and DualStack enabled", - "expect": { - "endpoint": { - "url": "https://lambda-fips.ca-central-1.api.aws" - } - }, - "params": { - "UseDualStack": true, - "UseFIPS": true, - "Region": "ca-central-1" - } - }, - { - "documentation": "For region ca-central-1 with FIPS enabled and DualStack disabled", - "expect": { - "endpoint": { - "url": "https://lambda-fips.ca-central-1.amazonaws.com" - } - }, - "params": { - "UseDualStack": false, - "UseFIPS": true, - "Region": "ca-central-1" - } - }, - { - "documentation": "For region ca-central-1 with FIPS disabled and DualStack enabled", - "expect": { - "endpoint": { - "url": "https://lambda.ca-central-1.api.aws" - } - }, - "params": { - "UseDualStack": true, - "UseFIPS": false, - "Region": "ca-central-1" - } - }, - { - "documentation": "For region ca-central-1 with FIPS disabled and DualStack disabled", - "expect": { - "endpoint": { - "url": "https://lambda.ca-central-1.amazonaws.com" - } - }, - "params": { - "UseDualStack": false, - "UseFIPS": false, - "Region": "ca-central-1" - } - }, - { - "documentation": "For region eu-central-1 with FIPS enabled and DualStack enabled", - "expect": { - "endpoint": { - "url": "https://lambda-fips.eu-central-1.api.aws" - } - }, - "params": { - "UseDualStack": true, - "UseFIPS": true, - "Region": "eu-central-1" - } - }, - { - "documentation": "For region eu-central-1 with FIPS enabled and DualStack disabled", - "expect": { - "endpoint": { - "url": "https://lambda-fips.eu-central-1.amazonaws.com" - } - }, - "params": { - "UseDualStack": false, - "UseFIPS": true, - "Region": "eu-central-1" - } - }, - { - "documentation": "For region eu-central-1 with FIPS disabled and DualStack enabled", - "expect": { - "endpoint": { - "url": "https://lambda.eu-central-1.api.aws" - } - }, - "params": { - "UseDualStack": true, - "UseFIPS": false, - "Region": "eu-central-1" - } - }, - { - "documentation": "For region eu-central-1 with FIPS disabled and DualStack disabled", - "expect": { - "endpoint": { - "url": "https://lambda.eu-central-1.amazonaws.com" - } - }, - "params": { - "UseDualStack": false, - "UseFIPS": false, - "Region": "eu-central-1" - } - }, - { - "documentation": "For region us-iso-west-1 with FIPS enabled and DualStack enabled", - "expect": { - "error": "FIPS and DualStack are enabled, but this partition does not support one or both" - }, - "params": { - "UseDualStack": true, - "UseFIPS": true, - "Region": "us-iso-west-1" - } - }, - { - "documentation": "For region us-iso-west-1 with FIPS enabled and DualStack disabled", - "expect": { - "endpoint": { - "url": "https://lambda-fips.us-iso-west-1.c2s.ic.gov" - } - }, - "params": { - "UseDualStack": false, - "UseFIPS": true, - "Region": "us-iso-west-1" - } - }, - { - "documentation": "For region us-iso-west-1 with FIPS disabled and DualStack enabled", - "expect": { - "error": "DualStack is enabled but this partition does not support DualStack" - }, - "params": { - "UseDualStack": true, - "UseFIPS": false, - "Region": "us-iso-west-1" - } - }, - { - "documentation": "For region us-iso-west-1 with FIPS disabled and DualStack disabled", - "expect": { - "endpoint": { - "url": "https://lambda.us-iso-west-1.c2s.ic.gov" - } - }, - "params": { - "UseDualStack": false, - "UseFIPS": false, - "Region": "us-iso-west-1" - } - }, - { - "documentation": "For region eu-central-2 with FIPS enabled and DualStack enabled", - "expect": { - "endpoint": { - "url": "https://lambda-fips.eu-central-2.api.aws" - } - }, - "params": { - "UseDualStack": true, - "UseFIPS": true, - "Region": "eu-central-2" - } - }, - { - "documentation": "For region eu-central-2 with FIPS enabled and DualStack disabled", - "expect": { - "endpoint": { - "url": "https://lambda-fips.eu-central-2.amazonaws.com" - } - }, - "params": { - "UseDualStack": false, - "UseFIPS": true, - "Region": "eu-central-2" - } - }, - { - "documentation": "For region eu-central-2 with FIPS disabled and DualStack enabled", - "expect": { - "endpoint": { - "url": "https://lambda.eu-central-2.api.aws" - } - }, - "params": { - "UseDualStack": true, - "UseFIPS": false, - "Region": "eu-central-2" - } - }, - { - "documentation": "For region eu-central-2 with FIPS disabled and DualStack disabled", - "expect": { - "endpoint": { - "url": "https://lambda.eu-central-2.amazonaws.com" - } - }, - "params": { - "UseDualStack": false, - "UseFIPS": false, - "Region": "eu-central-2" - } - }, - { - "documentation": "For region us-west-1 with FIPS enabled and DualStack enabled", - "expect": { - "endpoint": { - "url": "https://lambda-fips.us-west-1.api.aws" - } - }, - "params": { - "UseDualStack": true, - "UseFIPS": true, - "Region": "us-west-1" - } - }, - { - "documentation": "For region us-west-1 with FIPS enabled and DualStack disabled", - "expect": { - "endpoint": { - "url": "https://lambda-fips.us-west-1.amazonaws.com" - } - }, - "params": { - "UseDualStack": false, - "UseFIPS": true, - "Region": "us-west-1" - } - }, - { - "documentation": "For region us-west-1 with FIPS disabled and DualStack enabled", - "expect": { - "endpoint": { - "url": "https://lambda.us-west-1.api.aws" - } - }, - "params": { - "UseDualStack": true, - "UseFIPS": false, - "Region": "us-west-1" - } - }, - { - "documentation": "For region us-west-1 with FIPS disabled and DualStack disabled", - "expect": { - "endpoint": { - "url": "https://lambda.us-west-1.amazonaws.com" - } - }, - "params": { - "UseDualStack": false, - "UseFIPS": false, - "Region": "us-west-1" - } - }, - { - "documentation": "For region us-west-2 with FIPS enabled and DualStack enabled", - "expect": { - "endpoint": { - "url": "https://lambda-fips.us-west-2.api.aws" - } - }, - "params": { - "UseDualStack": true, - "UseFIPS": true, - "Region": "us-west-2" - } - }, - { - "documentation": "For region us-west-2 with FIPS enabled and DualStack disabled", - "expect": { - "endpoint": { - "url": "https://lambda-fips.us-west-2.amazonaws.com" - } - }, - "params": { - "UseDualStack": false, - "UseFIPS": true, - "Region": "us-west-2" - } - }, - { - "documentation": "For region us-west-2 with FIPS disabled and DualStack enabled", - "expect": { - "endpoint": { - "url": "https://lambda.us-west-2.api.aws" - } - }, - "params": { - "UseDualStack": true, - "UseFIPS": false, - "Region": "us-west-2" - } - }, - { - "documentation": "For region us-west-2 with FIPS disabled and DualStack disabled", - "expect": { - "endpoint": { - "url": "https://lambda.us-west-2.amazonaws.com" - } - }, - "params": { - "UseDualStack": false, - "UseFIPS": false, - "Region": "us-west-2" - } - }, - { - "documentation": "For region af-south-1 with FIPS enabled and DualStack enabled", - "expect": { - "endpoint": { - "url": "https://lambda-fips.af-south-1.api.aws" - } - }, - "params": { - "UseDualStack": true, - "UseFIPS": true, - "Region": "af-south-1" - } - }, - { - "documentation": "For region af-south-1 with FIPS enabled and DualStack disabled", - "expect": { - "endpoint": { - "url": "https://lambda-fips.af-south-1.amazonaws.com" - } - }, - "params": { - "UseDualStack": false, - "UseFIPS": true, - "Region": "af-south-1" - } - }, - { - "documentation": "For region af-south-1 with FIPS disabled and DualStack enabled", - "expect": { - "endpoint": { - "url": "https://lambda.af-south-1.api.aws" - } - }, - "params": { - "UseDualStack": true, - "UseFIPS": false, - "Region": "af-south-1" - } - }, - { - "documentation": "For region af-south-1 with FIPS disabled and DualStack disabled", - "expect": { - "endpoint": { - "url": "https://lambda.af-south-1.amazonaws.com" - } - }, - "params": { - "UseDualStack": false, - "UseFIPS": false, - "Region": "af-south-1" - } - }, - { - "documentation": "For region eu-north-1 with FIPS enabled and DualStack enabled", - "expect": { - "endpoint": { - "url": "https://lambda-fips.eu-north-1.api.aws" - } - }, - "params": { - "UseDualStack": true, - "UseFIPS": true, - "Region": "eu-north-1" - } - }, - { - "documentation": "For region eu-north-1 with FIPS enabled and DualStack disabled", - "expect": { - "endpoint": { - "url": "https://lambda-fips.eu-north-1.amazonaws.com" - } - }, - "params": { - "UseDualStack": false, - "UseFIPS": true, - "Region": "eu-north-1" - } - }, - { - "documentation": "For region eu-north-1 with FIPS disabled and DualStack enabled", - "expect": { - "endpoint": { - "url": "https://lambda.eu-north-1.api.aws" - } - }, - "params": { - "UseDualStack": true, - "UseFIPS": false, - "Region": "eu-north-1" - } - }, - { - "documentation": "For region eu-north-1 with FIPS disabled and DualStack disabled", - "expect": { - "endpoint": { - "url": "https://lambda.eu-north-1.amazonaws.com" - } - }, - "params": { - "UseDualStack": false, - "UseFIPS": false, - "Region": "eu-north-1" - } - }, - { - "documentation": "For region eu-west-3 with FIPS enabled and DualStack enabled", - "expect": { - "endpoint": { - "url": "https://lambda-fips.eu-west-3.api.aws" - } - }, - "params": { - "UseDualStack": true, - "UseFIPS": true, - "Region": "eu-west-3" - } - }, - { - "documentation": "For region eu-west-3 with FIPS enabled and DualStack disabled", - "expect": { - "endpoint": { - "url": "https://lambda-fips.eu-west-3.amazonaws.com" - } - }, - "params": { - "UseDualStack": false, - "UseFIPS": true, - "Region": "eu-west-3" - } - }, - { - "documentation": "For region eu-west-3 with FIPS disabled and DualStack enabled", - "expect": { - "endpoint": { - "url": "https://lambda.eu-west-3.api.aws" - } - }, - "params": { - "UseDualStack": true, - "UseFIPS": false, - "Region": "eu-west-3" - } - }, - { - "documentation": "For region eu-west-3 with FIPS disabled and DualStack disabled", - "expect": { - "endpoint": { - "url": "https://lambda.eu-west-3.amazonaws.com" - } - }, - "params": { - "UseDualStack": false, - "UseFIPS": false, - "Region": "eu-west-3" - } - }, - { - "documentation": "For region eu-west-2 with FIPS enabled and DualStack enabled", - "expect": { - "endpoint": { - "url": "https://lambda-fips.eu-west-2.api.aws" - } - }, - "params": { - "UseDualStack": true, - "UseFIPS": true, - "Region": "eu-west-2" - } - }, - { - "documentation": "For region eu-west-2 with FIPS enabled and DualStack disabled", - "expect": { - "endpoint": { - "url": "https://lambda-fips.eu-west-2.amazonaws.com" - } - }, - "params": { - "UseDualStack": false, - "UseFIPS": true, - "Region": "eu-west-2" - } - }, - { - "documentation": "For region eu-west-2 with FIPS disabled and DualStack enabled", - "expect": { - "endpoint": { - "url": "https://lambda.eu-west-2.api.aws" + "conditions": [], + "error": "Invalid Configuration: Missing Region", + "type": "error" } - }, - "params": { - "UseDualStack": true, - "UseFIPS": false, - "Region": "eu-west-2" - } - }, + ] + } + ] + }, + "smithy.rules#endpointTests": { + "testCases": [ { - "documentation": "For region eu-west-2 with FIPS disabled and DualStack disabled", + "documentation": "For region af-south-1 with FIPS disabled and DualStack disabled", "expect": { "endpoint": { - "url": "https://lambda.eu-west-2.amazonaws.com" + "url": "https://lambda.af-south-1.amazonaws.com" } }, "params": { "UseDualStack": false, "UseFIPS": false, - "Region": "eu-west-2" - } - }, - { - "documentation": "For region eu-west-1 with FIPS enabled and DualStack enabled", - "expect": { - "endpoint": { - "url": "https://lambda-fips.eu-west-1.api.aws" - } - }, - "params": { - "UseDualStack": true, - "UseFIPS": true, - "Region": "eu-west-1" - } - }, - { - "documentation": "For region eu-west-1 with FIPS enabled and DualStack disabled", - "expect": { - "endpoint": { - "url": "https://lambda-fips.eu-west-1.amazonaws.com" - } - }, - "params": { - "UseDualStack": false, - "UseFIPS": true, - "Region": "eu-west-1" + "Region": "af-south-1" } }, { - "documentation": "For region eu-west-1 with FIPS disabled and DualStack enabled", + "documentation": "For region af-south-1 with FIPS disabled and DualStack enabled", "expect": { "endpoint": { - "url": "https://lambda.eu-west-1.api.aws" + "url": "https://lambda.af-south-1.api.aws" } }, "params": { "UseDualStack": true, "UseFIPS": false, - "Region": "eu-west-1" + "Region": "af-south-1" } }, { - "documentation": "For region eu-west-1 with FIPS disabled and DualStack disabled", + "documentation": "For region ap-east-1 with FIPS disabled and DualStack disabled", "expect": { "endpoint": { - "url": "https://lambda.eu-west-1.amazonaws.com" + "url": "https://lambda.ap-east-1.amazonaws.com" } }, "params": { "UseDualStack": false, "UseFIPS": false, - "Region": "eu-west-1" - } - }, - { - "documentation": "For region ap-northeast-3 with FIPS enabled and DualStack enabled", - "expect": { - "endpoint": { - "url": "https://lambda-fips.ap-northeast-3.api.aws" - } - }, - "params": { - "UseDualStack": true, - "UseFIPS": true, - "Region": "ap-northeast-3" - } - }, - { - "documentation": "For region ap-northeast-3 with FIPS enabled and DualStack disabled", - "expect": { - "endpoint": { - "url": "https://lambda-fips.ap-northeast-3.amazonaws.com" - } - }, - "params": { - "UseDualStack": false, - "UseFIPS": true, - "Region": "ap-northeast-3" + "Region": "ap-east-1" } }, { - "documentation": "For region ap-northeast-3 with FIPS disabled and DualStack enabled", + "documentation": "For region ap-east-1 with FIPS disabled and DualStack enabled", "expect": { "endpoint": { - "url": "https://lambda.ap-northeast-3.api.aws" + "url": "https://lambda.ap-east-1.api.aws" } }, "params": { "UseDualStack": true, "UseFIPS": false, - "Region": "ap-northeast-3" + "Region": "ap-east-1" } }, { - "documentation": "For region ap-northeast-3 with FIPS disabled and DualStack disabled", + "documentation": "For region ap-northeast-1 with FIPS disabled and DualStack disabled", "expect": { "endpoint": { - "url": "https://lambda.ap-northeast-3.amazonaws.com" + "url": "https://lambda.ap-northeast-1.amazonaws.com" } }, "params": { "UseDualStack": false, "UseFIPS": false, - "Region": "ap-northeast-3" + "Region": "ap-northeast-1" } }, { - "documentation": "For region ap-northeast-2 with FIPS enabled and DualStack enabled", + "documentation": "For region ap-northeast-1 with FIPS disabled and DualStack enabled", "expect": { "endpoint": { - "url": "https://lambda-fips.ap-northeast-2.api.aws" + "url": "https://lambda.ap-northeast-1.api.aws" } }, "params": { "UseDualStack": true, - "UseFIPS": true, - "Region": "ap-northeast-2" + "UseFIPS": false, + "Region": "ap-northeast-1" } }, { - "documentation": "For region ap-northeast-2 with FIPS enabled and DualStack disabled", + "documentation": "For region ap-northeast-2 with FIPS disabled and DualStack disabled", "expect": { "endpoint": { - "url": "https://lambda-fips.ap-northeast-2.amazonaws.com" + "url": "https://lambda.ap-northeast-2.amazonaws.com" } }, "params": { "UseDualStack": false, - "UseFIPS": true, + "UseFIPS": false, "Region": "ap-northeast-2" } }, @@ -1523,792 +700,775 @@ } }, { - "documentation": "For region ap-northeast-2 with FIPS disabled and DualStack disabled", + "documentation": "For region ap-northeast-3 with FIPS disabled and DualStack disabled", "expect": { "endpoint": { - "url": "https://lambda.ap-northeast-2.amazonaws.com" + "url": "https://lambda.ap-northeast-3.amazonaws.com" } }, "params": { "UseDualStack": false, "UseFIPS": false, - "Region": "ap-northeast-2" - } - }, - { - "documentation": "For region ap-northeast-1 with FIPS enabled and DualStack enabled", - "expect": { - "endpoint": { - "url": "https://lambda-fips.ap-northeast-1.api.aws" - } - }, - "params": { - "UseDualStack": true, - "UseFIPS": true, - "Region": "ap-northeast-1" - } - }, - { - "documentation": "For region ap-northeast-1 with FIPS enabled and DualStack disabled", - "expect": { - "endpoint": { - "url": "https://lambda-fips.ap-northeast-1.amazonaws.com" - } - }, - "params": { - "UseDualStack": false, - "UseFIPS": true, - "Region": "ap-northeast-1" + "Region": "ap-northeast-3" } }, { - "documentation": "For region ap-northeast-1 with FIPS disabled and DualStack enabled", + "documentation": "For region ap-northeast-3 with FIPS disabled and DualStack enabled", "expect": { "endpoint": { - "url": "https://lambda.ap-northeast-1.api.aws" + "url": "https://lambda.ap-northeast-3.api.aws" } }, "params": { "UseDualStack": true, "UseFIPS": false, - "Region": "ap-northeast-1" + "Region": "ap-northeast-3" } }, { - "documentation": "For region ap-northeast-1 with FIPS disabled and DualStack disabled", + "documentation": "For region ap-south-1 with FIPS disabled and DualStack disabled", "expect": { "endpoint": { - "url": "https://lambda.ap-northeast-1.amazonaws.com" + "url": "https://lambda.ap-south-1.amazonaws.com" } }, "params": { "UseDualStack": false, "UseFIPS": false, - "Region": "ap-northeast-1" + "Region": "ap-south-1" } }, { - "documentation": "For region me-south-1 with FIPS enabled and DualStack enabled", + "documentation": "For region ap-south-1 with FIPS disabled and DualStack enabled", "expect": { "endpoint": { - "url": "https://lambda-fips.me-south-1.api.aws" + "url": "https://lambda.ap-south-1.api.aws" } }, "params": { "UseDualStack": true, - "UseFIPS": true, - "Region": "me-south-1" + "UseFIPS": false, + "Region": "ap-south-1" } }, { - "documentation": "For region me-south-1 with FIPS enabled and DualStack disabled", + "documentation": "For region ap-southeast-1 with FIPS disabled and DualStack disabled", "expect": { "endpoint": { - "url": "https://lambda-fips.me-south-1.amazonaws.com" + "url": "https://lambda.ap-southeast-1.amazonaws.com" } }, "params": { "UseDualStack": false, - "UseFIPS": true, - "Region": "me-south-1" + "UseFIPS": false, + "Region": "ap-southeast-1" } }, { - "documentation": "For region me-south-1 with FIPS disabled and DualStack enabled", + "documentation": "For region ap-southeast-1 with FIPS disabled and DualStack enabled", "expect": { "endpoint": { - "url": "https://lambda.me-south-1.api.aws" + "url": "https://lambda.ap-southeast-1.api.aws" } }, "params": { "UseDualStack": true, "UseFIPS": false, - "Region": "me-south-1" + "Region": "ap-southeast-1" } }, { - "documentation": "For region me-south-1 with FIPS disabled and DualStack disabled", + "documentation": "For region ap-southeast-2 with FIPS disabled and DualStack disabled", "expect": { "endpoint": { - "url": "https://lambda.me-south-1.amazonaws.com" + "url": "https://lambda.ap-southeast-2.amazonaws.com" } }, "params": { "UseDualStack": false, "UseFIPS": false, - "Region": "me-south-1" + "Region": "ap-southeast-2" } }, { - "documentation": "For region sa-east-1 with FIPS enabled and DualStack enabled", + "documentation": "For region ap-southeast-2 with FIPS disabled and DualStack enabled", "expect": { "endpoint": { - "url": "https://lambda-fips.sa-east-1.api.aws" + "url": "https://lambda.ap-southeast-2.api.aws" } }, "params": { "UseDualStack": true, - "UseFIPS": true, - "Region": "sa-east-1" + "UseFIPS": false, + "Region": "ap-southeast-2" } }, { - "documentation": "For region sa-east-1 with FIPS enabled and DualStack disabled", + "documentation": "For region ap-southeast-3 with FIPS disabled and DualStack disabled", "expect": { "endpoint": { - "url": "https://lambda-fips.sa-east-1.amazonaws.com" + "url": "https://lambda.ap-southeast-3.amazonaws.com" } }, "params": { "UseDualStack": false, - "UseFIPS": true, - "Region": "sa-east-1" + "UseFIPS": false, + "Region": "ap-southeast-3" } }, { - "documentation": "For region sa-east-1 with FIPS disabled and DualStack enabled", + "documentation": "For region ap-southeast-3 with FIPS disabled and DualStack enabled", "expect": { "endpoint": { - "url": "https://lambda.sa-east-1.api.aws" + "url": "https://lambda.ap-southeast-3.api.aws" } }, "params": { "UseDualStack": true, "UseFIPS": false, - "Region": "sa-east-1" + "Region": "ap-southeast-3" } }, { - "documentation": "For region sa-east-1 with FIPS disabled and DualStack disabled", + "documentation": "For region ca-central-1 with FIPS disabled and DualStack disabled", "expect": { "endpoint": { - "url": "https://lambda.sa-east-1.amazonaws.com" + "url": "https://lambda.ca-central-1.amazonaws.com" } }, "params": { "UseDualStack": false, "UseFIPS": false, - "Region": "sa-east-1" + "Region": "ca-central-1" } }, { - "documentation": "For region ap-east-1 with FIPS enabled and DualStack enabled", + "documentation": "For region ca-central-1 with FIPS disabled and DualStack enabled", "expect": { "endpoint": { - "url": "https://lambda-fips.ap-east-1.api.aws" + "url": "https://lambda.ca-central-1.api.aws" } }, "params": { "UseDualStack": true, - "UseFIPS": true, - "Region": "ap-east-1" + "UseFIPS": false, + "Region": "ca-central-1" } }, { - "documentation": "For region ap-east-1 with FIPS enabled and DualStack disabled", + "documentation": "For region eu-central-1 with FIPS disabled and DualStack disabled", "expect": { "endpoint": { - "url": "https://lambda-fips.ap-east-1.amazonaws.com" + "url": "https://lambda.eu-central-1.amazonaws.com" } }, "params": { "UseDualStack": false, - "UseFIPS": true, - "Region": "ap-east-1" + "UseFIPS": false, + "Region": "eu-central-1" } }, { - "documentation": "For region ap-east-1 with FIPS disabled and DualStack enabled", + "documentation": "For region eu-central-1 with FIPS disabled and DualStack enabled", "expect": { "endpoint": { - "url": "https://lambda.ap-east-1.api.aws" + "url": "https://lambda.eu-central-1.api.aws" } }, "params": { "UseDualStack": true, "UseFIPS": false, - "Region": "ap-east-1" + "Region": "eu-central-1" } }, { - "documentation": "For region ap-east-1 with FIPS disabled and DualStack disabled", + "documentation": "For region eu-north-1 with FIPS disabled and DualStack disabled", "expect": { "endpoint": { - "url": "https://lambda.ap-east-1.amazonaws.com" + "url": "https://lambda.eu-north-1.amazonaws.com" } }, "params": { "UseDualStack": false, "UseFIPS": false, - "Region": "ap-east-1" + "Region": "eu-north-1" } }, { - "documentation": "For region cn-north-1 with FIPS enabled and DualStack enabled", + "documentation": "For region eu-north-1 with FIPS disabled and DualStack enabled", "expect": { "endpoint": { - "url": "https://lambda-fips.cn-north-1.api.amazonwebservices.com.cn" + "url": "https://lambda.eu-north-1.api.aws" } }, "params": { "UseDualStack": true, - "UseFIPS": true, - "Region": "cn-north-1" + "UseFIPS": false, + "Region": "eu-north-1" } }, { - "documentation": "For region cn-north-1 with FIPS enabled and DualStack disabled", + "documentation": "For region eu-south-1 with FIPS disabled and DualStack disabled", "expect": { "endpoint": { - "url": "https://lambda-fips.cn-north-1.amazonaws.com.cn" + "url": "https://lambda.eu-south-1.amazonaws.com" } }, "params": { "UseDualStack": false, - "UseFIPS": true, - "Region": "cn-north-1" + "UseFIPS": false, + "Region": "eu-south-1" } }, { - "documentation": "For region cn-north-1 with FIPS disabled and DualStack enabled", + "documentation": "For region eu-south-1 with FIPS disabled and DualStack enabled", "expect": { "endpoint": { - "url": "https://lambda.cn-north-1.api.amazonwebservices.com.cn" + "url": "https://lambda.eu-south-1.api.aws" } }, "params": { "UseDualStack": true, "UseFIPS": false, - "Region": "cn-north-1" + "Region": "eu-south-1" } }, { - "documentation": "For region cn-north-1 with FIPS disabled and DualStack disabled", + "documentation": "For region eu-west-1 with FIPS disabled and DualStack disabled", "expect": { "endpoint": { - "url": "https://lambda.cn-north-1.amazonaws.com.cn" + "url": "https://lambda.eu-west-1.amazonaws.com" } }, "params": { "UseDualStack": false, "UseFIPS": false, - "Region": "cn-north-1" + "Region": "eu-west-1" } }, { - "documentation": "For region us-gov-west-1 with FIPS enabled and DualStack enabled", + "documentation": "For region eu-west-1 with FIPS disabled and DualStack enabled", "expect": { "endpoint": { - "url": "https://lambda-fips.us-gov-west-1.api.aws" + "url": "https://lambda.eu-west-1.api.aws" } }, "params": { "UseDualStack": true, - "UseFIPS": true, - "Region": "us-gov-west-1" + "UseFIPS": false, + "Region": "eu-west-1" } }, { - "documentation": "For region us-gov-west-1 with FIPS enabled and DualStack disabled", + "documentation": "For region eu-west-2 with FIPS disabled and DualStack disabled", "expect": { "endpoint": { - "url": "https://lambda-fips.us-gov-west-1.amazonaws.com" + "url": "https://lambda.eu-west-2.amazonaws.com" } }, "params": { "UseDualStack": false, - "UseFIPS": true, - "Region": "us-gov-west-1" + "UseFIPS": false, + "Region": "eu-west-2" } }, { - "documentation": "For region us-gov-west-1 with FIPS disabled and DualStack enabled", + "documentation": "For region eu-west-2 with FIPS disabled and DualStack enabled", "expect": { "endpoint": { - "url": "https://lambda.us-gov-west-1.api.aws" + "url": "https://lambda.eu-west-2.api.aws" } }, "params": { "UseDualStack": true, "UseFIPS": false, - "Region": "us-gov-west-1" + "Region": "eu-west-2" } }, { - "documentation": "For region us-gov-west-1 with FIPS disabled and DualStack disabled", + "documentation": "For region eu-west-3 with FIPS disabled and DualStack disabled", "expect": { "endpoint": { - "url": "https://lambda.us-gov-west-1.amazonaws.com" + "url": "https://lambda.eu-west-3.amazonaws.com" } }, "params": { "UseDualStack": false, "UseFIPS": false, - "Region": "us-gov-west-1" + "Region": "eu-west-3" } }, { - "documentation": "For region ap-southeast-1 with FIPS enabled and DualStack enabled", + "documentation": "For region eu-west-3 with FIPS disabled and DualStack enabled", "expect": { "endpoint": { - "url": "https://lambda-fips.ap-southeast-1.api.aws" + "url": "https://lambda.eu-west-3.api.aws" } }, "params": { "UseDualStack": true, - "UseFIPS": true, - "Region": "ap-southeast-1" + "UseFIPS": false, + "Region": "eu-west-3" } }, { - "documentation": "For region ap-southeast-1 with FIPS enabled and DualStack disabled", + "documentation": "For region me-south-1 with FIPS disabled and DualStack disabled", "expect": { "endpoint": { - "url": "https://lambda-fips.ap-southeast-1.amazonaws.com" + "url": "https://lambda.me-south-1.amazonaws.com" } }, "params": { "UseDualStack": false, - "UseFIPS": true, - "Region": "ap-southeast-1" + "UseFIPS": false, + "Region": "me-south-1" } }, { - "documentation": "For region ap-southeast-1 with FIPS disabled and DualStack enabled", + "documentation": "For region me-south-1 with FIPS disabled and DualStack enabled", "expect": { "endpoint": { - "url": "https://lambda.ap-southeast-1.api.aws" + "url": "https://lambda.me-south-1.api.aws" } }, "params": { "UseDualStack": true, "UseFIPS": false, - "Region": "ap-southeast-1" + "Region": "me-south-1" } }, { - "documentation": "For region ap-southeast-1 with FIPS disabled and DualStack disabled", + "documentation": "For region sa-east-1 with FIPS disabled and DualStack disabled", "expect": { "endpoint": { - "url": "https://lambda.ap-southeast-1.amazonaws.com" + "url": "https://lambda.sa-east-1.amazonaws.com" } }, "params": { "UseDualStack": false, "UseFIPS": false, - "Region": "ap-southeast-1" + "Region": "sa-east-1" } }, { - "documentation": "For region ap-southeast-2 with FIPS enabled and DualStack enabled", + "documentation": "For region sa-east-1 with FIPS disabled and DualStack enabled", "expect": { "endpoint": { - "url": "https://lambda-fips.ap-southeast-2.api.aws" + "url": "https://lambda.sa-east-1.api.aws" } }, "params": { "UseDualStack": true, - "UseFIPS": true, - "Region": "ap-southeast-2" + "UseFIPS": false, + "Region": "sa-east-1" } }, { - "documentation": "For region ap-southeast-2 with FIPS enabled and DualStack disabled", + "documentation": "For region us-east-1 with FIPS disabled and DualStack disabled", "expect": { "endpoint": { - "url": "https://lambda-fips.ap-southeast-2.amazonaws.com" + "url": "https://lambda.us-east-1.amazonaws.com" } }, "params": { "UseDualStack": false, - "UseFIPS": true, - "Region": "ap-southeast-2" + "UseFIPS": false, + "Region": "us-east-1" } }, { - "documentation": "For region ap-southeast-2 with FIPS disabled and DualStack enabled", + "documentation": "For region us-east-1 with FIPS enabled and DualStack disabled", "expect": { "endpoint": { - "url": "https://lambda.ap-southeast-2.api.aws" + "url": "https://lambda-fips.us-east-1.amazonaws.com" } }, "params": { - "UseDualStack": true, - "UseFIPS": false, - "Region": "ap-southeast-2" + "UseDualStack": false, + "UseFIPS": true, + "Region": "us-east-1" } }, { - "documentation": "For region ap-southeast-2 with FIPS disabled and DualStack disabled", + "documentation": "For region us-east-1 with FIPS disabled and DualStack enabled", "expect": { "endpoint": { - "url": "https://lambda.ap-southeast-2.amazonaws.com" + "url": "https://lambda.us-east-1.api.aws" } }, "params": { - "UseDualStack": false, + "UseDualStack": true, "UseFIPS": false, - "Region": "ap-southeast-2" + "Region": "us-east-1" } }, { - "documentation": "For region us-iso-east-1 with FIPS enabled and DualStack enabled", + "documentation": "For region us-east-2 with FIPS disabled and DualStack disabled", "expect": { - "error": "FIPS and DualStack are enabled, but this partition does not support one or both" + "endpoint": { + "url": "https://lambda.us-east-2.amazonaws.com" + } }, "params": { - "UseDualStack": true, - "UseFIPS": true, - "Region": "us-iso-east-1" + "UseDualStack": false, + "UseFIPS": false, + "Region": "us-east-2" } }, { - "documentation": "For region us-iso-east-1 with FIPS enabled and DualStack disabled", + "documentation": "For region us-east-2 with FIPS enabled and DualStack disabled", "expect": { "endpoint": { - "url": "https://lambda-fips.us-iso-east-1.c2s.ic.gov" + "url": "https://lambda-fips.us-east-2.amazonaws.com" } }, "params": { "UseDualStack": false, "UseFIPS": true, - "Region": "us-iso-east-1" + "Region": "us-east-2" } }, { - "documentation": "For region us-iso-east-1 with FIPS disabled and DualStack enabled", + "documentation": "For region us-east-2 with FIPS disabled and DualStack enabled", "expect": { - "error": "DualStack is enabled but this partition does not support DualStack" + "endpoint": { + "url": "https://lambda.us-east-2.api.aws" + } }, "params": { "UseDualStack": true, "UseFIPS": false, - "Region": "us-iso-east-1" + "Region": "us-east-2" } }, { - "documentation": "For region us-iso-east-1 with FIPS disabled and DualStack disabled", + "documentation": "For region us-west-1 with FIPS disabled and DualStack disabled", "expect": { "endpoint": { - "url": "https://lambda.us-iso-east-1.c2s.ic.gov" + "url": "https://lambda.us-west-1.amazonaws.com" } }, "params": { "UseDualStack": false, "UseFIPS": false, - "Region": "us-iso-east-1" + "Region": "us-west-1" } }, { - "documentation": "For region ap-southeast-3 with FIPS enabled and DualStack enabled", + "documentation": "For region us-west-1 with FIPS enabled and DualStack disabled", "expect": { "endpoint": { - "url": "https://lambda-fips.ap-southeast-3.api.aws" + "url": "https://lambda-fips.us-west-1.amazonaws.com" } }, "params": { - "UseDualStack": true, + "UseDualStack": false, "UseFIPS": true, - "Region": "ap-southeast-3" + "Region": "us-west-1" } }, { - "documentation": "For region ap-southeast-3 with FIPS enabled and DualStack disabled", + "documentation": "For region us-west-1 with FIPS disabled and DualStack enabled", "expect": { "endpoint": { - "url": "https://lambda-fips.ap-southeast-3.amazonaws.com" + "url": "https://lambda.us-west-1.api.aws" } }, "params": { - "UseDualStack": false, - "UseFIPS": true, - "Region": "ap-southeast-3" + "UseDualStack": true, + "UseFIPS": false, + "Region": "us-west-1" } }, { - "documentation": "For region ap-southeast-3 with FIPS disabled and DualStack enabled", + "documentation": "For region us-west-2 with FIPS disabled and DualStack disabled", "expect": { "endpoint": { - "url": "https://lambda.ap-southeast-3.api.aws" + "url": "https://lambda.us-west-2.amazonaws.com" } }, "params": { - "UseDualStack": true, + "UseDualStack": false, "UseFIPS": false, - "Region": "ap-southeast-3" + "Region": "us-west-2" } }, { - "documentation": "For region ap-southeast-3 with FIPS disabled and DualStack disabled", + "documentation": "For region us-west-2 with FIPS enabled and DualStack disabled", "expect": { "endpoint": { - "url": "https://lambda.ap-southeast-3.amazonaws.com" + "url": "https://lambda-fips.us-west-2.amazonaws.com" } }, "params": { "UseDualStack": false, - "UseFIPS": false, - "Region": "ap-southeast-3" + "UseFIPS": true, + "Region": "us-west-2" } }, { - "documentation": "For region ap-southeast-4 with FIPS enabled and DualStack enabled", + "documentation": "For region us-west-2 with FIPS disabled and DualStack enabled", "expect": { "endpoint": { - "url": "https://lambda-fips.ap-southeast-4.api.aws" + "url": "https://lambda.us-west-2.api.aws" } }, "params": { "UseDualStack": true, - "UseFIPS": true, - "Region": "ap-southeast-4" + "UseFIPS": false, + "Region": "us-west-2" } }, { - "documentation": "For region ap-southeast-4 with FIPS enabled and DualStack disabled", + "documentation": "For region us-east-1 with FIPS enabled and DualStack enabled", "expect": { "endpoint": { - "url": "https://lambda-fips.ap-southeast-4.amazonaws.com" + "url": "https://lambda-fips.us-east-1.api.aws" } }, "params": { - "UseDualStack": false, + "UseDualStack": true, "UseFIPS": true, - "Region": "ap-southeast-4" + "Region": "us-east-1" } }, { - "documentation": "For region ap-southeast-4 with FIPS disabled and DualStack enabled", + "documentation": "For region cn-north-1 with FIPS disabled and DualStack disabled", "expect": { "endpoint": { - "url": "https://lambda.ap-southeast-4.api.aws" + "url": "https://lambda.cn-north-1.amazonaws.com.cn" } }, "params": { - "UseDualStack": true, + "UseDualStack": false, "UseFIPS": false, - "Region": "ap-southeast-4" + "Region": "cn-north-1" } }, { - "documentation": "For region ap-southeast-4 with FIPS disabled and DualStack disabled", + "documentation": "For region cn-north-1 with FIPS disabled and DualStack enabled", "expect": { "endpoint": { - "url": "https://lambda.ap-southeast-4.amazonaws.com" + "url": "https://lambda.cn-north-1.api.amazonwebservices.com.cn" } }, "params": { - "UseDualStack": false, + "UseDualStack": true, "UseFIPS": false, - "Region": "ap-southeast-4" + "Region": "cn-north-1" } }, { - "documentation": "For region us-east-1 with FIPS enabled and DualStack enabled", + "documentation": "For region cn-northwest-1 with FIPS disabled and DualStack disabled", "expect": { "endpoint": { - "url": "https://lambda-fips.us-east-1.api.aws" + "url": "https://lambda.cn-northwest-1.amazonaws.com.cn" } }, "params": { - "UseDualStack": true, - "UseFIPS": true, - "Region": "us-east-1" + "UseDualStack": false, + "UseFIPS": false, + "Region": "cn-northwest-1" } }, { - "documentation": "For region us-east-1 with FIPS enabled and DualStack disabled", + "documentation": "For region cn-northwest-1 with FIPS disabled and DualStack enabled", "expect": { "endpoint": { - "url": "https://lambda-fips.us-east-1.amazonaws.com" + "url": "https://lambda.cn-northwest-1.api.amazonwebservices.com.cn" } }, "params": { - "UseDualStack": false, - "UseFIPS": true, - "Region": "us-east-1" + "UseDualStack": true, + "UseFIPS": false, + "Region": "cn-northwest-1" } }, { - "documentation": "For region us-east-1 with FIPS disabled and DualStack enabled", + "documentation": "For region cn-north-1 with FIPS enabled and DualStack enabled", "expect": { "endpoint": { - "url": "https://lambda.us-east-1.api.aws" + "url": "https://lambda-fips.cn-north-1.api.amazonwebservices.com.cn" } }, "params": { "UseDualStack": true, - "UseFIPS": false, - "Region": "us-east-1" + "UseFIPS": true, + "Region": "cn-north-1" } }, { - "documentation": "For region us-east-1 with FIPS disabled and DualStack disabled", + "documentation": "For region cn-north-1 with FIPS enabled and DualStack disabled", "expect": { "endpoint": { - "url": "https://lambda.us-east-1.amazonaws.com" + "url": "https://lambda-fips.cn-north-1.amazonaws.com.cn" } }, "params": { "UseDualStack": false, - "UseFIPS": false, - "Region": "us-east-1" + "UseFIPS": true, + "Region": "cn-north-1" } }, { - "documentation": "For region us-east-2 with FIPS enabled and DualStack enabled", + "documentation": "For region us-gov-east-1 with FIPS disabled and DualStack disabled", "expect": { "endpoint": { - "url": "https://lambda-fips.us-east-2.api.aws" + "url": "https://lambda.us-gov-east-1.amazonaws.com" } }, "params": { - "UseDualStack": true, - "UseFIPS": true, - "Region": "us-east-2" + "UseDualStack": false, + "UseFIPS": false, + "Region": "us-gov-east-1" } }, { - "documentation": "For region us-east-2 with FIPS enabled and DualStack disabled", + "documentation": "For region us-gov-east-1 with FIPS enabled and DualStack disabled", "expect": { "endpoint": { - "url": "https://lambda-fips.us-east-2.amazonaws.com" + "url": "https://lambda-fips.us-gov-east-1.amazonaws.com" } }, "params": { "UseDualStack": false, "UseFIPS": true, - "Region": "us-east-2" + "Region": "us-gov-east-1" } }, { - "documentation": "For region us-east-2 with FIPS disabled and DualStack enabled", + "documentation": "For region us-gov-west-1 with FIPS disabled and DualStack disabled", "expect": { "endpoint": { - "url": "https://lambda.us-east-2.api.aws" + "url": "https://lambda.us-gov-west-1.amazonaws.com" } }, "params": { - "UseDualStack": true, + "UseDualStack": false, "UseFIPS": false, - "Region": "us-east-2" + "Region": "us-gov-west-1" } }, { - "documentation": "For region us-east-2 with FIPS disabled and DualStack disabled", + "documentation": "For region us-gov-west-1 with FIPS enabled and DualStack disabled", "expect": { "endpoint": { - "url": "https://lambda.us-east-2.amazonaws.com" + "url": "https://lambda-fips.us-gov-west-1.amazonaws.com" } }, "params": { "UseDualStack": false, - "UseFIPS": false, - "Region": "us-east-2" + "UseFIPS": true, + "Region": "us-gov-west-1" } }, { - "documentation": "For region cn-northwest-1 with FIPS enabled and DualStack enabled", + "documentation": "For region us-gov-east-1 with FIPS enabled and DualStack enabled", "expect": { "endpoint": { - "url": "https://lambda-fips.cn-northwest-1.api.amazonwebservices.com.cn" + "url": "https://lambda-fips.us-gov-east-1.api.aws" } }, "params": { "UseDualStack": true, "UseFIPS": true, - "Region": "cn-northwest-1" + "Region": "us-gov-east-1" } }, { - "documentation": "For region cn-northwest-1 with FIPS enabled and DualStack disabled", + "documentation": "For region us-gov-east-1 with FIPS disabled and DualStack enabled", "expect": { "endpoint": { - "url": "https://lambda-fips.cn-northwest-1.amazonaws.com.cn" + "url": "https://lambda.us-gov-east-1.api.aws" } }, "params": { - "UseDualStack": false, - "UseFIPS": true, - "Region": "cn-northwest-1" + "UseDualStack": true, + "UseFIPS": false, + "Region": "us-gov-east-1" } }, { - "documentation": "For region cn-northwest-1 with FIPS disabled and DualStack enabled", + "documentation": "For region us-iso-east-1 with FIPS disabled and DualStack disabled", "expect": { "endpoint": { - "url": "https://lambda.cn-northwest-1.api.amazonwebservices.com.cn" + "url": "https://lambda.us-iso-east-1.c2s.ic.gov" } }, "params": { - "UseDualStack": true, + "UseDualStack": false, "UseFIPS": false, - "Region": "cn-northwest-1" + "Region": "us-iso-east-1" } }, { - "documentation": "For region cn-northwest-1 with FIPS disabled and DualStack disabled", + "documentation": "For region us-iso-west-1 with FIPS disabled and DualStack disabled", "expect": { "endpoint": { - "url": "https://lambda.cn-northwest-1.amazonaws.com.cn" + "url": "https://lambda.us-iso-west-1.c2s.ic.gov" } }, "params": { "UseDualStack": false, "UseFIPS": false, - "Region": "cn-northwest-1" + "Region": "us-iso-west-1" } }, { - "documentation": "For region us-isob-east-1 with FIPS enabled and DualStack enabled", + "documentation": "For region us-iso-east-1 with FIPS enabled and DualStack disabled", "expect": { - "error": "FIPS and DualStack are enabled, but this partition does not support one or both" + "endpoint": { + "url": "https://lambda-fips.us-iso-east-1.c2s.ic.gov" + } }, "params": { - "UseDualStack": true, + "UseDualStack": false, "UseFIPS": true, - "Region": "us-isob-east-1" + "Region": "us-iso-east-1" } }, { - "documentation": "For region us-isob-east-1 with FIPS enabled and DualStack disabled", + "documentation": "For region us-isob-east-1 with FIPS disabled and DualStack disabled", "expect": { "endpoint": { - "url": "https://lambda-fips.us-isob-east-1.sc2s.sgov.gov" + "url": "https://lambda.us-isob-east-1.sc2s.sgov.gov" } }, "params": { "UseDualStack": false, - "UseFIPS": true, + "UseFIPS": false, "Region": "us-isob-east-1" } }, { - "documentation": "For region us-isob-east-1 with FIPS disabled and DualStack enabled", + "documentation": "For region us-isob-east-1 with FIPS enabled and DualStack disabled", "expect": { - "error": "DualStack is enabled but this partition does not support DualStack" + "endpoint": { + "url": "https://lambda-fips.us-isob-east-1.sc2s.sgov.gov" + } }, "params": { - "UseDualStack": true, - "UseFIPS": false, + "UseDualStack": false, + "UseFIPS": true, "Region": "us-isob-east-1" } }, { - "documentation": "For region us-isob-east-1 with FIPS disabled and DualStack disabled", + "documentation": "For custom endpoint with region set and fips disabled and dualstack disabled", "expect": { "endpoint": { - "url": "https://lambda.us-isob-east-1.sc2s.sgov.gov" + "url": "https://example.com" } }, "params": { "UseDualStack": false, "UseFIPS": false, - "Region": "us-isob-east-1" + "Region": "us-east-1", + "Endpoint": "https://example.com" } }, { - "documentation": "For custom endpoint with fips disabled and dualstack disabled", + "documentation": "For custom endpoint with region not set and fips disabled and dualstack disabled", "expect": { "endpoint": { "url": "https://example.com" @@ -2317,7 +1477,6 @@ "params": { "UseDualStack": false, "UseFIPS": false, - "Region": "us-east-1", "Endpoint": "https://example.com" } }, @@ -2514,6 +1673,9 @@ "smithy.api#httpQuery": "RevisionId" } } + }, + "traits": { + "smithy.api#input": {} } }, "com.amazonaws.lambda#AddLayerVersionPermissionResponse": { @@ -2531,6 +1693,9 @@ "smithy.api#documentation": "

A unique identifier for the current revision of the policy.

" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.lambda#AddPermission": { @@ -2645,9 +1810,12 @@ "FunctionUrlAuthType": { "target": "com.amazonaws.lambda#FunctionUrlAuthType", "traits": { - "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 IAM 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#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.

" } } + }, + "traits": { + "smithy.api#input": {} } }, "com.amazonaws.lambda#AddPermissionResponse": { @@ -2659,6 +1827,9 @@ "smithy.api#documentation": "

The permission statement that's added to the function policy.

" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.lambda#AdditionalVersion": { @@ -3028,6 +2199,16 @@ "smithy.api#httpError": 400 } }, + "com.amazonaws.lambda#CollectionName": { + "type": "string", + "traits": { + "smithy.api#length": { + "min": 1, + "max": 57 + }, + "smithy.api#pattern": "^(^(?!(system\\x2e)))(^[_a-zA-Z0-9])([^$]*)$" + } + }, "com.amazonaws.lambda#CompatibleArchitectures": { "type": "list", "member": { @@ -3178,6 +2359,9 @@ "smithy.api#documentation": "

The routing\n configuration of the alias.

" } } + }, + "traits": { + "smithy.api#input": {} } }, "com.amazonaws.lambda#CreateCodeSigningConfig": { @@ -3227,6 +2411,9 @@ "smithy.api#documentation": "

The code signing policies define the actions to take if the validation checks fail.

" } } + }, + "traits": { + "smithy.api#input": {} } }, "com.amazonaws.lambda#CreateCodeSigningConfigResponse": { @@ -3239,6 +2426,9 @@ "smithy.api#required": {} } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.lambda#CreateEventSourceMapping": { @@ -3410,7 +2600,16 @@ "traits": { "smithy.api#documentation": "

(Amazon SQS only) The scaling configuration for the event source. For more information, see Configuring maximum concurrency for Amazon SQS event sources.

" } + }, + "DocumentDBEventSourceConfig": { + "target": "com.amazonaws.lambda#DocumentDBEventSourceConfig", + "traits": { + "smithy.api#documentation": "

Specific configuration settings for a DocumentDB event source.

" + } } + }, + "traits": { + "smithy.api#input": {} } }, "com.amazonaws.lambda#CreateFunction": { @@ -3472,7 +2671,7 @@ "Runtime": { "target": "com.amazonaws.lambda#Runtime", "traits": { - "smithy.api#documentation": "

The identifier of the function's runtime. Runtime is required if the deployment package is a .zip file archive.\n

" + "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": { @@ -3547,7 +2746,7 @@ "KMSKeyArn": { "target": "com.amazonaws.lambda#KMSKeyArn", "traits": { - "smithy.api#documentation": "

The ARN of the Key Management Service (KMS) key that's used to encrypt your function's environment\n variables. If it's not provided, 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 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.

" } }, "TracingConfig": { @@ -3604,6 +2803,9 @@ "smithy.api#documentation": "

The function's SnapStart setting.

" } } + }, + "traits": { + "smithy.api#input": {} } }, "com.amazonaws.lambda#CreateFunctionUrlConfig": { @@ -3661,7 +2863,7 @@ "AuthType": { "target": "com.amazonaws.lambda#FunctionUrlAuthType", "traits": { - "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 IAM 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#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": {} } }, @@ -3671,6 +2873,9 @@ "smithy.api#documentation": "

The cross-origin resource sharing (CORS) settings\n for your function URL.

" } } + }, + "traits": { + "smithy.api#input": {} } }, "com.amazonaws.lambda#CreateFunctionUrlConfigResponse": { @@ -3693,7 +2898,7 @@ "AuthType": { "target": "com.amazonaws.lambda#FunctionUrlAuthType", "traits": { - "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 IAM 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#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": {} } }, @@ -3710,6 +2915,19 @@ "smithy.api#required": {} } } + }, + "traits": { + "smithy.api#output": {} + } + }, + "com.amazonaws.lambda#DatabaseName": { + "type": "string", + "traits": { + "smithy.api#length": { + "min": 1, + "max": 63 + }, + "smithy.api#pattern": "^[^ /\\.$\\x22]*$" } }, "com.amazonaws.lambda#Date": { @@ -3779,6 +2997,9 @@ "smithy.api#required": {} } } + }, + "traits": { + "smithy.api#input": {} } }, "com.amazonaws.lambda#DeleteCodeSigningConfig": { @@ -3823,11 +3044,17 @@ "smithy.api#required": {} } } + }, + "traits": { + "smithy.api#input": {} } }, "com.amazonaws.lambda#DeleteCodeSigningConfigResponse": { "type": "structure", - "members": {} + "members": {}, + "traits": { + "smithy.api#output": {} + } }, "com.amazonaws.lambda#DeleteEventSourceMapping": { "type": "operation", @@ -3874,6 +3101,9 @@ "smithy.api#required": {} } } + }, + "traits": { + "smithy.api#input": {} } }, "com.amazonaws.lambda#DeleteFunction": { @@ -3958,6 +3188,9 @@ "smithy.api#required": {} } } + }, + "traits": { + "smithy.api#input": {} } }, "com.amazonaws.lambda#DeleteFunctionConcurrency": { @@ -4005,6 +3238,9 @@ "smithy.api#required": {} } } + }, + "traits": { + "smithy.api#input": {} } }, "com.amazonaws.lambda#DeleteFunctionEventInvokeConfig": { @@ -4059,6 +3295,9 @@ "smithy.api#httpQuery": "Qualifier" } } + }, + "traits": { + "smithy.api#input": {} } }, "com.amazonaws.lambda#DeleteFunctionRequest": { @@ -4079,6 +3318,9 @@ "smithy.api#httpQuery": "Qualifier" } } + }, + "traits": { + "smithy.api#input": {} } }, "com.amazonaws.lambda#DeleteFunctionUrlConfig": { @@ -4130,6 +3372,9 @@ "smithy.api#httpQuery": "Qualifier" } } + }, + "traits": { + "smithy.api#input": {} } }, "com.amazonaws.lambda#DeleteLayerVersion": { @@ -4177,6 +3422,9 @@ "smithy.api#required": {} } } + }, + "traits": { + "smithy.api#input": {} } }, "com.amazonaws.lambda#DeleteProvisionedConcurrencyConfig": { @@ -4232,6 +3480,9 @@ "smithy.api#required": {} } } + }, + "traits": { + "smithy.api#input": {} } }, "com.amazonaws.lambda#Description": { @@ -4273,6 +3524,32 @@ "smithy.api#documentation": "

A configuration object that specifies the destination of an event after Lambda processes it.

" } }, + "com.amazonaws.lambda#DocumentDBEventSourceConfig": { + "type": "structure", + "members": { + "DatabaseName": { + "target": "com.amazonaws.lambda#DatabaseName", + "traits": { + "smithy.api#documentation": "

\n The name of the database to consume within the DocumentDB cluster.\n

" + } + }, + "CollectionName": { + "target": "com.amazonaws.lambda#CollectionName", + "traits": { + "smithy.api#documentation": "

\n The name of the collection to consume within the database. If you do not specify a collection, Lambda consumes all collections.\n

" + } + }, + "FullDocument": { + "target": "com.amazonaws.lambda#FullDocument", + "traits": { + "smithy.api#documentation": "

\n Determines what DocumentDB sends to your event stream during document update operations. If set to UpdateLookup, DocumentDB sends a delta describing the changes, along with a copy of the entire document. Otherwise, DocumentDB sends only a partial document that contains the changes.\n

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

\n Specific configuration settings for a DocumentDB event source.\n

" + } + }, "com.amazonaws.lambda#EC2AccessDeniedException": { "type": "structure", "members": { @@ -4716,6 +3993,12 @@ "traits": { "smithy.api#documentation": "

(Amazon SQS only) The scaling configuration for the event source. For more information, see Configuring maximum concurrency for Amazon SQS event sources.

" } + }, + "DocumentDBEventSourceConfig": { + "target": "com.amazonaws.lambda#DocumentDBEventSourceConfig", + "traits": { + "smithy.api#documentation": "

Specific configuration settings for a DocumentDB event source.

" + } } }, "traits": { @@ -4839,6 +4122,23 @@ "target": "com.amazonaws.lambda#Filter" } }, + "com.amazonaws.lambda#FullDocument": { + "type": "enum", + "members": { + "UpdateLookup": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "UpdateLookup" + } + }, + "Default": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "Default" + } + } + } + }, "com.amazonaws.lambda#FunctionArn": { "type": "string", "traits": { @@ -5018,7 +4318,7 @@ "KMSKeyArn": { "target": "com.amazonaws.lambda#KMSKeyArn", "traits": { - "smithy.api#documentation": "

The KMS key that's used to encrypt the function's environment variables. This key is\n returned only if you've configured a customer managed key.

" + "smithy.api#documentation": "

The KMS key that's used to encrypt the function's environment variables. When Lambda SnapStart is activated, this key is also used to encrypt the function's snapshot. This key is\n returned only if you've configured a customer managed key.

" } }, "TracingConfig": { @@ -5286,7 +4586,7 @@ "AuthType": { "target": "com.amazonaws.lambda#FunctionUrlAuthType", "traits": { - "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 IAM 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#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": {} } } @@ -5349,7 +4649,10 @@ }, "com.amazonaws.lambda#GetAccountSettingsRequest": { "type": "structure", - "members": {} + "members": {}, + "traits": { + "smithy.api#input": {} + } }, "com.amazonaws.lambda#GetAccountSettingsResponse": { "type": "structure", @@ -5366,6 +4669,9 @@ "smithy.api#documentation": "

The number of functions and amount of storage in use.

" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.lambda#GetAlias": { @@ -5418,6 +4724,9 @@ "smithy.api#required": {} } } + }, + "traits": { + "smithy.api#input": {} } }, "com.amazonaws.lambda#GetCodeSigningConfig": { @@ -5459,6 +4768,9 @@ "smithy.api#required": {} } } + }, + "traits": { + "smithy.api#input": {} } }, "com.amazonaws.lambda#GetCodeSigningConfigResponse": { @@ -5471,6 +4783,9 @@ "smithy.api#required": {} } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.lambda#GetEventSourceMapping": { @@ -5515,6 +4830,9 @@ "smithy.api#required": {} } } + }, + "traits": { + "smithy.api#input": {} } }, "com.amazonaws.lambda#GetFunction": { @@ -5681,6 +4999,9 @@ "smithy.api#required": {} } } + }, + "traits": { + "smithy.api#input": {} } }, "com.amazonaws.lambda#GetFunctionCodeSigningConfigResponse": { @@ -5700,6 +5021,9 @@ "smithy.api#required": {} } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.lambda#GetFunctionConcurrency": { @@ -5744,6 +5068,9 @@ "smithy.api#required": {} } } + }, + "traits": { + "smithy.api#input": {} } }, "com.amazonaws.lambda#GetFunctionConcurrencyResponse": { @@ -5755,6 +5082,9 @@ "smithy.api#documentation": "

The number of simultaneous executions that are reserved for the function.

" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.lambda#GetFunctionConfiguration": { @@ -5916,6 +5246,9 @@ "smithy.api#httpQuery": "Qualifier" } } + }, + "traits": { + "smithy.api#input": {} } }, "com.amazonaws.lambda#GetFunctionEventInvokeConfig": { @@ -5967,6 +5300,9 @@ "smithy.api#httpQuery": "Qualifier" } } + }, + "traits": { + "smithy.api#input": {} } }, "com.amazonaws.lambda#GetFunctionRequest": { @@ -5987,6 +5323,9 @@ "smithy.api#httpQuery": "Qualifier" } } + }, + "traits": { + "smithy.api#input": {} } }, "com.amazonaws.lambda#GetFunctionResponse": { @@ -6016,6 +5355,9 @@ "smithy.api#documentation": "

The function's reserved\n concurrency.

" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.lambda#GetFunctionUrlConfig": { @@ -6067,6 +5409,9 @@ "smithy.api#httpQuery": "Qualifier" } } + }, + "traits": { + "smithy.api#input": {} } }, "com.amazonaws.lambda#GetFunctionUrlConfigResponse": { @@ -6089,7 +5434,7 @@ "AuthType": { "target": "com.amazonaws.lambda#FunctionUrlAuthType", "traits": { - "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 IAM 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#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": {} } }, @@ -6113,6 +5458,9 @@ "smithy.api#required": {} } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.lambda#GetLayerVersion": { @@ -6188,6 +5536,9 @@ "smithy.api#required": {} } } + }, + "traits": { + "smithy.api#input": {} } }, "com.amazonaws.lambda#GetLayerVersionPolicy": { @@ -6241,6 +5592,9 @@ "smithy.api#required": {} } } + }, + "traits": { + "smithy.api#input": {} } }, "com.amazonaws.lambda#GetLayerVersionPolicyResponse": { @@ -6258,6 +5612,9 @@ "smithy.api#documentation": "

A unique identifier for the current revision of the policy.

" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.lambda#GetLayerVersionRequest": { @@ -6280,6 +5637,9 @@ "smithy.api#required": {} } } + }, + "traits": { + "smithy.api#input": {} } }, "com.amazonaws.lambda#GetLayerVersionResponse": { @@ -6391,6 +5751,9 @@ "smithy.api#httpQuery": "Qualifier" } } + }, + "traits": { + "smithy.api#input": {} } }, "com.amazonaws.lambda#GetPolicyResponse": { @@ -6408,6 +5771,9 @@ "smithy.api#documentation": "

A unique identifier for the current revision of the policy.

" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.lambda#GetProvisionedConcurrencyConfig": { @@ -6463,6 +5829,9 @@ "smithy.api#required": {} } } + }, + "traits": { + "smithy.api#input": {} } }, "com.amazonaws.lambda#GetProvisionedConcurrencyConfigResponse": { @@ -6504,6 +5873,9 @@ "smithy.api#documentation": "

The date and time that a user last updated the configuration, in ISO 8601 format.

" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.lambda#GetRuntimeManagementConfig": { @@ -6541,7 +5913,7 @@ "type": "structure", "members": { "FunctionName": { - "target": "com.amazonaws.lambda#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": {}, @@ -6555,6 +5927,9 @@ "smithy.api#httpQuery": "Qualifier" } } + }, + "traits": { + "smithy.api#input": {} } }, "com.amazonaws.lambda#GetRuntimeManagementConfigResponse": { @@ -6569,9 +5944,18 @@ "RuntimeVersionArn": { "target": "com.amazonaws.lambda#RuntimeVersionArn", "traits": { - "smithy.api#documentation": "

The ARN of the runtime the function is configured to use. If the runtime update mode is Manual, the ARN is returned, otherwise null \n is returned.

" + "smithy.api#documentation": "

The ARN of the runtime the function is configured to use. If the runtime update mode is Manual, the ARN is returned, otherwise null \n is returned.

" + } + }, + "FunctionArn": { + "target": "com.amazonaws.lambda#NameSpacedFunctionArn", + "traits": { + "smithy.api#documentation": "

The Amazon Resource Name (ARN) of your function.

" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.lambda#Handler": { @@ -6854,6 +6238,9 @@ "smithy.api#httpQuery": "Qualifier" } } + }, + "traits": { + "smithy.api#input": {} } }, "com.amazonaws.lambda#InvocationResponse": { @@ -6895,6 +6282,9 @@ "smithy.api#httpHeader": "X-Amz-Executed-Version" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.lambda#InvocationType": { @@ -7082,7 +6472,8 @@ } }, "traits": { - "smithy.api#deprecated": {} + "smithy.api#deprecated": {}, + "smithy.api#input": {} } }, "com.amazonaws.lambda#InvokeAsyncResponse": { @@ -7099,7 +6490,8 @@ }, "traits": { "smithy.api#deprecated": {}, - "smithy.api#documentation": "

A success response (202 Accepted) indicates that the request is queued for invocation.

" + "smithy.api#documentation": "

A success response (202 Accepted) indicates that the request is queued for invocation.

", + "smithy.api#output": {} } }, "com.amazonaws.lambda#KMSAccessDeniedException": { @@ -7664,6 +7056,9 @@ "smithy.api#httpQuery": "MaxItems" } } + }, + "traits": { + "smithy.api#input": {} } }, "com.amazonaws.lambda#ListAliasesResponse": { @@ -7681,6 +7076,9 @@ "smithy.api#documentation": "

A list of aliases.

" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.lambda#ListCodeSigningConfigs": { @@ -7731,6 +7129,9 @@ "smithy.api#httpQuery": "MaxItems" } } + }, + "traits": { + "smithy.api#input": {} } }, "com.amazonaws.lambda#ListCodeSigningConfigsResponse": { @@ -7748,6 +7149,9 @@ "smithy.api#documentation": "

The code signing configurations

" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.lambda#ListEventSourceMappings": { @@ -7818,6 +7222,9 @@ "smithy.api#httpQuery": "MaxItems" } } + }, + "traits": { + "smithy.api#input": {} } }, "com.amazonaws.lambda#ListEventSourceMappingsResponse": { @@ -7835,6 +7242,9 @@ "smithy.api#documentation": "

A list of event source mappings.

" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.lambda#ListFunctionEventInvokeConfigs": { @@ -7899,6 +7309,9 @@ "smithy.api#httpQuery": "MaxItems" } } + }, + "traits": { + "smithy.api#input": {} } }, "com.amazonaws.lambda#ListFunctionEventInvokeConfigsResponse": { @@ -7916,6 +7329,9 @@ "smithy.api#documentation": "

The pagination token that's included if more results are available.

" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.lambda#ListFunctionUrlConfigs": { @@ -7980,6 +7396,9 @@ "smithy.api#httpQuery": "MaxItems" } } + }, + "traits": { + "smithy.api#input": {} } }, "com.amazonaws.lambda#ListFunctionUrlConfigsResponse": { @@ -7998,6 +7417,9 @@ "smithy.api#documentation": "

The pagination token that's included if more results are available.

" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.lambda#ListFunctions": { @@ -8093,6 +7515,9 @@ "smithy.api#httpQuery": "MaxItems" } } + }, + "traits": { + "smithy.api#input": {} } }, "com.amazonaws.lambda#ListFunctionsByCodeSigningConfigResponse": { @@ -8110,6 +7535,9 @@ "smithy.api#documentation": "

The function ARNs.

" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.lambda#ListFunctionsRequest": { @@ -8143,6 +7571,9 @@ "smithy.api#httpQuery": "MaxItems" } } + }, + "traits": { + "smithy.api#input": {} } }, "com.amazonaws.lambda#ListFunctionsResponse": { @@ -8162,7 +7593,8 @@ } }, "traits": { - "smithy.api#documentation": "

A list of Lambda functions.

" + "smithy.api#documentation": "

A list of Lambda functions.

", + "smithy.api#output": {} } }, "com.amazonaws.lambda#ListLayerVersions": { @@ -8241,6 +7673,9 @@ "smithy.api#httpQuery": "CompatibleArchitecture" } } + }, + "traits": { + "smithy.api#input": {} } }, "com.amazonaws.lambda#ListLayerVersionsResponse": { @@ -8258,6 +7693,9 @@ "smithy.api#documentation": "

A list of versions.

" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.lambda#ListLayers": { @@ -8325,6 +7763,9 @@ "smithy.api#httpQuery": "CompatibleArchitecture" } } + }, + "traits": { + "smithy.api#input": {} } }, "com.amazonaws.lambda#ListLayersResponse": { @@ -8342,6 +7783,9 @@ "smithy.api#documentation": "

A list of function layers.

" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.lambda#ListProvisionedConcurrencyConfigs": { @@ -8406,6 +7850,9 @@ "smithy.api#httpQuery": "MaxItems" } } + }, + "traits": { + "smithy.api#input": {} } }, "com.amazonaws.lambda#ListProvisionedConcurrencyConfigsResponse": { @@ -8423,6 +7870,9 @@ "smithy.api#documentation": "

The pagination token that's included if more results are available.

" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.lambda#ListTags": { @@ -8467,6 +7917,9 @@ "smithy.api#required": {} } } + }, + "traits": { + "smithy.api#input": {} } }, "com.amazonaws.lambda#ListTagsResponse": { @@ -8478,6 +7931,9 @@ "smithy.api#documentation": "

The function's tags.

" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.lambda#ListVersionsByFunction": { @@ -8542,6 +7998,9 @@ "smithy.api#httpQuery": "MaxItems" } } + }, + "traits": { + "smithy.api#input": {} } }, "com.amazonaws.lambda#ListVersionsByFunctionResponse": { @@ -8559,6 +8018,9 @@ "smithy.api#documentation": "

A list of Lambda function versions.

" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.lambda#LocalMountPath": { @@ -9078,6 +8540,9 @@ "smithy.api#documentation": "

A list of compatible \ninstruction set architectures.

" } } + }, + "traits": { + "smithy.api#input": {} } }, "com.amazonaws.lambda#PublishLayerVersionResponse": { @@ -9138,6 +8603,9 @@ "smithy.api#documentation": "

A list of compatible \ninstruction set architectures.

" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.lambda#PublishVersion": { @@ -9209,6 +8677,9 @@ "smithy.api#documentation": "

Only update the function if the revision ID matches the ID that's specified. Use this option to avoid\n publishing a version if the function configuration has changed since you last updated it.

" } } + }, + "traits": { + "smithy.api#input": {} } }, "com.amazonaws.lambda#PutFunctionCodeSigningConfig": { @@ -9266,6 +8737,9 @@ "smithy.api#required": {} } } + }, + "traits": { + "smithy.api#input": {} } }, "com.amazonaws.lambda#PutFunctionCodeSigningConfigResponse": { @@ -9285,6 +8759,9 @@ "smithy.api#required": {} } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.lambda#PutFunctionConcurrency": { @@ -9339,6 +8816,9 @@ "smithy.api#required": {} } } + }, + "traits": { + "smithy.api#input": {} } }, "com.amazonaws.lambda#PutFunctionEventInvokeConfig": { @@ -9411,6 +8891,9 @@ "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
" } } + }, + "traits": { + "smithy.api#input": {} } }, "com.amazonaws.lambda#PutProvisionedConcurrencyConfig": { @@ -9473,6 +8956,9 @@ "smithy.api#required": {} } } + }, + "traits": { + "smithy.api#input": {} } }, "com.amazonaws.lambda#PutProvisionedConcurrencyConfigResponse": { @@ -9514,6 +9000,9 @@ "smithy.api#documentation": "

The date and time that a user last updated the configuration, in ISO 8601 format.

" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.lambda#PutRuntimeManagementConfig": { @@ -9581,6 +9070,9 @@ "smithy.api#documentation": "

The ARN of the runtime version you want the function to use.

\n \n

This is only required if you're using the Manual runtime update mode.

\n
" } } + }, + "traits": { + "smithy.api#input": {} } }, "com.amazonaws.lambda#PutRuntimeManagementConfigResponse": { @@ -9606,6 +9098,9 @@ "smithy.api#documentation": "

The ARN of the runtime the function is configured to use. If the runtime update mode is manual, the ARN is returned, otherwise null \n is returned.

" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.lambda#Qualifier": { @@ -9709,6 +9204,9 @@ "smithy.api#httpQuery": "RevisionId" } } + }, + "traits": { + "smithy.api#input": {} } }, "com.amazonaws.lambda#RemovePermission": { @@ -9778,6 +9276,9 @@ "smithy.api#httpQuery": "RevisionId" } } + }, + "traits": { + "smithy.api#input": {} } }, "com.amazonaws.lambda#RequestTooLargeException": { @@ -10743,6 +10244,9 @@ "smithy.api#required": {} } } + }, + "traits": { + "smithy.api#input": {} } }, "com.amazonaws.lambda#TagValue": { @@ -10998,6 +10502,9 @@ "smithy.api#required": {} } } + }, + "traits": { + "smithy.api#input": {} } }, "com.amazonaws.lambda#UpdateAlias": { @@ -11080,6 +10587,9 @@ "smithy.api#documentation": "

Only update the alias if the revision ID matches the ID that's specified. Use this option to avoid modifying\n an alias that has changed since you last read it.

" } } + }, + "traits": { + "smithy.api#input": {} } }, "com.amazonaws.lambda#UpdateCodeSigningConfig": { @@ -11139,6 +10649,9 @@ "smithy.api#documentation": "

The code signing policy.

" } } + }, + "traits": { + "smithy.api#input": {} } }, "com.amazonaws.lambda#UpdateCodeSigningConfigResponse": { @@ -11151,6 +10664,9 @@ "smithy.api#required": {} } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.lambda#UpdateEventSourceMapping": { @@ -11284,7 +10800,16 @@ "traits": { "smithy.api#documentation": "

(Amazon SQS only) The scaling configuration for the event source. For more information, see Configuring maximum concurrency for Amazon SQS event sources.

" } + }, + "DocumentDBEventSourceConfig": { + "target": "com.amazonaws.lambda#DocumentDBEventSourceConfig", + "traits": { + "smithy.api#documentation": "

Specific configuration settings for a DocumentDB event source.

" + } } + }, + "traits": { + "smithy.api#input": {} } }, "com.amazonaws.lambda#UpdateFunctionCode": { @@ -11403,6 +10928,9 @@ "smithy.api#documentation": "

The instruction set architecture that the function supports. Enter a string array with one of the valid values (arm64 or x86_64).\n The default value is x86_64.

" } } + }, + "traits": { + "smithy.api#input": {} } }, "com.amazonaws.lambda#UpdateFunctionConfiguration": { @@ -11507,7 +11035,7 @@ "Runtime": { "target": "com.amazonaws.lambda#Runtime", "traits": { - "smithy.api#documentation": "

The identifier of the function's runtime. Runtime is required if the deployment package is a .zip file archive.\n

" + "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.

" } }, "DeadLetterConfig": { @@ -11519,7 +11047,7 @@ "KMSKeyArn": { "target": "com.amazonaws.lambda#KMSKeyArn", "traits": { - "smithy.api#documentation": "

The ARN of the Key Management Service (KMS) key that's used to encrypt your function's environment\n variables. If it's not provided, 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 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.

" } }, "TracingConfig": { @@ -11564,6 +11092,9 @@ "smithy.api#documentation": "

The function's SnapStart setting.

" } } + }, + "traits": { + "smithy.api#input": {} } }, "com.amazonaws.lambda#UpdateFunctionEventInvokeConfig": { @@ -11636,6 +11167,9 @@ "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
" } } + }, + "traits": { + "smithy.api#input": {} } }, "com.amazonaws.lambda#UpdateFunctionUrlConfig": { @@ -11693,7 +11227,7 @@ "AuthType": { "target": "com.amazonaws.lambda#FunctionUrlAuthType", "traits": { - "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 IAM 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#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.

" } }, "Cors": { @@ -11702,6 +11236,9 @@ "smithy.api#documentation": "

The cross-origin resource sharing (CORS) settings\n for your function URL.

" } } + }, + "traits": { + "smithy.api#input": {} } }, "com.amazonaws.lambda#UpdateFunctionUrlConfigResponse": { @@ -11724,7 +11261,7 @@ "AuthType": { "target": "com.amazonaws.lambda#FunctionUrlAuthType", "traits": { - "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 IAM 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#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": {} } }, @@ -11748,6 +11285,9 @@ "smithy.api#required": {} } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.lambda#UpdateRuntimeOn": { diff --git a/aws/sdk/aws-models/polly.json b/aws/sdk/aws-models/polly.json index 6b1eb41b83..70db8b9661 100644 --- a/aws/sdk/aws-models/polly.json +++ b/aws/sdk/aws-models/polly.json @@ -80,11 +80,17 @@ "smithy.api#required": {} } } + }, + "traits": { + "smithy.api#input": {} } }, "com.amazonaws.polly#DeleteLexiconOutput": { "type": "structure", - "members": {} + "members": {}, + "traits": { + "smithy.api#output": {} + } }, "com.amazonaws.polly#DescribeVoices": { "type": "operation", @@ -143,6 +149,9 @@ "smithy.api#httpQuery": "NextToken" } } + }, + "traits": { + "smithy.api#input": {} } }, "com.amazonaws.polly#DescribeVoicesOutput": { @@ -160,6 +169,9 @@ "smithy.api#documentation": "

The pagination token to use in the next request to continue the\n listing of voices. NextToken is returned only if the response\n is truncated.

" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.polly#Engine": { @@ -254,6 +266,9 @@ "smithy.api#required": {} } } + }, + "traits": { + "smithy.api#input": {} } }, "com.amazonaws.polly#GetLexiconOutput": { @@ -271,6 +286,9 @@ "smithy.api#documentation": "

Metadata of the lexicon, including phonetic alphabetic used,\n language code, lexicon ARN, number of lexemes defined in the lexicon, and\n size of lexicon in bytes.

" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.polly#GetSpeechSynthesisTask": { @@ -312,6 +330,9 @@ "smithy.api#required": {} } } + }, + "traits": { + "smithy.api#input": {} } }, "com.amazonaws.polly#GetSpeechSynthesisTaskOutput": { @@ -323,6 +344,9 @@ "smithy.api#documentation": "

SynthesisTask object that provides information from the requested\n task, including output format, creation time, task status, and so\n on.

" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.polly#IncludeAdditionalLanguageCodes": { @@ -867,6 +891,9 @@ "smithy.api#httpQuery": "NextToken" } } + }, + "traits": { + "smithy.api#input": {} } }, "com.amazonaws.polly#ListLexiconsOutput": { @@ -884,6 +911,9 @@ "smithy.api#documentation": "

The pagination token to use in the next request to continue the\n listing of lexicons. NextToken is returned only if the\n response is truncated.

" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.polly#ListSpeechSynthesisTasks": { @@ -940,6 +970,9 @@ "smithy.api#httpQuery": "Status" } } + }, + "traits": { + "smithy.api#input": {} } }, "com.amazonaws.polly#ListSpeechSynthesisTasksOutput": { @@ -957,6 +990,9 @@ "smithy.api#documentation": "

List of SynthesisTask objects that provides information from the\n specified task in the list request, including output format, creation\n time, task status, and so on.

" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.polly#MarksNotSupportedForFormatException": { @@ -1114,7 +1150,7 @@ "parameters": { "Region": { "builtIn": "AWS::Region", - "required": true, + "required": false, "documentation": "The AWS region used to dispatch the request.", "type": "String" }, @@ -1143,13 +1179,12 @@ { "conditions": [ { - "fn": "aws.partition", + "fn": "isSet", "argv": [ { - "ref": "Region" + "ref": "Endpoint" } - ], - "assign": "PartitionResult" + ] } ], "type": "tree", @@ -1157,14 +1192,20 @@ { "conditions": [ { - "fn": "isSet", + "fn": "booleanEquals", "argv": [ { - "ref": "Endpoint" - } + "ref": "UseFIPS" + }, + true ] } ], + "error": "Invalid Configuration: FIPS and custom endpoint are not supported", + "type": "error" + }, + { + "conditions": [], "type": "tree", "rules": [ { @@ -1173,67 +1214,42 @@ "fn": "booleanEquals", "argv": [ { - "ref": "UseFIPS" + "ref": "UseDualStack" }, true ] } ], - "error": "Invalid Configuration: FIPS and custom endpoint are not supported", + "error": "Invalid Configuration: Dualstack 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" + "endpoint": { + "url": { + "ref": "Endpoint" }, - { - "conditions": [], - "endpoint": { - "url": { - "ref": "Endpoint" - }, - "properties": {}, - "headers": {} - }, - "type": "endpoint" - } - ] + "properties": {}, + "headers": {} + }, + "type": "endpoint" } ] - }, + } + ] + }, + { + "conditions": [], + "type": "tree", + "rules": [ { "conditions": [ { - "fn": "booleanEquals", - "argv": [ - { - "ref": "UseFIPS" - }, - true - ] - }, - { - "fn": "booleanEquals", + "fn": "isSet", "argv": [ { - "ref": "UseDualStack" - }, - true + "ref": "Region" + } ] } ], @@ -1242,738 +1258,282 @@ { "conditions": [ { - "fn": "booleanEquals", + "fn": "aws.partition", "argv": [ - true, { - "fn": "getAttr", + "ref": "Region" + } + ], + "assign": "PartitionResult" + } + ], + "type": "tree", + "rules": [ + { + "conditions": [ + { + "fn": "booleanEquals", "argv": [ { - "ref": "PartitionResult" + "ref": "UseFIPS" }, - "supportsFIPS" + true ] - } - ] - }, - { - "fn": "booleanEquals", - "argv": [ - true, + }, { - "fn": "getAttr", + "fn": "booleanEquals", "argv": [ { - "ref": "PartitionResult" + "ref": "UseDualStack" + }, + true + ] + } + ], + "type": "tree", + "rules": [ + { + "conditions": [ + { + "fn": "booleanEquals", + "argv": [ + true, + { + "fn": "getAttr", + "argv": [ + { + "ref": "PartitionResult" + }, + "supportsFIPS" + ] + } + ] }, - "supportsDualStack" + { + "fn": "booleanEquals", + "argv": [ + true, + { + "fn": "getAttr", + "argv": [ + { + "ref": "PartitionResult" + }, + "supportsDualStack" + ] + } + ] + } + ], + "type": "tree", + "rules": [ + { + "conditions": [], + "type": "tree", + "rules": [ + { + "conditions": [], + "endpoint": { + "url": "https://polly-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" } ] - } - ], - "type": "tree", - "rules": [ - { - "conditions": [], - "endpoint": { - "url": "https://polly-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, + "conditions": [ { - "fn": "getAttr", + "fn": "booleanEquals", "argv": [ { - "ref": "PartitionResult" + "ref": "UseFIPS" }, - "supportsFIPS" + true ] } - ] - } - ], - "type": "tree", - "rules": [ - { - "conditions": [], + ], "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://polly-fips.{Region}.{PartitionResult#dnsSuffix}", + "properties": {}, + "headers": {} + }, + "type": "endpoint" + } + ] + } + ] + }, { "conditions": [], - "endpoint": { - "url": "https://polly-fips.{Region}.{PartitionResult#dnsSuffix}", - "properties": {}, - "headers": {} - }, - "type": "endpoint" + "error": "FIPS is enabled but this partition does not support FIPS", + "type": "error" } ] - } - ] - }, - { - "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, + "conditions": [ { - "fn": "getAttr", + "fn": "booleanEquals", "argv": [ { - "ref": "PartitionResult" + "ref": "UseDualStack" }, - "supportsDualStack" + 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://polly.{Region}.{PartitionResult#dualStackDnsSuffix}", + "properties": {}, + "headers": {} + }, + "type": "endpoint" + } + ] + } ] + }, + { + "conditions": [], + "error": "DualStack is enabled but this partition does not support DualStack", + "type": "error" } ] - } - ], - "type": "tree", - "rules": [ + }, { "conditions": [], - "endpoint": { - "url": "https://polly.{Region}.{PartitionResult#dualStackDnsSuffix}", - "properties": {}, - "headers": {} - }, - "type": "endpoint" - } - ] - }, - { - "conditions": [], - "error": "DualStack is enabled but this partition does not support DualStack", - "type": "error" - } - ] - }, - { - "conditions": [], - "endpoint": { - "url": "https://polly.{Region}.{PartitionResult#dnsSuffix}", - "properties": {}, - "headers": {} - }, - "type": "endpoint" - } - ] - } - ] - }, - "smithy.rules#endpointTests": { - "testCases": [ - { - "documentation": "For region ap-south-1 with FIPS enabled and DualStack enabled", - "expect": { - "endpoint": { - "url": "https://polly-fips.ap-south-1.api.aws" - } - }, - "params": { - "UseDualStack": true, - "UseFIPS": true, - "Region": "ap-south-1" - } - }, - { - "documentation": "For region ap-south-1 with FIPS enabled and DualStack disabled", - "expect": { - "endpoint": { - "url": "https://polly-fips.ap-south-1.amazonaws.com" - } - }, - "params": { - "UseDualStack": false, - "UseFIPS": true, - "Region": "ap-south-1" - } - }, - { - "documentation": "For region ap-south-1 with FIPS disabled and DualStack enabled", - "expect": { - "endpoint": { - "url": "https://polly.ap-south-1.api.aws" - } - }, - "params": { - "UseDualStack": true, - "UseFIPS": false, - "Region": "ap-south-1" - } - }, - { - "documentation": "For region ap-south-1 with FIPS disabled and DualStack disabled", - "expect": { - "endpoint": { - "url": "https://polly.ap-south-1.amazonaws.com" - } - }, - "params": { - "UseDualStack": false, - "UseFIPS": false, - "Region": "ap-south-1" - } - }, - { - "documentation": "For region ca-central-1 with FIPS enabled and DualStack enabled", - "expect": { - "endpoint": { - "url": "https://polly-fips.ca-central-1.api.aws" - } - }, - "params": { - "UseDualStack": true, - "UseFIPS": true, - "Region": "ca-central-1" - } - }, - { - "documentation": "For region ca-central-1 with FIPS enabled and DualStack disabled", - "expect": { - "endpoint": { - "url": "https://polly-fips.ca-central-1.amazonaws.com" - } - }, - "params": { - "UseDualStack": false, - "UseFIPS": true, - "Region": "ca-central-1" - } - }, - { - "documentation": "For region ca-central-1 with FIPS disabled and DualStack enabled", - "expect": { - "endpoint": { - "url": "https://polly.ca-central-1.api.aws" - } - }, - "params": { - "UseDualStack": true, - "UseFIPS": false, - "Region": "ca-central-1" - } - }, - { - "documentation": "For region ca-central-1 with FIPS disabled and DualStack disabled", - "expect": { - "endpoint": { - "url": "https://polly.ca-central-1.amazonaws.com" - } - }, - "params": { - "UseDualStack": false, - "UseFIPS": false, - "Region": "ca-central-1" - } - }, - { - "documentation": "For region eu-central-1 with FIPS enabled and DualStack enabled", - "expect": { - "endpoint": { - "url": "https://polly-fips.eu-central-1.api.aws" - } - }, - "params": { - "UseDualStack": true, - "UseFIPS": true, - "Region": "eu-central-1" - } - }, - { - "documentation": "For region eu-central-1 with FIPS enabled and DualStack disabled", - "expect": { - "endpoint": { - "url": "https://polly-fips.eu-central-1.amazonaws.com" - } - }, - "params": { - "UseDualStack": false, - "UseFIPS": true, - "Region": "eu-central-1" - } - }, - { - "documentation": "For region eu-central-1 with FIPS disabled and DualStack enabled", - "expect": { - "endpoint": { - "url": "https://polly.eu-central-1.api.aws" - } - }, - "params": { - "UseDualStack": true, - "UseFIPS": false, - "Region": "eu-central-1" - } - }, - { - "documentation": "For region eu-central-1 with FIPS disabled and DualStack disabled", - "expect": { - "endpoint": { - "url": "https://polly.eu-central-1.amazonaws.com" - } - }, - "params": { - "UseDualStack": false, - "UseFIPS": false, - "Region": "eu-central-1" - } - }, - { - "documentation": "For region us-west-1 with FIPS enabled and DualStack enabled", - "expect": { - "endpoint": { - "url": "https://polly-fips.us-west-1.api.aws" - } - }, - "params": { - "UseDualStack": true, - "UseFIPS": true, - "Region": "us-west-1" - } - }, - { - "documentation": "For region us-west-1 with FIPS enabled and DualStack disabled", - "expect": { - "endpoint": { - "url": "https://polly-fips.us-west-1.amazonaws.com" - } - }, - "params": { - "UseDualStack": false, - "UseFIPS": true, - "Region": "us-west-1" - } - }, - { - "documentation": "For region us-west-1 with FIPS disabled and DualStack enabled", - "expect": { - "endpoint": { - "url": "https://polly.us-west-1.api.aws" - } - }, - "params": { - "UseDualStack": true, - "UseFIPS": false, - "Region": "us-west-1" - } - }, - { - "documentation": "For region us-west-1 with FIPS disabled and DualStack disabled", - "expect": { - "endpoint": { - "url": "https://polly.us-west-1.amazonaws.com" - } - }, - "params": { - "UseDualStack": false, - "UseFIPS": false, - "Region": "us-west-1" - } - }, - { - "documentation": "For region us-west-2 with FIPS enabled and DualStack enabled", - "expect": { - "endpoint": { - "url": "https://polly-fips.us-west-2.api.aws" - } - }, - "params": { - "UseDualStack": true, - "UseFIPS": true, - "Region": "us-west-2" - } - }, - { - "documentation": "For region us-west-2 with FIPS enabled and DualStack disabled", - "expect": { - "endpoint": { - "url": "https://polly-fips.us-west-2.amazonaws.com" - } - }, - "params": { - "UseDualStack": false, - "UseFIPS": true, - "Region": "us-west-2" - } - }, - { - "documentation": "For region us-west-2 with FIPS disabled and DualStack enabled", - "expect": { - "endpoint": { - "url": "https://polly.us-west-2.api.aws" - } - }, - "params": { - "UseDualStack": true, - "UseFIPS": false, - "Region": "us-west-2" - } - }, - { - "documentation": "For region us-west-2 with FIPS disabled and DualStack disabled", - "expect": { - "endpoint": { - "url": "https://polly.us-west-2.amazonaws.com" - } - }, - "params": { - "UseDualStack": false, - "UseFIPS": false, - "Region": "us-west-2" - } - }, - { - "documentation": "For region af-south-1 with FIPS enabled and DualStack enabled", - "expect": { - "endpoint": { - "url": "https://polly-fips.af-south-1.api.aws" - } - }, - "params": { - "UseDualStack": true, - "UseFIPS": true, - "Region": "af-south-1" - } - }, - { - "documentation": "For region af-south-1 with FIPS enabled and DualStack disabled", - "expect": { - "endpoint": { - "url": "https://polly-fips.af-south-1.amazonaws.com" - } - }, - "params": { - "UseDualStack": false, - "UseFIPS": true, - "Region": "af-south-1" - } - }, - { - "documentation": "For region af-south-1 with FIPS disabled and DualStack enabled", - "expect": { - "endpoint": { - "url": "https://polly.af-south-1.api.aws" - } - }, - "params": { - "UseDualStack": true, - "UseFIPS": false, - "Region": "af-south-1" - } - }, - { - "documentation": "For region af-south-1 with FIPS disabled and DualStack disabled", - "expect": { - "endpoint": { - "url": "https://polly.af-south-1.amazonaws.com" - } - }, - "params": { - "UseDualStack": false, - "UseFIPS": false, - "Region": "af-south-1" - } - }, - { - "documentation": "For region eu-north-1 with FIPS enabled and DualStack enabled", - "expect": { - "endpoint": { - "url": "https://polly-fips.eu-north-1.api.aws" - } - }, - "params": { - "UseDualStack": true, - "UseFIPS": true, - "Region": "eu-north-1" - } - }, - { - "documentation": "For region eu-north-1 with FIPS enabled and DualStack disabled", - "expect": { - "endpoint": { - "url": "https://polly-fips.eu-north-1.amazonaws.com" - } - }, - "params": { - "UseDualStack": false, - "UseFIPS": true, - "Region": "eu-north-1" - } - }, - { - "documentation": "For region eu-north-1 with FIPS disabled and DualStack enabled", - "expect": { - "endpoint": { - "url": "https://polly.eu-north-1.api.aws" - } - }, - "params": { - "UseDualStack": true, - "UseFIPS": false, - "Region": "eu-north-1" - } - }, - { - "documentation": "For region eu-north-1 with FIPS disabled and DualStack disabled", - "expect": { - "endpoint": { - "url": "https://polly.eu-north-1.amazonaws.com" - } - }, - "params": { - "UseDualStack": false, - "UseFIPS": false, - "Region": "eu-north-1" - } - }, - { - "documentation": "For region eu-west-3 with FIPS enabled and DualStack enabled", - "expect": { - "endpoint": { - "url": "https://polly-fips.eu-west-3.api.aws" - } - }, - "params": { - "UseDualStack": true, - "UseFIPS": true, - "Region": "eu-west-3" - } - }, - { - "documentation": "For region eu-west-3 with FIPS enabled and DualStack disabled", - "expect": { - "endpoint": { - "url": "https://polly-fips.eu-west-3.amazonaws.com" - } - }, - "params": { - "UseDualStack": false, - "UseFIPS": true, - "Region": "eu-west-3" - } - }, - { - "documentation": "For region eu-west-3 with FIPS disabled and DualStack enabled", - "expect": { - "endpoint": { - "url": "https://polly.eu-west-3.api.aws" - } - }, - "params": { - "UseDualStack": true, - "UseFIPS": false, - "Region": "eu-west-3" - } - }, - { - "documentation": "For region eu-west-3 with FIPS disabled and DualStack disabled", - "expect": { - "endpoint": { - "url": "https://polly.eu-west-3.amazonaws.com" - } - }, - "params": { - "UseDualStack": false, - "UseFIPS": false, - "Region": "eu-west-3" - } - }, - { - "documentation": "For region eu-west-2 with FIPS enabled and DualStack enabled", - "expect": { - "endpoint": { - "url": "https://polly-fips.eu-west-2.api.aws" - } - }, - "params": { - "UseDualStack": true, - "UseFIPS": true, - "Region": "eu-west-2" - } - }, - { - "documentation": "For region eu-west-2 with FIPS enabled and DualStack disabled", - "expect": { - "endpoint": { - "url": "https://polly-fips.eu-west-2.amazonaws.com" - } - }, - "params": { - "UseDualStack": false, - "UseFIPS": true, - "Region": "eu-west-2" - } - }, - { - "documentation": "For region eu-west-2 with FIPS disabled and DualStack enabled", - "expect": { - "endpoint": { - "url": "https://polly.eu-west-2.api.aws" - } - }, - "params": { - "UseDualStack": true, - "UseFIPS": false, - "Region": "eu-west-2" - } - }, - { - "documentation": "For region eu-west-2 with FIPS disabled and DualStack disabled", - "expect": { - "endpoint": { - "url": "https://polly.eu-west-2.amazonaws.com" - } - }, - "params": { - "UseDualStack": false, - "UseFIPS": false, - "Region": "eu-west-2" - } - }, - { - "documentation": "For region eu-west-1 with FIPS enabled and DualStack enabled", - "expect": { - "endpoint": { - "url": "https://polly-fips.eu-west-1.api.aws" - } - }, - "params": { - "UseDualStack": true, - "UseFIPS": true, - "Region": "eu-west-1" - } - }, - { - "documentation": "For region eu-west-1 with FIPS enabled and DualStack disabled", - "expect": { - "endpoint": { - "url": "https://polly-fips.eu-west-1.amazonaws.com" - } - }, - "params": { - "UseDualStack": false, - "UseFIPS": true, - "Region": "eu-west-1" - } - }, - { - "documentation": "For region eu-west-1 with FIPS disabled and DualStack enabled", - "expect": { - "endpoint": { - "url": "https://polly.eu-west-1.api.aws" + "type": "tree", + "rules": [ + { + "conditions": [], + "endpoint": { + "url": "https://polly.{Region}.{PartitionResult#dnsSuffix}", + "properties": {}, + "headers": {} + }, + "type": "endpoint" + } + ] + } + ] + } + ] + }, + { + "conditions": [], + "error": "Invalid Configuration: Missing Region", + "type": "error" } - }, - "params": { - "UseDualStack": true, - "UseFIPS": false, - "Region": "eu-west-1" - } - }, + ] + } + ] + }, + "smithy.rules#endpointTests": { + "testCases": [ { - "documentation": "For region eu-west-1 with FIPS disabled and DualStack disabled", + "documentation": "For region af-south-1 with FIPS disabled and DualStack disabled", "expect": { "endpoint": { - "url": "https://polly.eu-west-1.amazonaws.com" + "url": "https://polly.af-south-1.amazonaws.com" } }, "params": { + "Region": "af-south-1", "UseDualStack": false, - "UseFIPS": false, - "Region": "eu-west-1" - } - }, - { - "documentation": "For region ap-northeast-2 with FIPS enabled and DualStack enabled", - "expect": { - "endpoint": { - "url": "https://polly-fips.ap-northeast-2.api.aws" - } - }, - "params": { - "UseDualStack": true, - "UseFIPS": true, - "Region": "ap-northeast-2" + "UseFIPS": false } }, { - "documentation": "For region ap-northeast-2 with FIPS enabled and DualStack disabled", + "documentation": "For region ap-east-1 with FIPS disabled and DualStack disabled", "expect": { "endpoint": { - "url": "https://polly-fips.ap-northeast-2.amazonaws.com" + "url": "https://polly.ap-east-1.amazonaws.com" } }, "params": { + "Region": "ap-east-1", "UseDualStack": false, - "UseFIPS": true, - "Region": "ap-northeast-2" + "UseFIPS": false } }, { - "documentation": "For region ap-northeast-2 with FIPS disabled and DualStack enabled", + "documentation": "For region ap-northeast-1 with FIPS disabled and DualStack disabled", "expect": { "endpoint": { - "url": "https://polly.ap-northeast-2.api.aws" + "url": "https://polly.ap-northeast-1.amazonaws.com" } }, "params": { - "UseDualStack": true, - "UseFIPS": false, - "Region": "ap-northeast-2" + "Region": "ap-northeast-1", + "UseDualStack": false, + "UseFIPS": false } }, { @@ -1984,152 +1544,139 @@ } }, "params": { + "Region": "ap-northeast-2", "UseDualStack": false, - "UseFIPS": false, - "Region": "ap-northeast-2" - } - }, - { - "documentation": "For region ap-northeast-1 with FIPS enabled and DualStack enabled", - "expect": { - "endpoint": { - "url": "https://polly-fips.ap-northeast-1.api.aws" - } - }, - "params": { - "UseDualStack": true, - "UseFIPS": true, - "Region": "ap-northeast-1" + "UseFIPS": false } }, { - "documentation": "For region ap-northeast-1 with FIPS enabled and DualStack disabled", + "documentation": "For region ap-south-1 with FIPS disabled and DualStack disabled", "expect": { "endpoint": { - "url": "https://polly-fips.ap-northeast-1.amazonaws.com" + "url": "https://polly.ap-south-1.amazonaws.com" } }, "params": { + "Region": "ap-south-1", "UseDualStack": false, - "UseFIPS": true, - "Region": "ap-northeast-1" + "UseFIPS": false } }, { - "documentation": "For region ap-northeast-1 with FIPS disabled and DualStack enabled", + "documentation": "For region ap-southeast-1 with FIPS disabled and DualStack disabled", "expect": { "endpoint": { - "url": "https://polly.ap-northeast-1.api.aws" + "url": "https://polly.ap-southeast-1.amazonaws.com" } }, "params": { - "UseDualStack": true, - "UseFIPS": false, - "Region": "ap-northeast-1" + "Region": "ap-southeast-1", + "UseDualStack": false, + "UseFIPS": false } }, { - "documentation": "For region ap-northeast-1 with FIPS disabled and DualStack disabled", + "documentation": "For region ap-southeast-2 with FIPS disabled and DualStack disabled", "expect": { "endpoint": { - "url": "https://polly.ap-northeast-1.amazonaws.com" + "url": "https://polly.ap-southeast-2.amazonaws.com" } }, "params": { + "Region": "ap-southeast-2", "UseDualStack": false, - "UseFIPS": false, - "Region": "ap-northeast-1" + "UseFIPS": false } }, { - "documentation": "For region me-south-1 with FIPS enabled and DualStack enabled", + "documentation": "For region ca-central-1 with FIPS disabled and DualStack disabled", "expect": { "endpoint": { - "url": "https://polly-fips.me-south-1.api.aws" + "url": "https://polly.ca-central-1.amazonaws.com" } }, "params": { - "UseDualStack": true, - "UseFIPS": true, - "Region": "me-south-1" + "Region": "ca-central-1", + "UseDualStack": false, + "UseFIPS": false } }, { - "documentation": "For region me-south-1 with FIPS enabled and DualStack disabled", + "documentation": "For region eu-central-1 with FIPS disabled and DualStack disabled", "expect": { "endpoint": { - "url": "https://polly-fips.me-south-1.amazonaws.com" + "url": "https://polly.eu-central-1.amazonaws.com" } }, "params": { + "Region": "eu-central-1", "UseDualStack": false, - "UseFIPS": true, - "Region": "me-south-1" + "UseFIPS": false } }, { - "documentation": "For region me-south-1 with FIPS disabled and DualStack enabled", + "documentation": "For region eu-north-1 with FIPS disabled and DualStack disabled", "expect": { "endpoint": { - "url": "https://polly.me-south-1.api.aws" + "url": "https://polly.eu-north-1.amazonaws.com" } }, "params": { - "UseDualStack": true, - "UseFIPS": false, - "Region": "me-south-1" + "Region": "eu-north-1", + "UseDualStack": false, + "UseFIPS": false } }, { - "documentation": "For region me-south-1 with FIPS disabled and DualStack disabled", + "documentation": "For region eu-west-1 with FIPS disabled and DualStack disabled", "expect": { "endpoint": { - "url": "https://polly.me-south-1.amazonaws.com" + "url": "https://polly.eu-west-1.amazonaws.com" } }, "params": { + "Region": "eu-west-1", "UseDualStack": false, - "UseFIPS": false, - "Region": "me-south-1" + "UseFIPS": false } }, { - "documentation": "For region sa-east-1 with FIPS enabled and DualStack enabled", + "documentation": "For region eu-west-2 with FIPS disabled and DualStack disabled", "expect": { "endpoint": { - "url": "https://polly-fips.sa-east-1.api.aws" + "url": "https://polly.eu-west-2.amazonaws.com" } }, "params": { - "UseDualStack": true, - "UseFIPS": true, - "Region": "sa-east-1" + "Region": "eu-west-2", + "UseDualStack": false, + "UseFIPS": false } }, { - "documentation": "For region sa-east-1 with FIPS enabled and DualStack disabled", + "documentation": "For region eu-west-3 with FIPS disabled and DualStack disabled", "expect": { "endpoint": { - "url": "https://polly-fips.sa-east-1.amazonaws.com" + "url": "https://polly.eu-west-3.amazonaws.com" } }, "params": { + "Region": "eu-west-3", "UseDualStack": false, - "UseFIPS": true, - "Region": "sa-east-1" + "UseFIPS": false } }, { - "documentation": "For region sa-east-1 with FIPS disabled and DualStack enabled", + "documentation": "For region me-south-1 with FIPS disabled and DualStack disabled", "expect": { "endpoint": { - "url": "https://polly.sa-east-1.api.aws" + "url": "https://polly.me-south-1.amazonaws.com" } }, "params": { - "UseDualStack": true, - "UseFIPS": false, - "Region": "sa-east-1" + "Region": "me-south-1", + "UseDualStack": false, + "UseFIPS": false } }, { @@ -2140,377 +1687,352 @@ } }, "params": { + "Region": "sa-east-1", "UseDualStack": false, - "UseFIPS": false, - "Region": "sa-east-1" + "UseFIPS": false } }, { - "documentation": "For region ap-east-1 with FIPS enabled and DualStack enabled", + "documentation": "For region us-east-1 with FIPS disabled and DualStack disabled", "expect": { "endpoint": { - "url": "https://polly-fips.ap-east-1.api.aws" + "url": "https://polly.us-east-1.amazonaws.com" } }, "params": { - "UseDualStack": true, - "UseFIPS": true, - "Region": "ap-east-1" + "Region": "us-east-1", + "UseDualStack": false, + "UseFIPS": false } }, { - "documentation": "For region ap-east-1 with FIPS enabled and DualStack disabled", + "documentation": "For region us-east-1 with FIPS enabled and DualStack disabled", "expect": { "endpoint": { - "url": "https://polly-fips.ap-east-1.amazonaws.com" + "url": "https://polly-fips.us-east-1.amazonaws.com" } }, "params": { + "Region": "us-east-1", "UseDualStack": false, - "UseFIPS": true, - "Region": "ap-east-1" + "UseFIPS": true } }, { - "documentation": "For region ap-east-1 with FIPS disabled and DualStack enabled", + "documentation": "For region us-east-2 with FIPS disabled and DualStack disabled", "expect": { "endpoint": { - "url": "https://polly.ap-east-1.api.aws" + "url": "https://polly.us-east-2.amazonaws.com" } }, "params": { - "UseDualStack": true, - "UseFIPS": false, - "Region": "ap-east-1" + "Region": "us-east-2", + "UseDualStack": false, + "UseFIPS": false } }, { - "documentation": "For region ap-east-1 with FIPS disabled and DualStack disabled", + "documentation": "For region us-east-2 with FIPS enabled and DualStack disabled", "expect": { "endpoint": { - "url": "https://polly.ap-east-1.amazonaws.com" + "url": "https://polly-fips.us-east-2.amazonaws.com" } }, "params": { + "Region": "us-east-2", "UseDualStack": false, - "UseFIPS": false, - "Region": "ap-east-1" + "UseFIPS": true } }, { - "documentation": "For region us-gov-west-1 with FIPS enabled and DualStack enabled", + "documentation": "For region us-west-1 with FIPS disabled and DualStack disabled", "expect": { "endpoint": { - "url": "https://polly-fips.us-gov-west-1.api.aws" + "url": "https://polly.us-west-1.amazonaws.com" } }, "params": { - "UseDualStack": true, - "UseFIPS": true, - "Region": "us-gov-west-1" + "Region": "us-west-1", + "UseDualStack": false, + "UseFIPS": false } }, { - "documentation": "For region us-gov-west-1 with FIPS enabled and DualStack disabled", + "documentation": "For region us-west-1 with FIPS enabled and DualStack disabled", "expect": { "endpoint": { - "url": "https://polly-fips.us-gov-west-1.amazonaws.com" + "url": "https://polly-fips.us-west-1.amazonaws.com" } }, "params": { + "Region": "us-west-1", "UseDualStack": false, - "UseFIPS": true, - "Region": "us-gov-west-1" + "UseFIPS": true } }, { - "documentation": "For region us-gov-west-1 with FIPS disabled and DualStack enabled", + "documentation": "For region us-west-2 with FIPS disabled and DualStack disabled", "expect": { "endpoint": { - "url": "https://polly.us-gov-west-1.api.aws" + "url": "https://polly.us-west-2.amazonaws.com" } }, "params": { - "UseDualStack": true, - "UseFIPS": false, - "Region": "us-gov-west-1" + "Region": "us-west-2", + "UseDualStack": false, + "UseFIPS": false } }, { - "documentation": "For region us-gov-west-1 with FIPS disabled and DualStack disabled", + "documentation": "For region us-west-2 with FIPS enabled and DualStack disabled", "expect": { "endpoint": { - "url": "https://polly.us-gov-west-1.amazonaws.com" + "url": "https://polly-fips.us-west-2.amazonaws.com" } }, "params": { + "Region": "us-west-2", "UseDualStack": false, - "UseFIPS": false, - "Region": "us-gov-west-1" + "UseFIPS": true } }, { - "documentation": "For region ap-southeast-1 with FIPS enabled and DualStack enabled", + "documentation": "For region us-east-1 with FIPS enabled and DualStack enabled", "expect": { "endpoint": { - "url": "https://polly-fips.ap-southeast-1.api.aws" + "url": "https://polly-fips.us-east-1.api.aws" } }, "params": { + "Region": "us-east-1", "UseDualStack": true, - "UseFIPS": true, - "Region": "ap-southeast-1" - } - }, - { - "documentation": "For region ap-southeast-1 with FIPS enabled and DualStack disabled", - "expect": { - "endpoint": { - "url": "https://polly-fips.ap-southeast-1.amazonaws.com" - } - }, - "params": { - "UseDualStack": false, - "UseFIPS": true, - "Region": "ap-southeast-1" + "UseFIPS": true } }, { - "documentation": "For region ap-southeast-1 with FIPS disabled and DualStack enabled", + "documentation": "For region us-east-1 with FIPS disabled and DualStack enabled", "expect": { "endpoint": { - "url": "https://polly.ap-southeast-1.api.aws" + "url": "https://polly.us-east-1.api.aws" } }, "params": { + "Region": "us-east-1", "UseDualStack": true, - "UseFIPS": false, - "Region": "ap-southeast-1" + "UseFIPS": false } }, { - "documentation": "For region ap-southeast-1 with FIPS disabled and DualStack disabled", + "documentation": "For region cn-northwest-1 with FIPS disabled and DualStack disabled", "expect": { "endpoint": { - "url": "https://polly.ap-southeast-1.amazonaws.com" + "url": "https://polly.cn-northwest-1.amazonaws.com.cn" } }, "params": { + "Region": "cn-northwest-1", "UseDualStack": false, - "UseFIPS": false, - "Region": "ap-southeast-1" + "UseFIPS": false } }, { - "documentation": "For region ap-southeast-2 with FIPS enabled and DualStack enabled", + "documentation": "For region cn-north-1 with FIPS enabled and DualStack enabled", "expect": { "endpoint": { - "url": "https://polly-fips.ap-southeast-2.api.aws" + "url": "https://polly-fips.cn-north-1.api.amazonwebservices.com.cn" } }, "params": { + "Region": "cn-north-1", "UseDualStack": true, - "UseFIPS": true, - "Region": "ap-southeast-2" + "UseFIPS": true } }, { - "documentation": "For region ap-southeast-2 with FIPS enabled and DualStack disabled", + "documentation": "For region cn-north-1 with FIPS enabled and DualStack disabled", "expect": { "endpoint": { - "url": "https://polly-fips.ap-southeast-2.amazonaws.com" + "url": "https://polly-fips.cn-north-1.amazonaws.com.cn" } }, "params": { + "Region": "cn-north-1", "UseDualStack": false, - "UseFIPS": true, - "Region": "ap-southeast-2" + "UseFIPS": true } }, { - "documentation": "For region ap-southeast-2 with FIPS disabled and DualStack enabled", + "documentation": "For region cn-north-1 with FIPS disabled and DualStack enabled", "expect": { "endpoint": { - "url": "https://polly.ap-southeast-2.api.aws" + "url": "https://polly.cn-north-1.api.amazonwebservices.com.cn" } }, "params": { + "Region": "cn-north-1", "UseDualStack": true, - "UseFIPS": false, - "Region": "ap-southeast-2" + "UseFIPS": false } }, { - "documentation": "For region ap-southeast-2 with FIPS disabled and DualStack disabled", + "documentation": "For region cn-north-1 with FIPS disabled and DualStack disabled", "expect": { "endpoint": { - "url": "https://polly.ap-southeast-2.amazonaws.com" + "url": "https://polly.cn-north-1.amazonaws.com.cn" } }, "params": { + "Region": "cn-north-1", "UseDualStack": false, - "UseFIPS": false, - "Region": "ap-southeast-2" + "UseFIPS": false } }, { - "documentation": "For region us-east-1 with FIPS enabled and DualStack enabled", + "documentation": "For region us-gov-west-1 with FIPS disabled and DualStack disabled", "expect": { "endpoint": { - "url": "https://polly-fips.us-east-1.api.aws" + "url": "https://polly.us-gov-west-1.amazonaws.com" } }, "params": { - "UseDualStack": true, - "UseFIPS": true, - "Region": "us-east-1" + "Region": "us-gov-west-1", + "UseDualStack": false, + "UseFIPS": false } }, { - "documentation": "For region us-east-1 with FIPS enabled and DualStack disabled", + "documentation": "For region us-gov-west-1 with FIPS enabled and DualStack disabled", "expect": { "endpoint": { - "url": "https://polly-fips.us-east-1.amazonaws.com" + "url": "https://polly-fips.us-gov-west-1.amazonaws.com" } }, "params": { + "Region": "us-gov-west-1", "UseDualStack": false, - "UseFIPS": true, - "Region": "us-east-1" + "UseFIPS": true } }, { - "documentation": "For region us-east-1 with FIPS disabled and DualStack enabled", + "documentation": "For region us-gov-east-1 with FIPS enabled and DualStack enabled", "expect": { "endpoint": { - "url": "https://polly.us-east-1.api.aws" + "url": "https://polly-fips.us-gov-east-1.api.aws" } }, "params": { + "Region": "us-gov-east-1", "UseDualStack": true, - "UseFIPS": false, - "Region": "us-east-1" + "UseFIPS": true } }, { - "documentation": "For region us-east-1 with FIPS disabled and DualStack disabled", + "documentation": "For region us-gov-east-1 with FIPS enabled and DualStack disabled", "expect": { "endpoint": { - "url": "https://polly.us-east-1.amazonaws.com" + "url": "https://polly-fips.us-gov-east-1.amazonaws.com" } }, "params": { + "Region": "us-gov-east-1", "UseDualStack": false, - "UseFIPS": false, - "Region": "us-east-1" + "UseFIPS": true } }, { - "documentation": "For region us-east-2 with FIPS enabled and DualStack enabled", + "documentation": "For region us-gov-east-1 with FIPS disabled and DualStack enabled", "expect": { "endpoint": { - "url": "https://polly-fips.us-east-2.api.aws" + "url": "https://polly.us-gov-east-1.api.aws" } }, "params": { + "Region": "us-gov-east-1", "UseDualStack": true, - "UseFIPS": true, - "Region": "us-east-2" + "UseFIPS": false } }, { - "documentation": "For region us-east-2 with FIPS enabled and DualStack disabled", + "documentation": "For region us-gov-east-1 with FIPS disabled and DualStack disabled", "expect": { "endpoint": { - "url": "https://polly-fips.us-east-2.amazonaws.com" + "url": "https://polly.us-gov-east-1.amazonaws.com" } }, "params": { + "Region": "us-gov-east-1", "UseDualStack": false, - "UseFIPS": true, - "Region": "us-east-2" - } - }, - { - "documentation": "For region us-east-2 with FIPS disabled and DualStack enabled", - "expect": { - "endpoint": { - "url": "https://polly.us-east-2.api.aws" - } - }, - "params": { - "UseDualStack": true, - "UseFIPS": false, - "Region": "us-east-2" + "UseFIPS": false } }, { - "documentation": "For region us-east-2 with FIPS disabled and DualStack disabled", + "documentation": "For region us-iso-east-1 with FIPS enabled and DualStack disabled", "expect": { "endpoint": { - "url": "https://polly.us-east-2.amazonaws.com" + "url": "https://polly-fips.us-iso-east-1.c2s.ic.gov" } }, "params": { + "Region": "us-iso-east-1", "UseDualStack": false, - "UseFIPS": false, - "Region": "us-east-2" + "UseFIPS": true } }, { - "documentation": "For region cn-northwest-1 with FIPS enabled and DualStack enabled", + "documentation": "For region us-iso-east-1 with FIPS disabled and DualStack disabled", "expect": { "endpoint": { - "url": "https://polly-fips.cn-northwest-1.api.amazonwebservices.com.cn" + "url": "https://polly.us-iso-east-1.c2s.ic.gov" } }, "params": { - "UseDualStack": true, - "UseFIPS": true, - "Region": "cn-northwest-1" + "Region": "us-iso-east-1", + "UseDualStack": false, + "UseFIPS": false } }, { - "documentation": "For region cn-northwest-1 with FIPS enabled and DualStack disabled", + "documentation": "For region us-isob-east-1 with FIPS enabled and DualStack disabled", "expect": { "endpoint": { - "url": "https://polly-fips.cn-northwest-1.amazonaws.com.cn" + "url": "https://polly-fips.us-isob-east-1.sc2s.sgov.gov" } }, "params": { + "Region": "us-isob-east-1", "UseDualStack": false, - "UseFIPS": true, - "Region": "cn-northwest-1" + "UseFIPS": true } }, { - "documentation": "For region cn-northwest-1 with FIPS disabled and DualStack enabled", + "documentation": "For region us-isob-east-1 with FIPS disabled and DualStack disabled", "expect": { "endpoint": { - "url": "https://polly.cn-northwest-1.api.amazonwebservices.com.cn" + "url": "https://polly.us-isob-east-1.sc2s.sgov.gov" } }, "params": { - "UseDualStack": true, - "UseFIPS": false, - "Region": "cn-northwest-1" + "Region": "us-isob-east-1", + "UseDualStack": false, + "UseFIPS": false } }, { - "documentation": "For region cn-northwest-1 with FIPS disabled and DualStack disabled", + "documentation": "For custom endpoint with region set and fips disabled and dualstack disabled", "expect": { "endpoint": { - "url": "https://polly.cn-northwest-1.amazonaws.com.cn" + "url": "https://example.com" } }, "params": { + "Region": "us-east-1", "UseDualStack": false, "UseFIPS": false, - "Region": "cn-northwest-1" + "Endpoint": "https://example.com" } }, { - "documentation": "For custom endpoint with fips disabled and dualstack disabled", + "documentation": "For custom endpoint with region not set and fips disabled and dualstack disabled", "expect": { "endpoint": { "url": "https://example.com" @@ -2519,7 +2041,6 @@ "params": { "UseDualStack": false, "UseFIPS": false, - "Region": "us-east-1", "Endpoint": "https://example.com" } }, @@ -2529,9 +2050,9 @@ "error": "Invalid Configuration: FIPS and custom endpoint are not supported" }, "params": { + "Region": "us-east-1", "UseDualStack": false, "UseFIPS": true, - "Region": "us-east-1", "Endpoint": "https://example.com" } }, @@ -2541,9 +2062,9 @@ "error": "Invalid Configuration: Dualstack and custom endpoint are not supported" }, "params": { + "Region": "us-east-1", "UseDualStack": true, "UseFIPS": false, - "Region": "us-east-1", "Endpoint": "https://example.com" } } @@ -2610,11 +2131,17 @@ "smithy.api#required": {} } } + }, + "traits": { + "smithy.api#input": {} } }, "com.amazonaws.polly#PutLexiconOutput": { "type": "structure", - "members": {} + "members": {}, + "traits": { + "smithy.api#output": {} + } }, "com.amazonaws.polly#RequestCharacters": { "type": "integer", @@ -2838,6 +2365,9 @@ "smithy.api#required": {} } } + }, + "traits": { + "smithy.api#input": {} } }, "com.amazonaws.polly#StartSpeechSynthesisTaskOutput": { @@ -2849,6 +2379,9 @@ "smithy.api#documentation": "

SynthesisTask object that provides information and attributes about a\n newly submitted speech synthesis task.

" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.polly#SynthesisTask": { @@ -3075,6 +2608,9 @@ "smithy.api#required": {} } } + }, + "traits": { + "smithy.api#input": {} } }, "com.amazonaws.polly#SynthesizeSpeechOutput": { @@ -3103,6 +2639,9 @@ "smithy.api#httpHeader": "x-amzn-RequestCharacters" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.polly#TaskId": { @@ -3758,6 +3297,30 @@ "traits": { "smithy.api#enumValue": "Thiago" } + }, + "Ruth": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "Ruth" + } + }, + "Stephen": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "Stephen" + } + }, + "Kazuha": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "Kazuha" + } + }, + "Tomoko": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "Tomoko" + } } } }, diff --git a/aws/sdk/aws-models/qldb-session.json b/aws/sdk/aws-models/qldb-session.json index 9b50e436f4..37782b8b96 100644 --- a/aws/sdk/aws-models/qldb-session.json +++ b/aws/sdk/aws-models/qldb-session.json @@ -416,7 +416,7 @@ "parameters": { "Region": { "builtIn": "AWS::Region", - "required": true, + "required": false, "documentation": "The AWS region used to dispatch the request.", "type": "String" }, @@ -445,13 +445,12 @@ { "conditions": [ { - "fn": "aws.partition", + "fn": "isSet", "argv": [ { - "ref": "Region" + "ref": "Endpoint" } - ], - "assign": "PartitionResult" + ] } ], "type": "tree", @@ -459,14 +458,20 @@ { "conditions": [ { - "fn": "isSet", + "fn": "booleanEquals", "argv": [ { - "ref": "Endpoint" - } + "ref": "UseFIPS" + }, + true ] } ], + "error": "Invalid Configuration: FIPS and custom endpoint are not supported", + "type": "error" + }, + { + "conditions": [], "type": "tree", "rules": [ { @@ -475,67 +480,42 @@ "fn": "booleanEquals", "argv": [ { - "ref": "UseFIPS" + "ref": "UseDualStack" }, true ] } ], - "error": "Invalid Configuration: FIPS and custom endpoint are not supported", + "error": "Invalid Configuration: Dualstack 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" + "endpoint": { + "url": { + "ref": "Endpoint" }, - { - "conditions": [], - "endpoint": { - "url": { - "ref": "Endpoint" - }, - "properties": {}, - "headers": {} - }, - "type": "endpoint" - } - ] + "properties": {}, + "headers": {} + }, + "type": "endpoint" } ] - }, + } + ] + }, + { + "conditions": [], + "type": "tree", + "rules": [ { "conditions": [ { - "fn": "booleanEquals", - "argv": [ - { - "ref": "UseFIPS" - }, - true - ] - }, - { - "fn": "booleanEquals", + "fn": "isSet", "argv": [ { - "ref": "UseDualStack" - }, - true + "ref": "Region" + } ] } ], @@ -544,154 +524,215 @@ { "conditions": [ { - "fn": "booleanEquals", + "fn": "aws.partition", "argv": [ - true, { - "fn": "getAttr", + "ref": "Region" + } + ], + "assign": "PartitionResult" + } + ], + "type": "tree", + "rules": [ + { + "conditions": [ + { + "fn": "booleanEquals", "argv": [ { - "ref": "PartitionResult" + "ref": "UseFIPS" }, - "supportsFIPS" + true ] - } - ] - }, - { - "fn": "booleanEquals", - "argv": [ - true, + }, { - "fn": "getAttr", + "fn": "booleanEquals", "argv": [ { - "ref": "PartitionResult" + "ref": "UseDualStack" }, - "supportsDualStack" + true ] } - ] - } - ], - "type": "tree", - "rules": [ - { - "conditions": [], + ], "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://session.qldb-fips.{Region}.{PartitionResult#dualStackDnsSuffix}", + "properties": {}, + "headers": {} + }, + "type": "endpoint" + } + ] + } + ] + }, { "conditions": [], - "endpoint": { - "url": "https://session.qldb-fips.{Region}.{PartitionResult#dualStackDnsSuffix}", - "properties": {}, - "headers": {} - }, - "type": "endpoint" + "error": "FIPS and DualStack are enabled, but this partition does not support one or both", + "type": "error" } ] - } - ] - }, - { - "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, + "conditions": [ { - "fn": "getAttr", + "fn": "booleanEquals", "argv": [ { - "ref": "PartitionResult" + "ref": "UseFIPS" }, - "supportsFIPS" + true ] } - ] - } - ], - "type": "tree", - "rules": [ - { - "conditions": [], + ], "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://session.qldb-fips.{Region}.{PartitionResult#dnsSuffix}", + "properties": {}, + "headers": {} + }, + "type": "endpoint" + } + ] + } + ] + }, { "conditions": [], - "endpoint": { - "url": "https://session.qldb-fips.{Region}.{PartitionResult#dnsSuffix}", - "properties": {}, - "headers": {} - }, - "type": "endpoint" + "error": "FIPS is enabled but this partition does not support FIPS", + "type": "error" } ] - } - ] - }, - { - "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, + "conditions": [ { - "fn": "getAttr", + "fn": "booleanEquals", "argv": [ { - "ref": "PartitionResult" + "ref": "UseDualStack" }, - "supportsDualStack" + 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://session.qldb.{Region}.{PartitionResult#dualStackDnsSuffix}", + "properties": {}, + "headers": {} + }, + "type": "endpoint" + } + ] + } + ] + }, + { + "conditions": [], + "error": "DualStack is enabled but this partition does not support DualStack", + "type": "error" + } ] - } - ], - "type": "tree", - "rules": [ + }, { "conditions": [], "type": "tree", @@ -699,7 +740,7 @@ { "conditions": [], "endpoint": { - "url": "https://session.qldb.{Region}.{PartitionResult#dualStackDnsSuffix}", + "url": "https://session.qldb.{Region}.{PartitionResult#dnsSuffix}", "properties": {}, "headers": {} }, @@ -708,28 +749,13 @@ ] } ] - }, - { - "conditions": [], - "error": "DualStack is enabled but this partition does not support DualStack", - "type": "error" } ] }, { "conditions": [], - "type": "tree", - "rules": [ - { - "conditions": [], - "endpoint": { - "url": "https://session.qldb.{Region}.{PartitionResult#dnsSuffix}", - "properties": {}, - "headers": {} - }, - "type": "endpoint" - } - ] + "error": "Invalid Configuration: Missing Region", + "type": "error" } ] } @@ -738,185 +764,185 @@ "smithy.rules#endpointTests": { "testCases": [ { - "documentation": "For region ap-southeast-2 with FIPS disabled and DualStack disabled", + "documentation": "For region ap-northeast-1 with FIPS disabled and DualStack disabled", "expect": { "endpoint": { - "url": "https://session.qldb.ap-southeast-2.amazonaws.com" + "url": "https://session.qldb.ap-northeast-1.amazonaws.com" } }, "params": { - "UseFIPS": false, + "Region": "ap-northeast-1", "UseDualStack": false, - "Region": "ap-southeast-2" + "UseFIPS": false } }, { - "documentation": "For region us-east-2 with FIPS disabled and DualStack disabled", + "documentation": "For region ap-northeast-2 with FIPS disabled and DualStack disabled", "expect": { "endpoint": { - "url": "https://session.qldb.us-east-2.amazonaws.com" + "url": "https://session.qldb.ap-northeast-2.amazonaws.com" } }, "params": { - "UseFIPS": false, + "Region": "ap-northeast-2", "UseDualStack": false, - "Region": "us-east-2" + "UseFIPS": false } }, { - "documentation": "For region us-east-2 with FIPS enabled and DualStack disabled", + "documentation": "For region ap-southeast-1 with FIPS disabled and DualStack disabled", "expect": { "endpoint": { - "url": "https://session.qldb-fips.us-east-2.amazonaws.com" + "url": "https://session.qldb.ap-southeast-1.amazonaws.com" } }, "params": { - "UseFIPS": true, + "Region": "ap-southeast-1", "UseDualStack": false, - "Region": "us-east-2" + "UseFIPS": false } }, { - "documentation": "For region ca-central-1 with FIPS disabled and DualStack disabled", + "documentation": "For region ap-southeast-2 with FIPS disabled and DualStack disabled", "expect": { "endpoint": { - "url": "https://session.qldb.ca-central-1.amazonaws.com" + "url": "https://session.qldb.ap-southeast-2.amazonaws.com" } }, "params": { - "UseFIPS": false, + "Region": "ap-southeast-2", "UseDualStack": false, - "Region": "ca-central-1" + "UseFIPS": false } }, { - "documentation": "For region us-west-2 with FIPS disabled and DualStack disabled", + "documentation": "For region ca-central-1 with FIPS disabled and DualStack disabled", "expect": { "endpoint": { - "url": "https://session.qldb.us-west-2.amazonaws.com" + "url": "https://session.qldb.ca-central-1.amazonaws.com" } }, "params": { - "UseFIPS": false, + "Region": "ca-central-1", "UseDualStack": false, - "Region": "us-west-2" + "UseFIPS": false } }, { - "documentation": "For region us-west-2 with FIPS enabled and DualStack disabled", + "documentation": "For region eu-central-1 with FIPS disabled and DualStack disabled", "expect": { "endpoint": { - "url": "https://session.qldb-fips.us-west-2.amazonaws.com" + "url": "https://session.qldb.eu-central-1.amazonaws.com" } }, "params": { - "UseFIPS": true, + "Region": "eu-central-1", "UseDualStack": false, - "Region": "us-west-2" + "UseFIPS": false } }, { - "documentation": "For region ap-northeast-1 with FIPS disabled and DualStack disabled", + "documentation": "For region eu-west-1 with FIPS disabled and DualStack disabled", "expect": { "endpoint": { - "url": "https://session.qldb.ap-northeast-1.amazonaws.com" + "url": "https://session.qldb.eu-west-1.amazonaws.com" } }, "params": { - "UseFIPS": false, + "Region": "eu-west-1", "UseDualStack": false, - "Region": "ap-northeast-1" + "UseFIPS": false } }, { - "documentation": "For region ap-northeast-2 with FIPS disabled and DualStack disabled", + "documentation": "For region eu-west-2 with FIPS disabled and DualStack disabled", "expect": { "endpoint": { - "url": "https://session.qldb.ap-northeast-2.amazonaws.com" + "url": "https://session.qldb.eu-west-2.amazonaws.com" } }, "params": { - "UseFIPS": false, + "Region": "eu-west-2", "UseDualStack": false, - "Region": "ap-northeast-2" + "UseFIPS": false } }, { - "documentation": "For region eu-central-1 with FIPS disabled and DualStack disabled", + "documentation": "For region us-east-1 with FIPS disabled and DualStack disabled", "expect": { "endpoint": { - "url": "https://session.qldb.eu-central-1.amazonaws.com" + "url": "https://session.qldb.us-east-1.amazonaws.com" } }, "params": { - "UseFIPS": false, + "Region": "us-east-1", "UseDualStack": false, - "Region": "eu-central-1" + "UseFIPS": false } }, { - "documentation": "For region us-east-1 with FIPS disabled and DualStack disabled", + "documentation": "For region us-east-1 with FIPS enabled and DualStack disabled", "expect": { "endpoint": { - "url": "https://session.qldb.us-east-1.amazonaws.com" + "url": "https://session.qldb-fips.us-east-1.amazonaws.com" } }, "params": { - "UseFIPS": false, + "Region": "us-east-1", "UseDualStack": false, - "Region": "us-east-1" + "UseFIPS": true } }, { - "documentation": "For region us-east-1 with FIPS enabled and DualStack disabled", + "documentation": "For region us-east-2 with FIPS disabled and DualStack disabled", "expect": { "endpoint": { - "url": "https://session.qldb-fips.us-east-1.amazonaws.com" + "url": "https://session.qldb.us-east-2.amazonaws.com" } }, "params": { - "UseFIPS": true, + "Region": "us-east-2", "UseDualStack": false, - "Region": "us-east-1" + "UseFIPS": false } }, { - "documentation": "For region eu-west-1 with FIPS disabled and DualStack disabled", + "documentation": "For region us-east-2 with FIPS enabled and DualStack disabled", "expect": { "endpoint": { - "url": "https://session.qldb.eu-west-1.amazonaws.com" + "url": "https://session.qldb-fips.us-east-2.amazonaws.com" } }, "params": { - "UseFIPS": false, + "Region": "us-east-2", "UseDualStack": false, - "Region": "eu-west-1" + "UseFIPS": true } }, { - "documentation": "For region eu-west-2 with FIPS disabled and DualStack disabled", + "documentation": "For region us-west-2 with FIPS disabled and DualStack disabled", "expect": { "endpoint": { - "url": "https://session.qldb.eu-west-2.amazonaws.com" + "url": "https://session.qldb.us-west-2.amazonaws.com" } }, "params": { - "UseFIPS": false, + "Region": "us-west-2", "UseDualStack": false, - "Region": "eu-west-2" + "UseFIPS": false } }, { - "documentation": "For region ap-southeast-1 with FIPS disabled and DualStack disabled", + "documentation": "For region us-west-2 with FIPS enabled and DualStack disabled", "expect": { "endpoint": { - "url": "https://session.qldb.ap-southeast-1.amazonaws.com" + "url": "https://session.qldb-fips.us-west-2.amazonaws.com" } }, "params": { - "UseFIPS": false, + "Region": "us-west-2", "UseDualStack": false, - "Region": "ap-southeast-1" + "UseFIPS": true } }, { @@ -927,9 +953,9 @@ } }, "params": { - "UseFIPS": true, + "Region": "us-east-1", "UseDualStack": true, - "Region": "us-east-1" + "UseFIPS": true } }, { @@ -940,178 +966,191 @@ } }, "params": { - "UseFIPS": false, + "Region": "us-east-1", "UseDualStack": true, - "Region": "us-east-1" + "UseFIPS": false } }, { - "documentation": "For region us-gov-east-1 with FIPS enabled and DualStack enabled", + "documentation": "For region cn-north-1 with FIPS enabled and DualStack enabled", "expect": { "endpoint": { - "url": "https://session.qldb-fips.us-gov-east-1.api.aws" + "url": "https://session.qldb-fips.cn-north-1.api.amazonwebservices.com.cn" } }, "params": { - "UseFIPS": true, + "Region": "cn-north-1", "UseDualStack": true, - "Region": "us-gov-east-1" + "UseFIPS": true } }, { - "documentation": "For region us-gov-east-1 with FIPS enabled and DualStack disabled", + "documentation": "For region cn-north-1 with FIPS enabled and DualStack disabled", "expect": { "endpoint": { - "url": "https://session.qldb-fips.us-gov-east-1.amazonaws.com" + "url": "https://session.qldb-fips.cn-north-1.amazonaws.com.cn" } }, "params": { - "UseFIPS": true, + "Region": "cn-north-1", "UseDualStack": false, - "Region": "us-gov-east-1" + "UseFIPS": true } }, { - "documentation": "For region us-gov-east-1 with FIPS disabled and DualStack enabled", + "documentation": "For region cn-north-1 with FIPS disabled and DualStack enabled", "expect": { "endpoint": { - "url": "https://session.qldb.us-gov-east-1.api.aws" + "url": "https://session.qldb.cn-north-1.api.amazonwebservices.com.cn" } }, "params": { - "UseFIPS": false, + "Region": "cn-north-1", "UseDualStack": true, - "Region": "us-gov-east-1" + "UseFIPS": false } }, { - "documentation": "For region us-gov-east-1 with FIPS disabled and DualStack disabled", + "documentation": "For region cn-north-1 with FIPS disabled and DualStack disabled", "expect": { "endpoint": { - "url": "https://session.qldb.us-gov-east-1.amazonaws.com" + "url": "https://session.qldb.cn-north-1.amazonaws.com.cn" } }, "params": { - "UseFIPS": false, + "Region": "cn-north-1", "UseDualStack": false, - "Region": "us-gov-east-1" + "UseFIPS": false } }, { - "documentation": "For region us-isob-east-1 with FIPS enabled and DualStack disabled", + "documentation": "For region us-gov-east-1 with FIPS enabled and DualStack enabled", "expect": { "endpoint": { - "url": "https://session.qldb-fips.us-isob-east-1.sc2s.sgov.gov" + "url": "https://session.qldb-fips.us-gov-east-1.api.aws" } }, "params": { - "UseFIPS": true, - "UseDualStack": false, - "Region": "us-isob-east-1" + "Region": "us-gov-east-1", + "UseDualStack": true, + "UseFIPS": true } }, { - "documentation": "For region us-isob-east-1 with FIPS disabled and DualStack disabled", + "documentation": "For region us-gov-east-1 with FIPS enabled and DualStack disabled", "expect": { "endpoint": { - "url": "https://session.qldb.us-isob-east-1.sc2s.sgov.gov" + "url": "https://session.qldb-fips.us-gov-east-1.amazonaws.com" } }, "params": { - "UseFIPS": false, + "Region": "us-gov-east-1", "UseDualStack": false, - "Region": "us-isob-east-1" + "UseFIPS": true } }, { - "documentation": "For region cn-north-1 with FIPS enabled and DualStack enabled", + "documentation": "For region us-gov-east-1 with FIPS disabled and DualStack enabled", "expect": { "endpoint": { - "url": "https://session.qldb-fips.cn-north-1.api.amazonwebservices.com.cn" + "url": "https://session.qldb.us-gov-east-1.api.aws" } }, "params": { - "UseFIPS": true, + "Region": "us-gov-east-1", "UseDualStack": true, - "Region": "cn-north-1" + "UseFIPS": false } }, { - "documentation": "For region cn-north-1 with FIPS enabled and DualStack disabled", + "documentation": "For region us-gov-east-1 with FIPS disabled and DualStack disabled", "expect": { "endpoint": { - "url": "https://session.qldb-fips.cn-north-1.amazonaws.com.cn" + "url": "https://session.qldb.us-gov-east-1.amazonaws.com" } }, "params": { - "UseFIPS": true, + "Region": "us-gov-east-1", "UseDualStack": false, - "Region": "cn-north-1" + "UseFIPS": false } }, { - "documentation": "For region cn-north-1 with FIPS disabled and DualStack enabled", + "documentation": "For region us-iso-east-1 with FIPS enabled and DualStack disabled", "expect": { "endpoint": { - "url": "https://session.qldb.cn-north-1.api.amazonwebservices.com.cn" + "url": "https://session.qldb-fips.us-iso-east-1.c2s.ic.gov" } }, "params": { - "UseFIPS": false, - "UseDualStack": true, - "Region": "cn-north-1" + "Region": "us-iso-east-1", + "UseDualStack": false, + "UseFIPS": true } }, { - "documentation": "For region cn-north-1 with FIPS disabled and DualStack disabled", + "documentation": "For region us-iso-east-1 with FIPS disabled and DualStack disabled", "expect": { "endpoint": { - "url": "https://session.qldb.cn-north-1.amazonaws.com.cn" + "url": "https://session.qldb.us-iso-east-1.c2s.ic.gov" } }, "params": { - "UseFIPS": false, + "Region": "us-iso-east-1", "UseDualStack": false, - "Region": "cn-north-1" + "UseFIPS": false } }, { - "documentation": "For region us-iso-east-1 with FIPS enabled and DualStack disabled", + "documentation": "For region us-isob-east-1 with FIPS enabled and DualStack disabled", "expect": { "endpoint": { - "url": "https://session.qldb-fips.us-iso-east-1.c2s.ic.gov" + "url": "https://session.qldb-fips.us-isob-east-1.sc2s.sgov.gov" } }, "params": { - "UseFIPS": true, + "Region": "us-isob-east-1", "UseDualStack": false, - "Region": "us-iso-east-1" + "UseFIPS": true } }, { - "documentation": "For region us-iso-east-1 with FIPS disabled and DualStack disabled", + "documentation": "For region us-isob-east-1 with FIPS disabled and DualStack disabled", "expect": { "endpoint": { - "url": "https://session.qldb.us-iso-east-1.c2s.ic.gov" + "url": "https://session.qldb.us-isob-east-1.sc2s.sgov.gov" } }, "params": { - "UseFIPS": false, + "Region": "us-isob-east-1", "UseDualStack": false, - "Region": "us-iso-east-1" + "UseFIPS": false } }, { - "documentation": "For custom endpoint with fips disabled and dualstack disabled", + "documentation": "For custom endpoint with region set and fips disabled and dualstack disabled", "expect": { "endpoint": { "url": "https://example.com" } }, "params": { + "Region": "us-east-1", + "UseDualStack": false, "UseFIPS": 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": { "UseDualStack": false, - "Region": "us-east-1", + "UseFIPS": false, "Endpoint": "https://example.com" } }, @@ -1121,9 +1160,9 @@ "error": "Invalid Configuration: FIPS and custom endpoint are not supported" }, "params": { - "UseFIPS": true, - "UseDualStack": false, "Region": "us-east-1", + "UseDualStack": false, + "UseFIPS": true, "Endpoint": "https://example.com" } }, @@ -1133,9 +1172,9 @@ "error": "Invalid Configuration: Dualstack and custom endpoint are not supported" }, "params": { - "UseFIPS": false, - "UseDualStack": true, "Region": "us-east-1", + "UseDualStack": true, + "UseFIPS": false, "Endpoint": "https://example.com" } } diff --git a/aws/sdk/aws-models/route53.json b/aws/sdk/aws-models/route53.json index 38b7bca903..8c5011b2f3 100644 --- a/aws/sdk/aws-models/route53.json +++ b/aws/sdk/aws-models/route53.json @@ -279,7 +279,7 @@ "parameters": { "Region": { "builtIn": "AWS::Region", - "required": true, + "required": false, "documentation": "The AWS region used to dispatch the request.", "type": "String" }, @@ -308,13 +308,12 @@ { "conditions": [ { - "fn": "aws.partition", + "fn": "isSet", "argv": [ { - "ref": "Region" + "ref": "Endpoint" } - ], - "assign": "PartitionResult" + ] } ], "type": "tree", @@ -322,14 +321,20 @@ { "conditions": [ { - "fn": "isSet", + "fn": "booleanEquals", "argv": [ { - "ref": "Endpoint" - } + "ref": "UseFIPS" + }, + true ] } ], + "error": "Invalid Configuration: FIPS and custom endpoint are not supported", + "type": "error" + }, + { + "conditions": [], "type": "tree", "rules": [ { @@ -338,64 +343,42 @@ "fn": "booleanEquals", "argv": [ { - "ref": "UseFIPS" + "ref": "UseDualStack" }, true ] } ], - "error": "Invalid Configuration: FIPS and custom endpoint are not supported", + "error": "Invalid Configuration: Dualstack 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" + "endpoint": { + "url": { + "ref": "Endpoint" }, - { - "conditions": [], - "endpoint": { - "url": { - "ref": "Endpoint" - }, - "properties": {}, - "headers": {} - }, - "type": "endpoint" - } - ] + "properties": {}, + "headers": {} + }, + "type": "endpoint" } ] - }, + } + ] + }, + { + "conditions": [], + "type": "tree", + "rules": [ { "conditions": [ { - "fn": "stringEquals", + "fn": "isSet", "argv": [ { - "fn": "getAttr", - "argv": [ - { - "ref": "PartitionResult" - }, - "name" - ] - }, - "aws" + "ref": "Region" + } ] } ], @@ -404,22 +387,13 @@ { "conditions": [ { - "fn": "booleanEquals", - "argv": [ - { - "ref": "UseFIPS" - }, - true - ] - }, - { - "fn": "booleanEquals", + "fn": "aws.partition", "argv": [ { - "ref": "UseDualStack" - }, - true - ] + "ref": "Region" + } + ], + "assign": "PartitionResult" } ], "type": "tree", @@ -427,48 +401,221 @@ { "conditions": [ { - "fn": "booleanEquals", + "fn": "stringEquals", "argv": [ - true, { "fn": "getAttr", "argv": [ { "ref": "PartitionResult" }, - "supportsFIPS" + "name" + ] + }, + "aws" + ] + } + ], + "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.aws", + "properties": {}, + "headers": {} + }, + "type": "endpoint" + } ] + }, + { + "conditions": [], + "error": "FIPS and DualStack are enabled, but this partition does not support one or both", + "type": "error" } ] }, { - "fn": "booleanEquals", - "argv": [ - true, + "conditions": [ { - "fn": "getAttr", + "fn": "booleanEquals", "argv": [ { - "ref": "PartitionResult" + "ref": "UseFIPS" }, - "supportsDualStack" + 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" + } ] + }, + { + "conditions": [], + "error": "FIPS is enabled but this partition does not support FIPS", + "type": "error" } ] - } - ], - "type": "tree", - "rules": [ + }, + { + "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": [], + "endpoint": { + "url": "https://route53.{Region}.api.aws", + "properties": {}, + "headers": {} + }, + "type": "endpoint" + } + ] + }, + { + "conditions": [], + "error": "DualStack is enabled but this partition does not support DualStack", + "type": "error" + } + ] + }, { "conditions": [], "endpoint": { - "url": "https://route-53-fips.{Region}.api.aws", + "url": "https://route53.amazonaws.com", "properties": { "authSchemes": [ { "name": "sigv4", - "signingRegion": "us-east-1", - "signingName": "route53" + "signingName": "route53", + "signingRegion": "us-east-1" } ] }, @@ -478,57 +625,216 @@ } ] }, - { - "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", + "fn": "stringEquals", "argv": [ - true, { "fn": "getAttr", "argv": [ { "ref": "PartitionResult" }, - "supportsFIPS" + "name" ] - } + }, + "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": [ + { + "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" + } + ] + }, + { + "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": [], + "endpoint": { + "url": "https://route53.{Region}.api.amazonwebservices.com.cn", + "properties": {}, + "headers": {} + }, + "type": "endpoint" + } + ] + }, + { + "conditions": [], + "error": "DualStack is enabled but this partition does not support DualStack", + "type": "error" + } + ] + }, { "conditions": [], "endpoint": { - "url": "https://route53-fips.amazonaws.com", + "url": "https://route53.amazonaws.com.cn", "properties": { "authSchemes": [ { "name": "sigv4", - "signingRegion": "us-east-1", - "signingName": "route53" + "signingName": "route53", + "signingRegion": "cn-northwest-1" } ] }, @@ -538,180 +844,224 @@ } ] }, - { - "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", + "fn": "stringEquals", "argv": [ - true, { "fn": "getAttr", "argv": [ { "ref": "PartitionResult" }, - "supportsDualStack" + "name" ] - } + }, + "aws-us-gov" ] } ], "type": "tree", "rules": [ { - "conditions": [], - "endpoint": { - "url": "https://route-53.{Region}.api.aws", - "properties": { - "authSchemes": [ + "conditions": [ + { + "fn": "booleanEquals", + "argv": [ { - "name": "sigv4", - "signingRegion": "us-east-1", - "signingName": "route53" + "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.aws", + "properties": {}, + "headers": {} + }, + "type": "endpoint" } ] }, - "headers": {} - }, - "type": "endpoint" - } - ] - }, - { - "conditions": [], - "error": "DualStack is enabled but this partition does not support DualStack", - "type": "error" - } - ] - }, - { - "conditions": [], - "endpoint": { - "url": "https://route53.amazonaws.com", - "properties": { - "authSchemes": [ - { - "name": "sigv4", - "signingRegion": "us-east-1", - "signingName": "route53" - } - ] - }, - "headers": {} - }, - "type": "endpoint" - } - ] - }, - { - "conditions": [ - { - "fn": "stringEquals", - "argv": [ - { - "fn": "getAttr", - "argv": [ - { - "ref": "PartitionResult" - }, - "name" - ] - }, - "aws-cn" - ] - } - ], - "type": "tree", - "rules": [ - { - "conditions": [ - { - "fn": "booleanEquals", - "argv": [ - { - "ref": "UseFIPS" - }, - true - ] - }, - { - "fn": "booleanEquals", - "argv": [ - { - "ref": "UseDualStack" + { + "conditions": [], + "error": "FIPS and DualStack are enabled, but this partition does not support one or both", + "type": "error" + } + ] }, - true - ] - } - ], - "type": "tree", - "rules": [ - { - "conditions": [ { - "fn": "booleanEquals", - "argv": [ - true, + "conditions": [ { - "fn": "getAttr", + "fn": "booleanEquals", "argv": [ { - "ref": "PartitionResult" + "ref": "UseFIPS" }, - "supportsFIPS" + 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" + } ] + }, + { + "conditions": [], + "error": "FIPS is enabled but this partition does not support FIPS", + "type": "error" } ] }, { - "fn": "booleanEquals", - "argv": [ - true, + "conditions": [ { - "fn": "getAttr", + "fn": "booleanEquals", "argv": [ { - "ref": "PartitionResult" + "ref": "UseDualStack" }, - "supportsDualStack" + true + ] + } + ], + "type": "tree", + "rules": [ + { + "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" + } ] + }, + { + "conditions": [], + "error": "DualStack is enabled but this partition does not support DualStack", + "type": "error" } ] - } - ], - "type": "tree", - "rules": [ + }, { "conditions": [], "endpoint": { - "url": "https://route-53-fips.{Region}.api.amazonwebservices.com.cn", + "url": "https://route53.us-gov.amazonaws.com", "properties": { "authSchemes": [ { "name": "sigv4", - "signingRegion": "cn-northwest-1", - "signingName": "route53" + "signingName": "route53", + "signingRegion": "us-gov-west-1" } ] }, @@ -721,57 +1071,88 @@ } ] }, - { - "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", + "fn": "stringEquals", "argv": [ - true, { "fn": "getAttr", "argv": [ { "ref": "PartitionResult" }, - "supportsFIPS" + "name" ] - } + }, + "aws-iso" ] } ], "type": "tree", "rules": [ + { + "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": [], + "endpoint": { + "url": "https://route53-fips.{Region}.c2s.ic.gov", + "properties": {}, + "headers": {} + }, + "type": "endpoint" + } + ] + }, + { + "conditions": [], + "error": "FIPS is enabled but this partition does not support FIPS", + "type": "error" + } + ] + }, { "conditions": [], "endpoint": { - "url": "https://route-53-fips.{Region}.amazonaws.com.cn", + "url": "https://route53.c2s.ic.gov", "properties": { "authSchemes": [ { "name": "sigv4", - "signingRegion": "cn-northwest-1", - "signingName": "route53" + "signingName": "route53", + "signingRegion": "us-iso-east-1" } ] }, @@ -781,57 +1162,88 @@ } ] }, - { - "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", + "fn": "stringEquals", "argv": [ - true, { "fn": "getAttr", "argv": [ { "ref": "PartitionResult" }, - "supportsDualStack" + "name" ] - } + }, + "aws-iso-b" ] } ], "type": "tree", "rules": [ + { + "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": [], + "endpoint": { + "url": "https://route53-fips.{Region}.sc2s.sgov.gov", + "properties": {}, + "headers": {} + }, + "type": "endpoint" + } + ] + }, + { + "conditions": [], + "error": "FIPS is enabled but this partition does not support FIPS", + "type": "error" + } + ] + }, { "conditions": [], "endpoint": { - "url": "https://route-53.{Region}.api.amazonwebservices.com.cn", + "url": "https://route53.sc2s.sgov.gov", "properties": { "authSchemes": [ { "name": "sigv4", - "signingRegion": "cn-northwest-1", - "signingName": "route53" + "signingName": "route53", + "signingRegion": "us-isob-east-1" } ] }, @@ -842,586 +1254,369 @@ ] }, { - "conditions": [], - "error": "DualStack is enabled but this partition does not support DualStack", - "type": "error" - } - ] - }, - { - "conditions": [], - "endpoint": { - "url": "https://route53.amazonaws.com.cn", - "properties": { - "authSchemes": [ + "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://route53-fips.{Region}.{PartitionResult#dualStackDnsSuffix}", + "properties": {}, + "headers": {} + }, + "type": "endpoint" + } + ] + } + ] + }, { - "name": "sigv4", - "signingRegion": "cn-northwest-1", - "signingName": "route53" + "conditions": [], + "error": "FIPS and DualStack are enabled, but this partition does not support one or both", + "type": "error" } ] }, - "headers": {} - }, - "type": "endpoint" - } - ] - }, - { - "conditions": [ - { - "fn": "stringEquals", - "argv": [ - { - "fn": "getAttr", - "argv": [ - { - "ref": "PartitionResult" - }, - "name" - ] - }, - "aws-us-gov" - ] - } - ], - "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", + "ref": "UseFIPS" + }, + true + ] + } + ], + "type": "tree", + "rules": [ + { + "conditions": [ + { + "fn": "booleanEquals", "argv": [ + true, { - "ref": "PartitionResult" + "fn": "getAttr", + "argv": [ + { + "ref": "PartitionResult" + }, + "supportsFIPS" + ] + } + ] + } + ], + "type": "tree", + "rules": [ + { + "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" }, - "supportsFIPS" + { + "conditions": [], + "endpoint": { + "url": "https://route53-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": [ - true, { - "fn": "getAttr", + "ref": "UseDualStack" + }, + true + ] + } + ], + "type": "tree", + "rules": [ + { + "conditions": [ + { + "fn": "booleanEquals", "argv": [ + true, { - "ref": "PartitionResult" - }, - "supportsDualStack" + "fn": "getAttr", + "argv": [ + { + "ref": "PartitionResult" + }, + "supportsDualStack" + ] + } + ] + } + ], + "type": "tree", + "rules": [ + { + "conditions": [], + "type": "tree", + "rules": [ + { + "conditions": [], + "endpoint": { + "url": "https://route53.{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": [], + "conditions": [ + { + "fn": "stringEquals", + "argv": [ + { + "ref": "Region" + }, + "aws-global" + ] + } + ], "endpoint": { - "url": "https://route-53-fips.{Region}.api.aws", + "url": "https://route53.amazonaws.com", "properties": { "authSchemes": [ { "name": "sigv4", - "signingRegion": "us-gov-west-1", - "signingName": "route53" + "signingName": "route53", + "signingRegion": "us-east-1" } ] }, "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, + "conditions": [ { - "fn": "getAttr", + "fn": "stringEquals", "argv": [ { - "ref": "PartitionResult" + "ref": "Region" }, - "supportsFIPS" + "aws-cn-global" ] } - ] - } - ], - "type": "tree", - "rules": [ - { - "conditions": [], + ], "endpoint": { - "url": "https://route53.us-gov.amazonaws.com", + "url": "https://route53.amazonaws.com.cn", "properties": { "authSchemes": [ { "name": "sigv4", - "signingRegion": "us-gov-west-1", - "signingName": "route53" + "signingName": "route53", + "signingRegion": "cn-northwest-1" } ] }, "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, + "conditions": [ { - "fn": "getAttr", + "fn": "stringEquals", "argv": [ { - "ref": "PartitionResult" + "ref": "Region" }, - "supportsDualStack" + "aws-us-gov-global" ] } - ] - } - ], - "type": "tree", - "rules": [ - { - "conditions": [], + ], "endpoint": { - "url": "https://route-53.{Region}.api.aws", + "url": "https://route53.us-gov.amazonaws.com", "properties": { "authSchemes": [ { "name": "sigv4", - "signingRegion": "us-gov-west-1", - "signingName": "route53" + "signingName": "route53", + "signingRegion": "us-gov-west-1" } ] }, "headers": {} }, "type": "endpoint" - } - ] - }, - { - "conditions": [], - "error": "DualStack is enabled but this partition does not support DualStack", - "type": "error" - } - ] - }, - { - "conditions": [], - "endpoint": { - "url": "https://route53.us-gov.amazonaws.com", - "properties": { - "authSchemes": [ - { - "name": "sigv4", - "signingRegion": "us-gov-west-1", - "signingName": "route53" - } - ] - }, - "headers": {} - }, - "type": "endpoint" - } - ] - }, - { - "conditions": [ - { - "fn": "stringEquals", - "argv": [ - { - "fn": "getAttr", - "argv": [ - { - "ref": "PartitionResult" - }, - "name" - ] - }, - "aws-iso" - ] - } - ], - "type": "tree", - "rules": [ - { - "conditions": [ - { - "fn": "booleanEquals", - "argv": [ - { - "ref": "UseFIPS" }, - true - ] - } - ], - "type": "tree", - "rules": [ - { - "conditions": [ { - "fn": "booleanEquals", - "argv": [ - true, + "conditions": [ { - "fn": "getAttr", + "fn": "stringEquals", "argv": [ { - "ref": "PartitionResult" + "ref": "Region" }, - "supportsFIPS" + "aws-iso-global" ] } - ] - } - ], - "type": "tree", - "rules": [ - { - "conditions": [], + ], "endpoint": { - "url": "https://route-53-fips.{Region}.c2s.ic.gov", + "url": "https://route53.c2s.ic.gov", "properties": { "authSchemes": [ { "name": "sigv4", - "signingRegion": "us-iso-east-1", - "signingName": "route53" + "signingName": "route53", + "signingRegion": "us-iso-east-1" } ] }, "headers": {} }, "type": "endpoint" - } - ] - }, - { - "conditions": [], - "error": "FIPS is enabled but this partition does not support FIPS", - "type": "error" - } - ] - }, - { - "conditions": [], - "endpoint": { - "url": "https://route53.c2s.ic.gov", - "properties": { - "authSchemes": [ - { - "name": "sigv4", - "signingRegion": "us-iso-east-1", - "signingName": "route53" - } - ] - }, - "headers": {} - }, - "type": "endpoint" - } - ] - }, - { - "conditions": [ - { - "fn": "stringEquals", - "argv": [ - { - "fn": "getAttr", - "argv": [ - { - "ref": "PartitionResult" - }, - "name" - ] - }, - "aws-iso-b" - ] - } - ], - "type": "tree", - "rules": [ - { - "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": [], - "endpoint": { - "url": "https://route-53-fips.{Region}.sc2s.sgov.gov", - "properties": { - "authSchemes": [ - { - "name": "sigv4", - "signingRegion": "us-isob-east-1", - "signingName": "route53" - } - ] - }, - "headers": {} - }, - "type": "endpoint" - } - ] - }, - { - "conditions": [], - "error": "FIPS is enabled but this partition does not support FIPS", - "type": "error" - } - ] - }, - { - "conditions": [], - "endpoint": { - "url": "https://route53.sc2s.sgov.gov", - "properties": { - "authSchemes": [ - { - "name": "sigv4", - "signingRegion": "us-isob-east-1", - "signingName": "route53" - } - ] - }, - "headers": {} - }, - "type": "endpoint" - } - ] - }, - { - "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}.{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": [ { @@ -1430,45 +1625,18 @@ { "ref": "Region" }, - "aws-global" + "aws-iso-b-global" ] } ], "endpoint": { - "url": "https://route53-fips.amazonaws.com", + "url": "https://route53.sc2s.sgov.gov", "properties": { "authSchemes": [ { "name": "sigv4", - "signingRegion": "us-east-1", - "signingName": "route53" - } - ] - }, - "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", - "signingRegion": "us-gov-west-1", - "signingName": "route53" + "signingName": "route53", + "signingRegion": "us-isob-east-1" } ] }, @@ -1479,7 +1647,7 @@ { "conditions": [], "endpoint": { - "url": "https://route53-fips.{Region}.{PartitionResult#dnsSuffix}", + "url": "https://route53.{Region}.{PartitionResult#dnsSuffix}", "properties": {}, "headers": {} }, @@ -1488,222 +1656,134 @@ ] } ] - }, - { - "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": [], - "endpoint": { - "url": "https://route53.{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": [ - { - "fn": "stringEquals", - "argv": [ - { - "ref": "Region" - }, - "aws-global" - ] - } - ], - "endpoint": { - "url": "https://route53.amazonaws.com", - "properties": { - "authSchemes": [ - { - "name": "sigv4", - "signingRegion": "us-east-1", - "signingName": "route53" - } - ] - }, - "headers": {} - }, - "type": "endpoint" - }, - { - "conditions": [ - { - "fn": "stringEquals", - "argv": [ - { - "ref": "Region" - }, - "aws-cn-global" - ] - } - ], - "endpoint": { - "url": "https://route53.amazonaws.com.cn", - "properties": { - "authSchemes": [ - { - "name": "sigv4", - "signingRegion": "cn-northwest-1", - "signingName": "route53" - } - ] - }, - "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", - "signingRegion": "us-gov-west-1", - "signingName": "route53" - } - ] - }, - "headers": {} - }, - "type": "endpoint" - }, - { - "conditions": [ - { - "fn": "stringEquals", - "argv": [ - { - "ref": "Region" - }, - "aws-iso-global" - ] - } - ], - "endpoint": { - "url": "https://route53.c2s.ic.gov", - "properties": { - "authSchemes": [ - { - "name": "sigv4", - "signingRegion": "us-iso-east-1", - "signingName": "route53" - } - ] - }, - "headers": {} - }, - "type": "endpoint" - }, - { - "conditions": [ - { - "fn": "stringEquals", - "argv": [ - { - "ref": "Region" - }, - "aws-iso-b-global" - ] - } - ], - "endpoint": { - "url": "https://route53.sc2s.sgov.gov", - "properties": { - "authSchemes": [ - { - "name": "sigv4", - "signingRegion": "us-isob-east-1", - "signingName": "route53" - } - ] - }, - "headers": {} - }, - "type": "endpoint" - }, - { - "conditions": [], - "endpoint": { - "url": "https://route53.{Region}.{PartitionResult#dnsSuffix}", - "properties": {}, - "headers": {} - }, - "type": "endpoint" - } - ] + "error": "Invalid Configuration: Missing Region", + "type": "error" + } + ] + } + ] + }, + "smithy.rules#endpointTests": { + "testCases": [ + { + "documentation": "For region aws-global with FIPS disabled and DualStack disabled", + "expect": { + "endpoint": { + "properties": { + "authSchemes": [ + { + "name": "sigv4", + "signingName": "route53", + "signingRegion": "us-east-1" + } + ] + }, + "url": "https://route53.amazonaws.com" + } + }, + "params": { + "Region": "aws-global", + "UseDualStack": false, + "UseFIPS": false + } + }, + { + "documentation": "For region aws-global with FIPS enabled and DualStack disabled", + "expect": { + "endpoint": { + "properties": { + "authSchemes": [ + { + "name": "sigv4", + "signingName": "route53", + "signingRegion": "us-east-1" + } + ] + }, + "url": "https://route53-fips.amazonaws.com" + } + }, + "params": { + "Region": "aws-global", + "UseDualStack": false, + "UseFIPS": true + } + }, + { + "documentation": "For region us-east-1 with FIPS enabled and DualStack enabled", + "expect": { + "endpoint": { + "url": "https://route53-fips.us-east-1.api.aws" } - ] - } - ] - }, - "smithy.rules#endpointTests": { - "testCases": [ + }, + "params": { + "Region": "us-east-1", + "UseDualStack": true, + "UseFIPS": true + } + }, + { + "documentation": "For region us-east-1 with FIPS enabled and DualStack disabled", + "expect": { + "endpoint": { + "properties": { + "authSchemes": [ + { + "name": "sigv4", + "signingName": "route53", + "signingRegion": "us-east-1" + } + ] + }, + "url": "https://route53-fips.amazonaws.com" + } + }, + "params": { + "Region": "us-east-1", + "UseDualStack": false, + "UseFIPS": true + } + }, + { + "documentation": "For region us-east-1 with FIPS disabled and DualStack enabled", + "expect": { + "endpoint": { + "url": "https://route53.us-east-1.api.aws" + } + }, + "params": { + "Region": "us-east-1", + "UseDualStack": true, + "UseFIPS": false + } + }, + { + "documentation": "For region us-east-1 with FIPS disabled and DualStack disabled", + "expect": { + "endpoint": { + "properties": { + "authSchemes": [ + { + "name": "sigv4", + "signingName": "route53", + "signingRegion": "us-east-1" + } + ] + }, + "url": "https://route53.amazonaws.com" + } + }, + "params": { + "Region": "us-east-1", + "UseDualStack": false, + "UseFIPS": false + } + }, { "documentation": "For region aws-cn-global with FIPS disabled and DualStack disabled", "expect": { @@ -1721,13 +1801,52 @@ } }, "params": { + "Region": "aws-cn-global", "UseDualStack": false, - "UseFIPS": false, - "Region": "aws-cn-global" + "UseFIPS": false } }, { - "documentation": "For region aws-global with FIPS disabled and DualStack disabled", + "documentation": "For region cn-north-1 with FIPS enabled and DualStack enabled", + "expect": { + "endpoint": { + "url": "https://route53-fips.cn-north-1.api.amazonwebservices.com.cn" + } + }, + "params": { + "Region": "cn-north-1", + "UseDualStack": true, + "UseFIPS": true + } + }, + { + "documentation": "For region cn-north-1 with FIPS enabled and DualStack disabled", + "expect": { + "endpoint": { + "url": "https://route53-fips.cn-north-1.amazonaws.com.cn" + } + }, + "params": { + "Region": "cn-north-1", + "UseDualStack": false, + "UseFIPS": true + } + }, + { + "documentation": "For region cn-north-1 with FIPS disabled and DualStack enabled", + "expect": { + "endpoint": { + "url": "https://route53.cn-north-1.api.amazonwebservices.com.cn" + } + }, + "params": { + "Region": "cn-north-1", + "UseDualStack": true, + "UseFIPS": false + } + }, + { + "documentation": "For region cn-north-1 with FIPS disabled and DualStack disabled", "expect": { "endpoint": { "properties": { @@ -1735,17 +1854,131 @@ { "name": "sigv4", "signingName": "route53", - "signingRegion": "us-east-1" + "signingRegion": "cn-northwest-1" } ] }, - "url": "https://route53.amazonaws.com" + "url": "https://route53.amazonaws.com.cn" } }, "params": { + "Region": "cn-north-1", "UseDualStack": false, - "UseFIPS": false, - "Region": "aws-global" + "UseFIPS": false + } + }, + { + "documentation": "For region aws-us-gov-global with FIPS disabled and DualStack disabled", + "expect": { + "endpoint": { + "properties": { + "authSchemes": [ + { + "name": "sigv4", + "signingName": "route53", + "signingRegion": "us-gov-west-1" + } + ] + }, + "url": "https://route53.us-gov.amazonaws.com" + } + }, + "params": { + "Region": "aws-us-gov-global", + "UseDualStack": false, + "UseFIPS": false + } + }, + { + "documentation": "For region aws-us-gov-global with FIPS enabled and DualStack disabled", + "expect": { + "endpoint": { + "properties": { + "authSchemes": [ + { + "name": "sigv4", + "signingName": "route53", + "signingRegion": "us-gov-west-1" + } + ] + }, + "url": "https://route53.us-gov.amazonaws.com" + } + }, + "params": { + "Region": "aws-us-gov-global", + "UseDualStack": false, + "UseFIPS": true + } + }, + { + "documentation": "For region us-gov-east-1 with FIPS enabled and DualStack enabled", + "expect": { + "endpoint": { + "url": "https://route53-fips.us-gov-east-1.api.aws" + } + }, + "params": { + "Region": "us-gov-east-1", + "UseDualStack": true, + "UseFIPS": true + } + }, + { + "documentation": "For region us-gov-east-1 with FIPS enabled and DualStack disabled", + "expect": { + "endpoint": { + "properties": { + "authSchemes": [ + { + "name": "sigv4", + "signingName": "route53", + "signingRegion": "us-gov-west-1" + } + ] + }, + "url": "https://route53.us-gov.amazonaws.com" + } + }, + "params": { + "Region": "us-gov-east-1", + "UseDualStack": false, + "UseFIPS": true + } + }, + { + "documentation": "For region us-gov-east-1 with FIPS disabled and DualStack enabled", + "expect": { + "endpoint": { + "url": "https://route53.us-gov-east-1.api.aws" + } + }, + "params": { + "Region": "us-gov-east-1", + "UseDualStack": true, + "UseFIPS": false + } + }, + { + "documentation": "For region us-gov-east-1 with FIPS disabled and DualStack disabled", + "expect": { + "endpoint": { + "properties": { + "authSchemes": [ + { + "name": "sigv4", + "signingName": "route53", + "signingRegion": "us-gov-west-1" + } + ] + }, + "url": "https://route53.us-gov.amazonaws.com" + } + }, + "params": { + "Region": "us-gov-east-1", + "UseDualStack": false, + "UseFIPS": false } }, { @@ -1765,9 +1998,44 @@ } }, "params": { + "Region": "aws-iso-global", "UseDualStack": false, - "UseFIPS": false, - "Region": "aws-iso-global" + "UseFIPS": false + } + }, + { + "documentation": "For region us-iso-east-1 with FIPS enabled and DualStack disabled", + "expect": { + "endpoint": { + "url": "https://route53-fips.us-iso-east-1.c2s.ic.gov" + } + }, + "params": { + "Region": "us-iso-east-1", + "UseDualStack": false, + "UseFIPS": true + } + }, + { + "documentation": "For region us-iso-east-1 with FIPS disabled and DualStack disabled", + "expect": { + "endpoint": { + "properties": { + "authSchemes": [ + { + "name": "sigv4", + "signingName": "route53", + "signingRegion": "us-iso-east-1" + } + ] + }, + "url": "https://route53.c2s.ic.gov" + } + }, + "params": { + "Region": "us-iso-east-1", + "UseDualStack": false, + "UseFIPS": false } }, { @@ -1787,13 +2055,26 @@ } }, "params": { + "Region": "aws-iso-b-global", "UseDualStack": false, - "UseFIPS": false, - "Region": "aws-iso-b-global" + "UseFIPS": false } }, { - "documentation": "For region aws-us-gov-global with FIPS disabled and DualStack disabled", + "documentation": "For region us-isob-east-1 with FIPS enabled and DualStack disabled", + "expect": { + "endpoint": { + "url": "https://route53-fips.us-isob-east-1.sc2s.sgov.gov" + } + }, + "params": { + "Region": "us-isob-east-1", + "UseDualStack": false, + "UseFIPS": true + } + }, + { + "documentation": "For region us-isob-east-1 with FIPS disabled and DualStack disabled", "expect": { "endpoint": { "properties": { @@ -1801,21 +2082,35 @@ { "name": "sigv4", "signingName": "route53", - "signingRegion": "us-gov-west-1" + "signingRegion": "us-isob-east-1" } ] }, - "url": "https://route53.us-gov.amazonaws.com" + "url": "https://route53.sc2s.sgov.gov" + } + }, + "params": { + "Region": "us-isob-east-1", + "UseDualStack": false, + "UseFIPS": 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", "UseDualStack": false, "UseFIPS": false, - "Region": "aws-us-gov-global" + "Endpoint": "https://example.com" } }, { - "documentation": "For custom endpoint with fips disabled and dualstack disabled", + "documentation": "For custom endpoint with region not set and fips disabled and dualstack disabled", "expect": { "endpoint": { "url": "https://example.com" @@ -1824,7 +2119,6 @@ "params": { "UseDualStack": false, "UseFIPS": false, - "Region": "us-east-1", "Endpoint": "https://example.com" } }, @@ -1834,9 +2128,9 @@ "error": "Invalid Configuration: FIPS and custom endpoint are not supported" }, "params": { + "Region": "us-east-1", "UseDualStack": false, "UseFIPS": true, - "Region": "us-east-1", "Endpoint": "https://example.com" } }, @@ -1846,9 +2140,9 @@ "error": "Invalid Configuration: Dualstack and custom endpoint are not supported" }, "params": { + "Region": "us-east-1", "UseDualStack": true, "UseFIPS": false, - "Region": "us-east-1", "Endpoint": "https://example.com" } } diff --git a/aws/sdk/aws-models/s3.json b/aws/sdk/aws-models/s3.json index 0dba936004..57c5645bde 100644 --- a/aws/sdk/aws-models/s3.json +++ b/aws/sdk/aws-models/s3.json @@ -816,10 +816,10 @@ "properties": { "authSchemes": [ { + "disableDoubleEncoding": true, "name": "sigv4", - "signingRegion": "{Region}", "signingName": "s3-outposts", - "disableDoubleEncoding": true + "signingRegion": "{Region}" } ] }, @@ -836,10 +836,10 @@ "properties": { "authSchemes": [ { + "disableDoubleEncoding": true, "name": "sigv4", - "signingRegion": "{Region}", "signingName": "s3-outposts", - "disableDoubleEncoding": true + "signingRegion": "{Region}" } ] }, @@ -921,10 +921,10 @@ "properties": { "authSchemes": [ { + "disableDoubleEncoding": true, "name": "sigv4", - "signingRegion": "{Region}", "signingName": "s3-outposts", - "disableDoubleEncoding": true + "signingRegion": "{Region}" } ] }, @@ -941,10 +941,10 @@ "properties": { "authSchemes": [ { + "disableDoubleEncoding": true, "name": "sigv4", - "signingRegion": "{Region}", "signingName": "s3-outposts", - "disableDoubleEncoding": true + "signingRegion": "{Region}" } ] }, @@ -1184,10 +1184,10 @@ "properties": { "authSchemes": [ { + "disableDoubleEncoding": true, "name": "sigv4", - "signingRegion": "us-east-1", "signingName": "s3", - "disableDoubleEncoding": true + "signingRegion": "us-east-1" } ] }, @@ -1243,10 +1243,10 @@ "properties": { "authSchemes": [ { + "disableDoubleEncoding": true, "name": "sigv4", - "signingRegion": "us-east-1", "signingName": "s3", - "disableDoubleEncoding": true + "signingRegion": "us-east-1" } ] }, @@ -1320,10 +1320,10 @@ "properties": { "authSchemes": [ { + "disableDoubleEncoding": true, "name": "sigv4", - "signingRegion": "{Region}", "signingName": "s3", - "disableDoubleEncoding": true + "signingRegion": "{Region}" } ] }, @@ -1395,10 +1395,10 @@ "properties": { "authSchemes": [ { + "disableDoubleEncoding": true, "name": "sigv4", - "signingRegion": "{Region}", "signingName": "s3", - "disableDoubleEncoding": true + "signingRegion": "{Region}" } ] }, @@ -1458,10 +1458,10 @@ "properties": { "authSchemes": [ { + "disableDoubleEncoding": true, "name": "sigv4", - "signingRegion": "us-east-1", "signingName": "s3", - "disableDoubleEncoding": true + "signingRegion": "us-east-1" } ] }, @@ -1521,10 +1521,10 @@ "properties": { "authSchemes": [ { + "disableDoubleEncoding": true, "name": "sigv4", - "signingRegion": "us-east-1", "signingName": "s3", - "disableDoubleEncoding": true + "signingRegion": "us-east-1" } ] }, @@ -1602,10 +1602,10 @@ "properties": { "authSchemes": [ { + "disableDoubleEncoding": true, "name": "sigv4", - "signingRegion": "{Region}", "signingName": "s3", - "disableDoubleEncoding": true + "signingRegion": "{Region}" } ] }, @@ -1681,10 +1681,10 @@ "properties": { "authSchemes": [ { + "disableDoubleEncoding": true, "name": "sigv4", - "signingRegion": "{Region}", "signingName": "s3", - "disableDoubleEncoding": true + "signingRegion": "{Region}" } ] }, @@ -1740,10 +1740,10 @@ "properties": { "authSchemes": [ { + "disableDoubleEncoding": true, "name": "sigv4", - "signingRegion": "us-east-1", "signingName": "s3", - "disableDoubleEncoding": true + "signingRegion": "us-east-1" } ] }, @@ -1799,10 +1799,10 @@ "properties": { "authSchemes": [ { + "disableDoubleEncoding": true, "name": "sigv4", - "signingRegion": "us-east-1", "signingName": "s3", - "disableDoubleEncoding": true + "signingRegion": "us-east-1" } ] }, @@ -1876,10 +1876,10 @@ "properties": { "authSchemes": [ { + "disableDoubleEncoding": true, "name": "sigv4", - "signingRegion": "{Region}", "signingName": "s3", - "disableDoubleEncoding": true + "signingRegion": "{Region}" } ] }, @@ -1951,10 +1951,10 @@ "properties": { "authSchemes": [ { + "disableDoubleEncoding": true, "name": "sigv4", - "signingRegion": "{Region}", "signingName": "s3", - "disableDoubleEncoding": true + "signingRegion": "{Region}" } ] }, @@ -2010,10 +2010,10 @@ "properties": { "authSchemes": [ { + "disableDoubleEncoding": true, "name": "sigv4", - "signingRegion": "us-east-1", "signingName": "s3", - "disableDoubleEncoding": true + "signingRegion": "us-east-1" } ] }, @@ -2069,10 +2069,10 @@ "properties": { "authSchemes": [ { + "disableDoubleEncoding": true, "name": "sigv4", - "signingRegion": "us-east-1", "signingName": "s3", - "disableDoubleEncoding": true + "signingRegion": "us-east-1" } ] }, @@ -2146,10 +2146,10 @@ "properties": { "authSchemes": [ { + "disableDoubleEncoding": true, "name": "sigv4", - "signingRegion": "{Region}", "signingName": "s3", - "disableDoubleEncoding": true + "signingRegion": "{Region}" } ] }, @@ -2221,10 +2221,10 @@ "properties": { "authSchemes": [ { + "disableDoubleEncoding": true, "name": "sigv4", - "signingRegion": "{Region}", "signingName": "s3", - "disableDoubleEncoding": true + "signingRegion": "{Region}" } ] }, @@ -2284,10 +2284,10 @@ "properties": { "authSchemes": [ { + "disableDoubleEncoding": true, "name": "sigv4", - "signingRegion": "us-east-1", "signingName": "s3", - "disableDoubleEncoding": true + "signingRegion": "us-east-1" } ] }, @@ -2347,10 +2347,10 @@ "properties": { "authSchemes": [ { + "disableDoubleEncoding": true, "name": "sigv4", - "signingRegion": "us-east-1", "signingName": "s3", - "disableDoubleEncoding": true + "signingRegion": "us-east-1" } ] }, @@ -2438,10 +2438,10 @@ "properties": { "authSchemes": [ { + "disableDoubleEncoding": true, "name": "sigv4", - "signingRegion": "{Region}", "signingName": "s3", - "disableDoubleEncoding": true + "signingRegion": "{Region}" } ] }, @@ -2456,10 +2456,10 @@ "properties": { "authSchemes": [ { + "disableDoubleEncoding": true, "name": "sigv4", - "signingRegion": "{Region}", "signingName": "s3", - "disableDoubleEncoding": true + "signingRegion": "{Region}" } ] }, @@ -2535,10 +2535,10 @@ "properties": { "authSchemes": [ { + "disableDoubleEncoding": true, "name": "sigv4", - "signingRegion": "{Region}", "signingName": "s3", - "disableDoubleEncoding": true + "signingRegion": "{Region}" } ] }, @@ -2594,10 +2594,10 @@ "properties": { "authSchemes": [ { + "disableDoubleEncoding": true, "name": "sigv4", - "signingRegion": "us-east-1", "signingName": "s3", - "disableDoubleEncoding": true + "signingRegion": "us-east-1" } ] }, @@ -2653,10 +2653,10 @@ "properties": { "authSchemes": [ { + "disableDoubleEncoding": true, "name": "sigv4", - "signingRegion": "us-east-1", "signingName": "s3", - "disableDoubleEncoding": true + "signingRegion": "us-east-1" } ] }, @@ -2740,10 +2740,10 @@ "properties": { "authSchemes": [ { + "disableDoubleEncoding": true, "name": "sigv4", - "signingRegion": "{Region}", "signingName": "s3", - "disableDoubleEncoding": true + "signingRegion": "{Region}" } ] }, @@ -2758,10 +2758,10 @@ "properties": { "authSchemes": [ { + "disableDoubleEncoding": true, "name": "sigv4", - "signingRegion": "{Region}", "signingName": "s3", - "disableDoubleEncoding": true + "signingRegion": "{Region}" } ] }, @@ -2833,10 +2833,10 @@ "properties": { "authSchemes": [ { + "disableDoubleEncoding": true, "name": "sigv4", - "signingRegion": "{Region}", "signingName": "s3", - "disableDoubleEncoding": true + "signingRegion": "{Region}" } ] }, @@ -3154,10 +3154,10 @@ "properties": { "authSchemes": [ { + "disableDoubleEncoding": true, "name": "sigv4", - "signingRegion": "us-east-1", "signingName": "s3", - "disableDoubleEncoding": true + "signingRegion": "us-east-1" } ] }, @@ -3222,10 +3222,10 @@ "properties": { "authSchemes": [ { + "disableDoubleEncoding": true, "name": "sigv4", - "signingRegion": "us-east-1", "signingName": "s3", - "disableDoubleEncoding": true + "signingRegion": "us-east-1" } ] }, @@ -3308,10 +3308,10 @@ "properties": { "authSchemes": [ { + "disableDoubleEncoding": true, "name": "sigv4", - "signingRegion": "{Region}", "signingName": "s3", - "disableDoubleEncoding": true + "signingRegion": "{Region}" } ] }, @@ -3392,10 +3392,10 @@ "properties": { "authSchemes": [ { + "disableDoubleEncoding": true, "name": "sigv4", - "signingRegion": "{Region}", "signingName": "s3", - "disableDoubleEncoding": true + "signingRegion": "{Region}" } ] }, @@ -3460,10 +3460,10 @@ "properties": { "authSchemes": [ { + "disableDoubleEncoding": true, "name": "sigv4", - "signingRegion": "us-east-1", "signingName": "s3", - "disableDoubleEncoding": true + "signingRegion": "us-east-1" } ] }, @@ -3528,10 +3528,10 @@ "properties": { "authSchemes": [ { + "disableDoubleEncoding": true, "name": "sigv4", - "signingRegion": "us-east-1", "signingName": "s3", - "disableDoubleEncoding": true + "signingRegion": "us-east-1" } ] }, @@ -3614,10 +3614,10 @@ "properties": { "authSchemes": [ { + "disableDoubleEncoding": true, "name": "sigv4", - "signingRegion": "{Region}", "signingName": "s3", - "disableDoubleEncoding": true + "signingRegion": "{Region}" } ] }, @@ -3698,10 +3698,10 @@ "properties": { "authSchemes": [ { + "disableDoubleEncoding": true, "name": "sigv4", - "signingRegion": "{Region}", "signingName": "s3", - "disableDoubleEncoding": true + "signingRegion": "{Region}" } ] }, @@ -3766,10 +3766,10 @@ "properties": { "authSchemes": [ { + "disableDoubleEncoding": true, "name": "sigv4", - "signingRegion": "us-east-1", "signingName": "s3", - "disableDoubleEncoding": true + "signingRegion": "us-east-1" } ] }, @@ -3834,10 +3834,10 @@ "properties": { "authSchemes": [ { + "disableDoubleEncoding": true, "name": "sigv4", - "signingRegion": "us-east-1", "signingName": "s3", - "disableDoubleEncoding": true + "signingRegion": "us-east-1" } ] }, @@ -3920,10 +3920,10 @@ "properties": { "authSchemes": [ { + "disableDoubleEncoding": true, "name": "sigv4", - "signingRegion": "{Region}", "signingName": "s3", - "disableDoubleEncoding": true + "signingRegion": "{Region}" } ] }, @@ -4004,10 +4004,10 @@ "properties": { "authSchemes": [ { + "disableDoubleEncoding": true, "name": "sigv4", - "signingRegion": "{Region}", "signingName": "s3", - "disableDoubleEncoding": true + "signingRegion": "{Region}" } ] }, @@ -4072,10 +4072,10 @@ "properties": { "authSchemes": [ { + "disableDoubleEncoding": true, "name": "sigv4", - "signingRegion": "us-east-1", "signingName": "s3", - "disableDoubleEncoding": true + "signingRegion": "us-east-1" } ] }, @@ -4140,10 +4140,10 @@ "properties": { "authSchemes": [ { + "disableDoubleEncoding": true, "name": "sigv4", - "signingRegion": "us-east-1", "signingName": "s3", - "disableDoubleEncoding": true + "signingRegion": "us-east-1" } ] }, @@ -4226,10 +4226,10 @@ "properties": { "authSchemes": [ { + "disableDoubleEncoding": true, "name": "sigv4", - "signingRegion": "{Region}", "signingName": "s3", - "disableDoubleEncoding": true + "signingRegion": "{Region}" } ] }, @@ -4310,10 +4310,10 @@ "properties": { "authSchemes": [ { + "disableDoubleEncoding": true, "name": "sigv4", - "signingRegion": "{Region}", "signingName": "s3", - "disableDoubleEncoding": true + "signingRegion": "{Region}" } ] }, @@ -4397,10 +4397,10 @@ "properties": { "authSchemes": [ { + "disableDoubleEncoding": true, "name": "sigv4", - "signingRegion": "us-east-1", "signingName": "s3", - "disableDoubleEncoding": true + "signingRegion": "us-east-1" } ] }, @@ -4484,10 +4484,10 @@ "properties": { "authSchemes": [ { + "disableDoubleEncoding": true, "name": "sigv4", - "signingRegion": "us-east-1", "signingName": "s3", - "disableDoubleEncoding": true + "signingRegion": "us-east-1" } ] }, @@ -4571,10 +4571,10 @@ "properties": { "authSchemes": [ { + "disableDoubleEncoding": true, "name": "sigv4", - "signingRegion": "us-east-1", "signingName": "s3", - "disableDoubleEncoding": true + "signingRegion": "us-east-1" } ] }, @@ -4658,10 +4658,10 @@ "properties": { "authSchemes": [ { + "disableDoubleEncoding": true, "name": "sigv4", - "signingRegion": "us-east-1", "signingName": "s3", - "disableDoubleEncoding": true + "signingRegion": "us-east-1" } ] }, @@ -4773,10 +4773,10 @@ "properties": { "authSchemes": [ { + "disableDoubleEncoding": true, "name": "sigv4", - "signingRegion": "{Region}", "signingName": "s3", - "disableDoubleEncoding": true + "signingRegion": "{Region}" } ] }, @@ -4791,10 +4791,10 @@ "properties": { "authSchemes": [ { + "disableDoubleEncoding": true, "name": "sigv4", - "signingRegion": "{Region}", "signingName": "s3", - "disableDoubleEncoding": true + "signingRegion": "{Region}" } ] }, @@ -4908,10 +4908,10 @@ "properties": { "authSchemes": [ { + "disableDoubleEncoding": true, "name": "sigv4", - "signingRegion": "{Region}", "signingName": "s3", - "disableDoubleEncoding": true + "signingRegion": "{Region}" } ] }, @@ -4926,10 +4926,10 @@ "properties": { "authSchemes": [ { + "disableDoubleEncoding": true, "name": "sigv4", - "signingRegion": "{Region}", "signingName": "s3", - "disableDoubleEncoding": true + "signingRegion": "{Region}" } ] }, @@ -5029,10 +5029,10 @@ "properties": { "authSchemes": [ { + "disableDoubleEncoding": true, "name": "sigv4", - "signingRegion": "{Region}", "signingName": "s3", - "disableDoubleEncoding": true + "signingRegion": "{Region}" } ] }, @@ -5130,10 +5130,10 @@ "properties": { "authSchemes": [ { + "disableDoubleEncoding": true, "name": "sigv4", - "signingRegion": "{Region}", "signingName": "s3", - "disableDoubleEncoding": true + "signingRegion": "{Region}" } ] }, @@ -5198,10 +5198,10 @@ "properties": { "authSchemes": [ { + "disableDoubleEncoding": true, "name": "sigv4", - "signingRegion": "us-east-1", "signingName": "s3", - "disableDoubleEncoding": true + "signingRegion": "us-east-1" } ] }, @@ -5266,10 +5266,10 @@ "properties": { "authSchemes": [ { + "disableDoubleEncoding": true, "name": "sigv4", - "signingRegion": "us-east-1", "signingName": "s3", - "disableDoubleEncoding": true + "signingRegion": "us-east-1" } ] }, @@ -5362,10 +5362,10 @@ "properties": { "authSchemes": [ { + "disableDoubleEncoding": true, "name": "sigv4", - "signingRegion": "{Region}", "signingName": "s3", - "disableDoubleEncoding": true + "signingRegion": "{Region}" } ] }, @@ -5380,10 +5380,10 @@ "properties": { "authSchemes": [ { + "disableDoubleEncoding": true, "name": "sigv4", - "signingRegion": "{Region}", "signingName": "s3", - "disableDoubleEncoding": true + "signingRegion": "{Region}" } ] }, @@ -5464,10 +5464,10 @@ "properties": { "authSchemes": [ { + "disableDoubleEncoding": true, "name": "sigv4", - "signingRegion": "{Region}", "signingName": "s3", - "disableDoubleEncoding": true + "signingRegion": "{Region}" } ] }, @@ -5532,10 +5532,10 @@ "properties": { "authSchemes": [ { + "disableDoubleEncoding": true, "name": "sigv4", - "signingRegion": "us-east-1", "signingName": "s3", - "disableDoubleEncoding": true + "signingRegion": "us-east-1" } ] }, @@ -5600,10 +5600,10 @@ "properties": { "authSchemes": [ { + "disableDoubleEncoding": true, "name": "sigv4", - "signingRegion": "us-east-1", "signingName": "s3", - "disableDoubleEncoding": true + "signingRegion": "us-east-1" } ] }, @@ -5696,10 +5696,10 @@ "properties": { "authSchemes": [ { + "disableDoubleEncoding": true, "name": "sigv4", - "signingRegion": "{Region}", "signingName": "s3", - "disableDoubleEncoding": true + "signingRegion": "{Region}" } ] }, @@ -5714,10 +5714,10 @@ "properties": { "authSchemes": [ { + "disableDoubleEncoding": true, "name": "sigv4", - "signingRegion": "{Region}", "signingName": "s3", - "disableDoubleEncoding": true + "signingRegion": "{Region}" } ] }, @@ -5798,10 +5798,10 @@ "properties": { "authSchemes": [ { + "disableDoubleEncoding": true, "name": "sigv4", - "signingRegion": "{Region}", "signingName": "s3", - "disableDoubleEncoding": true + "signingRegion": "{Region}" } ] }, @@ -5953,10 +5953,10 @@ "properties": { "authSchemes": [ { + "disableDoubleEncoding": true, "name": "sigv4", - "signingRegion": "{Region}", "signingName": "s3", - "disableDoubleEncoding": true + "signingRegion": "{Region}" } ] }, @@ -6493,10 +6493,10 @@ "properties": { "authSchemes": [ { + "disableDoubleEncoding": true, "name": "sigv4", - "signingRegion": "{bucketArn#region}", "signingName": "s3-object-lambda", - "disableDoubleEncoding": true + "signingRegion": "{bucketArn#region}" } ] }, @@ -6521,10 +6521,10 @@ "properties": { "authSchemes": [ { + "disableDoubleEncoding": true, "name": "sigv4", - "signingRegion": "{bucketArn#region}", "signingName": "s3-object-lambda", - "disableDoubleEncoding": true + "signingRegion": "{bucketArn#region}" } ] }, @@ -6539,10 +6539,10 @@ "properties": { "authSchemes": [ { + "disableDoubleEncoding": true, "name": "sigv4", - "signingRegion": "{bucketArn#region}", "signingName": "s3-object-lambda", - "disableDoubleEncoding": true + "signingRegion": "{bucketArn#region}" } ] }, @@ -7133,10 +7133,10 @@ "properties": { "authSchemes": [ { + "disableDoubleEncoding": true, "name": "sigv4", - "signingRegion": "{bucketArn#region}", "signingName": "s3", - "disableDoubleEncoding": true + "signingRegion": "{bucketArn#region}" } ] }, @@ -7170,10 +7170,10 @@ "properties": { "authSchemes": [ { + "disableDoubleEncoding": true, "name": "sigv4", - "signingRegion": "{bucketArn#region}", "signingName": "s3", - "disableDoubleEncoding": true + "signingRegion": "{bucketArn#region}" } ] }, @@ -7207,10 +7207,10 @@ "properties": { "authSchemes": [ { + "disableDoubleEncoding": true, "name": "sigv4", - "signingRegion": "{bucketArn#region}", "signingName": "s3", - "disableDoubleEncoding": true + "signingRegion": "{bucketArn#region}" } ] }, @@ -7261,10 +7261,10 @@ "properties": { "authSchemes": [ { + "disableDoubleEncoding": true, "name": "sigv4", - "signingRegion": "{bucketArn#region}", "signingName": "s3", - "disableDoubleEncoding": true + "signingRegion": "{bucketArn#region}" } ] }, @@ -7298,10 +7298,10 @@ "properties": { "authSchemes": [ { + "disableDoubleEncoding": true, "name": "sigv4", - "signingRegion": "{bucketArn#region}", "signingName": "s3", - "disableDoubleEncoding": true + "signingRegion": "{bucketArn#region}" } ] }, @@ -7555,12 +7555,12 @@ "properties": { "authSchemes": [ { + "disableDoubleEncoding": true, "name": "sigv4a", + "signingName": "s3", "signingRegionSet": [ "*" - ], - "signingName": "s3", - "disableDoubleEncoding": true + ] } ] }, @@ -8002,10 +8002,10 @@ "properties": { "authSchemes": [ { + "disableDoubleEncoding": true, "name": "sigv4", - "signingRegion": "{bucketArn#region}", "signingName": "s3-outposts", - "disableDoubleEncoding": true + "signingRegion": "{bucketArn#region}" } ] }, @@ -8020,10 +8020,10 @@ "properties": { "authSchemes": [ { + "disableDoubleEncoding": true, "name": "sigv4", - "signingRegion": "{bucketArn#region}", "signingName": "s3-outposts", - "disableDoubleEncoding": true + "signingRegion": "{bucketArn#region}" } ] }, @@ -8319,10 +8319,10 @@ "properties": { "authSchemes": [ { + "disableDoubleEncoding": true, "name": "sigv4", - "signingRegion": "us-east-1", "signingName": "s3", - "disableDoubleEncoding": true + "signingRegion": "us-east-1" } ] }, @@ -8378,10 +8378,10 @@ "properties": { "authSchemes": [ { + "disableDoubleEncoding": true, "name": "sigv4", - "signingRegion": "us-east-1", "signingName": "s3", - "disableDoubleEncoding": true + "signingRegion": "us-east-1" } ] }, @@ -8455,10 +8455,10 @@ "properties": { "authSchemes": [ { + "disableDoubleEncoding": true, "name": "sigv4", - "signingRegion": "{Region}", "signingName": "s3", - "disableDoubleEncoding": true + "signingRegion": "{Region}" } ] }, @@ -8530,10 +8530,10 @@ "properties": { "authSchemes": [ { + "disableDoubleEncoding": true, "name": "sigv4", - "signingRegion": "{Region}", "signingName": "s3", - "disableDoubleEncoding": true + "signingRegion": "{Region}" } ] }, @@ -8593,10 +8593,10 @@ "properties": { "authSchemes": [ { + "disableDoubleEncoding": true, "name": "sigv4", - "signingRegion": "us-east-1", "signingName": "s3", - "disableDoubleEncoding": true + "signingRegion": "us-east-1" } ] }, @@ -8656,10 +8656,10 @@ "properties": { "authSchemes": [ { + "disableDoubleEncoding": true, "name": "sigv4", - "signingRegion": "us-east-1", "signingName": "s3", - "disableDoubleEncoding": true + "signingRegion": "us-east-1" } ] }, @@ -8737,10 +8737,10 @@ "properties": { "authSchemes": [ { + "disableDoubleEncoding": true, "name": "sigv4", - "signingRegion": "{Region}", "signingName": "s3", - "disableDoubleEncoding": true + "signingRegion": "{Region}" } ] }, @@ -8816,10 +8816,10 @@ "properties": { "authSchemes": [ { + "disableDoubleEncoding": true, "name": "sigv4", - "signingRegion": "{Region}", "signingName": "s3", - "disableDoubleEncoding": true + "signingRegion": "{Region}" } ] }, @@ -8875,10 +8875,10 @@ "properties": { "authSchemes": [ { + "disableDoubleEncoding": true, "name": "sigv4", - "signingRegion": "us-east-1", "signingName": "s3", - "disableDoubleEncoding": true + "signingRegion": "us-east-1" } ] }, @@ -8934,10 +8934,10 @@ "properties": { "authSchemes": [ { + "disableDoubleEncoding": true, "name": "sigv4", - "signingRegion": "us-east-1", "signingName": "s3", - "disableDoubleEncoding": true + "signingRegion": "us-east-1" } ] }, @@ -9011,10 +9011,10 @@ "properties": { "authSchemes": [ { + "disableDoubleEncoding": true, "name": "sigv4", - "signingRegion": "{Region}", "signingName": "s3", - "disableDoubleEncoding": true + "signingRegion": "{Region}" } ] }, @@ -9086,10 +9086,10 @@ "properties": { "authSchemes": [ { + "disableDoubleEncoding": true, "name": "sigv4", - "signingRegion": "{Region}", "signingName": "s3", - "disableDoubleEncoding": true + "signingRegion": "{Region}" } ] }, @@ -9145,10 +9145,10 @@ "properties": { "authSchemes": [ { + "disableDoubleEncoding": true, "name": "sigv4", - "signingRegion": "us-east-1", "signingName": "s3", - "disableDoubleEncoding": true + "signingRegion": "us-east-1" } ] }, @@ -9204,10 +9204,10 @@ "properties": { "authSchemes": [ { + "disableDoubleEncoding": true, "name": "sigv4", - "signingRegion": "us-east-1", "signingName": "s3", - "disableDoubleEncoding": true + "signingRegion": "us-east-1" } ] }, @@ -9281,10 +9281,10 @@ "properties": { "authSchemes": [ { + "disableDoubleEncoding": true, "name": "sigv4", - "signingRegion": "{Region}", "signingName": "s3", - "disableDoubleEncoding": true + "signingRegion": "{Region}" } ] }, @@ -9356,10 +9356,10 @@ "properties": { "authSchemes": [ { + "disableDoubleEncoding": true, "name": "sigv4", - "signingRegion": "{Region}", "signingName": "s3", - "disableDoubleEncoding": true + "signingRegion": "{Region}" } ] }, @@ -9419,10 +9419,10 @@ "properties": { "authSchemes": [ { + "disableDoubleEncoding": true, "name": "sigv4", - "signingRegion": "us-east-1", "signingName": "s3", - "disableDoubleEncoding": true + "signingRegion": "us-east-1" } ] }, @@ -9482,10 +9482,10 @@ "properties": { "authSchemes": [ { + "disableDoubleEncoding": true, "name": "sigv4", - "signingRegion": "us-east-1", "signingName": "s3", - "disableDoubleEncoding": true + "signingRegion": "us-east-1" } ] }, @@ -9573,10 +9573,10 @@ "properties": { "authSchemes": [ { + "disableDoubleEncoding": true, "name": "sigv4", - "signingRegion": "{Region}", "signingName": "s3", - "disableDoubleEncoding": true + "signingRegion": "{Region}" } ] }, @@ -9591,10 +9591,10 @@ "properties": { "authSchemes": [ { + "disableDoubleEncoding": true, "name": "sigv4", - "signingRegion": "{Region}", "signingName": "s3", - "disableDoubleEncoding": true + "signingRegion": "{Region}" } ] }, @@ -9670,10 +9670,10 @@ "properties": { "authSchemes": [ { + "disableDoubleEncoding": true, "name": "sigv4", - "signingRegion": "{Region}", "signingName": "s3", - "disableDoubleEncoding": true + "signingRegion": "{Region}" } ] }, @@ -9729,10 +9729,10 @@ "properties": { "authSchemes": [ { + "disableDoubleEncoding": true, "name": "sigv4", - "signingRegion": "us-east-1", "signingName": "s3", - "disableDoubleEncoding": true + "signingRegion": "us-east-1" } ] }, @@ -9788,10 +9788,10 @@ "properties": { "authSchemes": [ { + "disableDoubleEncoding": true, "name": "sigv4", - "signingRegion": "us-east-1", "signingName": "s3", - "disableDoubleEncoding": true + "signingRegion": "us-east-1" } ] }, @@ -9875,10 +9875,10 @@ "properties": { "authSchemes": [ { + "disableDoubleEncoding": true, "name": "sigv4", - "signingRegion": "{Region}", "signingName": "s3", - "disableDoubleEncoding": true + "signingRegion": "{Region}" } ] }, @@ -9893,10 +9893,10 @@ "properties": { "authSchemes": [ { + "disableDoubleEncoding": true, "name": "sigv4", - "signingRegion": "{Region}", "signingName": "s3", - "disableDoubleEncoding": true + "signingRegion": "{Region}" } ] }, @@ -9968,10 +9968,10 @@ "properties": { "authSchemes": [ { + "disableDoubleEncoding": true, "name": "sigv4", - "signingRegion": "{Region}", "signingName": "s3", - "disableDoubleEncoding": true + "signingRegion": "{Region}" } ] }, @@ -10160,10 +10160,10 @@ "properties": { "authSchemes": [ { + "disableDoubleEncoding": true, "name": "sigv4", - "signingRegion": "{Region}", "signingName": "s3-object-lambda", - "disableDoubleEncoding": true + "signingRegion": "{Region}" } ] }, @@ -10188,10 +10188,10 @@ "properties": { "authSchemes": [ { + "disableDoubleEncoding": true, "name": "sigv4", - "signingRegion": "{Region}", "signingName": "s3-object-lambda", - "disableDoubleEncoding": true + "signingRegion": "{Region}" } ] }, @@ -10206,10 +10206,10 @@ "properties": { "authSchemes": [ { + "disableDoubleEncoding": true, "name": "sigv4", - "signingRegion": "{Region}", "signingName": "s3-object-lambda", - "disableDoubleEncoding": true + "signingRegion": "{Region}" } ] }, @@ -10383,10 +10383,10 @@ "properties": { "authSchemes": [ { + "disableDoubleEncoding": true, "name": "sigv4", - "signingRegion": "us-east-1", "signingName": "s3", - "disableDoubleEncoding": true + "signingRegion": "us-east-1" } ] }, @@ -10446,10 +10446,10 @@ "properties": { "authSchemes": [ { + "disableDoubleEncoding": true, "name": "sigv4", - "signingRegion": "us-east-1", "signingName": "s3", - "disableDoubleEncoding": true + "signingRegion": "us-east-1" } ] }, @@ -10527,10 +10527,10 @@ "properties": { "authSchemes": [ { + "disableDoubleEncoding": true, "name": "sigv4", - "signingRegion": "{Region}", "signingName": "s3", - "disableDoubleEncoding": true + "signingRegion": "{Region}" } ] }, @@ -10606,10 +10606,10 @@ "properties": { "authSchemes": [ { + "disableDoubleEncoding": true, "name": "sigv4", - "signingRegion": "{Region}", "signingName": "s3", - "disableDoubleEncoding": true + "signingRegion": "{Region}" } ] }, @@ -10665,10 +10665,10 @@ "properties": { "authSchemes": [ { + "disableDoubleEncoding": true, "name": "sigv4", - "signingRegion": "us-east-1", "signingName": "s3", - "disableDoubleEncoding": true + "signingRegion": "us-east-1" } ] }, @@ -10724,10 +10724,10 @@ "properties": { "authSchemes": [ { + "disableDoubleEncoding": true, "name": "sigv4", - "signingRegion": "us-east-1", "signingName": "s3", - "disableDoubleEncoding": true + "signingRegion": "us-east-1" } ] }, @@ -10801,10 +10801,10 @@ "properties": { "authSchemes": [ { + "disableDoubleEncoding": true, "name": "sigv4", - "signingRegion": "{Region}", "signingName": "s3", - "disableDoubleEncoding": true + "signingRegion": "{Region}" } ] }, @@ -10876,10 +10876,10 @@ "properties": { "authSchemes": [ { + "disableDoubleEncoding": true, "name": "sigv4", - "signingRegion": "{Region}", "signingName": "s3", - "disableDoubleEncoding": true + "signingRegion": "{Region}" } ] }, @@ -10939,10 +10939,10 @@ "properties": { "authSchemes": [ { + "disableDoubleEncoding": true, "name": "sigv4", - "signingRegion": "us-east-1", "signingName": "s3", - "disableDoubleEncoding": true + "signingRegion": "us-east-1" } ] }, @@ -11002,10 +11002,10 @@ "properties": { "authSchemes": [ { + "disableDoubleEncoding": true, "name": "sigv4", - "signingRegion": "us-east-1", "signingName": "s3", - "disableDoubleEncoding": true + "signingRegion": "us-east-1" } ] }, @@ -11083,10 +11083,10 @@ "properties": { "authSchemes": [ { + "disableDoubleEncoding": true, "name": "sigv4", - "signingRegion": "{Region}", "signingName": "s3", - "disableDoubleEncoding": true + "signingRegion": "{Region}" } ] }, @@ -11162,10 +11162,10 @@ "properties": { "authSchemes": [ { + "disableDoubleEncoding": true, "name": "sigv4", - "signingRegion": "{Region}", "signingName": "s3", - "disableDoubleEncoding": true + "signingRegion": "{Region}" } ] }, @@ -11221,10 +11221,10 @@ "properties": { "authSchemes": [ { + "disableDoubleEncoding": true, "name": "sigv4", - "signingRegion": "us-east-1", "signingName": "s3", - "disableDoubleEncoding": true + "signingRegion": "us-east-1" } ] }, @@ -11280,10 +11280,10 @@ "properties": { "authSchemes": [ { + "disableDoubleEncoding": true, "name": "sigv4", - "signingRegion": "us-east-1", "signingName": "s3", - "disableDoubleEncoding": true + "signingRegion": "us-east-1" } ] }, @@ -11357,10 +11357,10 @@ "properties": { "authSchemes": [ { + "disableDoubleEncoding": true, "name": "sigv4", - "signingRegion": "{Region}", "signingName": "s3", - "disableDoubleEncoding": true + "signingRegion": "{Region}" } ] }, @@ -11432,10 +11432,10 @@ "properties": { "authSchemes": [ { + "disableDoubleEncoding": true, "name": "sigv4", - "signingRegion": "{Region}", "signingName": "s3", - "disableDoubleEncoding": true + "signingRegion": "{Region}" } ] }, @@ -11495,10 +11495,10 @@ "properties": { "authSchemes": [ { + "disableDoubleEncoding": true, "name": "sigv4", - "signingRegion": "us-east-1", "signingName": "s3", - "disableDoubleEncoding": true + "signingRegion": "us-east-1" } ] }, @@ -11558,10 +11558,10 @@ "properties": { "authSchemes": [ { + "disableDoubleEncoding": true, "name": "sigv4", - "signingRegion": "us-east-1", "signingName": "s3", - "disableDoubleEncoding": true + "signingRegion": "us-east-1" } ] }, @@ -11639,10 +11639,10 @@ "properties": { "authSchemes": [ { + "disableDoubleEncoding": true, "name": "sigv4", - "signingRegion": "{Region}", "signingName": "s3", - "disableDoubleEncoding": true + "signingRegion": "{Region}" } ] }, @@ -11718,10 +11718,10 @@ "properties": { "authSchemes": [ { + "disableDoubleEncoding": true, "name": "sigv4", - "signingRegion": "{Region}", "signingName": "s3", - "disableDoubleEncoding": true + "signingRegion": "{Region}" } ] }, @@ -11777,10 +11777,10 @@ "properties": { "authSchemes": [ { + "disableDoubleEncoding": true, "name": "sigv4", - "signingRegion": "us-east-1", "signingName": "s3", - "disableDoubleEncoding": true + "signingRegion": "us-east-1" } ] }, @@ -11836,10 +11836,10 @@ "properties": { "authSchemes": [ { + "disableDoubleEncoding": true, "name": "sigv4", - "signingRegion": "us-east-1", "signingName": "s3", - "disableDoubleEncoding": true + "signingRegion": "us-east-1" } ] }, @@ -11913,10 +11913,10 @@ "properties": { "authSchemes": [ { + "disableDoubleEncoding": true, "name": "sigv4", - "signingRegion": "{Region}", "signingName": "s3", - "disableDoubleEncoding": true + "signingRegion": "{Region}" } ] }, @@ -11988,10 +11988,10 @@ "properties": { "authSchemes": [ { + "disableDoubleEncoding": true, "name": "sigv4", - "signingRegion": "{Region}", "signingName": "s3", - "disableDoubleEncoding": true + "signingRegion": "{Region}" } ] }, @@ -12051,10 +12051,10 @@ "properties": { "authSchemes": [ { + "disableDoubleEncoding": true, "name": "sigv4", - "signingRegion": "us-east-1", "signingName": "s3", - "disableDoubleEncoding": true + "signingRegion": "us-east-1" } ] }, @@ -12114,10 +12114,10 @@ "properties": { "authSchemes": [ { + "disableDoubleEncoding": true, "name": "sigv4", - "signingRegion": "us-east-1", "signingName": "s3", - "disableDoubleEncoding": true + "signingRegion": "us-east-1" } ] }, @@ -12205,10 +12205,10 @@ "properties": { "authSchemes": [ { + "disableDoubleEncoding": true, "name": "sigv4", - "signingRegion": "{Region}", "signingName": "s3", - "disableDoubleEncoding": true + "signingRegion": "{Region}" } ] }, @@ -12223,10 +12223,10 @@ "properties": { "authSchemes": [ { + "disableDoubleEncoding": true, "name": "sigv4", - "signingRegion": "{Region}", "signingName": "s3", - "disableDoubleEncoding": true + "signingRegion": "{Region}" } ] }, @@ -12302,10 +12302,10 @@ "properties": { "authSchemes": [ { + "disableDoubleEncoding": true, "name": "sigv4", - "signingRegion": "{Region}", "signingName": "s3", - "disableDoubleEncoding": true + "signingRegion": "{Region}" } ] }, @@ -12361,10 +12361,10 @@ "properties": { "authSchemes": [ { + "disableDoubleEncoding": true, "name": "sigv4", - "signingRegion": "us-east-1", "signingName": "s3", - "disableDoubleEncoding": true + "signingRegion": "us-east-1" } ] }, @@ -12420,10 +12420,10 @@ "properties": { "authSchemes": [ { + "disableDoubleEncoding": true, "name": "sigv4", - "signingRegion": "us-east-1", "signingName": "s3", - "disableDoubleEncoding": true + "signingRegion": "us-east-1" } ] }, @@ -12507,10 +12507,10 @@ "properties": { "authSchemes": [ { + "disableDoubleEncoding": true, "name": "sigv4", - "signingRegion": "{Region}", "signingName": "s3", - "disableDoubleEncoding": true + "signingRegion": "{Region}" } ] }, @@ -12525,10 +12525,10 @@ "properties": { "authSchemes": [ { + "disableDoubleEncoding": true, "name": "sigv4", - "signingRegion": "{Region}", "signingName": "s3", - "disableDoubleEncoding": true + "signingRegion": "{Region}" } ] }, @@ -12600,10 +12600,10 @@ "properties": { "authSchemes": [ { + "disableDoubleEncoding": true, "name": "sigv4", - "signingRegion": "{Region}", "signingName": "s3", - "disableDoubleEncoding": true + "signingRegion": "{Region}" } ] }, diff --git a/aws/sdk/aws-models/s3control.json b/aws/sdk/aws-models/s3control.json index 005646332f..c47218c753 100644 --- a/aws/sdk/aws-models/s3control.json +++ b/aws/sdk/aws-models/s3control.json @@ -69,6 +69,9 @@ { "target": "com.amazonaws.s3control#DeleteBucketPolicy" }, + { + "target": "com.amazonaws.s3control#DeleteBucketReplication" + }, { "target": "com.amazonaws.s3control#DeleteBucketTagging" }, @@ -123,6 +126,9 @@ { "target": "com.amazonaws.s3control#GetBucketPolicy" }, + { + "target": "com.amazonaws.s3control#GetBucketReplication" + }, { "target": "com.amazonaws.s3control#GetBucketTagging" }, @@ -186,6 +192,9 @@ { "target": "com.amazonaws.s3control#PutBucketPolicy" }, + { + "target": "com.amazonaws.s3control#PutBucketReplication" + }, { "target": "com.amazonaws.s3control#PutBucketTagging" }, @@ -546,9 +555,9 @@ "properties": { "authSchemes": [ { + "disableDoubleEncoding": true, "name": "sigv4", "signingName": "s3-outposts", - "disableDoubleEncoding": true, "signingRegion": "{Region}" } ] @@ -574,9 +583,9 @@ "properties": { "authSchemes": [ { + "disableDoubleEncoding": true, "name": "sigv4", "signingName": "s3-outposts", - "disableDoubleEncoding": true, "signingRegion": "{Region}" } ] @@ -592,9 +601,9 @@ "properties": { "authSchemes": [ { + "disableDoubleEncoding": true, "name": "sigv4", "signingName": "s3-outposts", - "disableDoubleEncoding": true, "signingRegion": "{Region}" } ] @@ -1063,9 +1072,9 @@ "properties": { "authSchemes": [ { + "disableDoubleEncoding": true, "name": "sigv4", "signingName": "s3-outposts", - "disableDoubleEncoding": true, "signingRegion": "{accessPointArn#region}" } ] @@ -1106,9 +1115,9 @@ "properties": { "authSchemes": [ { + "disableDoubleEncoding": true, "name": "sigv4", "signingName": "s3-outposts", - "disableDoubleEncoding": true, "signingRegion": "{accessPointArn#region}" } ] @@ -1131,9 +1140,9 @@ "properties": { "authSchemes": [ { + "disableDoubleEncoding": true, "name": "sigv4", "signingName": "s3-outposts", - "disableDoubleEncoding": true, "signingRegion": "{accessPointArn#region}" } ] @@ -1695,9 +1704,9 @@ "properties": { "authSchemes": [ { + "disableDoubleEncoding": true, "name": "sigv4", "signingName": "s3-outposts", - "disableDoubleEncoding": true, "signingRegion": "{bucketArn#region}" } ] @@ -1738,9 +1747,9 @@ "properties": { "authSchemes": [ { + "disableDoubleEncoding": true, "name": "sigv4", "signingName": "s3-outposts", - "disableDoubleEncoding": true, "signingRegion": "{bucketArn#region}" } ] @@ -1763,9 +1772,9 @@ "properties": { "authSchemes": [ { + "disableDoubleEncoding": true, "name": "sigv4", "signingName": "s3-outposts", - "disableDoubleEncoding": true, "signingRegion": "{bucketArn#region}" } ] @@ -2117,9 +2126,9 @@ "properties": { "authSchemes": [ { + "disableDoubleEncoding": true, "name": "sigv4", "signingName": "s3", - "disableDoubleEncoding": true, "signingRegion": "{Region}" } ] @@ -2135,9 +2144,9 @@ "properties": { "authSchemes": [ { + "disableDoubleEncoding": true, "name": "sigv4", "signingName": "s3", - "disableDoubleEncoding": true, "signingRegion": "{Region}" } ] @@ -2205,9 +2214,9 @@ "properties": { "authSchemes": [ { + "disableDoubleEncoding": true, "name": "sigv4", "signingName": "s3", - "disableDoubleEncoding": true, "signingRegion": "{Region}" } ] @@ -2242,9 +2251,9 @@ "properties": { "authSchemes": [ { + "disableDoubleEncoding": true, "name": "sigv4", "signingName": "s3", - "disableDoubleEncoding": true, "signingRegion": "{Region}" } ] @@ -2304,9 +2313,9 @@ "properties": { "authSchemes": [ { + "disableDoubleEncoding": true, "name": "sigv4", "signingName": "s3", - "disableDoubleEncoding": true, "signingRegion": "{Region}" } ] @@ -2341,9 +2350,9 @@ "properties": { "authSchemes": [ { + "disableDoubleEncoding": true, "name": "sigv4", "signingName": "s3", - "disableDoubleEncoding": true, "signingRegion": "{Region}" } ] @@ -2403,9 +2412,9 @@ "properties": { "authSchemes": [ { + "disableDoubleEncoding": true, "name": "sigv4", "signingName": "s3", - "disableDoubleEncoding": true, "signingRegion": "{Region}" } ] @@ -2440,9 +2449,9 @@ "properties": { "authSchemes": [ { + "disableDoubleEncoding": true, "name": "sigv4", "signingName": "s3", - "disableDoubleEncoding": true, "signingRegion": "{Region}" } ] @@ -2502,9 +2511,9 @@ "properties": { "authSchemes": [ { + "disableDoubleEncoding": true, "name": "sigv4", "signingName": "s3", - "disableDoubleEncoding": true, "signingRegion": "{Region}" } ] @@ -2539,9 +2548,9 @@ "properties": { "authSchemes": [ { + "disableDoubleEncoding": true, "name": "sigv4", "signingName": "s3", - "disableDoubleEncoding": true, "signingRegion": "{Region}" } ] @@ -6072,6 +6081,21 @@ "smithy.api#documentation": "

The container for abort incomplete multipart upload

" } }, + "com.amazonaws.s3control#AccessControlTranslation": { + "type": "structure", + "members": { + "Owner": { + "target": "com.amazonaws.s3control#OwnerOverride", + "traits": { + "smithy.api#documentation": "

Specifies the replica ownership.

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

A container for information about access control for replicas.

\n \n

This is not supported by Amazon S3 on Outposts buckets.

\n
" + } + }, "com.amazonaws.s3control#AccessPoint": { "type": "structure", "members": { @@ -6139,7 +6163,7 @@ "traits": { "smithy.api#length": { "min": 3, - "max": 63 + "max": 255 } } }, @@ -6204,7 +6228,7 @@ } }, "traits": { - "smithy.api#documentation": "

The container element for Amazon S3 Storage Lens activity metrics. Activity metrics show details about \n how your storage is requested, such as requests (for example, All requests, Get requests, \n Put requests), bytes uploaded or downloaded, and errors.

\n

For more information about S3 Storage Lens, see Assessing your storage activity and usage with S3 Storage Lens in the Amazon S3 User Guide. For a complete list of S3 Storage Lens metrics, see S3 Storage Lens metrics glossary in the Amazon S3 User Guide.

" + "smithy.api#documentation": "

The container element for Amazon S3 Storage Lens activity metrics. Activity metrics show details\n about how your storage is requested, such as requests (for example, All requests, Get\n requests, Put requests), bytes uploaded or downloaded, and errors.

\n

For more information about S3 Storage Lens, see Assessing your storage activity and usage with S3 Storage Lens in the Amazon S3 User Guide. For a complete list of S3 Storage Lens metrics, see S3 Storage Lens metrics glossary in the Amazon S3 User Guide.

" } }, "com.amazonaws.s3control#AdvancedCostOptimizationMetrics": { @@ -6505,6 +6529,9 @@ } } }, + "com.amazonaws.s3control#BucketIdentifierString": { + "type": "string" + }, "com.amazonaws.s3control#BucketLevel": { "type": "structure", "members": { @@ -6770,6 +6797,9 @@ "smithy.api#required": {} } } + }, + "traits": { + "smithy.api#input": {} } }, "com.amazonaws.s3control#CreateAccessPointForObjectLambdaResult": { @@ -6809,7 +6839,7 @@ "Bucket": { "target": "com.amazonaws.s3control#BucketName", "traits": { - "smithy.api#documentation": "

The name of the bucket that you want to associate this access point with.

\n

For using this parameter with Amazon S3 on Outposts with the REST API, you must specify the name and the x-amz-outpost-id as well.

\n

For using this parameter with S3 on Outposts with the Amazon Web Services SDK and CLI, you must specify the ARN of the bucket accessed in the format arn:aws:s3-outposts:::outpost//bucket/. For example, to access the bucket reports through outpost my-outpost owned by account 123456789012 in Region us-west-2, use the URL encoding of arn:aws:s3-outposts:us-west-2:123456789012:outpost/my-outpost/bucket/reports. The value must be URL encoded.

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

The name of the bucket that you want to associate this access point with.

\n

For using this parameter with Amazon S3 on Outposts with the REST API, you must specify the name and the x-amz-outpost-id as well.

\n

For using this parameter with S3 on Outposts with the Amazon Web Services SDK and CLI, you must specify the ARN of the bucket accessed in the format arn:aws:s3-outposts:::outpost//bucket/. For example, to access the bucket reports through Outpost my-outpost owned by account 123456789012 in Region us-west-2, use the URL encoding of arn:aws:s3-outposts:us-west-2:123456789012:outpost/my-outpost/bucket/reports. The value must be URL encoded.

", "smithy.api#required": {}, "smithy.rules#contextParam": { "name": "Bucket" @@ -6834,6 +6864,9 @@ "smithy.api#documentation": "

The Amazon Web Services account ID associated with the S3 bucket associated with this access point.

" } } + }, + "traits": { + "smithy.api#input": {} } }, "com.amazonaws.s3control#CreateAccessPointResult": { @@ -6975,6 +7008,9 @@ } } } + }, + "traits": { + "smithy.api#input": {} } }, "com.amazonaws.s3control#CreateBucketResult": { @@ -6990,7 +7026,7 @@ "BucketArn": { "target": "com.amazonaws.s3control#S3RegionalBucketArn", "traits": { - "smithy.api#documentation": "

The Amazon Resource Name (ARN) of the bucket.

\n

For using this parameter with Amazon S3 on Outposts with the REST API, you must specify the name and the x-amz-outpost-id as well.

\n

For using this parameter with S3 on Outposts with the Amazon Web Services SDK and CLI, you must specify the ARN of the bucket accessed in the format arn:aws:s3-outposts:::outpost//bucket/. For example, to access the bucket reports through outpost my-outpost owned by account 123456789012 in Region us-west-2, use the URL encoding of arn:aws:s3-outposts:us-west-2:123456789012:outpost/my-outpost/bucket/reports. The value must be URL encoded.

" + "smithy.api#documentation": "

The Amazon Resource Name (ARN) of the bucket.

\n

For using this parameter with Amazon S3 on Outposts with the REST API, you must specify the name and the x-amz-outpost-id as well.

\n

For using this parameter with S3 on Outposts with the Amazon Web Services SDK and CLI, you must specify the ARN of the bucket accessed in the format arn:aws:s3-outposts:::outpost//bucket/. For example, to access the bucket reports through Outpost my-outpost owned by account 123456789012 in Region us-west-2, use the URL encoding of arn:aws:s3-outposts:us-west-2:123456789012:outpost/my-outpost/bucket/reports. The value must be URL encoded.

" } } } @@ -7117,6 +7153,9 @@ "smithy.api#documentation": "

The attribute container for the ManifestGenerator details. Jobs must be created with\n either a manifest file or a ManifestGenerator, but not both.

" } } + }, + "traits": { + "smithy.api#input": {} } }, "com.amazonaws.s3control#CreateJobResult": { @@ -7211,6 +7250,9 @@ "smithy.api#required": {} } } + }, + "traits": { + "smithy.api#input": {} } }, "com.amazonaws.s3control#CreateMultiRegionAccessPointResult": { @@ -7318,6 +7360,9 @@ "smithy.api#required": {} } } + }, + "traits": { + "smithy.api#input": {} } }, "com.amazonaws.s3control#DeleteAccessPointPolicy": { @@ -7393,6 +7438,9 @@ "smithy.api#required": {} } } + }, + "traits": { + "smithy.api#input": {} } }, "com.amazonaws.s3control#DeleteAccessPointPolicyRequest": { @@ -7413,7 +7461,7 @@ "Name": { "target": "com.amazonaws.s3control#AccessPointName", "traits": { - "smithy.api#documentation": "

The name of the access point whose policy you want to delete.

\n

For using this parameter with Amazon S3 on Outposts with the REST API, you must specify the name and the x-amz-outpost-id as well.

\n

For using this parameter with S3 on Outposts with the Amazon Web Services SDK and CLI, you must specify the ARN of the access point accessed in the format arn:aws:s3-outposts:::outpost//accesspoint/. For example, to access the access point reports-ap through outpost my-outpost owned by account 123456789012 in Region us-west-2, use the URL encoding of arn:aws:s3-outposts:us-west-2:123456789012:outpost/my-outpost/accesspoint/reports-ap. The value must be URL encoded.

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

The name of the access point whose policy you want to delete.

\n

For using this parameter with Amazon S3 on Outposts with the REST API, you must specify the name and the x-amz-outpost-id as well.

\n

For using this parameter with S3 on Outposts with the Amazon Web Services SDK and CLI, you must specify the ARN of the access point accessed in the format arn:aws:s3-outposts:::outpost//accesspoint/. For example, to access the access point reports-ap through Outpost my-outpost owned by account 123456789012 in Region us-west-2, use the URL encoding of arn:aws:s3-outposts:us-west-2:123456789012:outpost/my-outpost/accesspoint/reports-ap. The value must be URL encoded.

", "smithy.api#httpLabel": {}, "smithy.api#required": {}, "smithy.rules#contextParam": { @@ -7421,6 +7469,9 @@ } } } + }, + "traits": { + "smithy.api#input": {} } }, "com.amazonaws.s3control#DeleteAccessPointRequest": { @@ -7441,7 +7492,7 @@ "Name": { "target": "com.amazonaws.s3control#AccessPointName", "traits": { - "smithy.api#documentation": "

The name of the access point you want to delete.

\n

For using this parameter with Amazon S3 on Outposts with the REST API, you must specify the name and the x-amz-outpost-id as well.

\n

For using this parameter with S3 on Outposts with the Amazon Web Services SDK and CLI, you must specify the ARN of the access point accessed in the format arn:aws:s3-outposts:::outpost//accesspoint/. For example, to access the access point reports-ap through outpost my-outpost owned by account 123456789012 in Region us-west-2, use the URL encoding of arn:aws:s3-outposts:us-west-2:123456789012:outpost/my-outpost/accesspoint/reports-ap. The value must be URL encoded.

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

The name of the access point you want to delete.

\n

For using this parameter with Amazon S3 on Outposts with the REST API, you must specify the name and the x-amz-outpost-id as well.

\n

For using this parameter with S3 on Outposts with the Amazon Web Services SDK and CLI, you must specify the ARN of the access point accessed in the format arn:aws:s3-outposts:::outpost//accesspoint/. For example, to access the access point reports-ap through Outpost my-outpost owned by account 123456789012 in Region us-west-2, use the URL encoding of arn:aws:s3-outposts:us-west-2:123456789012:outpost/my-outpost/accesspoint/reports-ap. The value must be URL encoded.

", "smithy.api#httpLabel": {}, "smithy.api#required": {}, "smithy.rules#contextParam": { @@ -7449,6 +7500,9 @@ } } } + }, + "traits": { + "smithy.api#input": {} } }, "com.amazonaws.s3control#DeleteBucket": { @@ -7519,7 +7573,7 @@ "Bucket": { "target": "com.amazonaws.s3control#BucketName", "traits": { - "smithy.api#documentation": "

Specifies the bucket.

\n

For using this parameter with Amazon S3 on Outposts with the REST API, you must specify the name and the x-amz-outpost-id as well.

\n

For using this parameter with S3 on Outposts with the Amazon Web Services SDK and CLI, you must specify the ARN of the bucket accessed in the format arn:aws:s3-outposts:::outpost//bucket/. For example, to access the bucket reports through outpost my-outpost owned by account 123456789012 in Region us-west-2, use the URL encoding of arn:aws:s3-outposts:us-west-2:123456789012:outpost/my-outpost/bucket/reports. The value must be URL encoded.

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

Specifies the bucket.

\n

For using this parameter with Amazon S3 on Outposts with the REST API, you must specify the name and the x-amz-outpost-id as well.

\n

For using this parameter with S3 on Outposts with the Amazon Web Services SDK and CLI, you must specify the ARN of the bucket accessed in the format arn:aws:s3-outposts:::outpost//bucket/. For example, to access the bucket reports through Outpost my-outpost owned by account 123456789012 in Region us-west-2, use the URL encoding of arn:aws:s3-outposts:us-west-2:123456789012:outpost/my-outpost/bucket/reports. The value must be URL encoded.

", "smithy.api#httpLabel": {}, "smithy.api#required": {}, "smithy.rules#contextParam": { @@ -7527,6 +7581,9 @@ } } } + }, + "traits": { + "smithy.api#input": {} } }, "com.amazonaws.s3control#DeleteBucketPolicy": { @@ -7572,7 +7629,63 @@ "Bucket": { "target": "com.amazonaws.s3control#BucketName", "traits": { - "smithy.api#documentation": "

Specifies the bucket.

\n

For using this parameter with Amazon S3 on Outposts with the REST API, you must specify the name and the x-amz-outpost-id as well.

\n

For using this parameter with S3 on Outposts with the Amazon Web Services SDK and CLI, you must specify the ARN of the bucket accessed in the format arn:aws:s3-outposts:::outpost//bucket/. For example, to access the bucket reports through outpost my-outpost owned by account 123456789012 in Region us-west-2, use the URL encoding of arn:aws:s3-outposts:us-west-2:123456789012:outpost/my-outpost/bucket/reports. The value must be URL encoded.

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

Specifies the bucket.

\n

For using this parameter with Amazon S3 on Outposts with the REST API, you must specify the name and the x-amz-outpost-id as well.

\n

For using this parameter with S3 on Outposts with the Amazon Web Services SDK and CLI, you must specify the ARN of the bucket accessed in the format arn:aws:s3-outposts:::outpost//bucket/. For example, to access the bucket reports through Outpost my-outpost owned by account 123456789012 in Region us-west-2, use the URL encoding of arn:aws:s3-outposts:us-west-2:123456789012:outpost/my-outpost/bucket/reports. The value must be URL encoded.

", + "smithy.api#httpLabel": {}, + "smithy.api#required": {}, + "smithy.rules#contextParam": { + "name": "Bucket" + } + } + } + }, + "traits": { + "smithy.api#input": {} + } + }, + "com.amazonaws.s3control#DeleteBucketReplication": { + "type": "operation", + "input": { + "target": "com.amazonaws.s3control#DeleteBucketReplicationRequest" + }, + "output": { + "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#endpoint": { + "hostPrefix": "{AccountId}." + }, + "smithy.api#http": { + "method": "DELETE", + "uri": "/v20180820/bucket/{Bucket}/replication", + "code": 200 + }, + "smithy.rules#staticContextParams": { + "RequiresAccountId": { + "value": true + } + } + } + }, + "com.amazonaws.s3control#DeleteBucketReplicationRequest": { + "type": "structure", + "members": { + "AccountId": { + "target": "com.amazonaws.s3control#AccountId", + "traits": { + "smithy.api#documentation": "

The Amazon Web Services account ID of the Outposts bucket to delete the replication configuration\n for.

", + "smithy.api#hostLabel": {}, + "smithy.api#httpHeader": "x-amz-account-id", + "smithy.api#required": {}, + "smithy.rules#contextParam": { + "name": "AccountId" + } + } + }, + "Bucket": { + "target": "com.amazonaws.s3control#BucketName", + "traits": { + "smithy.api#documentation": "

Specifies the S3 on Outposts bucket to delete the replication configuration for.

\n

For using this parameter with Amazon S3 on Outposts with the REST API, you must specify the name and the x-amz-outpost-id as well.

\n

For using this parameter with S3 on Outposts with the Amazon Web Services SDK and CLI, you must specify the ARN of the bucket accessed in the format arn:aws:s3-outposts:::outpost//bucket/. For example, to access the bucket reports through Outpost my-outpost owned by account 123456789012 in Region us-west-2, use the URL encoding of arn:aws:s3-outposts:us-west-2:123456789012:outpost/my-outpost/bucket/reports. The value must be URL encoded.

", "smithy.api#httpLabel": {}, "smithy.api#required": {}, "smithy.rules#contextParam": { @@ -7580,6 +7693,9 @@ } } } + }, + "traits": { + "smithy.api#input": {} } }, "com.amazonaws.s3control#DeleteBucketRequest": { @@ -7600,7 +7716,7 @@ "Bucket": { "target": "com.amazonaws.s3control#BucketName", "traits": { - "smithy.api#documentation": "

Specifies the bucket being deleted.

\n

For using this parameter with Amazon S3 on Outposts with the REST API, you must specify the name and the x-amz-outpost-id as well.

\n

For using this parameter with S3 on Outposts with the Amazon Web Services SDK and CLI, you must specify the ARN of the bucket accessed in the format arn:aws:s3-outposts:::outpost//bucket/. For example, to access the bucket reports through outpost my-outpost owned by account 123456789012 in Region us-west-2, use the URL encoding of arn:aws:s3-outposts:us-west-2:123456789012:outpost/my-outpost/bucket/reports. The value must be URL encoded.

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

Specifies the bucket being deleted.

\n

For using this parameter with Amazon S3 on Outposts with the REST API, you must specify the name and the x-amz-outpost-id as well.

\n

For using this parameter with S3 on Outposts with the Amazon Web Services SDK and CLI, you must specify the ARN of the bucket accessed in the format arn:aws:s3-outposts:::outpost//bucket/. For example, to access the bucket reports through Outpost my-outpost owned by account 123456789012 in Region us-west-2, use the URL encoding of arn:aws:s3-outposts:us-west-2:123456789012:outpost/my-outpost/bucket/reports. The value must be URL encoded.

", "smithy.api#httpLabel": {}, "smithy.api#required": {}, "smithy.rules#contextParam": { @@ -7608,6 +7724,9 @@ } } } + }, + "traits": { + "smithy.api#input": {} } }, "com.amazonaws.s3control#DeleteBucketTagging": { @@ -7653,7 +7772,7 @@ "Bucket": { "target": "com.amazonaws.s3control#BucketName", "traits": { - "smithy.api#documentation": "

The bucket ARN that has the tag set to be removed.

\n

For using this parameter with Amazon S3 on Outposts with the REST API, you must specify the name and the x-amz-outpost-id as well.

\n

For using this parameter with S3 on Outposts with the Amazon Web Services SDK and CLI, you must specify the ARN of the bucket accessed in the format arn:aws:s3-outposts:::outpost//bucket/. For example, to access the bucket reports through outpost my-outpost owned by account 123456789012 in Region us-west-2, use the URL encoding of arn:aws:s3-outposts:us-west-2:123456789012:outpost/my-outpost/bucket/reports. The value must be URL encoded.

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

The bucket ARN that has the tag set to be removed.

\n

For using this parameter with Amazon S3 on Outposts with the REST API, you must specify the name and the x-amz-outpost-id as well.

\n

For using this parameter with S3 on Outposts with the Amazon Web Services SDK and CLI, you must specify the ARN of the bucket accessed in the format arn:aws:s3-outposts:::outpost//bucket/. For example, to access the bucket reports through Outpost my-outpost owned by account 123456789012 in Region us-west-2, use the URL encoding of arn:aws:s3-outposts:us-west-2:123456789012:outpost/my-outpost/bucket/reports. The value must be URL encoded.

", "smithy.api#httpLabel": {}, "smithy.api#required": {}, "smithy.rules#contextParam": { @@ -7661,6 +7780,9 @@ } } } + }, + "traits": { + "smithy.api#input": {} } }, "com.amazonaws.s3control#DeleteJobTagging": { @@ -7683,7 +7805,7 @@ } ], "traits": { - "smithy.api#documentation": "

Removes the entire tag set from the specified S3 Batch Operations job. To use this operation,\n you must have permission to perform the s3:DeleteJobTagging action. For more\n information, see Controlling\n access and labeling jobs using tags in the\n Amazon S3 User Guide.

\n

\n

Related actions include:

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

Removes the entire tag set from the specified S3 Batch Operations job. To use\n the\n DeleteJobTagging operation, you must have permission to\n perform the s3:DeleteJobTagging action. For more information, see Controlling\n access and labeling jobs using tags in the\n Amazon S3 User Guide.

\n

\n

Related actions include:

\n ", "smithy.api#endpoint": { "hostPrefix": "{AccountId}." }, @@ -7722,12 +7844,47 @@ "smithy.api#required": {} } } + }, + "traits": { + "smithy.api#input": {} } }, "com.amazonaws.s3control#DeleteJobTaggingResult": { "type": "structure", "members": {} }, + "com.amazonaws.s3control#DeleteMarkerReplication": { + "type": "structure", + "members": { + "Status": { + "target": "com.amazonaws.s3control#DeleteMarkerReplicationStatus", + "traits": { + "smithy.api#documentation": "

Indicates whether to replicate delete markers.

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

Specifies whether S3 on Outposts replicates delete markers. If you specify a\n Filter element in your replication configuration, you must also include a\n DeleteMarkerReplication element. If your Filter includes a\n Tag element, the DeleteMarkerReplication element's\n Status child element must be set to Disabled, because\n S3 on Outposts does not support replicating delete markers for tag-based rules.

\n

For more information about delete marker replication, see How delete operations affect replication in the Amazon S3 User Guide.

" + } + }, + "com.amazonaws.s3control#DeleteMarkerReplicationStatus": { + "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.s3control#DeleteMultiRegionAccessPoint": { "type": "operation", "input": { @@ -7799,6 +7956,9 @@ "smithy.api#required": {} } } + }, + "traits": { + "smithy.api#input": {} } }, "com.amazonaws.s3control#DeleteMultiRegionAccessPointResult": { @@ -7852,6 +8012,9 @@ } } } + }, + "traits": { + "smithy.api#input": {} } }, "com.amazonaws.s3control#DeleteStorageLensConfiguration": { @@ -7902,6 +8065,9 @@ } } } + }, + "traits": { + "smithy.api#input": {} } }, "com.amazonaws.s3control#DeleteStorageLensConfigurationTagging": { @@ -7952,6 +8118,9 @@ } } } + }, + "traits": { + "smithy.api#input": {} } }, "com.amazonaws.s3control#DeleteStorageLensConfigurationTaggingResult": { @@ -8020,6 +8189,9 @@ "smithy.api#required": {} } } + }, + "traits": { + "smithy.api#input": {} } }, "com.amazonaws.s3control#DescribeJobResult": { @@ -8082,6 +8254,9 @@ "smithy.api#required": {} } } + }, + "traits": { + "smithy.api#input": {} } }, "com.amazonaws.s3control#DescribeMultiRegionAccessPointOperationResult": { @@ -8095,6 +8270,57 @@ } } }, + "com.amazonaws.s3control#Destination": { + "type": "structure", + "members": { + "Account": { + "target": "com.amazonaws.s3control#AccountId", + "traits": { + "smithy.api#documentation": "

The destination bucket owner's account ID.

" + } + }, + "Bucket": { + "target": "com.amazonaws.s3control#BucketIdentifierString", + "traits": { + "smithy.api#documentation": "

The Amazon Resource Name (ARN) of the access point for the destination bucket where you want\n S3 on Outposts to store the replication results.

", + "smithy.api#required": {} + } + }, + "ReplicationTime": { + "target": "com.amazonaws.s3control#ReplicationTime", + "traits": { + "smithy.api#documentation": "

A container that specifies S3 Replication Time Control (S3 RTC) settings, including whether S3 RTC is enabled\n and the time when all objects and operations on objects must be replicated. Must be\n specified together with a Metrics block.

\n \n

This is not supported by Amazon S3 on Outposts buckets.

\n
" + } + }, + "AccessControlTranslation": { + "target": "com.amazonaws.s3control#AccessControlTranslation", + "traits": { + "smithy.api#documentation": "

Specify this property only in a cross-account scenario (where the source and destination\n bucket owners are not the same), and you want to change replica ownership to the\n Amazon Web Services account that owns the destination bucket. If this property is not specified in the\n replication configuration, the replicas are owned by same Amazon Web Services account that owns the\n source object.

\n \n

This is not supported by Amazon S3 on Outposts buckets.

\n
" + } + }, + "EncryptionConfiguration": { + "target": "com.amazonaws.s3control#EncryptionConfiguration", + "traits": { + "smithy.api#documentation": "

A container that provides information about encryption. If\n SourceSelectionCriteria is specified, you must specify this element.

\n \n

This is not supported by Amazon S3 on Outposts buckets.

\n
" + } + }, + "Metrics": { + "target": "com.amazonaws.s3control#Metrics", + "traits": { + "smithy.api#documentation": "

A container that specifies replication metrics-related settings.

" + } + }, + "StorageClass": { + "target": "com.amazonaws.s3control#ReplicationStorageClass", + "traits": { + "smithy.api#documentation": "

The storage class to use when replicating objects. All objects stored on S3 on Outposts\n are stored in the OUTPOSTS storage class. S3 on Outposts uses the\n OUTPOSTS storage class to create the object replicas.

\n \n

Values other than OUTPOSTS are not supported by Amazon S3 on Outposts.

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

Specifies information about the replication destination bucket and its settings for an\n S3 on Outposts replication configuration.

" + } + }, "com.amazonaws.s3control#DetailedStatusCodesMetrics": { "type": "structure", "members": { @@ -8110,6 +8336,20 @@ "smithy.api#documentation": "

The container element for Amazon S3 Storage Lens detailed status code metrics. Detailed status\n code metrics generate metrics for HTTP status codes, such as 200 OK, 403\n Forbidden, 503 Service Unavailable and others.

\n

For more information about S3 Storage Lens, see Assessing your storage activity and usage with S3 Storage Lens in the Amazon S3 User Guide. For a complete list of S3 Storage Lens metrics, see S3 Storage Lens metrics glossary in the Amazon S3 User Guide.

" } }, + "com.amazonaws.s3control#EncryptionConfiguration": { + "type": "structure", + "members": { + "ReplicaKmsKeyID": { + "target": "com.amazonaws.s3control#ReplicaKmsKeyID", + "traits": { + "smithy.api#documentation": "

Specifies the ID of the customer managed KMS key that's stored in Key Management Service (KMS)\n for the destination bucket. This ID is either the Amazon Resource Name (ARN) for the\n KMS key or the alias ARN for the KMS key. Amazon S3 uses this KMS key to encrypt\n replica objects. Amazon S3 supports only symmetric encryption KMS keys. For more information,\n see Symmetric encryption\n KMS keys in the Amazon Web Services Key Management Service Developer\n Guide.

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

Specifies encryption-related information for an Amazon S3 bucket that is a destination for\n replicated objects.

\n \n

This is not supported by Amazon S3 on Outposts buckets.

\n
" + } + }, "com.amazonaws.s3control#Endpoints": { "type": "map", "key": { @@ -8162,6 +8402,38 @@ "smithy.api#documentation": "

A container for what Amazon S3 Storage Lens will exclude.

" } }, + "com.amazonaws.s3control#ExistingObjectReplication": { + "type": "structure", + "members": { + "Status": { + "target": "com.amazonaws.s3control#ExistingObjectReplicationStatus", + "traits": { + "smithy.api#documentation": "

Specifies whether Amazon S3 replicates existing source bucket objects.

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

An optional configuration to replicate existing source bucket objects.

\n \n

This is not supported by Amazon S3 on Outposts buckets.

\n
" + } + }, + "com.amazonaws.s3control#ExistingObjectReplicationStatus": { + "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.s3control#ExpirationStatus": { "type": "enum", "members": { @@ -8318,6 +8590,9 @@ "smithy.api#required": {} } } + }, + "traits": { + "smithy.api#input": {} } }, "com.amazonaws.s3control#GetAccessPointConfigurationForObjectLambdaResult": { @@ -8379,6 +8654,9 @@ "smithy.api#required": {} } } + }, + "traits": { + "smithy.api#input": {} } }, "com.amazonaws.s3control#GetAccessPointForObjectLambdaResult": { @@ -8477,6 +8755,9 @@ "smithy.api#required": {} } } + }, + "traits": { + "smithy.api#input": {} } }, "com.amazonaws.s3control#GetAccessPointPolicyForObjectLambdaResult": { @@ -8508,7 +8789,7 @@ "Name": { "target": "com.amazonaws.s3control#AccessPointName", "traits": { - "smithy.api#documentation": "

The name of the access point whose policy you want to retrieve.

\n

For using this parameter with Amazon S3 on Outposts with the REST API, you must specify the name and the x-amz-outpost-id as well.

\n

For using this parameter with S3 on Outposts with the Amazon Web Services SDK and CLI, you must specify the ARN of the access point accessed in the format arn:aws:s3-outposts:::outpost//accesspoint/. For example, to access the access point reports-ap through outpost my-outpost owned by account 123456789012 in Region us-west-2, use the URL encoding of arn:aws:s3-outposts:us-west-2:123456789012:outpost/my-outpost/accesspoint/reports-ap. The value must be URL encoded.

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

The name of the access point whose policy you want to retrieve.

\n

For using this parameter with Amazon S3 on Outposts with the REST API, you must specify the name and the x-amz-outpost-id as well.

\n

For using this parameter with S3 on Outposts with the Amazon Web Services SDK and CLI, you must specify the ARN of the access point accessed in the format arn:aws:s3-outposts:::outpost//accesspoint/. For example, to access the access point reports-ap through Outpost my-outpost owned by account 123456789012 in Region us-west-2, use the URL encoding of arn:aws:s3-outposts:us-west-2:123456789012:outpost/my-outpost/accesspoint/reports-ap. The value must be URL encoded.

", "smithy.api#httpLabel": {}, "smithy.api#required": {}, "smithy.rules#contextParam": { @@ -8516,6 +8797,9 @@ } } } + }, + "traits": { + "smithy.api#input": {} } }, "com.amazonaws.s3control#GetAccessPointPolicyResult": { @@ -8602,6 +8886,9 @@ "smithy.api#required": {} } } + }, + "traits": { + "smithy.api#input": {} } }, "com.amazonaws.s3control#GetAccessPointPolicyStatusForObjectLambdaResult": { @@ -8638,6 +8925,9 @@ } } } + }, + "traits": { + "smithy.api#input": {} } }, "com.amazonaws.s3control#GetAccessPointPolicyStatusResult": { @@ -8669,7 +8959,7 @@ "Name": { "target": "com.amazonaws.s3control#AccessPointName", "traits": { - "smithy.api#documentation": "

The name of the access point whose configuration information you want to retrieve.

\n

For using this parameter with Amazon S3 on Outposts with the REST API, you must specify the name and the x-amz-outpost-id as well.

\n

For using this parameter with S3 on Outposts with the Amazon Web Services SDK and CLI, you must specify the ARN of the access point accessed in the format arn:aws:s3-outposts:::outpost//accesspoint/. For example, to access the access point reports-ap through outpost my-outpost owned by account 123456789012 in Region us-west-2, use the URL encoding of arn:aws:s3-outposts:us-west-2:123456789012:outpost/my-outpost/accesspoint/reports-ap. The value must be URL encoded.

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

The name of the access point whose configuration information you want to retrieve.

\n

For using this parameter with Amazon S3 on Outposts with the REST API, you must specify the name and the x-amz-outpost-id as well.

\n

For using this parameter with S3 on Outposts with the Amazon Web Services SDK and CLI, you must specify the ARN of the access point accessed in the format arn:aws:s3-outposts:::outpost//accesspoint/. For example, to access the access point reports-ap through Outpost my-outpost owned by account 123456789012 in Region us-west-2, use the URL encoding of arn:aws:s3-outposts:us-west-2:123456789012:outpost/my-outpost/accesspoint/reports-ap. The value must be URL encoded.

", "smithy.api#httpLabel": {}, "smithy.api#required": {}, "smithy.rules#contextParam": { @@ -8677,6 +8967,9 @@ } } } + }, + "traits": { + "smithy.api#input": {} } }, "com.amazonaws.s3control#GetAccessPointResult": { @@ -8809,7 +9102,7 @@ "Bucket": { "target": "com.amazonaws.s3control#BucketName", "traits": { - "smithy.api#documentation": "

The Amazon Resource Name (ARN) of the bucket.

\n

For using this parameter with Amazon S3 on Outposts with the REST API, you must specify the name and the x-amz-outpost-id as well.

\n

For using this parameter with S3 on Outposts with the Amazon Web Services SDK and CLI, you must specify the ARN of the bucket accessed in the format arn:aws:s3-outposts:::outpost//bucket/. For example, to access the bucket reports through outpost my-outpost owned by account 123456789012 in Region us-west-2, use the URL encoding of arn:aws:s3-outposts:us-west-2:123456789012:outpost/my-outpost/bucket/reports. The value must be URL encoded.

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

The Amazon Resource Name (ARN) of the bucket.

\n

For using this parameter with Amazon S3 on Outposts with the REST API, you must specify the name and the x-amz-outpost-id as well.

\n

For using this parameter with S3 on Outposts with the Amazon Web Services SDK and CLI, you must specify the ARN of the bucket accessed in the format arn:aws:s3-outposts:::outpost//bucket/. For example, to access the bucket reports through Outpost my-outpost owned by account 123456789012 in Region us-west-2, use the URL encoding of arn:aws:s3-outposts:us-west-2:123456789012:outpost/my-outpost/bucket/reports. The value must be URL encoded.

", "smithy.api#httpLabel": {}, "smithy.api#required": {}, "smithy.rules#contextParam": { @@ -8817,7 +9110,10 @@ } } } - } + }, + "traits": { + "smithy.api#input": {} + } }, "com.amazonaws.s3control#GetBucketLifecycleConfigurationResult": { "type": "structure", @@ -8873,7 +9169,7 @@ "Bucket": { "target": "com.amazonaws.s3control#BucketName", "traits": { - "smithy.api#documentation": "

Specifies the bucket.

\n

For using this parameter with Amazon S3 on Outposts with the REST API, you must specify the name and the x-amz-outpost-id as well.

\n

For using this parameter with S3 on Outposts with the Amazon Web Services SDK and CLI, you must specify the ARN of the bucket accessed in the format arn:aws:s3-outposts:::outpost//bucket/. For example, to access the bucket reports through outpost my-outpost owned by account 123456789012 in Region us-west-2, use the URL encoding of arn:aws:s3-outposts:us-west-2:123456789012:outpost/my-outpost/bucket/reports. The value must be URL encoded.

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

Specifies the bucket.

\n

For using this parameter with Amazon S3 on Outposts with the REST API, you must specify the name and the x-amz-outpost-id as well.

\n

For using this parameter with S3 on Outposts with the Amazon Web Services SDK and CLI, you must specify the ARN of the bucket accessed in the format arn:aws:s3-outposts:::outpost//bucket/. For example, to access the bucket reports through Outpost my-outpost owned by account 123456789012 in Region us-west-2, use the URL encoding of arn:aws:s3-outposts:us-west-2:123456789012:outpost/my-outpost/bucket/reports. The value must be URL encoded.

", "smithy.api#httpLabel": {}, "smithy.api#required": {}, "smithy.rules#contextParam": { @@ -8881,6 +9177,9 @@ } } } + }, + "traits": { + "smithy.api#input": {} } }, "com.amazonaws.s3control#GetBucketPolicyResult": { @@ -8894,6 +9193,73 @@ } } }, + "com.amazonaws.s3control#GetBucketReplication": { + "type": "operation", + "input": { + "target": "com.amazonaws.s3control#GetBucketReplicationRequest" + }, + "output": { + "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#endpoint": { + "hostPrefix": "{AccountId}." + }, + "smithy.api#http": { + "method": "GET", + "uri": "/v20180820/bucket/{Bucket}/replication", + "code": 200 + }, + "smithy.rules#staticContextParams": { + "RequiresAccountId": { + "value": true + } + } + } + }, + "com.amazonaws.s3control#GetBucketReplicationRequest": { + "type": "structure", + "members": { + "AccountId": { + "target": "com.amazonaws.s3control#AccountId", + "traits": { + "smithy.api#documentation": "

The Amazon Web Services account ID of the Outposts bucket.

", + "smithy.api#hostLabel": {}, + "smithy.api#httpHeader": "x-amz-account-id", + "smithy.api#required": {}, + "smithy.rules#contextParam": { + "name": "AccountId" + } + } + }, + "Bucket": { + "target": "com.amazonaws.s3control#BucketName", + "traits": { + "smithy.api#documentation": "

Specifies the bucket to get the replication information for.

\n

For using this parameter with Amazon S3 on Outposts with the REST API, you must specify the name and the x-amz-outpost-id as well.

\n

For using this parameter with S3 on Outposts with the Amazon Web Services SDK and CLI, you must specify the ARN of the bucket accessed in the format arn:aws:s3-outposts:::outpost//bucket/. For example, to access the bucket reports through Outpost my-outpost owned by account 123456789012 in Region us-west-2, use the URL encoding of arn:aws:s3-outposts:us-west-2:123456789012:outpost/my-outpost/bucket/reports. The value must be URL encoded.

", + "smithy.api#httpLabel": {}, + "smithy.api#required": {}, + "smithy.rules#contextParam": { + "name": "Bucket" + } + } + } + }, + "traits": { + "smithy.api#input": {} + } + }, + "com.amazonaws.s3control#GetBucketReplicationResult": { + "type": "structure", + "members": { + "ReplicationConfiguration": { + "target": "com.amazonaws.s3control#ReplicationConfiguration", + "traits": { + "smithy.api#documentation": "

A container for one or more replication rules. A replication configuration must have at least one rule and you can add up to 100 rules. The maximum size of a\n replication configuration is 128 KB.

" + } + } + } + }, "com.amazonaws.s3control#GetBucketRequest": { "type": "structure", "members": { @@ -8912,7 +9278,7 @@ "Bucket": { "target": "com.amazonaws.s3control#BucketName", "traits": { - "smithy.api#documentation": "

Specifies the bucket.

\n

For using this parameter with Amazon S3 on Outposts with the REST API, you must specify the name and the x-amz-outpost-id as well.

\n

For using this parameter with S3 on Outposts with the Amazon Web Services SDK and CLI, you must specify the ARN of the bucket accessed in the format arn:aws:s3-outposts:::outpost//bucket/. For example, to access the bucket reports through outpost my-outpost owned by account 123456789012 in Region us-west-2, use the URL encoding of arn:aws:s3-outposts:us-west-2:123456789012:outpost/my-outpost/bucket/reports. The value must be URL encoded.

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

Specifies the bucket.

\n

For using this parameter with Amazon S3 on Outposts with the REST API, you must specify the name and the x-amz-outpost-id as well.

\n

For using this parameter with S3 on Outposts with the Amazon Web Services SDK and CLI, you must specify the ARN of the bucket accessed in the format arn:aws:s3-outposts:::outpost//bucket/. For example, to access the bucket reports through Outpost my-outpost owned by account 123456789012 in Region us-west-2, use the URL encoding of arn:aws:s3-outposts:us-west-2:123456789012:outpost/my-outpost/bucket/reports. The value must be URL encoded.

", "smithy.api#httpLabel": {}, "smithy.api#required": {}, "smithy.rules#contextParam": { @@ -8920,6 +9286,9 @@ } } } + }, + "traits": { + "smithy.api#input": {} } }, "com.amazonaws.s3control#GetBucketResult": { @@ -8989,7 +9358,7 @@ "Bucket": { "target": "com.amazonaws.s3control#BucketName", "traits": { - "smithy.api#documentation": "

Specifies the bucket.

\n

For using this parameter with Amazon S3 on Outposts with the REST API, you must specify the name and the x-amz-outpost-id as well.

\n

For using this parameter with S3 on Outposts with the Amazon Web Services SDK and CLI, you must specify the ARN of the bucket accessed in the format arn:aws:s3-outposts:::outpost//bucket/. For example, to access the bucket reports through outpost my-outpost owned by account 123456789012 in Region us-west-2, use the URL encoding of arn:aws:s3-outposts:us-west-2:123456789012:outpost/my-outpost/bucket/reports. The value must be URL encoded.

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

Specifies the bucket.

\n

For using this parameter with Amazon S3 on Outposts with the REST API, you must specify the name and the x-amz-outpost-id as well.

\n

For using this parameter with S3 on Outposts with the Amazon Web Services SDK and CLI, you must specify the ARN of the bucket accessed in the format arn:aws:s3-outposts:::outpost//bucket/. For example, to access the bucket reports through Outpost my-outpost owned by account 123456789012 in Region us-west-2, use the URL encoding of arn:aws:s3-outposts:us-west-2:123456789012:outpost/my-outpost/bucket/reports. The value must be URL encoded.

", "smithy.api#httpLabel": {}, "smithy.api#required": {}, "smithy.rules#contextParam": { @@ -8997,6 +9366,9 @@ } } } + }, + "traits": { + "smithy.api#input": {} } }, "com.amazonaws.s3control#GetBucketTaggingResult": { @@ -9020,7 +9392,7 @@ "target": "com.amazonaws.s3control#GetBucketVersioningResult" }, "traits": { - "smithy.api#documentation": "\n

This operation returns the versioning state only for S3 on Outposts buckets. To return\n the versioning state for an S3 bucket, see GetBucketVersioning in\n the Amazon S3 API Reference.

\n
\n

Returns the versioning state for an S3 on Outposts bucket. With versioning, you can save\n multiple distinct copies of your data and recover from unintended user actions and\n application failures.

\n

If you've never set versioning on your bucket, it has no versioning state. In that case,\n the GetBucketVersioning request does not return a versioning state\n value.

\n

For more information about versioning, 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 GetBucketVersioning for\n S3 on Outposts.

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

This operation returns the versioning state\n for\n S3 on Outposts\n buckets\n only. To return the versioning state for an S3 bucket, see GetBucketVersioning in the Amazon S3 API Reference.

\n
\n

Returns 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

If you've never set versioning on your bucket, it has no versioning state. In that case,\n the GetBucketVersioning request does not return a versioning state\n value.

\n

For more information about versioning, 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 GetBucketVersioning for\n S3 on Outposts.

\n ", "smithy.api#endpoint": { "hostPrefix": "{AccountId}." }, @@ -9062,6 +9434,9 @@ } } } + }, + "traits": { + "smithy.api#input": {} } }, "com.amazonaws.s3control#GetBucketVersioningResult": { @@ -9102,7 +9477,7 @@ } ], "traits": { - "smithy.api#documentation": "

Returns the tags on an S3 Batch Operations job. To use this operation, you must have\n permission to perform the s3:GetJobTagging action. For more information, see\n Controlling\n access and labeling jobs using tags in the\n Amazon S3 User Guide.

\n

\n

Related actions include:

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

Returns the tags on an S3 Batch Operations job. To use\n the\n GetJobTagging operation, you must have permission to\n perform the s3:GetJobTagging action. For more information, see Controlling\n access and labeling jobs using tags in the\n Amazon S3 User Guide.

\n

\n

Related actions include:

\n ", "smithy.api#endpoint": { "hostPrefix": "{AccountId}." }, @@ -9141,6 +9516,9 @@ "smithy.api#required": {} } } + }, + "traits": { + "smithy.api#input": {} } }, "com.amazonaws.s3control#GetJobTaggingResult": { @@ -9229,6 +9607,9 @@ "smithy.api#required": {} } } + }, + "traits": { + "smithy.api#input": {} } }, "com.amazonaws.s3control#GetMultiRegionAccessPointPolicyResult": { @@ -9291,6 +9672,9 @@ "smithy.api#required": {} } } + }, + "traits": { + "smithy.api#input": {} } }, "com.amazonaws.s3control#GetMultiRegionAccessPointPolicyStatusResult": { @@ -9324,6 +9708,9 @@ "smithy.api#required": {} } } + }, + "traits": { + "smithy.api#input": {} } }, "com.amazonaws.s3control#GetMultiRegionAccessPointResult": { @@ -9386,6 +9773,9 @@ "smithy.api#required": {} } } + }, + "traits": { + "smithy.api#input": {} } }, "com.amazonaws.s3control#GetMultiRegionAccessPointRoutesResult": { @@ -9445,6 +9835,9 @@ "smithy.api#httpPayload": {} } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.s3control#GetPublicAccessBlockRequest": { @@ -9462,6 +9855,9 @@ } } } + }, + "traits": { + "smithy.api#input": {} } }, "com.amazonaws.s3control#GetStorageLensConfiguration": { @@ -9473,7 +9869,7 @@ "target": "com.amazonaws.s3control#GetStorageLensConfigurationResult" }, "traits": { - "smithy.api#documentation": "

Gets the Amazon S3 Storage Lens configuration. For more information, see Assessing your storage\n activity and usage with Amazon S3 Storage Lens in the\n Amazon S3 User Guide. For a complete list of S3 Storage Lens metrics, see S3 Storage Lens metrics glossary in the Amazon S3 User Guide.

\n \n

To use this action, you must have permission to perform the\n s3:GetStorageLensConfiguration action. For more information, see Setting permissions to use Amazon S3 Storage Lens in the\n Amazon S3 User Guide.

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

Gets the Amazon S3 Storage Lens configuration. For more information, see Assessing your storage\n activity and usage with Amazon S3 Storage Lens in the\n Amazon S3 User Guide. For a complete list of S3 Storage Lens metrics, see S3 Storage Lens metrics glossary in the Amazon S3 User Guide.

\n \n

To use this action, you must have permission to perform the\n s3:GetStorageLensConfiguration action. For more information, see Setting permissions to use Amazon S3 Storage Lens in the\n Amazon S3 User Guide.

\n
", "smithy.api#endpoint": { "hostPrefix": "{AccountId}." }, @@ -9512,6 +9908,9 @@ } } } + }, + "traits": { + "smithy.api#input": {} } }, "com.amazonaws.s3control#GetStorageLensConfigurationResult": { @@ -9574,6 +9973,9 @@ } } } + }, + "traits": { + "smithy.api#input": {} } }, "com.amazonaws.s3control#GetStorageLensConfigurationTaggingResult": { @@ -10076,7 +10478,7 @@ "ObjectArn": { "target": "com.amazonaws.s3control#S3KeyArnString", "traits": { - "smithy.api#documentation": "

The Amazon Resource Name (ARN) for a manifest object.

\n \n

Replacement must be made for object keys containing special characters (such as carriage returns) when using \n XML requests. For more information, see \n XML related object key constraints.

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

The Amazon Resource Name (ARN) for a manifest object.

\n \n

When you're using XML requests, you must \nreplace special characters (such as carriage returns) in object keys with their equivalent XML entity codes. \nFor more information, see \n XML-related object key constraints in the Amazon S3 User Guide.

\n
", "smithy.api#required": {} } }, @@ -10614,7 +11016,7 @@ "Prefix": { "target": "com.amazonaws.s3control#Prefix", "traits": { - "smithy.api#documentation": "

Prefix identifying one or more objects to which the rule applies.

\n \n

Replacement must be made for object keys containing special characters (such as carriage returns) when using \n XML requests. For more information, see \n XML related object key constraints.

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

Prefix identifying one or more objects to which the rule applies.

\n \n

When you're using XML requests, you must \nreplace special characters (such as carriage returns) in object keys with their equivalent XML entity codes. \nFor more information, see \n XML-related object key constraints in the Amazon S3 User Guide.

\n
" } }, "Tag": { @@ -10663,7 +11065,7 @@ "target": "com.amazonaws.s3control#ListAccessPointsResult" }, "traits": { - "smithy.api#documentation": "

Returns a list of the access points owned by the current account associated with the specified bucket. You can\n retrieve up to 1000 access points per call. If the specified bucket has more than 1,000 access points (or\n the number specified in maxResults, whichever is less), the response will\n include a continuation token that you can use to list the additional access points.

\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 actions are related to ListAccessPoints:

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

Returns a list of the access points\n that are\n owned by the current account\n that's\n associated with the specified bucket. You can retrieve up to 1000 access points\n per call. If the specified bucket has more than 1,000 access points (or the number specified in\n maxResults, whichever is less), the response will include a continuation\n token that you can use to list the additional access points.

\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 actions are related to ListAccessPoints:

\n ", "smithy.api#endpoint": { "hostPrefix": "{AccountId}." }, @@ -10745,6 +11147,9 @@ "smithy.api#httpQuery": "maxResults" } } + }, + "traits": { + "smithy.api#input": {} } }, "com.amazonaws.s3control#ListAccessPointsForObjectLambdaResult": { @@ -10782,7 +11187,7 @@ "Bucket": { "target": "com.amazonaws.s3control#BucketName", "traits": { - "smithy.api#documentation": "

The name of the bucket whose associated access points you want to list.

\n

For using this parameter with Amazon S3 on Outposts with the REST API, you must specify the name and the x-amz-outpost-id as well.

\n

For using this parameter with S3 on Outposts with the Amazon Web Services SDK and CLI, you must specify the ARN of the bucket accessed in the format arn:aws:s3-outposts:::outpost//bucket/. For example, to access the bucket reports through outpost my-outpost owned by account 123456789012 in Region us-west-2, use the URL encoding of arn:aws:s3-outposts:us-west-2:123456789012:outpost/my-outpost/bucket/reports. The value must be URL encoded.

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

The name of the bucket whose associated access points you want to list.

\n

For using this parameter with Amazon S3 on Outposts with the REST API, you must specify the name and the x-amz-outpost-id as well.

\n

For using this parameter with S3 on Outposts with the Amazon Web Services SDK and CLI, you must specify the ARN of the bucket accessed in the format arn:aws:s3-outposts:::outpost//bucket/. For example, to access the bucket reports through Outpost my-outpost owned by account 123456789012 in Region us-west-2, use the URL encoding of arn:aws:s3-outposts:us-west-2:123456789012:outpost/my-outpost/bucket/reports. The value must be URL encoded.

", "smithy.api#httpQuery": "bucket", "smithy.rules#contextParam": { "name": "Bucket" @@ -10804,6 +11209,9 @@ "smithy.api#httpQuery": "maxResults" } } + }, + "traits": { + "smithy.api#input": {} } }, "com.amazonaws.s3control#ListAccessPointsResult": { @@ -10901,6 +11309,9 @@ "smithy.api#httpQuery": "maxResults" } } + }, + "traits": { + "smithy.api#input": {} } }, "com.amazonaws.s3control#ListJobsResult": { @@ -10981,6 +11392,9 @@ "smithy.api#httpQuery": "maxResults" } } + }, + "traits": { + "smithy.api#input": {} } }, "com.amazonaws.s3control#ListMultiRegionAccessPointsResult": { @@ -11070,6 +11484,9 @@ } } } + }, + "traits": { + "smithy.api#input": {} } }, "com.amazonaws.s3control#ListRegionalBucketsResult": { @@ -11176,6 +11593,9 @@ "smithy.api#httpQuery": "nextToken" } } + }, + "traits": { + "smithy.api#input": {} } }, "com.amazonaws.s3control#ListStorageLensConfigurationsResult": { @@ -11264,6 +11684,44 @@ } } }, + "com.amazonaws.s3control#Metrics": { + "type": "structure", + "members": { + "Status": { + "target": "com.amazonaws.s3control#MetricsStatus", + "traits": { + "smithy.api#documentation": "

Specifies whether replication metrics are enabled.

", + "smithy.api#required": {} + } + }, + "EventThreshold": { + "target": "com.amazonaws.s3control#ReplicationTimeValue", + "traits": { + "smithy.api#documentation": "

A container that specifies the time threshold for emitting the\n s3:Replication:OperationMissedThreshold event.

\n \n

This is not supported by Amazon S3 on Outposts buckets.

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

A container that specifies replication metrics-related settings.

" + } + }, + "com.amazonaws.s3control#MetricsStatus": { + "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.s3control#MinStorageBytesPercentage": { "type": "double", "traits": { @@ -11274,6 +11732,12 @@ } } }, + "com.amazonaws.s3control#Minutes": { + "type": "integer", + "traits": { + "smithy.api#default": 0 + } + }, "com.amazonaws.s3control#MultiRegionAccessPointAlias": { "type": "string", "traits": { @@ -11431,7 +11895,7 @@ "TrafficDialPercentage": { "target": "com.amazonaws.s3control#TrafficDialPercentage", "traits": { - "smithy.api#documentation": "

The traffic state for the specified bucket or Amazon Web Services Region.

\n

A value of 0 indicates a passive state, which means that no new traffic\n will be routed to the\n Region.

\n

A value of 100 indicates an active state, which means that traffic will be\n routed to the specified Region.

\n

When\n the routing configuration for a Region is changed from active to passive, any in-progress\n operations (uploads, copies, deletes, and so on) to the formerly active Region will\n continue to run to until a final success or failure status is reached.

\n

If all Regions in the routing configuration are designated as passive, you'll receive an\n InvalidRequest error.

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

The traffic state for the specified bucket or Amazon Web Services Region.

\n

A value of 0 indicates a passive state, which means that no new traffic\n will be routed to the Region.

\n

A value of 100 indicates an active state, which means that traffic will be\n routed to the specified Region.

\n

When the routing configuration for a Region is changed from active to passive, any\n in-progress operations (uploads, copies, deletes, and so on) to the formerly active Region\n will continue to run to until a final success or failure status is reached.

\n

If all Regions in the routing configuration are designated as passive, you'll receive an\n InvalidRequest error.

", "smithy.api#required": {} } } @@ -11584,7 +12048,7 @@ "target": "com.amazonaws.s3control#NoncurrentVersionCount", "traits": { "smithy.api#default": null, - "smithy.api#documentation": "

Specifies how many noncurrent versions S3 on Outposts will retain. If there are this many more\n recent noncurrent versions, S3 on Outposts will take the associated action. For more information\n about noncurrent versions, see Lifecycle configuration\n elements in the Amazon S3 User Guide.

" + "smithy.api#documentation": "

Specifies how many noncurrent versions S3 on Outposts will retain. If there are this many\n more recent noncurrent versions, S3 on Outposts will take the associated action. For more\n information about noncurrent versions, see Lifecycle configuration\n elements in the Amazon S3 User Guide.

" } } }, @@ -11944,6 +12408,17 @@ } } }, + "com.amazonaws.s3control#OwnerOverride": { + "type": "enum", + "members": { + "Destination": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "Destination" + } + } + } + }, "com.amazonaws.s3control#Policy": { "type": "string" }, @@ -11999,6 +12474,12 @@ "smithy.api#documentation": "

A container for the prefix-level storage metrics for S3 Storage Lens.

" } }, + "com.amazonaws.s3control#Priority": { + "type": "integer", + "traits": { + "smithy.api#default": 0 + } + }, "com.amazonaws.s3control#ProposedMultiRegionAccessPointPolicy": { "type": "structure", "members": { @@ -12114,6 +12595,9 @@ "smithy.api#required": {} } } + }, + "traits": { + "smithy.api#input": {} } }, "com.amazonaws.s3control#PutAccessPointPolicy": { @@ -12196,6 +12680,9 @@ "smithy.api#required": {} } } + }, + "traits": { + "smithy.api#input": {} } }, "com.amazonaws.s3control#PutAccessPointPolicyRequest": { @@ -12216,7 +12703,7 @@ "Name": { "target": "com.amazonaws.s3control#AccessPointName", "traits": { - "smithy.api#documentation": "

The name of the access point that you want to associate with the specified policy.

\n

For using this parameter with Amazon S3 on Outposts with the REST API, you must specify the name and the x-amz-outpost-id as well.

\n

For using this parameter with S3 on Outposts with the Amazon Web Services SDK and CLI, you must specify the ARN of the access point accessed in the format arn:aws:s3-outposts:::outpost//accesspoint/. For example, to access the access point reports-ap through outpost my-outpost owned by account 123456789012 in Region us-west-2, use the URL encoding of arn:aws:s3-outposts:us-west-2:123456789012:outpost/my-outpost/accesspoint/reports-ap. The value must be URL encoded.

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

The name of the access point that you want to associate with the specified policy.

\n

For using this parameter with Amazon S3 on Outposts with the REST API, you must specify the name and the x-amz-outpost-id as well.

\n

For using this parameter with S3 on Outposts with the Amazon Web Services SDK and CLI, you must specify the ARN of the access point accessed in the format arn:aws:s3-outposts:::outpost//accesspoint/. For example, to access the access point reports-ap through Outpost my-outpost owned by account 123456789012 in Region us-west-2, use the URL encoding of arn:aws:s3-outposts:us-west-2:123456789012:outpost/my-outpost/accesspoint/reports-ap. The value must be URL encoded.

", "smithy.api#httpLabel": {}, "smithy.api#required": {}, "smithy.rules#contextParam": { @@ -12231,6 +12718,9 @@ "smithy.api#required": {} } } + }, + "traits": { + "smithy.api#input": {} } }, "com.amazonaws.s3control#PutBucketLifecycleConfiguration": { @@ -12293,6 +12783,9 @@ "smithy.api#xmlName": "LifecycleConfiguration" } } + }, + "traits": { + "smithy.api#input": {} } }, "com.amazonaws.s3control#PutBucketPolicy": { @@ -12339,7 +12832,7 @@ "Bucket": { "target": "com.amazonaws.s3control#BucketName", "traits": { - "smithy.api#documentation": "

Specifies the bucket.

\n

For using this parameter with Amazon S3 on Outposts with the REST API, you must specify the name and the x-amz-outpost-id as well.

\n

For using this parameter with S3 on Outposts with the Amazon Web Services SDK and CLI, you must specify the ARN of the bucket accessed in the format arn:aws:s3-outposts:::outpost//bucket/. For example, to access the bucket reports through outpost my-outpost owned by account 123456789012 in Region us-west-2, use the URL encoding of arn:aws:s3-outposts:us-west-2:123456789012:outpost/my-outpost/bucket/reports. The value must be URL encoded.

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

Specifies the bucket.

\n

For using this parameter with Amazon S3 on Outposts with the REST API, you must specify the name and the x-amz-outpost-id as well.

\n

For using this parameter with S3 on Outposts with the Amazon Web Services SDK and CLI, you must specify the ARN of the bucket accessed in the format arn:aws:s3-outposts:::outpost//bucket/. For example, to access the bucket reports through Outpost my-outpost owned by account 123456789012 in Region us-west-2, use the URL encoding of arn:aws:s3-outposts:us-west-2:123456789012:outpost/my-outpost/bucket/reports. The value must be URL encoded.

", "smithy.api#httpLabel": {}, "smithy.api#required": {}, "smithy.rules#contextParam": { @@ -12362,6 +12855,75 @@ "smithy.api#required": {} } } + }, + "traits": { + "smithy.api#input": {} + } + }, + "com.amazonaws.s3control#PutBucketReplication": { + "type": "operation", + "input": { + "target": "com.amazonaws.s3control#PutBucketReplicationRequest" + }, + "output": { + "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#endpoint": { + "hostPrefix": "{AccountId}." + }, + "smithy.api#http": { + "method": "PUT", + "uri": "/v20180820/bucket/{Bucket}/replication", + "code": 200 + }, + "smithy.api#httpChecksumRequired": {}, + "smithy.rules#staticContextParams": { + "RequiresAccountId": { + "value": true + } + } + } + }, + "com.amazonaws.s3control#PutBucketReplicationRequest": { + "type": "structure", + "members": { + "AccountId": { + "target": "com.amazonaws.s3control#AccountId", + "traits": { + "smithy.api#documentation": "

The Amazon Web Services account ID of the Outposts bucket.

", + "smithy.api#hostLabel": {}, + "smithy.api#httpHeader": "x-amz-account-id", + "smithy.api#required": {}, + "smithy.rules#contextParam": { + "name": "AccountId" + } + } + }, + "Bucket": { + "target": "com.amazonaws.s3control#BucketName", + "traits": { + "smithy.api#documentation": "

Specifies the S3 on Outposts bucket to set the configuration for.

\n

For using this parameter with Amazon S3 on Outposts with the REST API, you must specify the name and the x-amz-outpost-id as well.

\n

For using this parameter with S3 on Outposts with the Amazon Web Services SDK and CLI, you must specify the ARN of the bucket accessed in the format arn:aws:s3-outposts:::outpost//bucket/. For example, to access the bucket reports through Outpost my-outpost owned by account 123456789012 in Region us-west-2, use the URL encoding of arn:aws:s3-outposts:us-west-2:123456789012:outpost/my-outpost/bucket/reports. The value must be URL encoded.

", + "smithy.api#httpLabel": {}, + "smithy.api#required": {}, + "smithy.rules#contextParam": { + "name": "Bucket" + } + } + }, + "ReplicationConfiguration": { + "target": "com.amazonaws.s3control#ReplicationConfiguration", + "traits": { + "smithy.api#documentation": "

", + "smithy.api#httpPayload": {}, + "smithy.api#required": {}, + "smithy.api#xmlName": "ReplicationConfiguration" + } + } + }, + "traits": { + "smithy.api#input": {} } }, "com.amazonaws.s3control#PutBucketTagging": { @@ -12408,7 +12970,7 @@ "Bucket": { "target": "com.amazonaws.s3control#BucketName", "traits": { - "smithy.api#documentation": "

The Amazon Resource Name (ARN) of the bucket.

\n

For using this parameter with Amazon S3 on Outposts with the REST API, you must specify the name and the x-amz-outpost-id as well.

\n

For using this parameter with S3 on Outposts with the Amazon Web Services SDK and CLI, you must specify the ARN of the bucket accessed in the format arn:aws:s3-outposts:::outpost//bucket/. For example, to access the bucket reports through outpost my-outpost owned by account 123456789012 in Region us-west-2, use the URL encoding of arn:aws:s3-outposts:us-west-2:123456789012:outpost/my-outpost/bucket/reports. The value must be URL encoded.

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

The Amazon Resource Name (ARN) of the bucket.

\n

For using this parameter with Amazon S3 on Outposts with the REST API, you must specify the name and the x-amz-outpost-id as well.

\n

For using this parameter with S3 on Outposts with the Amazon Web Services SDK and CLI, you must specify the ARN of the bucket accessed in the format arn:aws:s3-outposts:::outpost//bucket/. For example, to access the bucket reports through Outpost my-outpost owned by account 123456789012 in Region us-west-2, use the URL encoding of arn:aws:s3-outposts:us-west-2:123456789012:outpost/my-outpost/bucket/reports. The value must be URL encoded.

", "smithy.api#httpLabel": {}, "smithy.api#required": {}, "smithy.rules#contextParam": { @@ -12425,6 +12987,9 @@ "smithy.api#xmlName": "Tagging" } } + }, + "traits": { + "smithy.api#input": {} } }, "com.amazonaws.s3control#PutBucketVersioning": { @@ -12436,7 +13001,7 @@ "target": "smithy.api#Unit" }, "traits": { - "smithy.api#documentation": "\n

This operation sets the versioning state only for S3 on Outposts buckets. To set the\n versioning state for an S3 bucket, see PutBucketVersioning in\n the Amazon S3 API Reference.

\n
\n

Sets the versioning state for an S3 on Outposts bucket. With versioning, you can save\n multiple distinct copies of your data and recover from unintended user actions and\n 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 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#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#endpoint": { "hostPrefix": "{AccountId}." }, @@ -12495,6 +13060,9 @@ "smithy.api#xmlName": "VersioningConfiguration" } } + }, + "traits": { + "smithy.api#input": {} } }, "com.amazonaws.s3control#PutJobTagging": { @@ -12520,7 +13088,7 @@ } ], "traits": { - "smithy.api#documentation": "

Sets the supplied tag-set on an S3 Batch Operations job.

\n

A tag is a key-value pair. You can associate S3 Batch Operations tags with any job by sending\n a PUT request against the tagging subresource that is associated with the job. To modify\n the existing tag set, you can either replace the existing tag set entirely, or make changes\n within the existing tag set by retrieving the existing tag set using GetJobTagging, modify that tag set, and use this action to replace the tag set\n with the one you modified. For more information, see Controlling\n access and labeling jobs using tags in the Amazon S3 User Guide.

\n

\n \n
    \n
  • \n

    If you send this request with an empty tag set, Amazon S3 deletes the existing\n tag set on the Batch Operations job. If you use this method, you are charged for a Tier\n 1 Request (PUT). For more information, see Amazon S3 pricing.

    \n
  • \n
  • \n

    For deleting existing tags for your Batch Operations job, a DeleteJobTagging request is preferred because it achieves the same\n result without incurring charges.

    \n
  • \n
  • \n

    A few things to consider about using tags:

    \n
      \n
    • \n

      Amazon S3 limits the maximum number of tags to 50 tags per job.

      \n
    • \n
    • \n

      You can associate up to 50 tags with a job as long as they have unique\n tag keys.

      \n
    • \n
    • \n

      A tag key can be up to 128 Unicode characters in length, and tag values\n can be up to 256 Unicode characters in length.

      \n
    • \n
    • \n

      The key and values are case sensitive.

      \n
    • \n
    • \n

      For tagging-related restrictions related to characters and encodings, see\n User-Defined Tag Restrictions in the Billing and Cost Management User Guide.

      \n
    • \n
    \n
  • \n
\n
\n

\n

To use this action, you must have permission to perform the\n s3:PutJobTagging action.

\n

Related actions include:

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

Sets the supplied tag-set on an S3 Batch Operations job.

\n

A tag is a key-value pair. You can associate S3 Batch Operations tags with any job by sending\n a PUT request against the tagging subresource that is associated with the job. To modify\n the existing tag set, you can either replace the existing tag set entirely, or make changes\n within the existing tag set by retrieving the existing tag set using GetJobTagging, modify that tag set, and use this action to replace the tag set\n with the one you modified. For more information, see Controlling\n access and labeling jobs using tags in the Amazon S3 User Guide.

\n

\n \n
    \n
  • \n

    If you send this request with an empty tag set, Amazon S3 deletes the existing\n tag set on the Batch Operations job. If you use this method, you are charged for a Tier\n 1 Request (PUT). For more information, see Amazon S3 pricing.

    \n
  • \n
  • \n

    For deleting existing tags for your Batch Operations job, a DeleteJobTagging request is preferred because it achieves the same\n result without incurring charges.

    \n
  • \n
  • \n

    A few things to consider about using tags:

    \n
      \n
    • \n

      Amazon S3 limits the maximum number of tags to 50 tags per job.

      \n
    • \n
    • \n

      You can associate up to 50 tags with a job as long as they have unique\n tag keys.

      \n
    • \n
    • \n

      A tag key can be up to 128 Unicode characters in length, and tag values\n can be up to 256 Unicode characters in length.

      \n
    • \n
    • \n

      The key and values are case sensitive.

      \n
    • \n
    • \n

      For tagging-related restrictions related to characters and encodings, see\n User-Defined Tag Restrictions in the Billing and Cost Management User Guide.

      \n
    • \n
    \n
  • \n
\n
\n

\n

To use the\n PutJobTagging\n operation,\n you must have permission to perform the s3:PutJobTagging action.

\n

Related actions include:

\n ", "smithy.api#endpoint": { "hostPrefix": "{AccountId}." }, @@ -12566,6 +13134,9 @@ "smithy.api#required": {} } } + }, + "traits": { + "smithy.api#input": {} } }, "com.amazonaws.s3control#PutJobTaggingResult": { @@ -12650,6 +13221,9 @@ "smithy.api#required": {} } } + }, + "traits": { + "smithy.api#input": {} } }, "com.amazonaws.s3control#PutMultiRegionAccessPointPolicyResult": { @@ -12712,6 +13286,9 @@ } } } + }, + "traits": { + "smithy.api#input": {} } }, "com.amazonaws.s3control#PutStorageLensConfiguration": { @@ -12723,7 +13300,7 @@ "target": "smithy.api#Unit" }, "traits": { - "smithy.api#documentation": "

Puts an Amazon S3 Storage Lens configuration. For more information about S3 Storage Lens, see Working with\n Amazon S3 Storage Lens in the Amazon S3 User Guide. For a complete list of S3 Storage Lens metrics, see S3 Storage Lens metrics glossary in the Amazon S3 User Guide.

\n \n

To use this action, you must have permission to perform the\n s3:PutStorageLensConfiguration action. For more information, see Setting permissions to use Amazon S3 Storage Lens in the\n Amazon S3 User Guide.

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

Puts an Amazon S3 Storage Lens configuration. For more information about S3 Storage Lens, see Working with\n Amazon S3 Storage Lens in the Amazon S3 User Guide. For a complete list of S3 Storage Lens metrics, see S3 Storage Lens metrics glossary in the Amazon S3 User Guide.

\n \n

To use this action, you must have permission to perform the\n s3:PutStorageLensConfiguration action. For more information, see Setting permissions to use Amazon S3 Storage Lens in the\n Amazon S3 User Guide.

\n
", "smithy.api#endpoint": { "hostPrefix": "{AccountId}." }, @@ -12775,6 +13352,9 @@ "smithy.api#documentation": "

The tag set of the S3 Storage Lens configuration.

\n \n

You can set up to a maximum of 50 tags.

\n
" } } + }, + "traits": { + "smithy.api#input": {} } }, "com.amazonaws.s3control#PutStorageLensConfigurationTagging": { @@ -12832,6 +13412,9 @@ "smithy.api#required": {} } } + }, + "traits": { + "smithy.api#input": {} } }, "com.amazonaws.s3control#PutStorageLensConfigurationTaggingResult": { @@ -12847,6 +13430,12 @@ "smithy.api#documentation": "

The name of the associated bucket for the Region.

", "smithy.api#required": {} } + }, + "BucketAccountId": { + "target": "com.amazonaws.s3control#AccountId", + "traits": { + "smithy.api#documentation": "

The Amazon Web Services account ID that owns the Amazon S3 bucket that's associated with this\n Multi-Region Access Point.

" + } } }, "traits": { @@ -12885,6 +13474,12 @@ "traits": { "smithy.api#documentation": "

The name of the Region.

" } + }, + "BucketAccountId": { + "target": "com.amazonaws.s3control#AccountId", + "traits": { + "smithy.api#documentation": "

The Amazon Web Services account ID that owns the Amazon S3 bucket that's associated with this\n Multi-Region Access Point.

" + } } }, "traits": { @@ -12960,19 +13555,220 @@ } } }, - "com.amazonaws.s3control#ReplicationStatus": { + "com.amazonaws.s3control#ReplicaKmsKeyID": { + "type": "string" + }, + "com.amazonaws.s3control#ReplicaModifications": { + "type": "structure", + "members": { + "Status": { + "target": "com.amazonaws.s3control#ReplicaModificationsStatus", + "traits": { + "smithy.api#documentation": "

Specifies whether S3 on Outposts replicates modifications to object metadata on\n replicas.

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

A filter that you can use to specify whether replica modification sync is enabled.\n S3 on Outposts replica modification sync can help you keep object metadata synchronized\n between replicas and source objects. By default, S3 on Outposts replicates metadata from the\n source objects to the replicas only. When replica modification sync is enabled,\n S3 on Outposts replicates metadata changes made to the replica copies back to the source\n object, making the replication bidirectional.

\n

To replicate object metadata modifications on replicas, you can specify this element and\n set the Status of this element to Enabled.

\n \n

You must enable replica modification sync on the source and destination buckets to\n replicate replica metadata changes between the source and the replicas.

\n
" + } + }, + "com.amazonaws.s3control#ReplicaModificationsStatus": { "type": "enum", "members": { - "COMPLETED": { + "Enabled": { "target": "smithy.api#Unit", "traits": { - "smithy.api#enumValue": "COMPLETED" + "smithy.api#enumValue": "Enabled" } }, - "FAILED": { + "Disabled": { "target": "smithy.api#Unit", "traits": { - "smithy.api#enumValue": "FAILED" + "smithy.api#enumValue": "Disabled" + } + } + } + }, + "com.amazonaws.s3control#ReplicationConfiguration": { + "type": "structure", + "members": { + "Role": { + "target": "com.amazonaws.s3control#Role", + "traits": { + "smithy.api#documentation": "

The Amazon Resource Name (ARN) of the Identity and Access Management (IAM) role that S3 on Outposts assumes\n when replicating objects. For information about S3 replication on Outposts configuration,\n see Setting up\n replication in the Amazon S3 User Guide.

", + "smithy.api#required": {} + } + }, + "Rules": { + "target": "com.amazonaws.s3control#ReplicationRules", + "traits": { + "smithy.api#documentation": "

A container for one or more replication rules. A replication configuration must have at\n least one rule and can contain an array of 100 rules at the most.

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

A container for one or more replication rules. A replication configuration must have at least one rule and you can add up to 100 rules. The maximum size of a\n replication configuration is 128 KB.

" + } + }, + "com.amazonaws.s3control#ReplicationRule": { + "type": "structure", + "members": { + "ID": { + "target": "com.amazonaws.s3control#ID", + "traits": { + "smithy.api#documentation": "

A unique identifier for the rule. The maximum value is 255 characters.

" + } + }, + "Priority": { + "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.

" + } + }, + "Prefix": { + "target": "com.amazonaws.s3control#Prefix", + "traits": { + "smithy.api#deprecated": { + "message": "Prefix has been deprecated" + }, + "smithy.api#documentation": "

An object key name prefix that identifies the object or objects to which the rule\n applies. The maximum prefix length is 1,024 characters. To include all objects in an\n Outposts bucket, specify an empty string.

\n \n

When you're using XML requests, you must \nreplace special characters (such as carriage returns) in object keys with their equivalent XML entity codes. \nFor more information, see \n XML-related object key constraints in the Amazon S3 User Guide.

\n
" + } + }, + "Filter": { + "target": "com.amazonaws.s3control#ReplicationRuleFilter", + "traits": { + "smithy.api#documentation": "

A filter that identifies the subset of objects to which the replication rule applies. A\n Filter element must specify exactly one Prefix,\n Tag, or And child element.

" + } + }, + "Status": { + "target": "com.amazonaws.s3control#ReplicationRuleStatus", + "traits": { + "smithy.api#documentation": "

Specifies whether the rule is enabled.

", + "smithy.api#required": {} + } + }, + "SourceSelectionCriteria": { + "target": "com.amazonaws.s3control#SourceSelectionCriteria", + "traits": { + "smithy.api#documentation": "

A container that describes additional filters for identifying the source Outposts objects that\n you want to replicate. You can choose to enable or disable the replication of these\n objects.

" + } + }, + "ExistingObjectReplication": { + "target": "com.amazonaws.s3control#ExistingObjectReplication", + "traits": { + "smithy.api#documentation": "

An optional configuration to replicate existing source bucket objects.

\n \n

This is not supported by Amazon S3 on Outposts buckets.

\n
" + } + }, + "Destination": { + "target": "com.amazonaws.s3control#Destination", + "traits": { + "smithy.api#documentation": "

A container for information about the replication destination and its configurations.

", + "smithy.api#required": {} + } + }, + "DeleteMarkerReplication": { + "target": "com.amazonaws.s3control#DeleteMarkerReplication", + "traits": { + "smithy.api#documentation": "

Specifies whether S3 on Outposts replicates delete markers. If you specify a\n Filter element in your replication configuration, you must also include a\n DeleteMarkerReplication element. If your Filter includes a\n Tag element, the DeleteMarkerReplication element's\n Status child element must be set to Disabled, because\n S3 on Outposts doesn't support replicating delete markers for tag-based rules.

\n

For more information about delete marker replication, see How delete operations affect replication in the Amazon S3 User Guide.

" + } + }, + "Bucket": { + "target": "com.amazonaws.s3control#BucketIdentifierString", + "traits": { + "smithy.api#documentation": "

The Amazon Resource Name (ARN) of the access point for the source Outposts bucket that you want\n S3 on Outposts to replicate the objects from.

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

Specifies which S3 on Outposts objects to replicate and where to store the replicas.

" + } + }, + "com.amazonaws.s3control#ReplicationRuleAndOperator": { + "type": "structure", + "members": { + "Prefix": { + "target": "com.amazonaws.s3control#Prefix", + "traits": { + "smithy.api#documentation": "

An object key name prefix that identifies the subset of objects that the rule applies\n to.

" + } + }, + "Tags": { + "target": "com.amazonaws.s3control#S3TagSet", + "traits": { + "smithy.api#documentation": "

An array of tags that contain key and value pairs.

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

A container for specifying rule filters. The filters determine the subset of objects to\n which the rule applies. This element is required only if you specify more than one filter.

\n

For example:

\n
    \n
  • \n

    If you specify both a Prefix and a Tag filter, wrap\n these filters in an And element.

    \n
  • \n
  • \n

    If you specify a filter based on multiple tags, wrap the Tag elements\n in an And element.

    \n
  • \n
" + } + }, + "com.amazonaws.s3control#ReplicationRuleFilter": { + "type": "structure", + "members": { + "Prefix": { + "target": "com.amazonaws.s3control#Prefix", + "traits": { + "smithy.api#documentation": "

An object key name prefix that identifies the subset of objects that the rule applies\n to.

\n \n

When you're using XML requests, you must \nreplace special characters (such as carriage returns) in object keys with their equivalent XML entity codes. \nFor more information, see \n XML-related object key constraints in the Amazon S3 User Guide.

\n
" + } + }, + "Tag": { + "target": "com.amazonaws.s3control#S3Tag" + }, + "And": { + "target": "com.amazonaws.s3control#ReplicationRuleAndOperator", + "traits": { + "smithy.api#documentation": "

A container for specifying rule filters. The filters determine the subset of objects\n that the rule applies to. This element is required only if you specify more than one\n filter. For example:

\n
    \n
  • \n

    If you specify both a Prefix and a Tag filter, wrap\n these filters in an And element.

    \n
  • \n
  • \n

    If you specify a filter based on multiple tags, wrap the Tag elements\n in an And element.

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

A filter that identifies the subset of objects to which the replication rule applies. A\n Filter element must specify exactly one Prefix,\n Tag, or And child element.

" + } + }, + "com.amazonaws.s3control#ReplicationRuleStatus": { + "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.s3control#ReplicationRules": { + "type": "list", + "member": { + "target": "com.amazonaws.s3control#ReplicationRule", + "traits": { + "smithy.api#xmlName": "Rule" + } + } + }, + "com.amazonaws.s3control#ReplicationStatus": { + "type": "enum", + "members": { + "COMPLETED": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "COMPLETED" + } + }, + "FAILED": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "FAILED" } }, "REPLICA": { @@ -12995,6 +13791,119 @@ "target": "com.amazonaws.s3control#ReplicationStatus" } }, + "com.amazonaws.s3control#ReplicationStorageClass": { + "type": "enum", + "members": { + "STANDARD": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "STANDARD" + } + }, + "REDUCED_REDUNDANCY": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "REDUCED_REDUNDANCY" + } + }, + "STANDARD_IA": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "STANDARD_IA" + } + }, + "ONEZONE_IA": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "ONEZONE_IA" + } + }, + "INTELLIGENT_TIERING": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "INTELLIGENT_TIERING" + } + }, + "GLACIER": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "GLACIER" + } + }, + "DEEP_ARCHIVE": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "DEEP_ARCHIVE" + } + }, + "OUTPOSTS": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "OUTPOSTS" + } + }, + "GLACIER_IR": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "GLACIER_IR" + } + } + } + }, + "com.amazonaws.s3control#ReplicationTime": { + "type": "structure", + "members": { + "Status": { + "target": "com.amazonaws.s3control#ReplicationTimeStatus", + "traits": { + "smithy.api#documentation": "

Specifies whether S3 Replication Time Control (S3 RTC) is enabled.

", + "smithy.api#required": {} + } + }, + "Time": { + "target": "com.amazonaws.s3control#ReplicationTimeValue", + "traits": { + "smithy.api#documentation": "

A container that specifies the time by which replication should be complete for all\n objects and operations on objects.

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

A container that specifies S3 Replication Time Control (S3 RTC) related information, including whether S3 RTC\n is enabled and the time when all objects and operations on objects must be\n replicated.

\n \n

This is not supported by Amazon S3 on Outposts buckets.

\n
" + } + }, + "com.amazonaws.s3control#ReplicationTimeStatus": { + "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.s3control#ReplicationTimeValue": { + "type": "structure", + "members": { + "Minutes": { + "target": "com.amazonaws.s3control#Minutes", + "traits": { + "smithy.api#default": null, + "smithy.api#documentation": "

Contains an integer that specifies the time period in minutes.

\n

Valid value: 15

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

A container that specifies the time value for S3 Replication Time Control (S3 RTC). This value is also used for\n the replication metrics EventThreshold element.

\n \n

This is not supported by Amazon S3 on Outposts buckets.

\n
" + } + }, "com.amazonaws.s3control#ReportPrefixString": { "type": "string", "traits": { @@ -13021,6 +13930,9 @@ } } }, + "com.amazonaws.s3control#Role": { + "type": "string" + }, "com.amazonaws.s3control#RouteList": { "type": "list", "member": { @@ -13239,7 +14151,7 @@ "TargetResource": { "target": "com.amazonaws.s3control#S3BucketArnString", "traits": { - "smithy.api#documentation": "

Specifies the destination bucket ARN for the batch copy operation. For example, to copy\n objects to a bucket named destinationBucket, set the\n TargetResource property to\n arn:aws:s3:::destinationBucket.

" + "smithy.api#documentation": "

Specifies the destination bucket\n Amazon Resource Name\n (ARN)\n for the batch copy operation. For example, to copy objects to a bucket named\n destinationBucket, set the TargetResource property to\n arn:aws:s3:::destinationBucket.

" } }, "CannedAccessControlList": { @@ -13312,7 +14224,7 @@ "TargetKeyPrefix": { "target": "com.amazonaws.s3control#NonEmptyMaxLength1024String", "traits": { - "smithy.api#documentation": "

Specifies the folder prefix into which you would like the objects to be copied. For\n example, to copy objects into a folder named Folder1 in the destination\n bucket, set the TargetKeyPrefix to Folder1.

" + "smithy.api#documentation": "

Specifies the folder prefix\n that\n you\n want\n the objects to be\n copied\n into. For example, to copy objects into a folder named\n Folder1 in the destination bucket, set the\n TargetKeyPrefix\n property\n to Folder1.

" } }, "ObjectLockLegalHoldStatus": { @@ -13343,19 +14255,19 @@ "ChecksumAlgorithm": { "target": "com.amazonaws.s3control#S3ChecksumAlgorithm", "traits": { - "smithy.api#documentation": "

Indicates the algorithm you want Amazon S3 to use to create the checksum. For more\n information see Checking object\n integrity in the Amazon S3 User Guide.

" + "smithy.api#documentation": "

Indicates the algorithm\n that\n you want Amazon S3 to use to create the checksum. For more\n information,\n see Checking object integrity in the Amazon S3 User Guide.

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

Contains the configuration parameters for a PUT Copy object operation. S3 Batch Operations\n passes every object to the underlying PUT Copy object API. For more information about the\n parameters for this operation, see PUT Object - Copy.

" + "smithy.api#documentation": "

Contains\n the configuration parameters for a PUT Copy object operation. S3 Batch Operations passes every\n object to the underlying\n CopyObject\n API\n operation. For more information about the parameters for this operation,\n see CopyObject.

" } }, "com.amazonaws.s3control#S3DeleteObjectTaggingOperation": { "type": "structure", "members": {}, "traits": { - "smithy.api#documentation": "

Contains no configuration parameters because the DELETE Object tagging API only accepts\n the bucket name and key name as parameters, which are defined in the job's manifest.

" + "smithy.api#documentation": "

Contains no configuration parameters because the DELETE Object tagging\n (DeleteObjectTagging)\n API\n operation\n accepts\n only\n the bucket name and key name as parameters, which are defined in the\n job's manifest.

" } }, "com.amazonaws.s3control#S3ExpirationInDays": { @@ -13494,7 +14406,7 @@ } }, "traits": { - "smithy.api#documentation": "

Contains the configuration parameters for an S3 Initiate Restore Object job.\n S3 Batch Operations passes every object to the underlying POST Object restore API. For more\n information about the parameters for this operation, see RestoreObject.

" + "smithy.api#documentation": "

Contains the configuration parameters for\n a\n POST Object restore job. S3 Batch Operations passes every object to the\n underlying\n RestoreObject\n API\n operation. For more information about the parameters for this operation,\n see RestoreObject.

" } }, "com.amazonaws.s3control#S3JobManifestGenerator": { @@ -13875,7 +14787,7 @@ } }, "traits": { - "smithy.api#documentation": "

Contains the configuration parameters for a Set Object ACL operation. S3 Batch Operations\n passes every object to the underlying PutObjectAcl API. For more information\n about the parameters for this operation, see \n PutObjectAcl\n .

" + "smithy.api#documentation": "

Contains the configuration parameters for a\n PUT\n Object ACL operation. S3 Batch Operations passes every object to the underlying\n PutObjectAcl\n API\n operation. For more information about the parameters for this operation,\n see PutObjectAcl.

" } }, "com.amazonaws.s3control#S3SetObjectLegalHoldOperation": { @@ -13890,7 +14802,7 @@ } }, "traits": { - "smithy.api#documentation": "

Contains the configuration for an S3 Object Lock legal hold operation that an\n S3 Batch Operations job passes every object to the underlying PutObjectLegalHold\n API. For more information, see Using S3 Object Lock legal hold\n with S3 Batch Operations in the Amazon S3 User Guide.

" + "smithy.api#documentation": "

Contains the configuration for an S3 Object Lock legal hold operation that an\n S3 Batch Operations job passes\n to\n every object to the underlying\n PutObjectLegalHold\n API\n operation. For more information, see Using S3 Object Lock legal hold\n with S3 Batch Operations in the Amazon S3 User Guide.

" } }, "com.amazonaws.s3control#S3SetObjectRetentionOperation": { @@ -13912,7 +14824,7 @@ } }, "traits": { - "smithy.api#documentation": "

Contains the configuration parameters for the Object Lock retention action for an\n S3 Batch Operations job. Batch Operations passes every object to the underlying\n PutObjectRetention API. For more information, see Using\n S3 Object Lock retention with S3 Batch Operations in the\n Amazon S3 User Guide.

" + "smithy.api#documentation": "

Contains the configuration parameters for the Object Lock retention action for an\n S3 Batch Operations job. Batch Operations passes every object to the underlying\n PutObjectRetention\n API\n operation. For more information, see Using S3 Object Lock retention\n with S3 Batch Operations in the Amazon S3 User Guide.

" } }, "com.amazonaws.s3control#S3SetObjectTaggingOperation": { @@ -13926,7 +14838,7 @@ } }, "traits": { - "smithy.api#documentation": "

Contains the configuration parameters for a Set Object Tagging operation. S3 Batch Operations\n passes every object to the underlying PUT Object tagging API. For more information about\n the parameters for this operation, see PUT Object tagging.

" + "smithy.api#documentation": "

Contains the configuration parameters for a\n PUT\n Object Tagging operation. S3 Batch Operations passes every object to the underlying\n PutObjectTagging\n API\n operation. For more information about the parameters for this operation,\n see PutObjectTagging.

" } }, "com.amazonaws.s3control#S3StorageClass": { @@ -14104,6 +15016,58 @@ "smithy.api#default": false } }, + "com.amazonaws.s3control#SourceSelectionCriteria": { + "type": "structure", + "members": { + "SseKmsEncryptedObjects": { + "target": "com.amazonaws.s3control#SseKmsEncryptedObjects", + "traits": { + "smithy.api#documentation": "

A filter that you can use to select Amazon S3 objects that are encrypted with server-side\n encryption by using Key Management Service (KMS) keys. If you include\n SourceSelectionCriteria in the replication configuration, this element is\n required.

\n \n

This is not supported by Amazon S3 on Outposts buckets.

\n
" + } + }, + "ReplicaModifications": { + "target": "com.amazonaws.s3control#ReplicaModifications", + "traits": { + "smithy.api#documentation": "

A filter that you can use to specify whether replica modification sync is enabled.\n S3 on Outposts replica modification sync can help you keep object metadata synchronized\n between replicas and source objects. By default, S3 on Outposts replicates metadata from the\n source objects to the replicas only. When replica modification sync is enabled,\n S3 on Outposts replicates metadata changes made to the replica copies back to the source\n object, making the replication bidirectional.

\n

To replicate object metadata modifications on replicas, you can specify this element and\n set the Status of this element to Enabled.

\n \n

You must enable replica modification sync on the source and destination buckets to\n replicate replica metadata changes between the source and the replicas.

\n
" + } + } + }, + "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.

" + } + }, + "com.amazonaws.s3control#SseKmsEncryptedObjects": { + "type": "structure", + "members": { + "Status": { + "target": "com.amazonaws.s3control#SseKmsEncryptedObjectsStatus", + "traits": { + "smithy.api#documentation": "

Specifies whether Amazon S3 replicates objects that are created with server-side encryption\n by using an KMS key stored in Key Management Service.

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

A container for filter information that you can use to select S3 objects that are\n encrypted with Key Management Service (KMS).

\n \n

This is not supported by Amazon S3 on Outposts buckets.

\n
" + } + }, + "com.amazonaws.s3control#SseKmsEncryptedObjectsStatus": { + "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.s3control#StorageLensArn": { "type": "string", "traits": { @@ -14356,6 +15320,9 @@ "smithy.api#required": {} } } + }, + "traits": { + "smithy.api#input": {} } }, "com.amazonaws.s3control#SubmitMultiRegionAccessPointRoutesResult": { @@ -14587,6 +15554,9 @@ "smithy.api#required": {} } } + }, + "traits": { + "smithy.api#input": {} } }, "com.amazonaws.s3control#UpdateJobPriorityResult": { @@ -14689,6 +15659,9 @@ "smithy.api#httpQuery": "statusUpdateReason" } } + }, + "traits": { + "smithy.api#input": {} } }, "com.amazonaws.s3control#UpdateJobStatusResult": { diff --git a/aws/sdk/aws-models/sso.json b/aws/sdk/aws-models/sso.json index 336be4b83e..474fc29342 100644 --- a/aws/sdk/aws-models/sso.json +++ b/aws/sdk/aws-models/sso.json @@ -520,7 +520,7 @@ "parameters": { "Region": { "builtIn": "AWS::Region", - "required": true, + "required": false, "documentation": "The AWS region used to dispatch the request.", "type": "String" }, @@ -549,13 +549,12 @@ { "conditions": [ { - "fn": "aws.partition", + "fn": "isSet", "argv": [ { - "ref": "Region" + "ref": "Endpoint" } - ], - "assign": "PartitionResult" + ] } ], "type": "tree", @@ -563,14 +562,20 @@ { "conditions": [ { - "fn": "isSet", + "fn": "booleanEquals", "argv": [ { - "ref": "Endpoint" - } + "ref": "UseFIPS" + }, + true ] } ], + "error": "Invalid Configuration: FIPS and custom endpoint are not supported", + "type": "error" + }, + { + "conditions": [], "type": "tree", "rules": [ { @@ -579,67 +584,42 @@ "fn": "booleanEquals", "argv": [ { - "ref": "UseFIPS" + "ref": "UseDualStack" }, true ] } ], - "error": "Invalid Configuration: FIPS and custom endpoint are not supported", + "error": "Invalid Configuration: Dualstack 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" + "endpoint": { + "url": { + "ref": "Endpoint" }, - { - "conditions": [], - "endpoint": { - "url": { - "ref": "Endpoint" - }, - "properties": {}, - "headers": {} - }, - "type": "endpoint" - } - ] + "properties": {}, + "headers": {} + }, + "type": "endpoint" } ] - }, + } + ] + }, + { + "conditions": [], + "type": "tree", + "rules": [ { "conditions": [ { - "fn": "booleanEquals", - "argv": [ - { - "ref": "UseFIPS" - }, - true - ] - }, - { - "fn": "booleanEquals", + "fn": "isSet", "argv": [ { - "ref": "UseDualStack" - }, - true + "ref": "Region" + } ] } ], @@ -648,591 +628,637 @@ { "conditions": [ { - "fn": "booleanEquals", + "fn": "aws.partition", "argv": [ - true, { - "fn": "getAttr", + "ref": "Region" + } + ], + "assign": "PartitionResult" + } + ], + "type": "tree", + "rules": [ + { + "conditions": [ + { + "fn": "booleanEquals", "argv": [ { - "ref": "PartitionResult" + "ref": "UseFIPS" }, - "supportsFIPS" + true ] - } - ] - }, - { - "fn": "booleanEquals", - "argv": [ - true, + }, { - "fn": "getAttr", + "fn": "booleanEquals", "argv": [ { - "ref": "PartitionResult" + "ref": "UseDualStack" }, - "supportsDualStack" + true ] } - ] - } - ], - "type": "tree", - "rules": [ - { - "conditions": [], + ], "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://portal.sso-fips.{Region}.{PartitionResult#dualStackDnsSuffix}", + "properties": {}, + "headers": {} + }, + "type": "endpoint" + } + ] + } + ] + }, { "conditions": [], - "endpoint": { - "url": "https://portal.sso-fips.{Region}.{PartitionResult#dualStackDnsSuffix}", - "properties": {}, - "headers": {} - }, - "type": "endpoint" + "error": "FIPS and DualStack are enabled, but this partition does not support one or both", + "type": "error" } ] - } - ] - }, - { - "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, + "conditions": [ { - "fn": "getAttr", + "fn": "booleanEquals", "argv": [ { - "ref": "PartitionResult" + "ref": "UseFIPS" }, - "supportsFIPS" + true ] } - ] - } - ], - "type": "tree", - "rules": [ - { - "conditions": [], + ], "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://portal.sso-fips.{Region}.{PartitionResult#dnsSuffix}", + "properties": {}, + "headers": {} + }, + "type": "endpoint" + } + ] + } + ] + }, { "conditions": [], - "endpoint": { - "url": "https://portal.sso-fips.{Region}.{PartitionResult#dnsSuffix}", - "properties": {}, - "headers": {} - }, - "type": "endpoint" + "error": "FIPS is enabled but this partition does not support FIPS", + "type": "error" } ] - } - ] - }, - { - "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, + "conditions": [ { - "fn": "getAttr", + "fn": "booleanEquals", "argv": [ { - "ref": "PartitionResult" + "ref": "UseDualStack" }, - "supportsDualStack" + 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://portal.sso.{Region}.{PartitionResult#dualStackDnsSuffix}", + "properties": {}, + "headers": {} + }, + "type": "endpoint" + } + ] + } ] + }, + { + "conditions": [], + "error": "DualStack is enabled but this partition does not support DualStack", + "type": "error" } ] - } - ], - "type": "tree", - "rules": [ + }, { "conditions": [], "type": "tree", "rules": [ { - "conditions": [], + "conditions": [ + { + "fn": "stringEquals", + "argv": [ + { + "ref": "Region" + }, + "ap-east-1" + ] + } + ], "endpoint": { - "url": "https://portal.sso.{Region}.{PartitionResult#dualStackDnsSuffix}", + "url": "https://portal.sso.ap-east-1.amazonaws.com", "properties": {}, "headers": {} }, "type": "endpoint" - } - ] - } - ] - }, - { - "conditions": [], - "error": "DualStack is enabled but this partition does not support DualStack", - "type": "error" - } - ] - }, - { - "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" + "conditions": [ + { + "fn": "stringEquals", + "argv": [ + { + "ref": "Region" + }, + "ap-northeast-1" + ] + } + ], + "endpoint": { + "url": "https://portal.sso.ap-northeast-1.amazonaws.com", + "properties": {}, + "headers": {} + }, + "type": "endpoint" }, - "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" + "conditions": [ + { + "fn": "stringEquals", + "argv": [ + { + "ref": "Region" + }, + "ap-northeast-2" + ] + } + ], + "endpoint": { + "url": "https://portal.sso.ap-northeast-2.amazonaws.com", + "properties": {}, + "headers": {} + }, + "type": "endpoint" }, - "ap-northeast-3" - ] - } - ], - "endpoint": { - "url": "https://portal.sso.ap-northeast-3.amazonaws.com", - "properties": {}, - "headers": {} - }, - "type": "endpoint" - }, - { - "conditions": [ - { - "fn": "stringEquals", - "argv": [ { - "ref": "Region" + "conditions": [ + { + "fn": "stringEquals", + "argv": [ + { + "ref": "Region" + }, + "ap-northeast-3" + ] + } + ], + "endpoint": { + "url": "https://portal.sso.ap-northeast-3.amazonaws.com", + "properties": {}, + "headers": {} + }, + "type": "endpoint" }, - "ap-south-1" - ] - } - ], - "endpoint": { - "url": "https://portal.sso.ap-south-1.amazonaws.com", - "properties": {}, - "headers": {} - }, - "type": "endpoint" - }, - { - "conditions": [ - { - "fn": "stringEquals", - "argv": [ { - "ref": "Region" + "conditions": [ + { + "fn": "stringEquals", + "argv": [ + { + "ref": "Region" + }, + "ap-south-1" + ] + } + ], + "endpoint": { + "url": "https://portal.sso.ap-south-1.amazonaws.com", + "properties": {}, + "headers": {} + }, + "type": "endpoint" }, - "ap-southeast-1" - ] - } - ], - "endpoint": { - "url": "https://portal.sso.ap-southeast-1.amazonaws.com", - "properties": {}, - "headers": {} - }, - "type": "endpoint" - }, - { - "conditions": [ - { - "fn": "stringEquals", - "argv": [ { - "ref": "Region" + "conditions": [ + { + "fn": "stringEquals", + "argv": [ + { + "ref": "Region" + }, + "ap-southeast-1" + ] + } + ], + "endpoint": { + "url": "https://portal.sso.ap-southeast-1.amazonaws.com", + "properties": {}, + "headers": {} + }, + "type": "endpoint" }, - "ap-southeast-2" - ] - } - ], - "endpoint": { - "url": "https://portal.sso.ap-southeast-2.amazonaws.com", - "properties": {}, - "headers": {} - }, - "type": "endpoint" - }, - { - "conditions": [ - { - "fn": "stringEquals", - "argv": [ { - "ref": "Region" + "conditions": [ + { + "fn": "stringEquals", + "argv": [ + { + "ref": "Region" + }, + "ap-southeast-2" + ] + } + ], + "endpoint": { + "url": "https://portal.sso.ap-southeast-2.amazonaws.com", + "properties": {}, + "headers": {} + }, + "type": "endpoint" }, - "ca-central-1" - ] - } - ], - "endpoint": { - "url": "https://portal.sso.ca-central-1.amazonaws.com", - "properties": {}, - "headers": {} - }, - "type": "endpoint" - }, - { - "conditions": [ - { - "fn": "stringEquals", - "argv": [ { - "ref": "Region" + "conditions": [ + { + "fn": "stringEquals", + "argv": [ + { + "ref": "Region" + }, + "ca-central-1" + ] + } + ], + "endpoint": { + "url": "https://portal.sso.ca-central-1.amazonaws.com", + "properties": {}, + "headers": {} + }, + "type": "endpoint" }, - "eu-central-1" - ] - } - ], - "endpoint": { - "url": "https://portal.sso.eu-central-1.amazonaws.com", - "properties": {}, - "headers": {} - }, - "type": "endpoint" - }, - { - "conditions": [ - { - "fn": "stringEquals", - "argv": [ { - "ref": "Region" + "conditions": [ + { + "fn": "stringEquals", + "argv": [ + { + "ref": "Region" + }, + "eu-central-1" + ] + } + ], + "endpoint": { + "url": "https://portal.sso.eu-central-1.amazonaws.com", + "properties": {}, + "headers": {} + }, + "type": "endpoint" }, - "eu-north-1" - ] - } - ], - "endpoint": { - "url": "https://portal.sso.eu-north-1.amazonaws.com", - "properties": {}, - "headers": {} - }, - "type": "endpoint" - }, - { - "conditions": [ - { - "fn": "stringEquals", - "argv": [ { - "ref": "Region" + "conditions": [ + { + "fn": "stringEquals", + "argv": [ + { + "ref": "Region" + }, + "eu-north-1" + ] + } + ], + "endpoint": { + "url": "https://portal.sso.eu-north-1.amazonaws.com", + "properties": {}, + "headers": {} + }, + "type": "endpoint" }, - "eu-south-1" - ] - } - ], - "endpoint": { - "url": "https://portal.sso.eu-south-1.amazonaws.com", - "properties": {}, - "headers": {} - }, - "type": "endpoint" - }, - { - "conditions": [ - { - "fn": "stringEquals", - "argv": [ { - "ref": "Region" + "conditions": [ + { + "fn": "stringEquals", + "argv": [ + { + "ref": "Region" + }, + "eu-south-1" + ] + } + ], + "endpoint": { + "url": "https://portal.sso.eu-south-1.amazonaws.com", + "properties": {}, + "headers": {} + }, + "type": "endpoint" }, - "eu-west-1" - ] - } - ], - "endpoint": { - "url": "https://portal.sso.eu-west-1.amazonaws.com", - "properties": {}, - "headers": {} - }, - "type": "endpoint" - }, - { - "conditions": [ - { - "fn": "stringEquals", - "argv": [ { - "ref": "Region" + "conditions": [ + { + "fn": "stringEquals", + "argv": [ + { + "ref": "Region" + }, + "eu-west-1" + ] + } + ], + "endpoint": { + "url": "https://portal.sso.eu-west-1.amazonaws.com", + "properties": {}, + "headers": {} + }, + "type": "endpoint" }, - "eu-west-2" - ] - } - ], - "endpoint": { - "url": "https://portal.sso.eu-west-2.amazonaws.com", - "properties": {}, - "headers": {} - }, - "type": "endpoint" - }, - { - "conditions": [ - { - "fn": "stringEquals", - "argv": [ { - "ref": "Region" + "conditions": [ + { + "fn": "stringEquals", + "argv": [ + { + "ref": "Region" + }, + "eu-west-2" + ] + } + ], + "endpoint": { + "url": "https://portal.sso.eu-west-2.amazonaws.com", + "properties": {}, + "headers": {} + }, + "type": "endpoint" }, - "eu-west-3" - ] - } - ], - "endpoint": { - "url": "https://portal.sso.eu-west-3.amazonaws.com", - "properties": {}, - "headers": {} - }, - "type": "endpoint" - }, - { - "conditions": [ - { - "fn": "stringEquals", - "argv": [ { - "ref": "Region" + "conditions": [ + { + "fn": "stringEquals", + "argv": [ + { + "ref": "Region" + }, + "eu-west-3" + ] + } + ], + "endpoint": { + "url": "https://portal.sso.eu-west-3.amazonaws.com", + "properties": {}, + "headers": {} + }, + "type": "endpoint" }, - "me-south-1" - ] - } - ], - "endpoint": { - "url": "https://portal.sso.me-south-1.amazonaws.com", - "properties": {}, - "headers": {} - }, - "type": "endpoint" - }, - { - "conditions": [ - { - "fn": "stringEquals", - "argv": [ { - "ref": "Region" + "conditions": [ + { + "fn": "stringEquals", + "argv": [ + { + "ref": "Region" + }, + "me-south-1" + ] + } + ], + "endpoint": { + "url": "https://portal.sso.me-south-1.amazonaws.com", + "properties": {}, + "headers": {} + }, + "type": "endpoint" }, - "sa-east-1" - ] - } - ], - "endpoint": { - "url": "https://portal.sso.sa-east-1.amazonaws.com", - "properties": {}, - "headers": {} - }, - "type": "endpoint" - }, - { - "conditions": [ - { - "fn": "stringEquals", - "argv": [ { - "ref": "Region" + "conditions": [ + { + "fn": "stringEquals", + "argv": [ + { + "ref": "Region" + }, + "sa-east-1" + ] + } + ], + "endpoint": { + "url": "https://portal.sso.sa-east-1.amazonaws.com", + "properties": {}, + "headers": {} + }, + "type": "endpoint" }, - "us-east-1" - ] - } - ], - "endpoint": { - "url": "https://portal.sso.us-east-1.amazonaws.com", - "properties": {}, - "headers": {} - }, - "type": "endpoint" - }, - { - "conditions": [ - { - "fn": "stringEquals", - "argv": [ { - "ref": "Region" + "conditions": [ + { + "fn": "stringEquals", + "argv": [ + { + "ref": "Region" + }, + "us-east-1" + ] + } + ], + "endpoint": { + "url": "https://portal.sso.us-east-1.amazonaws.com", + "properties": {}, + "headers": {} + }, + "type": "endpoint" }, - "us-east-2" - ] - } - ], - "endpoint": { - "url": "https://portal.sso.us-east-2.amazonaws.com", - "properties": {}, - "headers": {} - }, - "type": "endpoint" - }, - { - "conditions": [ - { - "fn": "stringEquals", - "argv": [ { - "ref": "Region" + "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" }, - "us-west-2" - ] - } - ], - "endpoint": { - "url": "https://portal.sso.us-west-2.amazonaws.com", - "properties": {}, - "headers": {} - }, - "type": "endpoint" - }, - { - "conditions": [ - { - "fn": "stringEquals", - "argv": [ { - "ref": "Region" + "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" }, - "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" + "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" }, - "us-gov-west-1" + { + "conditions": [], + "endpoint": { + "url": "https://portal.sso.{Region}.{PartitionResult#dnsSuffix}", + "properties": {}, + "headers": {} + }, + "type": "endpoint" + } ] } - ], - "endpoint": { - "url": "https://portal.sso.us-gov-west-1.amazonaws.com", - "properties": {}, - "headers": {} - }, - "type": "endpoint" - }, - { - "conditions": [], - "endpoint": { - "url": "https://portal.sso.{Region}.{PartitionResult#dnsSuffix}", - "properties": {}, - "headers": {} - }, - "type": "endpoint" + ] } ] + }, + { + "conditions": [], + "error": "Invalid Configuration: Missing Region", + "type": "error" } ] } @@ -1241,250 +1267,250 @@ "smithy.rules#endpointTests": { "testCases": [ { - "documentation": "For region us-west-2 with FIPS disabled and DualStack disabled", + "documentation": "For region ap-east-1 with FIPS disabled and DualStack disabled", "expect": { "endpoint": { - "url": "https://portal.sso.us-west-2.amazonaws.com" + "url": "https://portal.sso.ap-east-1.amazonaws.com" } }, "params": { - "UseFIPS": false, + "Region": "ap-east-1", "UseDualStack": false, - "Region": "us-west-2" + "UseFIPS": false } }, { - "documentation": "For region me-south-1 with FIPS disabled and DualStack disabled", + "documentation": "For region ap-northeast-1 with FIPS disabled and DualStack disabled", "expect": { "endpoint": { - "url": "https://portal.sso.me-south-1.amazonaws.com" + "url": "https://portal.sso.ap-northeast-1.amazonaws.com" } }, "params": { - "UseFIPS": false, + "Region": "ap-northeast-1", "UseDualStack": false, - "Region": "me-south-1" + "UseFIPS": false } }, { - "documentation": "For region eu-south-1 with FIPS disabled and DualStack disabled", + "documentation": "For region ap-northeast-2 with FIPS disabled and DualStack disabled", "expect": { "endpoint": { - "url": "https://portal.sso.eu-south-1.amazonaws.com" + "url": "https://portal.sso.ap-northeast-2.amazonaws.com" } }, "params": { - "UseFIPS": false, + "Region": "ap-northeast-2", "UseDualStack": false, - "Region": "eu-south-1" + "UseFIPS": false } }, { - "documentation": "For region sa-east-1 with FIPS disabled and DualStack disabled", + "documentation": "For region ap-northeast-3 with FIPS disabled and DualStack disabled", "expect": { "endpoint": { - "url": "https://portal.sso.sa-east-1.amazonaws.com" + "url": "https://portal.sso.ap-northeast-3.amazonaws.com" } }, "params": { - "UseFIPS": false, + "Region": "ap-northeast-3", "UseDualStack": false, - "Region": "sa-east-1" + "UseFIPS": false } }, { - "documentation": "For region ap-east-1 with FIPS disabled and DualStack disabled", + "documentation": "For region ap-south-1 with FIPS disabled and DualStack disabled", "expect": { "endpoint": { - "url": "https://portal.sso.ap-east-1.amazonaws.com" + "url": "https://portal.sso.ap-south-1.amazonaws.com" } }, "params": { - "UseFIPS": false, + "Region": "ap-south-1", "UseDualStack": false, - "Region": "ap-east-1" + "UseFIPS": false } }, { - "documentation": "For region ca-central-1 with FIPS disabled and DualStack disabled", + "documentation": "For region ap-southeast-1 with FIPS disabled and DualStack disabled", "expect": { "endpoint": { - "url": "https://portal.sso.ca-central-1.amazonaws.com" + "url": "https://portal.sso.ap-southeast-1.amazonaws.com" } }, "params": { - "UseFIPS": false, + "Region": "ap-southeast-1", "UseDualStack": false, - "Region": "ca-central-1" + "UseFIPS": false } }, { - "documentation": "For region ap-southeast-1 with FIPS disabled and DualStack disabled", + "documentation": "For region ap-southeast-2 with FIPS disabled and DualStack disabled", "expect": { "endpoint": { - "url": "https://portal.sso.ap-southeast-1.amazonaws.com" + "url": "https://portal.sso.ap-southeast-2.amazonaws.com" } }, "params": { - "UseFIPS": false, + "Region": "ap-southeast-2", "UseDualStack": false, - "Region": "ap-southeast-1" + "UseFIPS": false } }, { - "documentation": "For region ap-south-1 with FIPS disabled and DualStack disabled", + "documentation": "For region ca-central-1 with FIPS disabled and DualStack disabled", "expect": { "endpoint": { - "url": "https://portal.sso.ap-south-1.amazonaws.com" + "url": "https://portal.sso.ca-central-1.amazonaws.com" } }, "params": { - "UseFIPS": false, + "Region": "ca-central-1", "UseDualStack": false, - "Region": "ap-south-1" + "UseFIPS": false } }, { - "documentation": "For region eu-north-1 with FIPS disabled and DualStack disabled", + "documentation": "For region eu-central-1 with FIPS disabled and DualStack disabled", "expect": { "endpoint": { - "url": "https://portal.sso.eu-north-1.amazonaws.com" + "url": "https://portal.sso.eu-central-1.amazonaws.com" } }, "params": { - "UseFIPS": false, + "Region": "eu-central-1", "UseDualStack": false, - "Region": "eu-north-1" + "UseFIPS": false } }, { - "documentation": "For region ap-southeast-2 with FIPS disabled and DualStack disabled", + "documentation": "For region eu-north-1 with FIPS disabled and DualStack disabled", "expect": { "endpoint": { - "url": "https://portal.sso.ap-southeast-2.amazonaws.com" + "url": "https://portal.sso.eu-north-1.amazonaws.com" } }, "params": { - "UseFIPS": false, + "Region": "eu-north-1", "UseDualStack": false, - "Region": "ap-southeast-2" + "UseFIPS": false } }, { - "documentation": "For region us-east-1 with FIPS disabled and DualStack disabled", + "documentation": "For region eu-south-1 with FIPS disabled and DualStack disabled", "expect": { "endpoint": { - "url": "https://portal.sso.us-east-1.amazonaws.com" + "url": "https://portal.sso.eu-south-1.amazonaws.com" } }, "params": { - "UseFIPS": false, + "Region": "eu-south-1", "UseDualStack": false, - "Region": "us-east-1" + "UseFIPS": false } }, { - "documentation": "For region us-east-2 with FIPS disabled and DualStack disabled", + "documentation": "For region eu-west-1 with FIPS disabled and DualStack disabled", "expect": { "endpoint": { - "url": "https://portal.sso.us-east-2.amazonaws.com" + "url": "https://portal.sso.eu-west-1.amazonaws.com" } }, "params": { - "UseFIPS": false, + "Region": "eu-west-1", "UseDualStack": false, - "Region": "us-east-2" + "UseFIPS": false } }, { - "documentation": "For region eu-west-1 with FIPS disabled and DualStack disabled", + "documentation": "For region eu-west-2 with FIPS disabled and DualStack disabled", "expect": { "endpoint": { - "url": "https://portal.sso.eu-west-1.amazonaws.com" + "url": "https://portal.sso.eu-west-2.amazonaws.com" } }, "params": { - "UseFIPS": false, + "Region": "eu-west-2", "UseDualStack": false, - "Region": "eu-west-1" + "UseFIPS": false } }, { - "documentation": "For region eu-west-2 with FIPS disabled and DualStack disabled", + "documentation": "For region eu-west-3 with FIPS disabled and DualStack disabled", "expect": { "endpoint": { - "url": "https://portal.sso.eu-west-2.amazonaws.com" + "url": "https://portal.sso.eu-west-3.amazonaws.com" } }, "params": { - "UseFIPS": false, + "Region": "eu-west-3", "UseDualStack": false, - "Region": "eu-west-2" + "UseFIPS": false } }, { - "documentation": "For region eu-west-3 with FIPS disabled and DualStack disabled", + "documentation": "For region me-south-1 with FIPS disabled and DualStack disabled", "expect": { "endpoint": { - "url": "https://portal.sso.eu-west-3.amazonaws.com" + "url": "https://portal.sso.me-south-1.amazonaws.com" } }, "params": { - "UseFIPS": false, + "Region": "me-south-1", "UseDualStack": false, - "Region": "eu-west-3" + "UseFIPS": false } }, { - "documentation": "For region ap-northeast-2 with FIPS disabled and DualStack disabled", + "documentation": "For region sa-east-1 with FIPS disabled and DualStack disabled", "expect": { "endpoint": { - "url": "https://portal.sso.ap-northeast-2.amazonaws.com" + "url": "https://portal.sso.sa-east-1.amazonaws.com" } }, "params": { - "UseFIPS": false, + "Region": "sa-east-1", "UseDualStack": false, - "Region": "ap-northeast-2" + "UseFIPS": false } }, { - "documentation": "For region ap-northeast-3 with FIPS disabled and DualStack disabled", + "documentation": "For region us-east-1 with FIPS disabled and DualStack disabled", "expect": { "endpoint": { - "url": "https://portal.sso.ap-northeast-3.amazonaws.com" + "url": "https://portal.sso.us-east-1.amazonaws.com" } }, "params": { - "UseFIPS": false, + "Region": "us-east-1", "UseDualStack": false, - "Region": "ap-northeast-3" + "UseFIPS": false } }, { - "documentation": "For region ap-northeast-1 with FIPS disabled and DualStack disabled", + "documentation": "For region us-east-2 with FIPS disabled and DualStack disabled", "expect": { "endpoint": { - "url": "https://portal.sso.ap-northeast-1.amazonaws.com" + "url": "https://portal.sso.us-east-2.amazonaws.com" } }, "params": { - "UseFIPS": false, + "Region": "us-east-2", "UseDualStack": false, - "Region": "ap-northeast-1" + "UseFIPS": false } }, { - "documentation": "For region eu-central-1 with FIPS disabled and DualStack disabled", + "documentation": "For region us-west-2 with FIPS disabled and DualStack disabled", "expect": { "endpoint": { - "url": "https://portal.sso.eu-central-1.amazonaws.com" + "url": "https://portal.sso.us-west-2.amazonaws.com" } }, "params": { - "UseFIPS": false, + "Region": "us-west-2", "UseDualStack": false, - "Region": "eu-central-1" + "UseFIPS": false } }, { @@ -1495,9 +1521,9 @@ } }, "params": { - "UseFIPS": true, + "Region": "us-east-1", "UseDualStack": true, - "Region": "us-east-1" + "UseFIPS": true } }, { @@ -1508,9 +1534,9 @@ } }, "params": { - "UseFIPS": true, + "Region": "us-east-1", "UseDualStack": false, - "Region": "us-east-1" + "UseFIPS": true } }, { @@ -1521,191 +1547,204 @@ } }, "params": { - "UseFIPS": false, + "Region": "us-east-1", "UseDualStack": true, - "Region": "us-east-1" + "UseFIPS": false } }, { - "documentation": "For region us-gov-west-1 with FIPS disabled and DualStack disabled", + "documentation": "For region cn-north-1 with FIPS enabled and DualStack enabled", "expect": { "endpoint": { - "url": "https://portal.sso.us-gov-west-1.amazonaws.com" + "url": "https://portal.sso-fips.cn-north-1.api.amazonwebservices.com.cn" } }, "params": { - "UseFIPS": false, - "UseDualStack": false, - "Region": "us-gov-west-1" + "Region": "cn-north-1", + "UseDualStack": true, + "UseFIPS": true } }, { - "documentation": "For region us-gov-east-1 with FIPS disabled and DualStack disabled", + "documentation": "For region cn-north-1 with FIPS enabled and DualStack disabled", "expect": { "endpoint": { - "url": "https://portal.sso.us-gov-east-1.amazonaws.com" + "url": "https://portal.sso-fips.cn-north-1.amazonaws.com.cn" } }, "params": { - "UseFIPS": false, + "Region": "cn-north-1", "UseDualStack": false, - "Region": "us-gov-east-1" + "UseFIPS": true } }, { - "documentation": "For region us-gov-east-1 with FIPS enabled and DualStack enabled", + "documentation": "For region cn-north-1 with FIPS disabled and DualStack enabled", "expect": { "endpoint": { - "url": "https://portal.sso-fips.us-gov-east-1.api.aws" + "url": "https://portal.sso.cn-north-1.api.amazonwebservices.com.cn" } }, "params": { - "UseFIPS": true, + "Region": "cn-north-1", "UseDualStack": true, - "Region": "us-gov-east-1" + "UseFIPS": false } }, { - "documentation": "For region us-gov-east-1 with FIPS enabled and DualStack disabled", + "documentation": "For region cn-north-1 with FIPS disabled and DualStack disabled", "expect": { "endpoint": { - "url": "https://portal.sso-fips.us-gov-east-1.amazonaws.com" + "url": "https://portal.sso.cn-north-1.amazonaws.com.cn" } }, "params": { - "UseFIPS": true, + "Region": "cn-north-1", "UseDualStack": false, - "Region": "us-gov-east-1" + "UseFIPS": false } }, { - "documentation": "For region us-gov-east-1 with FIPS disabled and DualStack enabled", + "documentation": "For region us-gov-east-1 with FIPS disabled and DualStack disabled", "expect": { "endpoint": { - "url": "https://portal.sso.us-gov-east-1.api.aws" + "url": "https://portal.sso.us-gov-east-1.amazonaws.com" } }, "params": { - "UseFIPS": false, - "UseDualStack": true, - "Region": "us-gov-east-1" + "Region": "us-gov-east-1", + "UseDualStack": false, + "UseFIPS": false } }, { - "documentation": "For region us-isob-east-1 with FIPS enabled and DualStack disabled", + "documentation": "For region us-gov-west-1 with FIPS disabled and DualStack disabled", "expect": { "endpoint": { - "url": "https://portal.sso-fips.us-isob-east-1.sc2s.sgov.gov" + "url": "https://portal.sso.us-gov-west-1.amazonaws.com" } }, "params": { - "UseFIPS": true, + "Region": "us-gov-west-1", "UseDualStack": false, - "Region": "us-isob-east-1" + "UseFIPS": false } }, { - "documentation": "For region us-isob-east-1 with FIPS disabled and DualStack disabled", + "documentation": "For region us-gov-east-1 with FIPS enabled and DualStack enabled", "expect": { "endpoint": { - "url": "https://portal.sso.us-isob-east-1.sc2s.sgov.gov" + "url": "https://portal.sso-fips.us-gov-east-1.api.aws" } }, "params": { - "UseFIPS": false, + "Region": "us-gov-east-1", + "UseDualStack": true, + "UseFIPS": true + } + }, + { + "documentation": "For region us-gov-east-1 with FIPS enabled and DualStack disabled", + "expect": { + "endpoint": { + "url": "https://portal.sso-fips.us-gov-east-1.amazonaws.com" + } + }, + "params": { + "Region": "us-gov-east-1", "UseDualStack": false, - "Region": "us-isob-east-1" + "UseFIPS": true } }, { - "documentation": "For region cn-north-1 with FIPS enabled and DualStack enabled", + "documentation": "For region us-gov-east-1 with FIPS disabled and DualStack enabled", "expect": { "endpoint": { - "url": "https://portal.sso-fips.cn-north-1.api.amazonwebservices.com.cn" + "url": "https://portal.sso.us-gov-east-1.api.aws" } }, "params": { - "UseFIPS": true, + "Region": "us-gov-east-1", "UseDualStack": true, - "Region": "cn-north-1" + "UseFIPS": false } }, { - "documentation": "For region cn-north-1 with FIPS enabled and DualStack disabled", + "documentation": "For region us-iso-east-1 with FIPS enabled and DualStack disabled", "expect": { "endpoint": { - "url": "https://portal.sso-fips.cn-north-1.amazonaws.com.cn" + "url": "https://portal.sso-fips.us-iso-east-1.c2s.ic.gov" } }, "params": { - "UseFIPS": true, + "Region": "us-iso-east-1", "UseDualStack": false, - "Region": "cn-north-1" + "UseFIPS": true } }, { - "documentation": "For region cn-north-1 with FIPS disabled and DualStack enabled", + "documentation": "For region us-iso-east-1 with FIPS disabled and DualStack disabled", "expect": { "endpoint": { - "url": "https://portal.sso.cn-north-1.api.amazonwebservices.com.cn" + "url": "https://portal.sso.us-iso-east-1.c2s.ic.gov" } }, "params": { - "UseFIPS": false, - "UseDualStack": true, - "Region": "cn-north-1" + "Region": "us-iso-east-1", + "UseDualStack": false, + "UseFIPS": false } }, { - "documentation": "For region cn-north-1 with FIPS disabled and DualStack disabled", + "documentation": "For region us-isob-east-1 with FIPS enabled and DualStack disabled", "expect": { "endpoint": { - "url": "https://portal.sso.cn-north-1.amazonaws.com.cn" + "url": "https://portal.sso-fips.us-isob-east-1.sc2s.sgov.gov" } }, "params": { - "UseFIPS": false, + "Region": "us-isob-east-1", "UseDualStack": false, - "Region": "cn-north-1" + "UseFIPS": true } }, { - "documentation": "For region us-iso-east-1 with FIPS enabled and DualStack disabled", + "documentation": "For region us-isob-east-1 with FIPS disabled and DualStack disabled", "expect": { "endpoint": { - "url": "https://portal.sso-fips.us-iso-east-1.c2s.ic.gov" + "url": "https://portal.sso.us-isob-east-1.sc2s.sgov.gov" } }, "params": { - "UseFIPS": true, + "Region": "us-isob-east-1", "UseDualStack": false, - "Region": "us-iso-east-1" + "UseFIPS": false } }, { - "documentation": "For region us-iso-east-1 with FIPS disabled and DualStack disabled", + "documentation": "For custom endpoint with region set and fips disabled and dualstack disabled", "expect": { "endpoint": { - "url": "https://portal.sso.us-iso-east-1.c2s.ic.gov" + "url": "https://example.com" } }, "params": { - "UseFIPS": false, + "Region": "us-east-1", "UseDualStack": false, - "Region": "us-iso-east-1" + "UseFIPS": false, + "Endpoint": "https://example.com" } }, { - "documentation": "For custom endpoint with fips disabled and dualstack disabled", + "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, - "Region": "us-east-1", + "UseFIPS": false, "Endpoint": "https://example.com" } }, @@ -1715,9 +1754,9 @@ "error": "Invalid Configuration: FIPS and custom endpoint are not supported" }, "params": { - "UseFIPS": true, - "UseDualStack": false, "Region": "us-east-1", + "UseDualStack": false, + "UseFIPS": true, "Endpoint": "https://example.com" } }, @@ -1727,9 +1766,9 @@ "error": "Invalid Configuration: Dualstack and custom endpoint are not supported" }, "params": { - "UseFIPS": false, - "UseDualStack": true, "Region": "us-east-1", + "UseDualStack": true, + "UseFIPS": false, "Endpoint": "https://example.com" } } diff --git a/aws/sdk/aws-models/sts.json b/aws/sdk/aws-models/sts.json index 7780eaf981..915bf2d076 100644 --- a/aws/sdk/aws-models/sts.json +++ b/aws/sdk/aws-models/sts.json @@ -115,6 +115,36 @@ "rules": [ { "conditions": [ + { + "fn": "booleanEquals", + "argv": [ + { + "ref": "UseGlobalEndpoint" + }, + true + ] + }, + { + "fn": "not", + "argv": [ + { + "fn": "isSet", + "argv": [ + { + "ref": "Endpoint" + } + ] + } + ] + }, + { + "fn": "isSet", + "argv": [ + { + "ref": "Region" + } + ] + }, { "fn": "aws.partition", "argv": [ @@ -123,6 +153,24 @@ } ], "assign": "PartitionResult" + }, + { + "fn": "booleanEquals", + "argv": [ + { + "ref": "UseFIPS" + }, + false + ] + }, + { + "fn": "booleanEquals", + "argv": [ + { + "ref": "UseDualStack" + }, + false + ] } ], "type": "tree", @@ -130,517 +178,529 @@ { "conditions": [ { - "fn": "booleanEquals", + "fn": "stringEquals", "argv": [ { - "ref": "UseGlobalEndpoint" + "ref": "Region" }, - true + "ap-northeast-1" + ] + } + ], + "endpoint": { + "url": "https://sts.amazonaws.com", + "properties": { + "authSchemes": [ + { + "name": "sigv4", + "signingName": "sts", + "signingRegion": "us-east-1" + } ] }, + "headers": {} + }, + "type": "endpoint" + }, + { + "conditions": [ { - "fn": "booleanEquals", + "fn": "stringEquals", "argv": [ { - "ref": "UseFIPS" + "ref": "Region" }, - false + "ap-south-1" + ] + } + ], + "endpoint": { + "url": "https://sts.amazonaws.com", + "properties": { + "authSchemes": [ + { + "name": "sigv4", + "signingName": "sts", + "signingRegion": "us-east-1" + } ] }, + "headers": {} + }, + "type": "endpoint" + }, + { + "conditions": [ { - "fn": "booleanEquals", + "fn": "stringEquals", "argv": [ { - "ref": "UseDualStack" + "ref": "Region" }, - false + "ap-southeast-1" + ] + } + ], + "endpoint": { + "url": "https://sts.amazonaws.com", + "properties": { + "authSchemes": [ + { + "name": "sigv4", + "signingName": "sts", + "signingRegion": "us-east-1" + } ] }, + "headers": {} + }, + "type": "endpoint" + }, + { + "conditions": [ { - "fn": "not", + "fn": "stringEquals", "argv": [ { - "fn": "isSet", - "argv": [ - { - "ref": "Endpoint" - } - ] - } + "ref": "Region" + }, + "ap-southeast-2" ] } ], - "type": "tree", - "rules": [ - { - "conditions": [ + "endpoint": { + "url": "https://sts.amazonaws.com", + "properties": { + "authSchemes": [ { - "fn": "stringEquals", - "argv": [ - { - "ref": "Region" - }, - "ap-northeast-1" - ] + "name": "sigv4", + "signingName": "sts", + "signingRegion": "us-east-1" } - ], - "endpoint": { - "url": "https://sts.amazonaws.com", - "properties": { - "authSchemes": [ - { - "name": "sigv4", - "signingRegion": "us-east-1", - "signingName": "sts" - } - ] - }, - "headers": {} - }, - "type": "endpoint" + ] }, + "headers": {} + }, + "type": "endpoint" + }, + { + "conditions": [ { - "conditions": [ + "fn": "stringEquals", + "argv": [ { - "fn": "stringEquals", - "argv": [ - { - "ref": "Region" - }, - "ap-south-1" - ] - } - ], - "endpoint": { - "url": "https://sts.amazonaws.com", - "properties": { - "authSchemes": [ - { - "name": "sigv4", - "signingRegion": "us-east-1", - "signingName": "sts" - } - ] + "ref": "Region" }, - "headers": {} - }, - "type": "endpoint" + "aws-global" + ] + } + ], + "endpoint": { + "url": "https://sts.amazonaws.com", + "properties": { + "authSchemes": [ + { + "name": "sigv4", + "signingName": "sts", + "signingRegion": "us-east-1" + } + ] }, + "headers": {} + }, + "type": "endpoint" + }, + { + "conditions": [ { - "conditions": [ + "fn": "stringEquals", + "argv": [ { - "fn": "stringEquals", - "argv": [ - { - "ref": "Region" - }, - "ap-southeast-1" - ] - } - ], - "endpoint": { - "url": "https://sts.amazonaws.com", - "properties": { - "authSchemes": [ - { - "name": "sigv4", - "signingRegion": "us-east-1", - "signingName": "sts" - } - ] + "ref": "Region" }, - "headers": {} - }, - "type": "endpoint" + "ca-central-1" + ] + } + ], + "endpoint": { + "url": "https://sts.amazonaws.com", + "properties": { + "authSchemes": [ + { + "name": "sigv4", + "signingName": "sts", + "signingRegion": "us-east-1" + } + ] }, + "headers": {} + }, + "type": "endpoint" + }, + { + "conditions": [ { - "conditions": [ + "fn": "stringEquals", + "argv": [ { - "fn": "stringEquals", - "argv": [ - { - "ref": "Region" - }, - "ap-southeast-2" - ] - } - ], - "endpoint": { - "url": "https://sts.amazonaws.com", - "properties": { - "authSchemes": [ - { - "name": "sigv4", - "signingRegion": "us-east-1", - "signingName": "sts" - } - ] + "ref": "Region" }, - "headers": {} - }, - "type": "endpoint" + "eu-central-1" + ] + } + ], + "endpoint": { + "url": "https://sts.amazonaws.com", + "properties": { + "authSchemes": [ + { + "name": "sigv4", + "signingName": "sts", + "signingRegion": "us-east-1" + } + ] }, + "headers": {} + }, + "type": "endpoint" + }, + { + "conditions": [ { - "conditions": [ + "fn": "stringEquals", + "argv": [ { - "fn": "stringEquals", - "argv": [ - { - "ref": "Region" - }, - "aws-global" - ] - } - ], - "endpoint": { - "url": "https://sts.amazonaws.com", - "properties": { - "authSchemes": [ - { - "name": "sigv4", - "signingRegion": "us-east-1", - "signingName": "sts" - } - ] + "ref": "Region" }, - "headers": {} - }, - "type": "endpoint" + "eu-north-1" + ] + } + ], + "endpoint": { + "url": "https://sts.amazonaws.com", + "properties": { + "authSchemes": [ + { + "name": "sigv4", + "signingName": "sts", + "signingRegion": "us-east-1" + } + ] }, + "headers": {} + }, + "type": "endpoint" + }, + { + "conditions": [ { - "conditions": [ + "fn": "stringEquals", + "argv": [ { - "fn": "stringEquals", - "argv": [ - { - "ref": "Region" - }, - "ca-central-1" - ] - } - ], - "endpoint": { - "url": "https://sts.amazonaws.com", - "properties": { - "authSchemes": [ - { - "name": "sigv4", - "signingRegion": "us-east-1", - "signingName": "sts" - } - ] + "ref": "Region" }, - "headers": {} - }, - "type": "endpoint" + "eu-west-1" + ] + } + ], + "endpoint": { + "url": "https://sts.amazonaws.com", + "properties": { + "authSchemes": [ + { + "name": "sigv4", + "signingName": "sts", + "signingRegion": "us-east-1" + } + ] }, + "headers": {} + }, + "type": "endpoint" + }, + { + "conditions": [ { - "conditions": [ + "fn": "stringEquals", + "argv": [ { - "fn": "stringEquals", - "argv": [ - { - "ref": "Region" - }, - "eu-central-1" - ] - } - ], - "endpoint": { - "url": "https://sts.amazonaws.com", - "properties": { - "authSchemes": [ - { - "name": "sigv4", - "signingRegion": "us-east-1", - "signingName": "sts" - } - ] + "ref": "Region" }, - "headers": {} - }, - "type": "endpoint" + "eu-west-2" + ] + } + ], + "endpoint": { + "url": "https://sts.amazonaws.com", + "properties": { + "authSchemes": [ + { + "name": "sigv4", + "signingName": "sts", + "signingRegion": "us-east-1" + } + ] }, + "headers": {} + }, + "type": "endpoint" + }, + { + "conditions": [ { - "conditions": [ + "fn": "stringEquals", + "argv": [ { - "fn": "stringEquals", - "argv": [ - { - "ref": "Region" - }, - "eu-north-1" - ] - } - ], - "endpoint": { - "url": "https://sts.amazonaws.com", - "properties": { - "authSchemes": [ - { - "name": "sigv4", - "signingRegion": "us-east-1", - "signingName": "sts" - } - ] + "ref": "Region" }, - "headers": {} - }, - "type": "endpoint" + "eu-west-3" + ] + } + ], + "endpoint": { + "url": "https://sts.amazonaws.com", + "properties": { + "authSchemes": [ + { + "name": "sigv4", + "signingName": "sts", + "signingRegion": "us-east-1" + } + ] }, + "headers": {} + }, + "type": "endpoint" + }, + { + "conditions": [ { - "conditions": [ + "fn": "stringEquals", + "argv": [ { - "fn": "stringEquals", - "argv": [ - { - "ref": "Region" - }, - "eu-west-1" - ] - } - ], - "endpoint": { - "url": "https://sts.amazonaws.com", - "properties": { - "authSchemes": [ - { - "name": "sigv4", - "signingRegion": "us-east-1", - "signingName": "sts" - } - ] + "ref": "Region" }, - "headers": {} - }, - "type": "endpoint" + "sa-east-1" + ] + } + ], + "endpoint": { + "url": "https://sts.amazonaws.com", + "properties": { + "authSchemes": [ + { + "name": "sigv4", + "signingName": "sts", + "signingRegion": "us-east-1" + } + ] }, + "headers": {} + }, + "type": "endpoint" + }, + { + "conditions": [ { - "conditions": [ + "fn": "stringEquals", + "argv": [ { - "fn": "stringEquals", - "argv": [ - { - "ref": "Region" - }, - "eu-west-2" - ] - } - ], - "endpoint": { - "url": "https://sts.amazonaws.com", - "properties": { - "authSchemes": [ - { - "name": "sigv4", - "signingRegion": "us-east-1", - "signingName": "sts" - } - ] + "ref": "Region" }, - "headers": {} - }, - "type": "endpoint" + "us-east-1" + ] + } + ], + "endpoint": { + "url": "https://sts.amazonaws.com", + "properties": { + "authSchemes": [ + { + "name": "sigv4", + "signingName": "sts", + "signingRegion": "us-east-1" + } + ] }, + "headers": {} + }, + "type": "endpoint" + }, + { + "conditions": [ { - "conditions": [ + "fn": "stringEquals", + "argv": [ { - "fn": "stringEquals", - "argv": [ - { - "ref": "Region" - }, - "eu-west-3" - ] - } - ], - "endpoint": { - "url": "https://sts.amazonaws.com", - "properties": { - "authSchemes": [ - { - "name": "sigv4", - "signingRegion": "us-east-1", - "signingName": "sts" - } - ] + "ref": "Region" }, - "headers": {} - }, - "type": "endpoint" + "us-east-2" + ] + } + ], + "endpoint": { + "url": "https://sts.amazonaws.com", + "properties": { + "authSchemes": [ + { + "name": "sigv4", + "signingName": "sts", + "signingRegion": "us-east-1" + } + ] }, + "headers": {} + }, + "type": "endpoint" + }, + { + "conditions": [ { - "conditions": [ + "fn": "stringEquals", + "argv": [ { - "fn": "stringEquals", - "argv": [ - { - "ref": "Region" - }, - "sa-east-1" - ] - } - ], - "endpoint": { - "url": "https://sts.amazonaws.com", - "properties": { - "authSchemes": [ - { - "name": "sigv4", - "signingRegion": "us-east-1", - "signingName": "sts" - } - ] + "ref": "Region" }, - "headers": {} - }, - "type": "endpoint" + "us-west-1" + ] + } + ], + "endpoint": { + "url": "https://sts.amazonaws.com", + "properties": { + "authSchemes": [ + { + "name": "sigv4", + "signingName": "sts", + "signingRegion": "us-east-1" + } + ] }, + "headers": {} + }, + "type": "endpoint" + }, + { + "conditions": [ { - "conditions": [ + "fn": "stringEquals", + "argv": [ { - "fn": "stringEquals", - "argv": [ - { - "ref": "Region" - }, - "us-east-1" - ] - } - ], - "endpoint": { - "url": "https://sts.amazonaws.com", - "properties": { - "authSchemes": [ - { - "name": "sigv4", - "signingRegion": "us-east-1", - "signingName": "sts" - } - ] + "ref": "Region" }, - "headers": {} - }, - "type": "endpoint" + "us-west-2" + ] + } + ], + "endpoint": { + "url": "https://sts.amazonaws.com", + "properties": { + "authSchemes": [ + { + "name": "sigv4", + "signingName": "sts", + "signingRegion": "us-east-1" + } + ] }, - { - "conditions": [ + "headers": {} + }, + "type": "endpoint" + }, + { + "conditions": [], + "endpoint": { + "url": "https://sts.{Region}.{PartitionResult#dnsSuffix}", + "properties": { + "authSchemes": [ { - "fn": "stringEquals", - "argv": [ - { - "ref": "Region" - }, - "us-east-2" - ] + "name": "sigv4", + "signingName": "sts", + "signingRegion": "{Region}" } - ], - "endpoint": { - "url": "https://sts.amazonaws.com", - "properties": { - "authSchemes": [ - { - "name": "sigv4", - "signingRegion": "us-east-1", - "signingName": "sts" - } - ] - }, - "headers": {} - }, - "type": "endpoint" + ] }, + "headers": {} + }, + "type": "endpoint" + } + ] + }, + { + "conditions": [ + { + "fn": "isSet", + "argv": [ { - "conditions": [ + "ref": "Endpoint" + } + ] + } + ], + "type": "tree", + "rules": [ + { + "conditions": [ + { + "fn": "booleanEquals", + "argv": [ { - "fn": "stringEquals", - "argv": [ - { - "ref": "Region" - }, - "us-west-1" - ] - } - ], - "endpoint": { - "url": "https://sts.amazonaws.com", - "properties": { - "authSchemes": [ - { - "name": "sigv4", - "signingRegion": "us-east-1", - "signingName": "sts" - } - ] + "ref": "UseFIPS" }, - "headers": {} - }, - "type": "endpoint" - }, + true + ] + } + ], + "error": "Invalid Configuration: FIPS and custom endpoint are not supported", + "type": "error" + }, + { + "conditions": [], + "type": "tree", + "rules": [ { "conditions": [ { - "fn": "stringEquals", + "fn": "booleanEquals", "argv": [ { - "ref": "Region" + "ref": "UseDualStack" }, - "us-west-2" + true ] } ], - "endpoint": { - "url": "https://sts.amazonaws.com", - "properties": { - "authSchemes": [ - { - "name": "sigv4", - "signingRegion": "us-east-1", - "signingName": "sts" - } - ] - }, - "headers": {} - }, - "type": "endpoint" + "error": "Invalid Configuration: Dualstack and custom endpoint are not supported", + "type": "error" }, { "conditions": [], "endpoint": { - "url": "https://sts.{Region}.{PartitionResult#dnsSuffix}", - "properties": { - "authSchemes": [ - { - "name": "sigv4", - "signingRegion": "{Region}", - "signingName": "sts" - } - ] + "url": { + "ref": "Endpoint" }, + "properties": {}, "headers": {} }, "type": "endpoint" } ] - }, + } + ] + }, + { + "conditions": [], + "type": "tree", + "rules": [ { "conditions": [ { "fn": "isSet", "argv": [ { - "ref": "Endpoint" + "ref": "Region" } ] - }, - { - "fn": "parseURL", - "argv": [ - { - "ref": "Endpoint" - } - ], - "assign": "url" } ], "type": "tree", @@ -648,24 +708,28 @@ { "conditions": [ { - "fn": "booleanEquals", + "fn": "aws.partition", "argv": [ { - "ref": "UseFIPS" - }, - true - ] + "ref": "Region" + } + ], + "assign": "PartitionResult" } ], - "error": "Invalid Configuration: FIPS and custom endpoint are not supported", - "type": "error" - }, - { - "conditions": [], "type": "tree", "rules": [ { "conditions": [ + { + "fn": "booleanEquals", + "argv": [ + { + "ref": "UseFIPS" + }, + true + ] + }, { "fn": "booleanEquals", "argv": [ @@ -676,159 +740,235 @@ ] } ], - "error": "Invalid Configuration: Dualstack and custom endpoint are not supported", - "type": "error" - }, - { - "conditions": [], - "endpoint": { - "url": { - "ref": "Endpoint" - }, - "properties": {}, - "headers": {} - }, - "type": "endpoint" - } - ] - } - ] - }, - { - "conditions": [ - { - "fn": "booleanEquals", - "argv": [ - { - "ref": "UseFIPS" - }, - true - ] - }, - { - "fn": "booleanEquals", - "argv": [ - { - "ref": "UseDualStack" - }, - true - ] - } - ], - "type": "tree", - "rules": [ - { - "conditions": [ - { - "fn": "booleanEquals", - "argv": [ - true, + "type": "tree", + "rules": [ { - "fn": "getAttr", - "argv": [ + "conditions": [ { - "ref": "PartitionResult" + "fn": "booleanEquals", + "argv": [ + true, + { + "fn": "getAttr", + "argv": [ + { + "ref": "PartitionResult" + }, + "supportsFIPS" + ] + } + ] }, - "supportsFIPS" + { + "fn": "booleanEquals", + "argv": [ + true, + { + "fn": "getAttr", + "argv": [ + { + "ref": "PartitionResult" + }, + "supportsDualStack" + ] + } + ] + } + ], + "type": "tree", + "rules": [ + { + "conditions": [], + "type": "tree", + "rules": [ + { + "conditions": [], + "endpoint": { + "url": "https://sts-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" } ] }, { - "fn": "booleanEquals", - "argv": [ - true, + "conditions": [ { - "fn": "getAttr", + "fn": "booleanEquals", "argv": [ { - "ref": "PartitionResult" + "ref": "UseFIPS" }, - "supportsDualStack" + true + ] + } + ], + "type": "tree", + "rules": [ + { + "conditions": [ + { + "fn": "booleanEquals", + "argv": [ + true, + { + "fn": "getAttr", + "argv": [ + { + "ref": "PartitionResult" + }, + "supportsFIPS" + ] + } + ] + } + ], + "type": "tree", + "rules": [ + { + "conditions": [], + "type": "tree", + "rules": [ + { + "conditions": [ + { + "fn": "stringEquals", + "argv": [ + "aws-us-gov", + { + "fn": "getAttr", + "argv": [ + { + "ref": "PartitionResult" + }, + "name" + ] + } + ] + } + ], + "endpoint": { + "url": "https://sts.{Region}.amazonaws.com", + "properties": {}, + "headers": {} + }, + "type": "endpoint" + }, + { + "conditions": [], + "endpoint": { + "url": "https://sts-fips.{Region}.{PartitionResult#dnsSuffix}", + "properties": {}, + "headers": {} + }, + "type": "endpoint" + } + ] + } ] + }, + { + "conditions": [], + "error": "FIPS is enabled but this partition does not support FIPS", + "type": "error" } ] - } - ], - "type": "tree", - "rules": [ - { - "conditions": [], - "endpoint": { - "url": "https://sts-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, + "conditions": [ { - "fn": "getAttr", + "fn": "booleanEquals", "argv": [ { - "ref": "PartitionResult" + "ref": "UseDualStack" }, - "supportsFIPS" + true ] } - ] - } - ], - "type": "tree", - "rules": [ - { - "conditions": [], + ], "type": "tree", "rules": [ { "conditions": [ { - "fn": "stringEquals", + "fn": "booleanEquals", "argv": [ - "aws-us-gov", + true, { "fn": "getAttr", "argv": [ { "ref": "PartitionResult" }, - "name" + "supportsDualStack" ] } ] } ], + "type": "tree", + "rules": [ + { + "conditions": [], + "type": "tree", + "rules": [ + { + "conditions": [], + "endpoint": { + "url": "https://sts.{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": [ + { + "fn": "stringEquals", + "argv": [ + { + "ref": "Region" + }, + "aws-global" + ] + } + ], "endpoint": { - "url": "https://sts.{Region}.{PartitionResult#dnsSuffix}", - "properties": {}, + "url": "https://sts.amazonaws.com", + "properties": { + "authSchemes": [ + { + "name": "sigv4", + "signingName": "sts", + "signingRegion": "us-east-1" + } + ] + }, "headers": {} }, "type": "endpoint" @@ -836,7 +976,7 @@ { "conditions": [], "endpoint": { - "url": "https://sts-fips.{Region}.{PartitionResult#dnsSuffix}", + "url": "https://sts.{Region}.{PartitionResult#dnsSuffix}", "properties": {}, "headers": {} }, @@ -845,1313 +985,31 @@ ] } ] - }, - { - "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": [], - "endpoint": { - "url": "https://sts.{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": [ - { - "fn": "stringEquals", - "argv": [ - { - "ref": "Region" - }, - "aws-global" - ] - } - ], - "endpoint": { - "url": "https://sts.amazonaws.com", - "properties": { - "authSchemes": [ - { - "name": "sigv4", - "signingRegion": "us-east-1", - "signingName": "sts" - } - ] - }, - "headers": {} - }, - "type": "endpoint" - }, - { - "conditions": [], - "endpoint": { - "url": "https://sts.{Region}.{PartitionResult#dnsSuffix}", - "properties": {}, - "headers": {} - }, - "type": "endpoint" - } - ] - } - ] - } - ] - }, - "smithy.rules#endpointTests": { - "testCases": [ - { - "documentation": "For region ap-south-2 with FIPS enabled and DualStack enabled", - "expect": { - "endpoint": { - "url": "https://sts-fips.ap-south-2.api.aws" - } - }, - "params": { - "UseFIPS": true, - "Region": "ap-south-2", - "UseDualStack": true - } - }, - { - "documentation": "For region ap-south-2 with FIPS enabled and DualStack disabled", - "expect": { - "endpoint": { - "url": "https://sts-fips.ap-south-2.amazonaws.com" - } - }, - "params": { - "UseFIPS": true, - "Region": "ap-south-2", - "UseDualStack": false - } - }, - { - "documentation": "For region ap-south-2 with FIPS disabled and DualStack enabled", - "expect": { - "endpoint": { - "url": "https://sts.ap-south-2.api.aws" - } - }, - "params": { - "UseFIPS": false, - "Region": "ap-south-2", - "UseDualStack": true - } - }, - { - "documentation": "For region ap-south-2 with FIPS disabled and DualStack disabled", - "expect": { - "endpoint": { - "url": "https://sts.ap-south-2.amazonaws.com" - } - }, - "params": { - "UseFIPS": false, - "Region": "ap-south-2", - "UseDualStack": false - } - }, - { - "documentation": "For region ap-south-1 with FIPS enabled and DualStack enabled", - "expect": { - "endpoint": { - "url": "https://sts-fips.ap-south-1.api.aws" - } - }, - "params": { - "UseFIPS": true, - "Region": "ap-south-1", - "UseDualStack": true - } - }, - { - "documentation": "For region ap-south-1 with FIPS enabled and DualStack disabled", - "expect": { - "endpoint": { - "url": "https://sts-fips.ap-south-1.amazonaws.com" - } - }, - "params": { - "UseFIPS": true, - "Region": "ap-south-1", - "UseDualStack": false - } - }, - { - "documentation": "For region ap-south-1 with FIPS disabled and DualStack enabled", - "expect": { - "endpoint": { - "url": "https://sts.ap-south-1.api.aws" - } - }, - "params": { - "UseFIPS": false, - "Region": "ap-south-1", - "UseDualStack": true - } - }, - { - "documentation": "For region ap-south-1 with FIPS disabled and DualStack disabled", - "expect": { - "endpoint": { - "url": "https://sts.ap-south-1.amazonaws.com" - } - }, - "params": { - "UseFIPS": false, - "Region": "ap-south-1", - "UseDualStack": false - } - }, - { - "documentation": "For region eu-south-1 with FIPS enabled and DualStack enabled", - "expect": { - "endpoint": { - "url": "https://sts-fips.eu-south-1.api.aws" - } - }, - "params": { - "UseFIPS": true, - "Region": "eu-south-1", - "UseDualStack": true - } - }, - { - "documentation": "For region eu-south-1 with FIPS enabled and DualStack disabled", - "expect": { - "endpoint": { - "url": "https://sts-fips.eu-south-1.amazonaws.com" - } - }, - "params": { - "UseFIPS": true, - "Region": "eu-south-1", - "UseDualStack": false - } - }, - { - "documentation": "For region eu-south-1 with FIPS disabled and DualStack enabled", - "expect": { - "endpoint": { - "url": "https://sts.eu-south-1.api.aws" - } - }, - "params": { - "UseFIPS": false, - "Region": "eu-south-1", - "UseDualStack": true - } - }, - { - "documentation": "For region eu-south-1 with FIPS disabled and DualStack disabled", - "expect": { - "endpoint": { - "url": "https://sts.eu-south-1.amazonaws.com" - } - }, - "params": { - "UseFIPS": false, - "Region": "eu-south-1", - "UseDualStack": false - } - }, - { - "documentation": "For region eu-south-2 with FIPS enabled and DualStack enabled", - "expect": { - "endpoint": { - "url": "https://sts-fips.eu-south-2.api.aws" - } - }, - "params": { - "UseFIPS": true, - "Region": "eu-south-2", - "UseDualStack": true - } - }, - { - "documentation": "For region eu-south-2 with FIPS enabled and DualStack disabled", - "expect": { - "endpoint": { - "url": "https://sts-fips.eu-south-2.amazonaws.com" - } - }, - "params": { - "UseFIPS": true, - "Region": "eu-south-2", - "UseDualStack": false - } - }, - { - "documentation": "For region eu-south-2 with FIPS disabled and DualStack enabled", - "expect": { - "endpoint": { - "url": "https://sts.eu-south-2.api.aws" - } - }, - "params": { - "UseFIPS": false, - "Region": "eu-south-2", - "UseDualStack": true - } - }, - { - "documentation": "For region eu-south-2 with FIPS disabled and DualStack disabled", - "expect": { - "endpoint": { - "url": "https://sts.eu-south-2.amazonaws.com" - } - }, - "params": { - "UseFIPS": false, - "Region": "eu-south-2", - "UseDualStack": false - } - }, - { - "documentation": "For region us-gov-east-1 with FIPS enabled and DualStack enabled", - "expect": { - "endpoint": { - "url": "https://sts-fips.us-gov-east-1.api.aws" - } - }, - "params": { - "UseFIPS": true, - "Region": "us-gov-east-1", - "UseDualStack": true - } - }, - { - "documentation": "For region us-gov-east-1 with FIPS enabled and DualStack disabled", - "expect": { - "endpoint": { - "url": "https://sts.us-gov-east-1.amazonaws.com" - } - }, - "params": { - "UseFIPS": true, - "Region": "us-gov-east-1", - "UseDualStack": false - } - }, - { - "documentation": "For region us-gov-east-1 with FIPS disabled and DualStack enabled", - "expect": { - "endpoint": { - "url": "https://sts.us-gov-east-1.api.aws" - } - }, - "params": { - "UseFIPS": false, - "Region": "us-gov-east-1", - "UseDualStack": true - } - }, - { - "documentation": "For region us-gov-east-1 with FIPS disabled and DualStack disabled", - "expect": { - "endpoint": { - "url": "https://sts.us-gov-east-1.amazonaws.com" - } - }, - "params": { - "UseFIPS": false, - "Region": "us-gov-east-1", - "UseDualStack": false - } - }, - { - "documentation": "For region me-central-1 with FIPS enabled and DualStack enabled", - "expect": { - "endpoint": { - "url": "https://sts-fips.me-central-1.api.aws" - } - }, - "params": { - "UseFIPS": true, - "Region": "me-central-1", - "UseDualStack": true - } - }, - { - "documentation": "For region me-central-1 with FIPS enabled and DualStack disabled", - "expect": { - "endpoint": { - "url": "https://sts-fips.me-central-1.amazonaws.com" - } - }, - "params": { - "UseFIPS": true, - "Region": "me-central-1", - "UseDualStack": false - } - }, - { - "documentation": "For region me-central-1 with FIPS disabled and DualStack enabled", - "expect": { - "endpoint": { - "url": "https://sts.me-central-1.api.aws" - } - }, - "params": { - "UseFIPS": false, - "Region": "me-central-1", - "UseDualStack": true - } - }, - { - "documentation": "For region me-central-1 with FIPS disabled and DualStack disabled", - "expect": { - "endpoint": { - "url": "https://sts.me-central-1.amazonaws.com" - } - }, - "params": { - "UseFIPS": false, - "Region": "me-central-1", - "UseDualStack": false - } - }, - { - "documentation": "For region ca-central-1 with FIPS enabled and DualStack enabled", - "expect": { - "endpoint": { - "url": "https://sts-fips.ca-central-1.api.aws" - } - }, - "params": { - "UseFIPS": true, - "Region": "ca-central-1", - "UseDualStack": true - } - }, - { - "documentation": "For region ca-central-1 with FIPS enabled and DualStack disabled", - "expect": { - "endpoint": { - "url": "https://sts-fips.ca-central-1.amazonaws.com" - } - }, - "params": { - "UseFIPS": true, - "Region": "ca-central-1", - "UseDualStack": false - } - }, - { - "documentation": "For region ca-central-1 with FIPS disabled and DualStack enabled", - "expect": { - "endpoint": { - "url": "https://sts.ca-central-1.api.aws" - } - }, - "params": { - "UseFIPS": false, - "Region": "ca-central-1", - "UseDualStack": true - } - }, - { - "documentation": "For region ca-central-1 with FIPS disabled and DualStack disabled", - "expect": { - "endpoint": { - "url": "https://sts.ca-central-1.amazonaws.com" - } - }, - "params": { - "UseFIPS": false, - "Region": "ca-central-1", - "UseDualStack": false - } - }, - { - "documentation": "For region eu-central-1 with FIPS enabled and DualStack enabled", - "expect": { - "endpoint": { - "url": "https://sts-fips.eu-central-1.api.aws" - } - }, - "params": { - "UseFIPS": true, - "Region": "eu-central-1", - "UseDualStack": true - } - }, - { - "documentation": "For region eu-central-1 with FIPS enabled and DualStack disabled", - "expect": { - "endpoint": { - "url": "https://sts-fips.eu-central-1.amazonaws.com" - } - }, - "params": { - "UseFIPS": true, - "Region": "eu-central-1", - "UseDualStack": false - } - }, - { - "documentation": "For region eu-central-1 with FIPS disabled and DualStack enabled", - "expect": { - "endpoint": { - "url": "https://sts.eu-central-1.api.aws" - } - }, - "params": { - "UseFIPS": false, - "Region": "eu-central-1", - "UseDualStack": true - } - }, - { - "documentation": "For region eu-central-1 with FIPS disabled and DualStack disabled", - "expect": { - "endpoint": { - "url": "https://sts.eu-central-1.amazonaws.com" - } - }, - "params": { - "UseFIPS": false, - "Region": "eu-central-1", - "UseDualStack": false - } - }, - { - "documentation": "For region us-iso-west-1 with FIPS enabled and DualStack enabled", - "expect": { - "error": "FIPS and DualStack are enabled, but this partition does not support one or both" - }, - "params": { - "UseFIPS": true, - "Region": "us-iso-west-1", - "UseDualStack": true - } - }, - { - "documentation": "For region us-iso-west-1 with FIPS enabled and DualStack disabled", - "expect": { - "endpoint": { - "url": "https://sts-fips.us-iso-west-1.c2s.ic.gov" - } - }, - "params": { - "UseFIPS": true, - "Region": "us-iso-west-1", - "UseDualStack": false - } - }, - { - "documentation": "For region us-iso-west-1 with FIPS disabled and DualStack enabled", - "expect": { - "error": "DualStack is enabled but this partition does not support DualStack" - }, - "params": { - "UseFIPS": false, - "Region": "us-iso-west-1", - "UseDualStack": true - } - }, - { - "documentation": "For region us-iso-west-1 with FIPS disabled and DualStack disabled", - "expect": { - "endpoint": { - "url": "https://sts.us-iso-west-1.c2s.ic.gov" - } - }, - "params": { - "UseFIPS": false, - "Region": "us-iso-west-1", - "UseDualStack": false - } - }, - { - "documentation": "For region eu-central-2 with FIPS enabled and DualStack enabled", - "expect": { - "endpoint": { - "url": "https://sts-fips.eu-central-2.api.aws" - } - }, - "params": { - "UseFIPS": true, - "Region": "eu-central-2", - "UseDualStack": true - } - }, - { - "documentation": "For region eu-central-2 with FIPS enabled and DualStack disabled", - "expect": { - "endpoint": { - "url": "https://sts-fips.eu-central-2.amazonaws.com" - } - }, - "params": { - "UseFIPS": true, - "Region": "eu-central-2", - "UseDualStack": false - } - }, - { - "documentation": "For region eu-central-2 with FIPS disabled and DualStack enabled", - "expect": { - "endpoint": { - "url": "https://sts.eu-central-2.api.aws" - } - }, - "params": { - "UseFIPS": false, - "Region": "eu-central-2", - "UseDualStack": true - } - }, - { - "documentation": "For region eu-central-2 with FIPS disabled and DualStack disabled", - "expect": { - "endpoint": { - "url": "https://sts.eu-central-2.amazonaws.com" - } - }, - "params": { - "UseFIPS": false, - "Region": "eu-central-2", - "UseDualStack": false - } - }, - { - "documentation": "For region us-west-1 with FIPS enabled and DualStack enabled", - "expect": { - "endpoint": { - "url": "https://sts-fips.us-west-1.api.aws" - } - }, - "params": { - "UseFIPS": true, - "Region": "us-west-1", - "UseDualStack": true - } - }, - { - "documentation": "For region us-west-1 with FIPS enabled and DualStack disabled", - "expect": { - "endpoint": { - "url": "https://sts-fips.us-west-1.amazonaws.com" - } - }, - "params": { - "UseFIPS": true, - "Region": "us-west-1", - "UseDualStack": false - } - }, - { - "documentation": "For region us-west-1 with FIPS disabled and DualStack enabled", - "expect": { - "endpoint": { - "url": "https://sts.us-west-1.api.aws" - } - }, - "params": { - "UseFIPS": false, - "Region": "us-west-1", - "UseDualStack": true - } - }, - { - "documentation": "For region us-west-1 with FIPS disabled and DualStack disabled", - "expect": { - "endpoint": { - "url": "https://sts.us-west-1.amazonaws.com" - } - }, - "params": { - "UseFIPS": false, - "Region": "us-west-1", - "UseDualStack": false - } - }, - { - "documentation": "For region us-west-2 with FIPS enabled and DualStack enabled", - "expect": { - "endpoint": { - "url": "https://sts-fips.us-west-2.api.aws" - } - }, - "params": { - "UseFIPS": true, - "Region": "us-west-2", - "UseDualStack": true - } - }, - { - "documentation": "For region us-west-2 with FIPS enabled and DualStack disabled", - "expect": { - "endpoint": { - "url": "https://sts-fips.us-west-2.amazonaws.com" - } - }, - "params": { - "UseFIPS": true, - "Region": "us-west-2", - "UseDualStack": false - } - }, - { - "documentation": "For region us-west-2 with FIPS disabled and DualStack enabled", - "expect": { - "endpoint": { - "url": "https://sts.us-west-2.api.aws" - } - }, - "params": { - "UseFIPS": false, - "Region": "us-west-2", - "UseDualStack": true - } - }, - { - "documentation": "For region us-west-2 with FIPS disabled and DualStack disabled", - "expect": { - "endpoint": { - "url": "https://sts.us-west-2.amazonaws.com" - } - }, - "params": { - "UseFIPS": false, - "Region": "us-west-2", - "UseDualStack": false - } - }, - { - "documentation": "For region aws-global with FIPS disabled and DualStack disabled", - "expect": { - "endpoint": { - "properties": { - "authSchemes": [ - { - "signingRegion": "us-east-1", - "signingName": "sts", - "name": "sigv4" - } - ] - }, - "url": "https://sts.amazonaws.com" - } - }, - "params": { - "UseFIPS": false, - "Region": "aws-global", - "UseDualStack": false - } - }, - { - "documentation": "For region af-south-1 with FIPS enabled and DualStack enabled", - "expect": { - "endpoint": { - "url": "https://sts-fips.af-south-1.api.aws" - } - }, - "params": { - "UseFIPS": true, - "Region": "af-south-1", - "UseDualStack": true - } - }, - { - "documentation": "For region af-south-1 with FIPS enabled and DualStack disabled", - "expect": { - "endpoint": { - "url": "https://sts-fips.af-south-1.amazonaws.com" - } - }, - "params": { - "UseFIPS": true, - "Region": "af-south-1", - "UseDualStack": false - } - }, - { - "documentation": "For region af-south-1 with FIPS disabled and DualStack enabled", - "expect": { - "endpoint": { - "url": "https://sts.af-south-1.api.aws" - } - }, - "params": { - "UseFIPS": false, - "Region": "af-south-1", - "UseDualStack": true - } - }, - { - "documentation": "For region af-south-1 with FIPS disabled and DualStack disabled", - "expect": { - "endpoint": { - "url": "https://sts.af-south-1.amazonaws.com" - } - }, - "params": { - "UseFIPS": false, - "Region": "af-south-1", - "UseDualStack": false - } - }, - { - "documentation": "For region eu-north-1 with FIPS enabled and DualStack enabled", - "expect": { - "endpoint": { - "url": "https://sts-fips.eu-north-1.api.aws" - } - }, - "params": { - "UseFIPS": true, - "Region": "eu-north-1", - "UseDualStack": true - } - }, - { - "documentation": "For region eu-north-1 with FIPS enabled and DualStack disabled", - "expect": { - "endpoint": { - "url": "https://sts-fips.eu-north-1.amazonaws.com" - } - }, - "params": { - "UseFIPS": true, - "Region": "eu-north-1", - "UseDualStack": false - } - }, - { - "documentation": "For region eu-north-1 with FIPS disabled and DualStack enabled", - "expect": { - "endpoint": { - "url": "https://sts.eu-north-1.api.aws" - } - }, - "params": { - "UseFIPS": false, - "Region": "eu-north-1", - "UseDualStack": true - } - }, - { - "documentation": "For region eu-north-1 with FIPS disabled and DualStack disabled", - "expect": { - "endpoint": { - "url": "https://sts.eu-north-1.amazonaws.com" - } - }, - "params": { - "UseFIPS": false, - "Region": "eu-north-1", - "UseDualStack": false - } - }, - { - "documentation": "For region eu-west-3 with FIPS enabled and DualStack enabled", - "expect": { - "endpoint": { - "url": "https://sts-fips.eu-west-3.api.aws" - } - }, - "params": { - "UseFIPS": true, - "Region": "eu-west-3", - "UseDualStack": true - } - }, - { - "documentation": "For region eu-west-3 with FIPS enabled and DualStack disabled", - "expect": { - "endpoint": { - "url": "https://sts-fips.eu-west-3.amazonaws.com" - } - }, - "params": { - "UseFIPS": true, - "Region": "eu-west-3", - "UseDualStack": false - } - }, - { - "documentation": "For region eu-west-3 with FIPS disabled and DualStack enabled", - "expect": { - "endpoint": { - "url": "https://sts.eu-west-3.api.aws" - } - }, - "params": { - "UseFIPS": false, - "Region": "eu-west-3", - "UseDualStack": true - } - }, - { - "documentation": "For region eu-west-3 with FIPS disabled and DualStack disabled", - "expect": { - "endpoint": { - "url": "https://sts.eu-west-3.amazonaws.com" - } - }, - "params": { - "UseFIPS": false, - "Region": "eu-west-3", - "UseDualStack": false - } - }, - { - "documentation": "For region eu-west-2 with FIPS enabled and DualStack enabled", - "expect": { - "endpoint": { - "url": "https://sts-fips.eu-west-2.api.aws" - } - }, - "params": { - "UseFIPS": true, - "Region": "eu-west-2", - "UseDualStack": true - } - }, - { - "documentation": "For region eu-west-2 with FIPS enabled and DualStack disabled", - "expect": { - "endpoint": { - "url": "https://sts-fips.eu-west-2.amazonaws.com" - } - }, - "params": { - "UseFIPS": true, - "Region": "eu-west-2", - "UseDualStack": false - } - }, - { - "documentation": "For region eu-west-2 with FIPS disabled and DualStack enabled", - "expect": { - "endpoint": { - "url": "https://sts.eu-west-2.api.aws" - } - }, - "params": { - "UseFIPS": false, - "Region": "eu-west-2", - "UseDualStack": true - } - }, - { - "documentation": "For region eu-west-2 with FIPS disabled and DualStack disabled", - "expect": { - "endpoint": { - "url": "https://sts.eu-west-2.amazonaws.com" - } - }, - "params": { - "UseFIPS": false, - "Region": "eu-west-2", - "UseDualStack": false - } - }, - { - "documentation": "For region eu-west-1 with FIPS enabled and DualStack enabled", - "expect": { - "endpoint": { - "url": "https://sts-fips.eu-west-1.api.aws" - } - }, - "params": { - "UseFIPS": true, - "Region": "eu-west-1", - "UseDualStack": true - } - }, - { - "documentation": "For region eu-west-1 with FIPS enabled and DualStack disabled", - "expect": { - "endpoint": { - "url": "https://sts-fips.eu-west-1.amazonaws.com" - } - }, - "params": { - "UseFIPS": true, - "Region": "eu-west-1", - "UseDualStack": false - } - }, - { - "documentation": "For region eu-west-1 with FIPS disabled and DualStack enabled", - "expect": { - "endpoint": { - "url": "https://sts.eu-west-1.api.aws" - } - }, - "params": { - "UseFIPS": false, - "Region": "eu-west-1", - "UseDualStack": true - } - }, - { - "documentation": "For region eu-west-1 with FIPS disabled and DualStack disabled", - "expect": { - "endpoint": { - "url": "https://sts.eu-west-1.amazonaws.com" - } - }, - "params": { - "UseFIPS": false, - "Region": "eu-west-1", - "UseDualStack": false - } - }, - { - "documentation": "For region ap-northeast-3 with FIPS enabled and DualStack enabled", - "expect": { - "endpoint": { - "url": "https://sts-fips.ap-northeast-3.api.aws" - } - }, - "params": { - "UseFIPS": true, - "Region": "ap-northeast-3", - "UseDualStack": true - } - }, - { - "documentation": "For region ap-northeast-3 with FIPS enabled and DualStack disabled", - "expect": { - "endpoint": { - "url": "https://sts-fips.ap-northeast-3.amazonaws.com" - } - }, - "params": { - "UseFIPS": true, - "Region": "ap-northeast-3", - "UseDualStack": false - } - }, - { - "documentation": "For region ap-northeast-3 with FIPS disabled and DualStack enabled", - "expect": { - "endpoint": { - "url": "https://sts.ap-northeast-3.api.aws" - } - }, - "params": { - "UseFIPS": false, - "Region": "ap-northeast-3", - "UseDualStack": true - } - }, - { - "documentation": "For region ap-northeast-3 with FIPS disabled and DualStack disabled", - "expect": { - "endpoint": { - "url": "https://sts.ap-northeast-3.amazonaws.com" - } - }, - "params": { - "UseFIPS": false, - "Region": "ap-northeast-3", - "UseDualStack": false - } - }, - { - "documentation": "For region ap-northeast-2 with FIPS enabled and DualStack enabled", - "expect": { - "endpoint": { - "url": "https://sts-fips.ap-northeast-2.api.aws" - } - }, - "params": { - "UseFIPS": true, - "Region": "ap-northeast-2", - "UseDualStack": true - } - }, - { - "documentation": "For region ap-northeast-2 with FIPS enabled and DualStack disabled", - "expect": { - "endpoint": { - "url": "https://sts-fips.ap-northeast-2.amazonaws.com" - } - }, - "params": { - "UseFIPS": true, - "Region": "ap-northeast-2", - "UseDualStack": false - } - }, - { - "documentation": "For region ap-northeast-2 with FIPS disabled and DualStack enabled", - "expect": { - "endpoint": { - "url": "https://sts.ap-northeast-2.api.aws" - } - }, - "params": { - "UseFIPS": false, - "Region": "ap-northeast-2", - "UseDualStack": true - } - }, - { - "documentation": "For region ap-northeast-2 with FIPS disabled and DualStack disabled", - "expect": { - "endpoint": { - "url": "https://sts.ap-northeast-2.amazonaws.com" - } - }, - "params": { - "UseFIPS": false, - "Region": "ap-northeast-2", - "UseDualStack": false - } - }, - { - "documentation": "For region ap-northeast-1 with FIPS enabled and DualStack enabled", - "expect": { - "endpoint": { - "url": "https://sts-fips.ap-northeast-1.api.aws" - } - }, - "params": { - "UseFIPS": true, - "Region": "ap-northeast-1", - "UseDualStack": true - } - }, - { - "documentation": "For region ap-northeast-1 with FIPS enabled and DualStack disabled", - "expect": { - "endpoint": { - "url": "https://sts-fips.ap-northeast-1.amazonaws.com" - } - }, - "params": { - "UseFIPS": true, - "Region": "ap-northeast-1", - "UseDualStack": false - } - }, - { - "documentation": "For region ap-northeast-1 with FIPS disabled and DualStack enabled", - "expect": { - "endpoint": { - "url": "https://sts.ap-northeast-1.api.aws" - } - }, - "params": { - "UseFIPS": false, - "Region": "ap-northeast-1", - "UseDualStack": true - } - }, - { - "documentation": "For region ap-northeast-1 with FIPS disabled and DualStack disabled", - "expect": { - "endpoint": { - "url": "https://sts.ap-northeast-1.amazonaws.com" - } - }, - "params": { - "UseFIPS": false, - "Region": "ap-northeast-1", - "UseDualStack": false - } - }, - { - "documentation": "For region me-south-1 with FIPS enabled and DualStack enabled", - "expect": { - "endpoint": { - "url": "https://sts-fips.me-south-1.api.aws" - } - }, - "params": { - "UseFIPS": true, - "Region": "me-south-1", - "UseDualStack": true - } - }, - { - "documentation": "For region me-south-1 with FIPS enabled and DualStack disabled", - "expect": { - "endpoint": { - "url": "https://sts-fips.me-south-1.amazonaws.com" - } - }, - "params": { - "UseFIPS": true, - "Region": "me-south-1", - "UseDualStack": false - } - }, - { - "documentation": "For region me-south-1 with FIPS disabled and DualStack enabled", - "expect": { - "endpoint": { - "url": "https://sts.me-south-1.api.aws" - } - }, - "params": { - "UseFIPS": false, - "Region": "me-south-1", - "UseDualStack": true - } - }, - { - "documentation": "For region me-south-1 with FIPS disabled and DualStack disabled", - "expect": { - "endpoint": { - "url": "https://sts.me-south-1.amazonaws.com" - } - }, - "params": { - "UseFIPS": false, - "Region": "me-south-1", - "UseDualStack": false - } - }, - { - "documentation": "For region sa-east-1 with FIPS enabled and DualStack enabled", - "expect": { - "endpoint": { - "url": "https://sts-fips.sa-east-1.api.aws" - } - }, - "params": { - "UseFIPS": true, - "Region": "sa-east-1", - "UseDualStack": true - } - }, - { - "documentation": "For region sa-east-1 with FIPS enabled and DualStack disabled", - "expect": { - "endpoint": { - "url": "https://sts-fips.sa-east-1.amazonaws.com" - } - }, - "params": { - "UseFIPS": true, - "Region": "sa-east-1", - "UseDualStack": false - } - }, - { - "documentation": "For region sa-east-1 with FIPS disabled and DualStack enabled", - "expect": { - "endpoint": { - "url": "https://sts.sa-east-1.api.aws" - } - }, - "params": { - "UseFIPS": false, - "Region": "sa-east-1", - "UseDualStack": true - } - }, - { - "documentation": "For region sa-east-1 with FIPS disabled and DualStack disabled", - "expect": { - "endpoint": { - "url": "https://sts.sa-east-1.amazonaws.com" - } - }, - "params": { - "UseFIPS": false, - "Region": "sa-east-1", - "UseDualStack": false - } - }, - { - "documentation": "For region ap-east-1 with FIPS enabled and DualStack enabled", - "expect": { - "endpoint": { - "url": "https://sts-fips.ap-east-1.api.aws" - } - }, - "params": { - "UseFIPS": true, - "Region": "ap-east-1", - "UseDualStack": true - } - }, - { - "documentation": "For region ap-east-1 with FIPS enabled and DualStack disabled", - "expect": { - "endpoint": { - "url": "https://sts-fips.ap-east-1.amazonaws.com" + ] + }, + { + "conditions": [], + "error": "Invalid Configuration: Missing Region", + "type": "error" } - }, - "params": { - "UseFIPS": true, - "Region": "ap-east-1", - "UseDualStack": false - } - }, + ] + } + ] + }, + "smithy.rules#endpointTests": { + "testCases": [ { - "documentation": "For region ap-east-1 with FIPS disabled and DualStack enabled", + "documentation": "For region af-south-1 with FIPS disabled and DualStack disabled", "expect": { "endpoint": { - "url": "https://sts.ap-east-1.api.aws" + "url": "https://sts.af-south-1.amazonaws.com" } }, "params": { - "UseFIPS": false, - "Region": "ap-east-1", - "UseDualStack": true + "Region": "af-south-1", + "UseDualStack": false, + "UseFIPS": false } }, { @@ -2162,204 +1020,61 @@ } }, "params": { - "UseFIPS": false, "Region": "ap-east-1", - "UseDualStack": false - } - }, - { - "documentation": "For region cn-north-1 with FIPS enabled and DualStack enabled", - "expect": { - "endpoint": { - "url": "https://sts-fips.cn-north-1.api.amazonwebservices.com.cn" - } - }, - "params": { - "UseFIPS": true, - "Region": "cn-north-1", - "UseDualStack": true - } - }, - { - "documentation": "For region cn-north-1 with FIPS enabled and DualStack disabled", - "expect": { - "endpoint": { - "url": "https://sts-fips.cn-north-1.amazonaws.com.cn" - } - }, - "params": { - "UseFIPS": true, - "Region": "cn-north-1", - "UseDualStack": false - } - }, - { - "documentation": "For region cn-north-1 with FIPS disabled and DualStack enabled", - "expect": { - "endpoint": { - "url": "https://sts.cn-north-1.api.amazonwebservices.com.cn" - } - }, - "params": { - "UseFIPS": false, - "Region": "cn-north-1", - "UseDualStack": true - } - }, - { - "documentation": "For region cn-north-1 with FIPS disabled and DualStack disabled", - "expect": { - "endpoint": { - "url": "https://sts.cn-north-1.amazonaws.com.cn" - } - }, - "params": { - "UseFIPS": false, - "Region": "cn-north-1", - "UseDualStack": false - } - }, - { - "documentation": "For region ca-west-1 with FIPS enabled and DualStack enabled", - "expect": { - "endpoint": { - "url": "https://sts-fips.ca-west-1.api.aws" - } - }, - "params": { - "UseFIPS": true, - "Region": "ca-west-1", - "UseDualStack": true - } - }, - { - "documentation": "For region ca-west-1 with FIPS enabled and DualStack disabled", - "expect": { - "endpoint": { - "url": "https://sts-fips.ca-west-1.amazonaws.com" - } - }, - "params": { - "UseFIPS": true, - "Region": "ca-west-1", - "UseDualStack": false - } - }, - { - "documentation": "For region ca-west-1 with FIPS disabled and DualStack enabled", - "expect": { - "endpoint": { - "url": "https://sts.ca-west-1.api.aws" - } - }, - "params": { - "UseFIPS": false, - "Region": "ca-west-1", - "UseDualStack": true - } - }, - { - "documentation": "For region ca-west-1 with FIPS disabled and DualStack disabled", - "expect": { - "endpoint": { - "url": "https://sts.ca-west-1.amazonaws.com" - } - }, - "params": { - "UseFIPS": false, - "Region": "ca-west-1", - "UseDualStack": false - } - }, - { - "documentation": "For region us-gov-west-1 with FIPS enabled and DualStack enabled", - "expect": { - "endpoint": { - "url": "https://sts-fips.us-gov-west-1.api.aws" - } - }, - "params": { - "UseFIPS": true, - "Region": "us-gov-west-1", - "UseDualStack": true - } - }, - { - "documentation": "For region us-gov-west-1 with FIPS enabled and DualStack disabled", - "expect": { - "endpoint": { - "url": "https://sts.us-gov-west-1.amazonaws.com" - } - }, - "params": { - "UseFIPS": true, - "Region": "us-gov-west-1", - "UseDualStack": false - } - }, - { - "documentation": "For region us-gov-west-1 with FIPS disabled and DualStack enabled", - "expect": { - "endpoint": { - "url": "https://sts.us-gov-west-1.api.aws" - } - }, - "params": { - "UseFIPS": false, - "Region": "us-gov-west-1", - "UseDualStack": true + "UseDualStack": false, + "UseFIPS": false } }, { - "documentation": "For region us-gov-west-1 with FIPS disabled and DualStack disabled", + "documentation": "For region ap-northeast-1 with FIPS disabled and DualStack disabled", "expect": { "endpoint": { - "url": "https://sts.us-gov-west-1.amazonaws.com" + "url": "https://sts.ap-northeast-1.amazonaws.com" } }, "params": { - "UseFIPS": false, - "Region": "us-gov-west-1", - "UseDualStack": false + "Region": "ap-northeast-1", + "UseDualStack": false, + "UseFIPS": false } }, { - "documentation": "For region ap-southeast-1 with FIPS enabled and DualStack enabled", + "documentation": "For region ap-northeast-2 with FIPS disabled and DualStack disabled", "expect": { "endpoint": { - "url": "https://sts-fips.ap-southeast-1.api.aws" + "url": "https://sts.ap-northeast-2.amazonaws.com" } }, "params": { - "UseFIPS": true, - "Region": "ap-southeast-1", - "UseDualStack": true + "Region": "ap-northeast-2", + "UseDualStack": false, + "UseFIPS": false } }, { - "documentation": "For region ap-southeast-1 with FIPS enabled and DualStack disabled", + "documentation": "For region ap-northeast-3 with FIPS disabled and DualStack disabled", "expect": { "endpoint": { - "url": "https://sts-fips.ap-southeast-1.amazonaws.com" + "url": "https://sts.ap-northeast-3.amazonaws.com" } }, "params": { - "UseFIPS": true, - "Region": "ap-southeast-1", - "UseDualStack": false + "Region": "ap-northeast-3", + "UseDualStack": false, + "UseFIPS": false } }, { - "documentation": "For region ap-southeast-1 with FIPS disabled and DualStack enabled", + "documentation": "For region ap-south-1 with FIPS disabled and DualStack disabled", "expect": { "endpoint": { - "url": "https://sts.ap-southeast-1.api.aws" + "url": "https://sts.ap-south-1.amazonaws.com" } }, "params": { - "UseFIPS": false, - "Region": "ap-southeast-1", - "UseDualStack": true + "Region": "ap-south-1", + "UseDualStack": false, + "UseFIPS": false } }, { @@ -2370,291 +1085,291 @@ } }, "params": { - "UseFIPS": false, "Region": "ap-southeast-1", - "UseDualStack": false + "UseDualStack": false, + "UseFIPS": false } }, { - "documentation": "For region ap-southeast-2 with FIPS enabled and DualStack enabled", + "documentation": "For region ap-southeast-2 with FIPS disabled and DualStack disabled", "expect": { "endpoint": { - "url": "https://sts-fips.ap-southeast-2.api.aws" + "url": "https://sts.ap-southeast-2.amazonaws.com" } }, "params": { - "UseFIPS": true, "Region": "ap-southeast-2", - "UseDualStack": true + "UseDualStack": false, + "UseFIPS": false } }, { - "documentation": "For region ap-southeast-2 with FIPS enabled and DualStack disabled", + "documentation": "For region ap-southeast-3 with FIPS disabled and DualStack disabled", "expect": { "endpoint": { - "url": "https://sts-fips.ap-southeast-2.amazonaws.com" + "url": "https://sts.ap-southeast-3.amazonaws.com" } }, "params": { - "UseFIPS": true, - "Region": "ap-southeast-2", - "UseDualStack": false + "Region": "ap-southeast-3", + "UseDualStack": false, + "UseFIPS": false } }, { - "documentation": "For region ap-southeast-2 with FIPS disabled and DualStack enabled", + "documentation": "For region aws-global with FIPS disabled and DualStack disabled", "expect": { "endpoint": { - "url": "https://sts.ap-southeast-2.api.aws" + "properties": { + "authSchemes": [ + { + "name": "sigv4", + "signingName": "sts", + "signingRegion": "us-east-1" + } + ] + }, + "url": "https://sts.amazonaws.com" } }, "params": { - "UseFIPS": false, - "Region": "ap-southeast-2", - "UseDualStack": true + "Region": "aws-global", + "UseDualStack": false, + "UseFIPS": false } }, { - "documentation": "For region ap-southeast-2 with FIPS disabled and DualStack disabled", + "documentation": "For region ca-central-1 with FIPS disabled and DualStack disabled", "expect": { "endpoint": { - "url": "https://sts.ap-southeast-2.amazonaws.com" + "url": "https://sts.ca-central-1.amazonaws.com" } }, "params": { - "UseFIPS": false, - "Region": "ap-southeast-2", - "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": { - "UseFIPS": true, - "Region": "us-iso-east-1", - "UseDualStack": true + "Region": "ca-central-1", + "UseDualStack": false, + "UseFIPS": false } }, { - "documentation": "For region us-iso-east-1 with FIPS enabled and DualStack disabled", + "documentation": "For region eu-central-1 with FIPS disabled and DualStack disabled", "expect": { "endpoint": { - "url": "https://sts-fips.us-iso-east-1.c2s.ic.gov" + "url": "https://sts.eu-central-1.amazonaws.com" } }, "params": { - "UseFIPS": true, - "Region": "us-iso-east-1", - "UseDualStack": false + "Region": "eu-central-1", + "UseDualStack": false, + "UseFIPS": false } }, { - "documentation": "For region us-iso-east-1 with FIPS disabled and DualStack enabled", + "documentation": "For region eu-north-1 with FIPS disabled and DualStack disabled", "expect": { - "error": "DualStack is enabled but this partition does not support DualStack" + "endpoint": { + "url": "https://sts.eu-north-1.amazonaws.com" + } }, "params": { - "UseFIPS": false, - "Region": "us-iso-east-1", - "UseDualStack": true + "Region": "eu-north-1", + "UseDualStack": false, + "UseFIPS": false } }, { - "documentation": "For region us-iso-east-1 with FIPS disabled and DualStack disabled", + "documentation": "For region eu-south-1 with FIPS disabled and DualStack disabled", "expect": { "endpoint": { - "url": "https://sts.us-iso-east-1.c2s.ic.gov" + "url": "https://sts.eu-south-1.amazonaws.com" } }, "params": { - "UseFIPS": false, - "Region": "us-iso-east-1", - "UseDualStack": false + "Region": "eu-south-1", + "UseDualStack": false, + "UseFIPS": false } }, { - "documentation": "For region ap-southeast-3 with FIPS enabled and DualStack enabled", + "documentation": "For region eu-west-1 with FIPS disabled and DualStack disabled", "expect": { "endpoint": { - "url": "https://sts-fips.ap-southeast-3.api.aws" + "url": "https://sts.eu-west-1.amazonaws.com" } }, "params": { - "UseFIPS": true, - "Region": "ap-southeast-3", - "UseDualStack": true + "Region": "eu-west-1", + "UseDualStack": false, + "UseFIPS": false } }, { - "documentation": "For region ap-southeast-3 with FIPS enabled and DualStack disabled", + "documentation": "For region eu-west-2 with FIPS disabled and DualStack disabled", "expect": { "endpoint": { - "url": "https://sts-fips.ap-southeast-3.amazonaws.com" + "url": "https://sts.eu-west-2.amazonaws.com" } }, "params": { - "UseFIPS": true, - "Region": "ap-southeast-3", - "UseDualStack": false + "Region": "eu-west-2", + "UseDualStack": false, + "UseFIPS": false } }, { - "documentation": "For region ap-southeast-3 with FIPS disabled and DualStack enabled", + "documentation": "For region eu-west-3 with FIPS disabled and DualStack disabled", "expect": { "endpoint": { - "url": "https://sts.ap-southeast-3.api.aws" + "url": "https://sts.eu-west-3.amazonaws.com" } }, "params": { - "UseFIPS": false, - "Region": "ap-southeast-3", - "UseDualStack": true + "Region": "eu-west-3", + "UseDualStack": false, + "UseFIPS": false } }, { - "documentation": "For region ap-southeast-3 with FIPS disabled and DualStack disabled", + "documentation": "For region me-south-1 with FIPS disabled and DualStack disabled", "expect": { "endpoint": { - "url": "https://sts.ap-southeast-3.amazonaws.com" + "url": "https://sts.me-south-1.amazonaws.com" } }, "params": { - "UseFIPS": false, - "Region": "ap-southeast-3", - "UseDualStack": false + "Region": "me-south-1", + "UseDualStack": false, + "UseFIPS": false } }, { - "documentation": "For region ap-southeast-4 with FIPS enabled and DualStack enabled", + "documentation": "For region sa-east-1 with FIPS disabled and DualStack disabled", "expect": { "endpoint": { - "url": "https://sts-fips.ap-southeast-4.api.aws" + "url": "https://sts.sa-east-1.amazonaws.com" } }, "params": { - "UseFIPS": true, - "Region": "ap-southeast-4", - "UseDualStack": true + "Region": "sa-east-1", + "UseDualStack": false, + "UseFIPS": false } }, { - "documentation": "For region ap-southeast-4 with FIPS enabled and DualStack disabled", + "documentation": "For region us-east-1 with FIPS disabled and DualStack disabled", "expect": { "endpoint": { - "url": "https://sts-fips.ap-southeast-4.amazonaws.com" + "url": "https://sts.us-east-1.amazonaws.com" } }, "params": { - "UseFIPS": true, - "Region": "ap-southeast-4", - "UseDualStack": false + "Region": "us-east-1", + "UseDualStack": false, + "UseFIPS": false } }, { - "documentation": "For region ap-southeast-4 with FIPS disabled and DualStack enabled", + "documentation": "For region us-east-1 with FIPS enabled and DualStack disabled", "expect": { "endpoint": { - "url": "https://sts.ap-southeast-4.api.aws" + "url": "https://sts-fips.us-east-1.amazonaws.com" } }, "params": { - "UseFIPS": false, - "Region": "ap-southeast-4", - "UseDualStack": true + "Region": "us-east-1", + "UseDualStack": false, + "UseFIPS": true } }, { - "documentation": "For region ap-southeast-4 with FIPS disabled and DualStack disabled", + "documentation": "For region us-east-2 with FIPS disabled and DualStack disabled", "expect": { "endpoint": { - "url": "https://sts.ap-southeast-4.amazonaws.com" + "url": "https://sts.us-east-2.amazonaws.com" } }, "params": { - "UseFIPS": false, - "Region": "ap-southeast-4", - "UseDualStack": false + "Region": "us-east-2", + "UseDualStack": false, + "UseFIPS": false } }, { - "documentation": "For region ap-southeast-5 with FIPS enabled and DualStack enabled", + "documentation": "For region us-east-2 with FIPS enabled and DualStack disabled", "expect": { "endpoint": { - "url": "https://sts-fips.ap-southeast-5.api.aws" + "url": "https://sts-fips.us-east-2.amazonaws.com" } }, "params": { - "UseFIPS": true, - "Region": "ap-southeast-5", - "UseDualStack": true + "Region": "us-east-2", + "UseDualStack": false, + "UseFIPS": true } }, { - "documentation": "For region ap-southeast-5 with FIPS enabled and DualStack disabled", + "documentation": "For region us-west-1 with FIPS disabled and DualStack disabled", "expect": { "endpoint": { - "url": "https://sts-fips.ap-southeast-5.amazonaws.com" + "url": "https://sts.us-west-1.amazonaws.com" } }, "params": { - "UseFIPS": true, - "Region": "ap-southeast-5", - "UseDualStack": false + "Region": "us-west-1", + "UseDualStack": false, + "UseFIPS": false } }, { - "documentation": "For region ap-southeast-5 with FIPS disabled and DualStack enabled", + "documentation": "For region us-west-1 with FIPS enabled and DualStack disabled", "expect": { "endpoint": { - "url": "https://sts.ap-southeast-5.api.aws" + "url": "https://sts-fips.us-west-1.amazonaws.com" } }, "params": { - "UseFIPS": false, - "Region": "ap-southeast-5", - "UseDualStack": true + "Region": "us-west-1", + "UseDualStack": false, + "UseFIPS": true } }, { - "documentation": "For region ap-southeast-5 with FIPS disabled and DualStack disabled", + "documentation": "For region us-west-2 with FIPS disabled and DualStack disabled", "expect": { "endpoint": { - "url": "https://sts.ap-southeast-5.amazonaws.com" + "url": "https://sts.us-west-2.amazonaws.com" } }, "params": { - "UseFIPS": false, - "Region": "ap-southeast-5", - "UseDualStack": false + "Region": "us-west-2", + "UseDualStack": false, + "UseFIPS": false } }, { - "documentation": "For region us-east-1 with FIPS enabled and DualStack enabled", + "documentation": "For region us-west-2 with FIPS enabled and DualStack disabled", "expect": { "endpoint": { - "url": "https://sts-fips.us-east-1.api.aws" + "url": "https://sts-fips.us-west-2.amazonaws.com" } }, "params": { - "UseFIPS": true, - "Region": "us-east-1", - "UseDualStack": true + "Region": "us-west-2", + "UseDualStack": false, + "UseFIPS": true } }, { - "documentation": "For region us-east-1 with FIPS enabled and DualStack disabled", + "documentation": "For region us-east-1 with FIPS enabled and DualStack enabled", "expect": { "endpoint": { - "url": "https://sts-fips.us-east-1.amazonaws.com" + "url": "https://sts-fips.us-east-1.api.aws" } }, "params": { - "UseFIPS": true, "Region": "us-east-1", - "UseDualStack": false + "UseDualStack": true, + "UseFIPS": true } }, { @@ -2665,239 +1380,243 @@ } }, "params": { - "UseFIPS": false, "Region": "us-east-1", - "UseDualStack": true + "UseDualStack": true, + "UseFIPS": false } }, { - "documentation": "For region us-east-1 with FIPS disabled and DualStack disabled", + "documentation": "For region cn-north-1 with FIPS disabled and DualStack disabled", "expect": { "endpoint": { - "url": "https://sts.us-east-1.amazonaws.com" + "url": "https://sts.cn-north-1.amazonaws.com.cn" } }, "params": { - "UseFIPS": false, - "Region": "us-east-1", - "UseDualStack": false + "Region": "cn-north-1", + "UseDualStack": false, + "UseFIPS": false } }, { - "documentation": "For region ap-southeast-6 with FIPS enabled and DualStack enabled", + "documentation": "For region cn-northwest-1 with FIPS disabled and DualStack disabled", "expect": { "endpoint": { - "url": "https://sts-fips.ap-southeast-6.api.aws" + "url": "https://sts.cn-northwest-1.amazonaws.com.cn" } }, "params": { - "UseFIPS": true, - "Region": "ap-southeast-6", - "UseDualStack": true + "Region": "cn-northwest-1", + "UseDualStack": false, + "UseFIPS": false } }, { - "documentation": "For region ap-southeast-6 with FIPS enabled and DualStack disabled", + "documentation": "For region cn-north-1 with FIPS enabled and DualStack enabled", "expect": { "endpoint": { - "url": "https://sts-fips.ap-southeast-6.amazonaws.com" + "url": "https://sts-fips.cn-north-1.api.amazonwebservices.com.cn" } }, "params": { - "UseFIPS": true, - "Region": "ap-southeast-6", - "UseDualStack": false + "Region": "cn-north-1", + "UseDualStack": true, + "UseFIPS": true } }, { - "documentation": "For region ap-southeast-6 with FIPS disabled and DualStack enabled", + "documentation": "For region cn-north-1 with FIPS enabled and DualStack disabled", "expect": { "endpoint": { - "url": "https://sts.ap-southeast-6.api.aws" + "url": "https://sts-fips.cn-north-1.amazonaws.com.cn" } }, "params": { - "UseFIPS": false, - "Region": "ap-southeast-6", - "UseDualStack": true + "Region": "cn-north-1", + "UseDualStack": false, + "UseFIPS": true } }, { - "documentation": "For region ap-southeast-6 with FIPS disabled and DualStack disabled", + "documentation": "For region cn-north-1 with FIPS disabled and DualStack enabled", "expect": { "endpoint": { - "url": "https://sts.ap-southeast-6.amazonaws.com" + "url": "https://sts.cn-north-1.api.amazonwebservices.com.cn" } }, "params": { - "UseFIPS": false, - "Region": "ap-southeast-6", - "UseDualStack": false + "Region": "cn-north-1", + "UseDualStack": true, + "UseFIPS": false } }, { - "documentation": "For region us-east-2 with FIPS enabled and DualStack enabled", + "documentation": "For region us-gov-east-1 with FIPS disabled and DualStack disabled", "expect": { "endpoint": { - "url": "https://sts-fips.us-east-2.api.aws" + "url": "https://sts.us-gov-east-1.amazonaws.com" } }, "params": { - "UseFIPS": true, - "Region": "us-east-2", - "UseDualStack": true + "Region": "us-gov-east-1", + "UseDualStack": false, + "UseFIPS": false } }, { - "documentation": "For region us-east-2 with FIPS enabled and DualStack disabled", + "documentation": "For region us-gov-east-1 with FIPS enabled and DualStack disabled", "expect": { "endpoint": { - "url": "https://sts-fips.us-east-2.amazonaws.com" + "url": "https://sts.us-gov-east-1.amazonaws.com" } }, "params": { - "UseFIPS": true, - "Region": "us-east-2", - "UseDualStack": false + "Region": "us-gov-east-1", + "UseDualStack": false, + "UseFIPS": true } }, { - "documentation": "For region us-east-2 with FIPS disabled and DualStack enabled", + "documentation": "For region us-gov-west-1 with FIPS disabled and DualStack disabled", "expect": { "endpoint": { - "url": "https://sts.us-east-2.api.aws" + "url": "https://sts.us-gov-west-1.amazonaws.com" } }, "params": { - "UseFIPS": false, - "Region": "us-east-2", - "UseDualStack": true + "Region": "us-gov-west-1", + "UseDualStack": false, + "UseFIPS": false } }, { - "documentation": "For region us-east-2 with FIPS disabled and DualStack disabled", + "documentation": "For region us-gov-west-1 with FIPS enabled and DualStack disabled", "expect": { "endpoint": { - "url": "https://sts.us-east-2.amazonaws.com" + "url": "https://sts.us-gov-west-1.amazonaws.com" } }, "params": { - "UseFIPS": false, - "Region": "us-east-2", - "UseDualStack": false + "Region": "us-gov-west-1", + "UseDualStack": false, + "UseFIPS": true } }, { - "documentation": "For region cn-northwest-1 with FIPS enabled and DualStack enabled", + "documentation": "For region us-gov-east-1 with FIPS enabled and DualStack enabled", "expect": { "endpoint": { - "url": "https://sts-fips.cn-northwest-1.api.amazonwebservices.com.cn" + "url": "https://sts-fips.us-gov-east-1.api.aws" } }, "params": { - "UseFIPS": true, - "Region": "cn-northwest-1", - "UseDualStack": true + "Region": "us-gov-east-1", + "UseDualStack": true, + "UseFIPS": true } }, { - "documentation": "For region cn-northwest-1 with FIPS enabled and DualStack disabled", + "documentation": "For region us-gov-east-1 with FIPS disabled and DualStack enabled", "expect": { "endpoint": { - "url": "https://sts-fips.cn-northwest-1.amazonaws.com.cn" + "url": "https://sts.us-gov-east-1.api.aws" } }, "params": { - "UseFIPS": true, - "Region": "cn-northwest-1", - "UseDualStack": false + "Region": "us-gov-east-1", + "UseDualStack": true, + "UseFIPS": false } }, { - "documentation": "For region cn-northwest-1 with FIPS disabled and DualStack enabled", + "documentation": "For region us-iso-east-1 with FIPS disabled and DualStack disabled", "expect": { "endpoint": { - "url": "https://sts.cn-northwest-1.api.amazonwebservices.com.cn" + "url": "https://sts.us-iso-east-1.c2s.ic.gov" } }, "params": { - "UseFIPS": false, - "Region": "cn-northwest-1", - "UseDualStack": true + "Region": "us-iso-east-1", + "UseDualStack": false, + "UseFIPS": false } }, { - "documentation": "For region cn-northwest-1 with FIPS disabled and DualStack disabled", + "documentation": "For region us-iso-west-1 with FIPS disabled and DualStack disabled", "expect": { "endpoint": { - "url": "https://sts.cn-northwest-1.amazonaws.com.cn" + "url": "https://sts.us-iso-west-1.c2s.ic.gov" } }, "params": { - "UseFIPS": false, - "Region": "cn-northwest-1", - "UseDualStack": false + "Region": "us-iso-west-1", + "UseDualStack": false, + "UseFIPS": false } }, { - "documentation": "For region us-isob-east-1 with FIPS enabled and DualStack enabled", + "documentation": "For region us-iso-east-1 with FIPS enabled and DualStack disabled", "expect": { - "error": "FIPS and DualStack are enabled, but this partition does not support one or both" + "endpoint": { + "url": "https://sts-fips.us-iso-east-1.c2s.ic.gov" + } }, "params": { - "UseFIPS": true, - "Region": "us-isob-east-1", - "UseDualStack": true + "Region": "us-iso-east-1", + "UseDualStack": false, + "UseFIPS": true } }, { - "documentation": "For region us-isob-east-1 with FIPS enabled and DualStack disabled", + "documentation": "For region us-isob-east-1 with FIPS disabled and DualStack disabled", "expect": { "endpoint": { - "url": "https://sts-fips.us-isob-east-1.sc2s.sgov.gov" + "url": "https://sts.us-isob-east-1.sc2s.sgov.gov" } }, "params": { - "UseFIPS": true, "Region": "us-isob-east-1", - "UseDualStack": false + "UseDualStack": false, + "UseFIPS": false } }, { - "documentation": "For region us-isob-east-1 with FIPS disabled and DualStack enabled", + "documentation": "For region us-isob-east-1 with FIPS enabled and DualStack disabled", "expect": { - "error": "DualStack is enabled but this partition does not support DualStack" + "endpoint": { + "url": "https://sts-fips.us-isob-east-1.sc2s.sgov.gov" + } }, "params": { - "UseFIPS": false, "Region": "us-isob-east-1", - "UseDualStack": true + "UseDualStack": false, + "UseFIPS": true } }, { - "documentation": "For region us-isob-east-1 with FIPS disabled and DualStack disabled", + "documentation": "For custom endpoint with region set and fips disabled and dualstack disabled", "expect": { "endpoint": { - "url": "https://sts.us-isob-east-1.sc2s.sgov.gov" + "url": "https://example.com" } }, "params": { + "Region": "us-east-1", + "UseDualStack": false, "UseFIPS": false, - "Region": "us-isob-east-1", - "UseDualStack": false + "Endpoint": "https://example.com" } }, { - "documentation": "For custom endpoint with fips disabled and dualstack disabled", + "documentation": "For custom endpoint with region not set and fips disabled and dualstack disabled", "expect": { "endpoint": { "url": "https://example.com" } }, "params": { - "UseFIPS": false, - "Region": "us-east-1", "UseDualStack": false, + "UseFIPS": false, "Endpoint": "https://example.com" } }, @@ -2907,9 +1626,9 @@ "error": "Invalid Configuration: FIPS and custom endpoint are not supported" }, "params": { - "UseFIPS": true, "Region": "us-east-1", "UseDualStack": false, + "UseFIPS": true, "Endpoint": "https://example.com" } }, @@ -2919,9 +1638,9 @@ "error": "Invalid Configuration: Dualstack and custom endpoint are not supported" }, "params": { - "UseFIPS": false, "Region": "us-east-1", "UseDualStack": true, + "UseFIPS": false, "Endpoint": "https://example.com" } }, @@ -2932,9 +1651,9 @@ "properties": { "authSchemes": [ { - "signingRegion": "us-east-1", + "name": "sigv4", "signingName": "sts", - "name": "sigv4" + "signingRegion": "us-east-1" } ] }, @@ -2951,10 +1670,10 @@ } ], "params": { - "UseGlobalEndpoint": true, - "UseDualStack": false, + "Region": "ap-northeast-1", "UseFIPS": false, - "Region": "ap-northeast-1" + "UseDualStack": false, + "UseGlobalEndpoint": true } }, { @@ -2964,9 +1683,9 @@ "properties": { "authSchemes": [ { - "signingRegion": "us-east-1", + "name": "sigv4", "signingName": "sts", - "name": "sigv4" + "signingRegion": "us-east-1" } ] }, @@ -2983,10 +1702,10 @@ } ], "params": { - "UseGlobalEndpoint": true, - "UseDualStack": false, + "Region": "ap-south-1", "UseFIPS": false, - "Region": "ap-south-1" + "UseDualStack": false, + "UseGlobalEndpoint": true } }, { @@ -2996,9 +1715,9 @@ "properties": { "authSchemes": [ { - "signingRegion": "us-east-1", + "name": "sigv4", "signingName": "sts", - "name": "sigv4" + "signingRegion": "us-east-1" } ] }, @@ -3015,10 +1734,10 @@ } ], "params": { - "UseGlobalEndpoint": true, - "UseDualStack": false, + "Region": "ap-southeast-1", "UseFIPS": false, - "Region": "ap-southeast-1" + "UseDualStack": false, + "UseGlobalEndpoint": true } }, { @@ -3028,9 +1747,9 @@ "properties": { "authSchemes": [ { - "signingRegion": "us-east-1", + "name": "sigv4", "signingName": "sts", - "name": "sigv4" + "signingRegion": "us-east-1" } ] }, @@ -3047,10 +1766,10 @@ } ], "params": { - "UseGlobalEndpoint": true, - "UseDualStack": false, + "Region": "ap-southeast-2", "UseFIPS": false, - "Region": "ap-southeast-2" + "UseDualStack": false, + "UseGlobalEndpoint": true } }, { @@ -3060,9 +1779,9 @@ "properties": { "authSchemes": [ { - "signingRegion": "us-east-1", + "name": "sigv4", "signingName": "sts", - "name": "sigv4" + "signingRegion": "us-east-1" } ] }, @@ -3079,10 +1798,10 @@ } ], "params": { - "UseGlobalEndpoint": true, - "UseDualStack": false, + "Region": "aws-global", "UseFIPS": false, - "Region": "aws-global" + "UseDualStack": false, + "UseGlobalEndpoint": true } }, { @@ -3092,9 +1811,9 @@ "properties": { "authSchemes": [ { - "signingRegion": "us-east-1", + "name": "sigv4", "signingName": "sts", - "name": "sigv4" + "signingRegion": "us-east-1" } ] }, @@ -3111,10 +1830,10 @@ } ], "params": { - "UseGlobalEndpoint": true, - "UseDualStack": false, + "Region": "ca-central-1", "UseFIPS": false, - "Region": "ca-central-1" + "UseDualStack": false, + "UseGlobalEndpoint": true } }, { @@ -3124,9 +1843,9 @@ "properties": { "authSchemes": [ { - "signingRegion": "us-east-1", + "name": "sigv4", "signingName": "sts", - "name": "sigv4" + "signingRegion": "us-east-1" } ] }, @@ -3143,10 +1862,10 @@ } ], "params": { - "UseGlobalEndpoint": true, - "UseDualStack": false, + "Region": "eu-central-1", "UseFIPS": false, - "Region": "eu-central-1" + "UseDualStack": false, + "UseGlobalEndpoint": true } }, { @@ -3156,9 +1875,9 @@ "properties": { "authSchemes": [ { - "signingRegion": "us-east-1", + "name": "sigv4", "signingName": "sts", - "name": "sigv4" + "signingRegion": "us-east-1" } ] }, @@ -3175,10 +1894,10 @@ } ], "params": { - "UseGlobalEndpoint": true, - "UseDualStack": false, + "Region": "eu-north-1", "UseFIPS": false, - "Region": "eu-north-1" + "UseDualStack": false, + "UseGlobalEndpoint": true } }, { @@ -3188,9 +1907,9 @@ "properties": { "authSchemes": [ { - "signingRegion": "us-east-1", + "name": "sigv4", "signingName": "sts", - "name": "sigv4" + "signingRegion": "us-east-1" } ] }, @@ -3207,10 +1926,10 @@ } ], "params": { - "UseGlobalEndpoint": true, - "UseDualStack": false, + "Region": "eu-west-1", "UseFIPS": false, - "Region": "eu-west-1" + "UseDualStack": false, + "UseGlobalEndpoint": true } }, { @@ -3220,9 +1939,9 @@ "properties": { "authSchemes": [ { - "signingRegion": "us-east-1", + "name": "sigv4", "signingName": "sts", - "name": "sigv4" + "signingRegion": "us-east-1" } ] }, @@ -3239,10 +1958,10 @@ } ], "params": { - "UseGlobalEndpoint": true, - "UseDualStack": false, + "Region": "eu-west-2", "UseFIPS": false, - "Region": "eu-west-2" + "UseDualStack": false, + "UseGlobalEndpoint": true } }, { @@ -3252,9 +1971,9 @@ "properties": { "authSchemes": [ { - "signingRegion": "us-east-1", + "name": "sigv4", "signingName": "sts", - "name": "sigv4" + "signingRegion": "us-east-1" } ] }, @@ -3271,10 +1990,10 @@ } ], "params": { - "UseGlobalEndpoint": true, - "UseDualStack": false, + "Region": "eu-west-3", "UseFIPS": false, - "Region": "eu-west-3" + "UseDualStack": false, + "UseGlobalEndpoint": true } }, { @@ -3284,9 +2003,9 @@ "properties": { "authSchemes": [ { - "signingRegion": "us-east-1", + "name": "sigv4", "signingName": "sts", - "name": "sigv4" + "signingRegion": "us-east-1" } ] }, @@ -3303,10 +2022,10 @@ } ], "params": { - "UseGlobalEndpoint": true, - "UseDualStack": false, + "Region": "sa-east-1", "UseFIPS": false, - "Region": "sa-east-1" + "UseDualStack": false, + "UseGlobalEndpoint": true } }, { @@ -3316,9 +2035,9 @@ "properties": { "authSchemes": [ { - "signingRegion": "us-east-1", + "name": "sigv4", "signingName": "sts", - "name": "sigv4" + "signingRegion": "us-east-1" } ] }, @@ -3335,10 +2054,10 @@ } ], "params": { - "UseGlobalEndpoint": true, - "UseDualStack": false, + "Region": "us-east-1", "UseFIPS": false, - "Region": "us-east-1" + "UseDualStack": false, + "UseGlobalEndpoint": true } }, { @@ -3348,9 +2067,9 @@ "properties": { "authSchemes": [ { - "signingRegion": "us-east-1", + "name": "sigv4", "signingName": "sts", - "name": "sigv4" + "signingRegion": "us-east-1" } ] }, @@ -3367,10 +2086,10 @@ } ], "params": { - "UseGlobalEndpoint": true, - "UseDualStack": false, + "Region": "us-east-2", "UseFIPS": false, - "Region": "us-east-2" + "UseDualStack": false, + "UseGlobalEndpoint": true } }, { @@ -3380,9 +2099,9 @@ "properties": { "authSchemes": [ { - "signingRegion": "us-east-1", + "name": "sigv4", "signingName": "sts", - "name": "sigv4" + "signingRegion": "us-east-1" } ] }, @@ -3399,10 +2118,10 @@ } ], "params": { - "UseGlobalEndpoint": true, - "UseDualStack": false, + "Region": "us-west-1", "UseFIPS": false, - "Region": "us-west-1" + "UseDualStack": false, + "UseGlobalEndpoint": true } }, { @@ -3412,9 +2131,9 @@ "properties": { "authSchemes": [ { - "signingRegion": "us-east-1", + "name": "sigv4", "signingName": "sts", - "name": "sigv4" + "signingRegion": "us-east-1" } ] }, @@ -3431,10 +2150,10 @@ } ], "params": { - "UseGlobalEndpoint": true, - "UseDualStack": false, + "Region": "us-west-2", "UseFIPS": false, - "Region": "us-west-2" + "UseDualStack": false, + "UseGlobalEndpoint": true } }, { @@ -3444,9 +2163,9 @@ "properties": { "authSchemes": [ { - "signingRegion": "us-east-3", + "name": "sigv4", "signingName": "sts", - "name": "sigv4" + "signingRegion": "us-east-3" } ] }, @@ -3463,10 +2182,10 @@ } ], "params": { - "UseGlobalEndpoint": true, - "UseDualStack": false, + "Region": "us-east-3", "UseFIPS": false, - "Region": "us-east-3" + "UseDualStack": false, + "UseGlobalEndpoint": true } }, { @@ -3487,10 +2206,24 @@ } ], "params": { - "UseGlobalEndpoint": true, + "Region": "us-west-1", + "UseFIPS": false, "UseDualStack": false, + "UseGlobalEndpoint": true, + "Endpoint": "https://example.com" + } + }, + { + "documentation": "UseGlobalEndpoint with unset region and custom endpoint", + "expect": { + "endpoint": { + "url": "https://example.com" + } + }, + "params": { "UseFIPS": false, - "Region": "us-west-1", + "UseDualStack": false, + "UseGlobalEndpoint": false, "Endpoint": "https://example.com" } } diff --git a/aws/sdk/aws-models/transcribe-streaming.json b/aws/sdk/aws-models/transcribe-streaming.json index 9580962c51..93e63cde42 100644 --- a/aws/sdk/aws-models/transcribe-streaming.json +++ b/aws/sdk/aws-models/transcribe-streaming.json @@ -2298,7 +2298,7 @@ "parameters": { "Region": { "builtIn": "AWS::Region", - "required": true, + "required": false, "documentation": "The AWS region used to dispatch the request.", "type": "String" }, @@ -2327,13 +2327,12 @@ { "conditions": [ { - "fn": "aws.partition", + "fn": "isSet", "argv": [ { - "ref": "Region" + "ref": "Endpoint" } - ], - "assign": "PartitionResult" + ] } ], "type": "tree", @@ -2341,14 +2340,20 @@ { "conditions": [ { - "fn": "isSet", + "fn": "booleanEquals", "argv": [ { - "ref": "Endpoint" - } + "ref": "UseFIPS" + }, + true ] } ], + "error": "Invalid Configuration: FIPS and custom endpoint are not supported", + "type": "error" + }, + { + "conditions": [], "type": "tree", "rules": [ { @@ -2357,67 +2362,42 @@ "fn": "booleanEquals", "argv": [ { - "ref": "UseFIPS" + "ref": "UseDualStack" }, true ] } ], - "error": "Invalid Configuration: FIPS and custom endpoint are not supported", + "error": "Invalid Configuration: Dualstack 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" + "endpoint": { + "url": { + "ref": "Endpoint" }, - { - "conditions": [], - "endpoint": { - "url": { - "ref": "Endpoint" - }, - "properties": {}, - "headers": {} - }, - "type": "endpoint" - } - ] + "properties": {}, + "headers": {} + }, + "type": "endpoint" } ] - }, + } + ] + }, + { + "conditions": [], + "type": "tree", + "rules": [ { "conditions": [ { - "fn": "booleanEquals", - "argv": [ - { - "ref": "UseFIPS" - }, - true - ] - }, - { - "fn": "booleanEquals", + "fn": "isSet", "argv": [ { - "ref": "UseDualStack" - }, - true + "ref": "Region" + } ] } ], @@ -2426,154 +2406,215 @@ { "conditions": [ { - "fn": "booleanEquals", + "fn": "aws.partition", "argv": [ - true, { - "fn": "getAttr", + "ref": "Region" + } + ], + "assign": "PartitionResult" + } + ], + "type": "tree", + "rules": [ + { + "conditions": [ + { + "fn": "booleanEquals", "argv": [ { - "ref": "PartitionResult" + "ref": "UseFIPS" }, - "supportsFIPS" + true ] - } - ] - }, - { - "fn": "booleanEquals", - "argv": [ - true, + }, { - "fn": "getAttr", + "fn": "booleanEquals", "argv": [ { - "ref": "PartitionResult" + "ref": "UseDualStack" }, - "supportsDualStack" + true ] } - ] - } - ], - "type": "tree", - "rules": [ - { - "conditions": [], + ], "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://transcribestreaming-fips.{Region}.{PartitionResult#dualStackDnsSuffix}", + "properties": {}, + "headers": {} + }, + "type": "endpoint" + } + ] + } + ] + }, { "conditions": [], - "endpoint": { - "url": "https://transcribestreaming-fips.{Region}.{PartitionResult#dualStackDnsSuffix}", - "properties": {}, - "headers": {} - }, - "type": "endpoint" + "error": "FIPS and DualStack are enabled, but this partition does not support one or both", + "type": "error" } ] - } - ] - }, - { - "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, + "conditions": [ { - "fn": "getAttr", + "fn": "booleanEquals", "argv": [ { - "ref": "PartitionResult" + "ref": "UseFIPS" }, - "supportsFIPS" + true ] } - ] - } - ], - "type": "tree", - "rules": [ - { - "conditions": [], + ], "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://transcribestreaming-fips.{Region}.{PartitionResult#dnsSuffix}", + "properties": {}, + "headers": {} + }, + "type": "endpoint" + } + ] + } + ] + }, { "conditions": [], - "endpoint": { - "url": "https://transcribestreaming-fips.{Region}.{PartitionResult#dnsSuffix}", - "properties": {}, - "headers": {} - }, - "type": "endpoint" + "error": "FIPS is enabled but this partition does not support FIPS", + "type": "error" } ] - } - ] - }, - { - "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, + "conditions": [ { - "fn": "getAttr", + "fn": "booleanEquals", "argv": [ { - "ref": "PartitionResult" + "ref": "UseDualStack" }, - "supportsDualStack" + 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://transcribestreaming.{Region}.{PartitionResult#dualStackDnsSuffix}", + "properties": {}, + "headers": {} + }, + "type": "endpoint" + } + ] + } + ] + }, + { + "conditions": [], + "error": "DualStack is enabled but this partition does not support DualStack", + "type": "error" + } ] - } - ], - "type": "tree", - "rules": [ + }, { "conditions": [], "type": "tree", @@ -2581,7 +2622,7 @@ { "conditions": [], "endpoint": { - "url": "https://transcribestreaming.{Region}.{PartitionResult#dualStackDnsSuffix}", + "url": "https://transcribestreaming.{Region}.{PartitionResult#dnsSuffix}", "properties": {}, "headers": {} }, @@ -2590,28 +2631,13 @@ ] } ] - }, - { - "conditions": [], - "error": "DualStack is enabled but this partition does not support DualStack", - "type": "error" } ] }, { "conditions": [], - "type": "tree", - "rules": [ - { - "conditions": [], - "endpoint": { - "url": "https://transcribestreaming.{Region}.{PartitionResult#dnsSuffix}", - "properties": {}, - "headers": {} - }, - "type": "endpoint" - } - ] + "error": "Invalid Configuration: Missing Region", + "type": "error" } ] } @@ -2620,146 +2646,146 @@ "smithy.rules#endpointTests": { "testCases": [ { - "documentation": "For region ap-southeast-2 with FIPS disabled and DualStack disabled", + "documentation": "For region ap-northeast-1 with FIPS disabled and DualStack disabled", "expect": { "endpoint": { - "url": "https://transcribestreaming.ap-southeast-2.amazonaws.com" + "url": "https://transcribestreaming.ap-northeast-1.amazonaws.com" } }, "params": { - "UseFIPS": false, + "Region": "ap-northeast-1", "UseDualStack": false, - "Region": "ap-southeast-2" + "UseFIPS": false } }, { - "documentation": "For region ca-central-1 with FIPS disabled and DualStack disabled", + "documentation": "For region ap-northeast-2 with FIPS disabled and DualStack disabled", "expect": { "endpoint": { - "url": "https://transcribestreaming.ca-central-1.amazonaws.com" + "url": "https://transcribestreaming.ap-northeast-2.amazonaws.com" } }, "params": { - "UseFIPS": false, + "Region": "ap-northeast-2", "UseDualStack": false, - "Region": "ca-central-1" + "UseFIPS": false } }, { - "documentation": "For region sa-east-1 with FIPS disabled and DualStack disabled", + "documentation": "For region ap-southeast-2 with FIPS disabled and DualStack disabled", "expect": { "endpoint": { - "url": "https://transcribestreaming.sa-east-1.amazonaws.com" + "url": "https://transcribestreaming.ap-southeast-2.amazonaws.com" } }, "params": { - "UseFIPS": false, + "Region": "ap-southeast-2", "UseDualStack": false, - "Region": "sa-east-1" + "UseFIPS": false } }, { - "documentation": "For region us-west-2 with FIPS disabled and DualStack disabled", + "documentation": "For region ca-central-1 with FIPS disabled and DualStack disabled", "expect": { "endpoint": { - "url": "https://transcribestreaming.us-west-2.amazonaws.com" + "url": "https://transcribestreaming.ca-central-1.amazonaws.com" } }, "params": { - "UseFIPS": false, + "Region": "ca-central-1", "UseDualStack": false, - "Region": "us-west-2" + "UseFIPS": false } }, { - "documentation": "For region ap-northeast-1 with FIPS disabled and DualStack disabled", + "documentation": "For region eu-central-1 with FIPS disabled and DualStack disabled", "expect": { "endpoint": { - "url": "https://transcribestreaming.ap-northeast-1.amazonaws.com" + "url": "https://transcribestreaming.eu-central-1.amazonaws.com" } }, "params": { - "UseFIPS": false, + "Region": "eu-central-1", "UseDualStack": false, - "Region": "ap-northeast-1" + "UseFIPS": false } }, { - "documentation": "For region ap-northeast-2 with FIPS disabled and DualStack disabled", + "documentation": "For region eu-west-1 with FIPS disabled and DualStack disabled", "expect": { "endpoint": { - "url": "https://transcribestreaming.ap-northeast-2.amazonaws.com" + "url": "https://transcribestreaming.eu-west-1.amazonaws.com" } }, "params": { - "UseFIPS": false, + "Region": "eu-west-1", "UseDualStack": false, - "Region": "ap-northeast-2" + "UseFIPS": false } }, { - "documentation": "For region eu-central-1 with FIPS disabled and DualStack disabled", + "documentation": "For region eu-west-2 with FIPS disabled and DualStack disabled", "expect": { "endpoint": { - "url": "https://transcribestreaming.eu-central-1.amazonaws.com" + "url": "https://transcribestreaming.eu-west-2.amazonaws.com" } }, "params": { - "UseFIPS": false, + "Region": "eu-west-2", "UseDualStack": false, - "Region": "eu-central-1" + "UseFIPS": false } }, { - "documentation": "For region us-east-1 with FIPS disabled and DualStack disabled", + "documentation": "For region sa-east-1 with FIPS disabled and DualStack disabled", "expect": { "endpoint": { - "url": "https://transcribestreaming.us-east-1.amazonaws.com" + "url": "https://transcribestreaming.sa-east-1.amazonaws.com" } }, "params": { - "UseFIPS": false, + "Region": "sa-east-1", "UseDualStack": false, - "Region": "us-east-1" + "UseFIPS": false } }, { - "documentation": "For region eu-west-1 with FIPS disabled and DualStack disabled", + "documentation": "For region us-east-1 with FIPS disabled and DualStack disabled", "expect": { "endpoint": { - "url": "https://transcribestreaming.eu-west-1.amazonaws.com" + "url": "https://transcribestreaming.us-east-1.amazonaws.com" } }, "params": { - "UseFIPS": false, + "Region": "us-east-1", "UseDualStack": false, - "Region": "eu-west-1" + "UseFIPS": false } }, { - "documentation": "For region eu-west-2 with FIPS disabled and DualStack disabled", + "documentation": "For region us-east-2 with FIPS disabled and DualStack disabled", "expect": { "endpoint": { - "url": "https://transcribestreaming.eu-west-2.amazonaws.com" + "url": "https://transcribestreaming.us-east-2.amazonaws.com" } }, "params": { - "UseFIPS": false, + "Region": "us-east-2", "UseDualStack": false, - "Region": "eu-west-2" + "UseFIPS": false } }, { - "documentation": "For region us-east-2 with FIPS disabled and DualStack disabled", + "documentation": "For region us-west-2 with FIPS disabled and DualStack disabled", "expect": { "endpoint": { - "url": "https://transcribestreaming.us-east-2.amazonaws.com" + "url": "https://transcribestreaming.us-west-2.amazonaws.com" } }, "params": { - "UseFIPS": false, + "Region": "us-west-2", "UseDualStack": false, - "Region": "us-east-2" + "UseFIPS": false } }, { @@ -2770,9 +2796,9 @@ } }, "params": { - "UseFIPS": true, + "Region": "us-east-1", "UseDualStack": true, - "Region": "us-east-1" + "UseFIPS": true } }, { @@ -2783,9 +2809,9 @@ } }, "params": { - "UseFIPS": true, + "Region": "us-east-1", "UseDualStack": false, - "Region": "us-east-1" + "UseFIPS": true } }, { @@ -2796,204 +2822,217 @@ } }, "params": { - "UseFIPS": false, + "Region": "us-east-1", "UseDualStack": true, - "Region": "us-east-1" + "UseFIPS": false } }, { - "documentation": "For region us-gov-west-1 with FIPS disabled and DualStack disabled", + "documentation": "For region cn-north-1 with FIPS disabled and DualStack disabled", "expect": { "endpoint": { - "url": "https://transcribestreaming.us-gov-west-1.amazonaws.com" + "url": "https://transcribestreaming.cn-north-1.amazonaws.com.cn" } }, "params": { - "UseFIPS": false, + "Region": "cn-north-1", "UseDualStack": false, - "Region": "us-gov-west-1" + "UseFIPS": false } }, { - "documentation": "For region us-gov-east-1 with FIPS disabled and DualStack disabled", + "documentation": "For region cn-northwest-1 with FIPS disabled and DualStack disabled", "expect": { "endpoint": { - "url": "https://transcribestreaming.us-gov-east-1.amazonaws.com" + "url": "https://transcribestreaming.cn-northwest-1.amazonaws.com.cn" } }, "params": { - "UseFIPS": false, + "Region": "cn-northwest-1", "UseDualStack": false, - "Region": "us-gov-east-1" + "UseFIPS": false } }, { - "documentation": "For region us-gov-east-1 with FIPS enabled and DualStack enabled", + "documentation": "For region cn-north-1 with FIPS enabled and DualStack enabled", "expect": { "endpoint": { - "url": "https://transcribestreaming-fips.us-gov-east-1.api.aws" + "url": "https://transcribestreaming-fips.cn-north-1.api.amazonwebservices.com.cn" } }, "params": { - "UseFIPS": true, + "Region": "cn-north-1", "UseDualStack": true, - "Region": "us-gov-east-1" + "UseFIPS": true } }, { - "documentation": "For region us-gov-east-1 with FIPS enabled and DualStack disabled", + "documentation": "For region cn-north-1 with FIPS enabled and DualStack disabled", "expect": { "endpoint": { - "url": "https://transcribestreaming-fips.us-gov-east-1.amazonaws.com" + "url": "https://transcribestreaming-fips.cn-north-1.amazonaws.com.cn" } }, "params": { - "UseFIPS": true, + "Region": "cn-north-1", "UseDualStack": false, - "Region": "us-gov-east-1" + "UseFIPS": true } }, { - "documentation": "For region us-gov-east-1 with FIPS disabled and DualStack enabled", + "documentation": "For region cn-north-1 with FIPS disabled and DualStack enabled", "expect": { "endpoint": { - "url": "https://transcribestreaming.us-gov-east-1.api.aws" + "url": "https://transcribestreaming.cn-north-1.api.amazonwebservices.com.cn" } }, "params": { - "UseFIPS": false, + "Region": "cn-north-1", "UseDualStack": true, - "Region": "us-gov-east-1" + "UseFIPS": false } }, { - "documentation": "For region us-isob-east-1 with FIPS enabled and DualStack disabled", + "documentation": "For region us-gov-east-1 with FIPS disabled and DualStack disabled", "expect": { "endpoint": { - "url": "https://transcribestreaming-fips.us-isob-east-1.sc2s.sgov.gov" + "url": "https://transcribestreaming.us-gov-east-1.amazonaws.com" } }, "params": { - "UseFIPS": true, + "Region": "us-gov-east-1", "UseDualStack": false, - "Region": "us-isob-east-1" + "UseFIPS": false } }, { - "documentation": "For region us-isob-east-1 with FIPS disabled and DualStack disabled", + "documentation": "For region us-gov-west-1 with FIPS disabled and DualStack disabled", "expect": { "endpoint": { - "url": "https://transcribestreaming.us-isob-east-1.sc2s.sgov.gov" + "url": "https://transcribestreaming.us-gov-west-1.amazonaws.com" } }, "params": { - "UseFIPS": false, + "Region": "us-gov-west-1", "UseDualStack": false, - "Region": "us-isob-east-1" + "UseFIPS": false } }, { - "documentation": "For region cn-northwest-1 with FIPS disabled and DualStack disabled", + "documentation": "For region us-gov-east-1 with FIPS enabled and DualStack enabled", "expect": { "endpoint": { - "url": "https://transcribestreaming.cn-northwest-1.amazonaws.com.cn" + "url": "https://transcribestreaming-fips.us-gov-east-1.api.aws" } }, "params": { - "UseFIPS": false, - "UseDualStack": false, - "Region": "cn-northwest-1" + "Region": "us-gov-east-1", + "UseDualStack": true, + "UseFIPS": true } }, { - "documentation": "For region cn-north-1 with FIPS disabled and DualStack disabled", + "documentation": "For region us-gov-east-1 with FIPS enabled and DualStack disabled", "expect": { "endpoint": { - "url": "https://transcribestreaming.cn-north-1.amazonaws.com.cn" + "url": "https://transcribestreaming-fips.us-gov-east-1.amazonaws.com" } }, "params": { - "UseFIPS": false, + "Region": "us-gov-east-1", "UseDualStack": false, - "Region": "cn-north-1" + "UseFIPS": true } }, { - "documentation": "For region cn-north-1 with FIPS enabled and DualStack enabled", + "documentation": "For region us-gov-east-1 with FIPS disabled and DualStack enabled", "expect": { "endpoint": { - "url": "https://transcribestreaming-fips.cn-north-1.api.amazonwebservices.com.cn" + "url": "https://transcribestreaming.us-gov-east-1.api.aws" } }, "params": { - "UseFIPS": true, + "Region": "us-gov-east-1", "UseDualStack": true, - "Region": "cn-north-1" + "UseFIPS": false } }, { - "documentation": "For region cn-north-1 with FIPS enabled and DualStack disabled", + "documentation": "For region us-iso-east-1 with FIPS disabled and DualStack disabled", "expect": { "endpoint": { - "url": "https://transcribestreaming-fips.cn-north-1.amazonaws.com.cn" + "url": "https://transcribestreaming.us-iso-east-1.c2s.ic.gov" } }, "params": { - "UseFIPS": true, + "Region": "us-iso-east-1", "UseDualStack": false, - "Region": "cn-north-1" + "UseFIPS": false } }, { - "documentation": "For region cn-north-1 with FIPS disabled and DualStack enabled", + "documentation": "For region us-iso-east-1 with FIPS enabled and DualStack disabled", "expect": { "endpoint": { - "url": "https://transcribestreaming.cn-north-1.api.amazonwebservices.com.cn" + "url": "https://transcribestreaming-fips.us-iso-east-1.c2s.ic.gov" } }, "params": { - "UseFIPS": false, - "UseDualStack": true, - "Region": "cn-north-1" + "Region": "us-iso-east-1", + "UseDualStack": false, + "UseFIPS": true } }, { - "documentation": "For region us-iso-east-1 with FIPS disabled and DualStack disabled", + "documentation": "For region us-isob-east-1 with FIPS enabled and DualStack disabled", "expect": { "endpoint": { - "url": "https://transcribestreaming.us-iso-east-1.c2s.ic.gov" + "url": "https://transcribestreaming-fips.us-isob-east-1.sc2s.sgov.gov" } }, "params": { - "UseFIPS": false, + "Region": "us-isob-east-1", "UseDualStack": false, - "Region": "us-iso-east-1" + "UseFIPS": true } }, { - "documentation": "For region us-iso-east-1 with FIPS enabled and DualStack disabled", + "documentation": "For region us-isob-east-1 with FIPS disabled and DualStack disabled", "expect": { "endpoint": { - "url": "https://transcribestreaming-fips.us-iso-east-1.c2s.ic.gov" + "url": "https://transcribestreaming.us-isob-east-1.sc2s.sgov.gov" } }, "params": { - "UseFIPS": true, + "Region": "us-isob-east-1", "UseDualStack": false, - "Region": "us-iso-east-1" + "UseFIPS": false } }, { - "documentation": "For custom endpoint with fips disabled and dualstack disabled", + "documentation": "For custom endpoint with region set and fips disabled and dualstack disabled", "expect": { "endpoint": { "url": "https://example.com" } }, "params": { + "Region": "us-east-1", + "UseDualStack": false, "UseFIPS": 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": { "UseDualStack": false, - "Region": "us-east-1", + "UseFIPS": false, "Endpoint": "https://example.com" } }, @@ -3003,9 +3042,9 @@ "error": "Invalid Configuration: FIPS and custom endpoint are not supported" }, "params": { - "UseFIPS": true, - "UseDualStack": false, "Region": "us-east-1", + "UseDualStack": false, + "UseFIPS": true, "Endpoint": "https://example.com" } }, @@ -3015,9 +3054,9 @@ "error": "Invalid Configuration: Dualstack and custom endpoint are not supported" }, "params": { - "UseFIPS": false, - "UseDualStack": true, "Region": "us-east-1", + "UseDualStack": true, + "UseFIPS": false, "Endpoint": "https://example.com" } } diff --git a/aws/sdk/build.gradle.kts b/aws/sdk/build.gradle.kts index e2b3d0b72b..3596d793dd 100644 --- a/aws/sdk/build.gradle.kts +++ b/aws/sdk/build.gradle.kts @@ -52,7 +52,9 @@ dependencies { // Class and functions for service and protocol membership for SDK generation -val awsServices: AwsServices by lazy { discoverServices(properties.get("aws.sdk.models.path"), loadServiceMembership()) } +val awsServices: AwsServices by lazy { + discoverServices(properties.get("aws.sdk.models.path"), loadServiceMembership()) +} val eventStreamAllowList: Set by lazy { eventStreamAllowList() } val crateVersioner by lazy { aws.sdk.CrateVersioner.defaultFor(rootProject, properties) } @@ -97,7 +99,9 @@ fun generateSmithyBuild(services: AwsServices): String { "codegen": { "includeFluentClient": false, "renameErrors": false, - "eventStreamAllowList": [$eventStreamAllowListMembers] + "eventStreamAllowList": [$eventStreamAllowListMembers], + "enableNewCrateOrganizationScheme": true, + "enableNewSmithyRuntime": false }, "service": "${service.service}", "module": "$moduleName", @@ -229,6 +233,7 @@ tasks.register("fixExampleManifests") { 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, diff --git a/aws/sdk/integration-tests/Cargo.toml b/aws/sdk/integration-tests/Cargo.toml index 406b718a94..286d73a6fb 100644 --- a/aws/sdk/integration-tests/Cargo.toml +++ b/aws/sdk/integration-tests/Cargo.toml @@ -2,6 +2,7 @@ # `./gradlew -Paws.fullsdk=true :aws:sdk:assemble` these tests are copied into their respective Service crates. [workspace] members = [ + "aws-smithy-runtime-test", "dynamodb", "ec2", "glacier", @@ -15,4 +16,5 @@ members = [ "s3control", "sts", "transcribestreaming", + "using-native-tls-instead-of-rustls", ] diff --git a/aws/sdk/integration-tests/aws-smithy-runtime-test/Cargo.toml b/aws/sdk/integration-tests/aws-smithy-runtime-test/Cargo.toml new file mode 100644 index 0000000000..b038527c92 --- /dev/null +++ b/aws/sdk/integration-tests/aws-smithy-runtime-test/Cargo.toml @@ -0,0 +1,25 @@ +[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 = "../../build/aws-sdk/sdk/aws-credential-types", features = ["test-util"] } +aws-config = { path = "../../build/aws-sdk/sdk/aws-config" } +aws-http = { path = "../../build/aws-sdk/sdk/aws-http" } +aws-sigv4 = { path = "../../build/aws-sdk/sdk/aws-sigv4" } +aws-sdk-s3 = { path = "../../build/aws-sdk/sdk/s3" } +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" } +aws-smithy-types = { path = "../../build/aws-sdk/sdk/aws-smithy-types" } +aws-smithy-http = { path = "../../build/aws-sdk/sdk/aws-smithy-http" } +aws-smithy-runtime = { path = "../../build/aws-sdk/sdk/aws-smithy-runtime" } +aws-smithy-runtime-api = { path = "../../build/aws-sdk/sdk/aws-smithy-runtime-api" } +aws-types = { path = "../../build/aws-sdk/sdk/aws-types" } +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/sdk/integration-tests/aws-smithy-runtime-test/src/auth.rs b/aws/sdk/integration-tests/aws-smithy-runtime-test/src/auth.rs new file mode 100644 index 0000000000..632f7957a3 --- /dev/null +++ b/aws/sdk/integration-tests/aws-smithy-runtime-test/src/auth.rs @@ -0,0 +1,24 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +use aws_smithy_runtime_api::client::orchestrator::BoxError; +use aws_smithy_runtime_api::client::runtime_plugin::RuntimePlugin; +use aws_smithy_runtime_api::config_bag::ConfigBag; + +#[derive(Debug)] +pub struct GetObjectAuthOrc {} + +impl GetObjectAuthOrc { + pub fn new() -> Self { + Self {} + } +} + +impl RuntimePlugin for GetObjectAuthOrc { + fn configure(&self, _cfg: &mut ConfigBag) -> Result<(), BoxError> { + // TODO(orchestrator) put an auth orchestrator in the bag + Ok(()) + } +} diff --git a/aws/sdk/integration-tests/aws-smithy-runtime-test/src/conn.rs b/aws/sdk/integration-tests/aws-smithy-runtime-test/src/conn.rs new file mode 100644 index 0000000000..84350fd7c3 --- /dev/null +++ b/aws/sdk/integration-tests/aws-smithy-runtime-test/src/conn.rs @@ -0,0 +1,44 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +use aws_smithy_client::conns::Https; +use aws_smithy_client::hyper_ext::Adapter; +use aws_smithy_http::body::SdkBody; +use aws_smithy_runtime_api::client::orchestrator::{ + BoxError, BoxFallibleFut, Connection, HttpRequest, +}; +use aws_smithy_runtime_api::client::runtime_plugin::RuntimePlugin; +use aws_smithy_runtime_api::config_bag::ConfigBag; + +#[derive(Debug)] +pub struct HyperConnection { + _adapter: Adapter, +} + +impl RuntimePlugin for HyperConnection { + fn configure(&self, _cfg: &mut ConfigBag) -> Result<(), BoxError> { + // TODO(orchestrator) put a connection in the bag + Ok(()) + } +} + +impl HyperConnection { + pub fn new() -> Self { + Self { + _adapter: Adapter::builder().build(aws_smithy_client::conns::https()), + } + } +} + +impl Connection for HyperConnection { + fn call( + &self, + _req: &mut HttpRequest, + _cfg: &ConfigBag, + ) -> BoxFallibleFut> { + todo!("hyper's connector wants to take ownership of req"); + // self.adapter.call(req) + } +} diff --git a/aws/sdk/integration-tests/aws-smithy-runtime-test/src/de.rs b/aws/sdk/integration-tests/aws-smithy-runtime-test/src/de.rs new file mode 100644 index 0000000000..522889f2e2 --- /dev/null +++ b/aws/sdk/integration-tests/aws-smithy-runtime-test/src/de.rs @@ -0,0 +1,35 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +use aws_smithy_runtime_api::client::interceptors::context::OutputOrError; +use aws_smithy_runtime_api::client::orchestrator::{BoxError, HttpResponse, ResponseDeserializer}; +use aws_smithy_runtime_api::client::runtime_plugin::RuntimePlugin; +use aws_smithy_runtime_api::config_bag::ConfigBag; + +#[derive(Debug)] +pub struct GetObjectResponseDeserializer {} + +impl GetObjectResponseDeserializer { + pub fn new() -> Self { + Self {} + } +} + +impl RuntimePlugin for GetObjectResponseDeserializer { + fn configure(&self, _cfg: &mut ConfigBag) -> Result<(), BoxError> { + // TODO(orchestrator) put a deserializer in the bag + Ok(()) + } +} + +impl ResponseDeserializer for GetObjectResponseDeserializer { + fn deserialize_streaming(&self, _response: &mut HttpResponse) -> Option { + todo!() + } + + fn deserialize_nonstreaming(&self, _response: &HttpResponse) -> OutputOrError { + todo!() + } +} diff --git a/aws/sdk/integration-tests/aws-smithy-runtime-test/src/endpoints.rs b/aws/sdk/integration-tests/aws-smithy-runtime-test/src/endpoints.rs new file mode 100644 index 0000000000..0eb3ac40a9 --- /dev/null +++ b/aws/sdk/integration-tests/aws-smithy-runtime-test/src/endpoints.rs @@ -0,0 +1,24 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +use aws_smithy_http::event_stream::BoxError; +use aws_smithy_runtime_api::client::runtime_plugin::RuntimePlugin; +use aws_smithy_runtime_api::config_bag::ConfigBag; + +#[derive(Debug)] +pub struct GetObjectEndpointOrc {} + +impl GetObjectEndpointOrc { + pub fn new() -> Self { + Self {} + } +} + +impl RuntimePlugin for GetObjectEndpointOrc { + fn configure(&self, _cfg: &mut ConfigBag) -> Result<(), BoxError> { + // TODO(orchestrator) put an endpoint orchestrator in the bag + Ok(()) + } +} diff --git a/aws/sdk/integration-tests/aws-smithy-runtime-test/src/interceptors.rs b/aws/sdk/integration-tests/aws-smithy-runtime-test/src/interceptors.rs new file mode 100644 index 0000000000..fd4c8649c0 --- /dev/null +++ b/aws/sdk/integration-tests/aws-smithy-runtime-test/src/interceptors.rs @@ -0,0 +1,103 @@ +/* + * 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/sdk/integration-tests/aws-smithy-runtime-test/src/main.rs b/aws/sdk/integration-tests/aws-smithy-runtime-test/src/main.rs new file mode 100644 index 0000000000..7309a4caae --- /dev/null +++ b/aws/sdk/integration-tests/aws-smithy-runtime-test/src/main.rs @@ -0,0 +1,70 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +mod auth; +mod conn; +mod de; +mod endpoints; +mod interceptors; +mod retry; +mod ser; + +use aws_sdk_s3::operation::get_object::{GetObjectError, GetObjectInput, GetObjectOutput}; +use aws_sdk_s3::types::ChecksumMode; +use aws_smithy_runtime::client::orchestrator::invoke; +use aws_smithy_runtime_api::client::interceptors::Interceptors; +use aws_smithy_runtime_api::client::orchestrator::{BoxError, HttpRequest, HttpResponse}; +use aws_smithy_runtime_api::client::runtime_plugin::RuntimePlugins; +use aws_smithy_runtime_api::config_bag::ConfigBag; +use aws_smithy_runtime_api::type_erasure::TypedBox; + +#[tokio::main] +async fn main() -> Result<(), BoxError> { + tracing_subscriber::fmt::init(); + + // Create the config we'll need to send the request + the request itself + let sdk_config = aws_config::load_from_env().await; + let _service_config = aws_sdk_s3::Config::from(&sdk_config); + + let input = TypedBox::new( + GetObjectInput::builder() + .bucket("zhessler-test-bucket") + .key("1000-lines.txt") + .checksum_mode(ChecksumMode::Enabled) + .build()?, + ) + .erase(); + + let mut runtime_plugins = RuntimePlugins::new(); + + // TODO(smithy-orchestrator-codegen) Make it so these are added by default for S3 + runtime_plugins + .with_client_plugin(auth::GetObjectAuthOrc::new()) + .with_client_plugin(conn::HyperConnection::new()) + // TODO(smithy-orchestrator-codegen) Make it so these are added by default for this S3 operation + .with_operation_plugin(endpoints::GetObjectEndpointOrc::new()) + .with_operation_plugin(retry::GetObjectRetryStrategy::new()) + .with_operation_plugin(de::GetObjectResponseDeserializer::new()) + .with_operation_plugin(ser::GetObjectInputSerializer::new()); + + let mut cfg = ConfigBag::base(); + let mut interceptors: Interceptors = Interceptors::new(); + let output = TypedBox::::assume_from( + invoke(input, &mut interceptors, &runtime_plugins, &mut cfg) + .await + .map_err(|err| { + err.map_service_error(|err| { + TypedBox::::assume_from(err) + .expect("error is GetObjectError") + .unwrap() + }) + })?, + ) + .expect("output is GetObjectOutput") + .unwrap(); + + dbg!(output); + Ok(()) +} diff --git a/aws/sdk/integration-tests/aws-smithy-runtime-test/src/retry.rs b/aws/sdk/integration-tests/aws-smithy-runtime-test/src/retry.rs new file mode 100644 index 0000000000..2a731a5f35 --- /dev/null +++ b/aws/sdk/integration-tests/aws-smithy-runtime-test/src/retry.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::client::interceptors::InterceptorContext; +use aws_smithy_runtime_api::client::orchestrator::{ + BoxError, HttpRequest, HttpResponse, RetryStrategy, +}; +use aws_smithy_runtime_api::client::runtime_plugin::RuntimePlugin; +use aws_smithy_runtime_api::config_bag::ConfigBag; + +#[derive(Debug)] +pub struct GetObjectRetryStrategy {} + +impl GetObjectRetryStrategy { + pub fn new() -> Self { + Self {} + } +} + +impl RuntimePlugin for GetObjectRetryStrategy { + fn configure(&self, _cfg: &mut ConfigBag) -> Result<(), BoxError> { + // TODO(orchestrator) put a retry strategy in the bag + Ok(()) + } +} + +impl RetryStrategy for GetObjectRetryStrategy { + fn should_attempt_initial_request(&self, _cfg: &ConfigBag) -> Result<(), BoxError> { + todo!() + } + + fn should_attempt_retry( + &self, + _context: &InterceptorContext, + _cfg: &ConfigBag, + ) -> Result { + todo!() + } +} + +// retry_classifier: Arc::new( +// |res: Result<&SdkSuccess, &SdkError>| -> RetryKind { +// let classifier = AwsResponseRetryClassifier::new(); +// classifier.classify_retry(res) +// }, +// ), diff --git a/aws/sdk/integration-tests/aws-smithy-runtime-test/src/ser.rs b/aws/sdk/integration-tests/aws-smithy-runtime-test/src/ser.rs new file mode 100644 index 0000000000..5aed30268e --- /dev/null +++ b/aws/sdk/integration-tests/aws-smithy-runtime-test/src/ser.rs @@ -0,0 +1,32 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +use aws_smithy_http::event_stream::BoxError; +use aws_smithy_runtime_api::client::interceptors::context::Input; +use aws_smithy_runtime_api::client::orchestrator::{HttpRequest, RequestSerializer}; +use aws_smithy_runtime_api::client::runtime_plugin::RuntimePlugin; +use aws_smithy_runtime_api::config_bag::ConfigBag; + +#[derive(Debug)] +pub struct GetObjectInputSerializer {} + +impl GetObjectInputSerializer { + pub fn new() -> Self { + Self {} + } +} + +impl RuntimePlugin for GetObjectInputSerializer { + fn configure(&self, _cfg: &mut ConfigBag) -> Result<(), BoxError> { + // TODO(orchestrator) put a serializer in the bag + Ok(()) + } +} + +impl RequestSerializer for GetObjectInputSerializer { + fn serialize_input(&self, _input: &Input, _cfg: &ConfigBag) -> Result { + todo!() + } +} diff --git a/aws/sdk/integration-tests/dynamodb/Cargo.toml b/aws/sdk/integration-tests/dynamodb/Cargo.toml index bf47eddcca..7801137286 100644 --- a/aws/sdk/integration-tests/dynamodb/Cargo.toml +++ b/aws/sdk/integration-tests/dynamodb/Cargo.toml @@ -22,10 +22,10 @@ aws-smithy-async = { path = "../../build/aws-sdk/sdk/aws-smithy-async" } aws-types = { path = "../../build/aws-sdk/sdk/aws-types" } bytes = "1.0.0" criterion = { version = "0.4.0" } -futures-util = "0.3.16" +futures-util = { version = "0.3.16", default-features = false } http = "0.2.0" serde_json = "1.0.0" -tokio = { version = "1.8.4", features = ["full", "test-util"] } +tokio = { version = "1.23.1", features = ["full", "test-util"] } tracing-subscriber = { version = "0.3.15", features = ["env-filter"] } tokio-stream = "0.1.5" diff --git a/aws/sdk/integration-tests/dynamodb/benches/deserialization_bench.rs b/aws/sdk/integration-tests/dynamodb/benches/deserialization_bench.rs index bea625d81f..faf7eba2ec 100644 --- a/aws/sdk/integration-tests/dynamodb/benches/deserialization_bench.rs +++ b/aws/sdk/integration-tests/dynamodb/benches/deserialization_bench.rs @@ -3,7 +3,7 @@ * SPDX-License-Identifier: Apache-2.0 */ -use aws_sdk_dynamodb::operation::Query; +use aws_sdk_dynamodb::operation::query::Query; use aws_smithy_http::response::ParseHttpResponse; use bytes::Bytes; use criterion::{criterion_group, criterion_main, Criterion}; diff --git a/aws/sdk/integration-tests/dynamodb/benches/serialization_bench.rs b/aws/sdk/integration-tests/dynamodb/benches/serialization_bench.rs index 694c7f7918..82e37cfe8b 100644 --- a/aws/sdk/integration-tests/dynamodb/benches/serialization_bench.rs +++ b/aws/sdk/integration-tests/dynamodb/benches/serialization_bench.rs @@ -3,8 +3,8 @@ * SPDX-License-Identifier: Apache-2.0 */ -use aws_sdk_dynamodb::input::PutItemInput; -use aws_sdk_dynamodb::model::AttributeValue; +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; diff --git a/aws/sdk/integration-tests/dynamodb/tests/endpoints.rs b/aws/sdk/integration-tests/dynamodb/tests/endpoints.rs index 4d3dedd71e..6a01fedefd 100644 --- a/aws/sdk/integration-tests/dynamodb/tests/endpoints.rs +++ b/aws/sdk/integration-tests/dynamodb/tests/endpoints.rs @@ -3,7 +3,7 @@ * SPDX-License-Identifier: Apache-2.0 */ -use aws_sdk_dynamodb::{config, Credentials, Region}; +use aws_sdk_dynamodb::config::{self, Credentials, Region}; use aws_types::SdkConfig; use http::Uri; diff --git a/aws/sdk/integration-tests/dynamodb/tests/movies.rs b/aws/sdk/integration-tests/dynamodb/tests/movies.rs index 918dc9c1ba..a3eaa244ad 100644 --- a/aws/sdk/integration-tests/dynamodb/tests/movies.rs +++ b/aws/sdk/integration-tests/dynamodb/tests/movies.rs @@ -6,12 +6,13 @@ use aws_sdk_dynamodb as dynamodb; use aws_smithy_client::test_connection::TestConnection; use aws_smithy_http::body::SdkBody; -use dynamodb::model::{ +use dynamodb::config::{Credentials, Region}; +use dynamodb::operation::query::QueryOutput; +use dynamodb::types::{ AttributeDefinition, AttributeValue, KeySchemaElement, KeyType, ProvisionedThroughput, ScalarAttributeType, TableStatus, }; -use dynamodb::output::QueryOutput; -use dynamodb::{Client, Credentials, Region}; +use dynamodb::Client; use http::header::{HeaderName, AUTHORIZATION}; use http::Uri; use serde_json::Value; diff --git a/aws/sdk/integration-tests/dynamodb/tests/paginators.rs b/aws/sdk/integration-tests/dynamodb/tests/paginators.rs index 59aa6e6101..807a11890d 100644 --- a/aws/sdk/integration-tests/dynamodb/tests/paginators.rs +++ b/aws/sdk/integration-tests/dynamodb/tests/paginators.rs @@ -9,7 +9,7 @@ use std::iter::FromIterator; use tokio_stream::StreamExt; use aws_credential_types::Credentials; -use aws_sdk_dynamodb::model::AttributeValue; +use aws_sdk_dynamodb::types::AttributeValue; use aws_sdk_dynamodb::{Client, Config}; use aws_smithy_client::http_connector::HttpConnector; use aws_smithy_client::test_connection::{capture_request, TestConnection}; diff --git a/aws/sdk/integration-tests/dynamodb/tests/shared-config.rs b/aws/sdk/integration-tests/dynamodb/tests/shared-config.rs index aabccc8371..3d5edf8cb2 100644 --- a/aws/sdk/integration-tests/dynamodb/tests/shared-config.rs +++ b/aws/sdk/integration-tests/dynamodb/tests/shared-config.rs @@ -3,7 +3,7 @@ * SPDX-License-Identifier: Apache-2.0 */ -use aws_sdk_dynamodb::{Credentials, Region}; +use aws_sdk_dynamodb::config::{Credentials, Region}; use http::Uri; /// Iterative test of loading clients from shared configuration diff --git a/aws/sdk/integration-tests/dynamodb/tests/timeouts.rs b/aws/sdk/integration-tests/dynamodb/tests/timeouts.rs index 5695b403d8..b566c598c7 100644 --- a/aws/sdk/integration-tests/dynamodb/tests/timeouts.rs +++ b/aws/sdk/integration-tests/dynamodb/tests/timeouts.rs @@ -8,7 +8,7 @@ use std::time::Duration; use aws_credential_types::provider::SharedCredentialsProvider; use aws_credential_types::Credentials; -use aws_sdk_dynamodb::types::SdkError; +use aws_sdk_dynamodb::error::SdkError; use aws_smithy_async::rt::sleep::{AsyncSleep, Sleep}; use aws_smithy_client::never::NeverConnector; use aws_smithy_types::retry::RetryConfig; diff --git a/aws/sdk/integration-tests/ec2/Cargo.toml b/aws/sdk/integration-tests/ec2/Cargo.toml index d7580d6b89..853b1b594f 100644 --- a/aws/sdk/integration-tests/ec2/Cargo.toml +++ b/aws/sdk/integration-tests/ec2/Cargo.toml @@ -10,6 +10,6 @@ publish = false aws-credential-types = { path = "../../build/aws-sdk/sdk/aws-credential-types", features = ["test-util"] } aws-sdk-ec2 = { path = "../../build/aws-sdk/sdk/ec2" } aws-smithy-client = { path = "../../build/aws-sdk/sdk/aws-smithy-client", features = ["test-util"]} -tokio = { version = "1.8.4", features = ["full"]} +tokio = { version = "1.23.1", features = ["full"]} http = "0.2.0" tokio-stream = "0.1.5" diff --git a/aws/sdk/integration-tests/ec2/tests/paginators.rs b/aws/sdk/integration-tests/ec2/tests/paginators.rs index 533e7d0dfd..83528f2075 100644 --- a/aws/sdk/integration-tests/ec2/tests/paginators.rs +++ b/aws/sdk/integration-tests/ec2/tests/paginators.rs @@ -5,7 +5,7 @@ use tokio_stream::StreamExt; -use aws_sdk_ec2::{model::InstanceType, Client, Config, Credentials, Region}; +use aws_sdk_ec2::{config::Credentials, config::Region, types::InstanceType, Client, Config}; use aws_smithy_client::http_connector::HttpConnector; use aws_smithy_client::test_connection::TestConnection; diff --git a/aws/sdk/integration-tests/glacier/Cargo.toml b/aws/sdk/integration-tests/glacier/Cargo.toml index 88e20751d2..4c4ce34887 100644 --- a/aws/sdk/integration-tests/glacier/Cargo.toml +++ b/aws/sdk/integration-tests/glacier/Cargo.toml @@ -18,5 +18,5 @@ aws-smithy-client = { path = "../../build/aws-sdk/sdk/aws-smithy-client", featur aws-smithy-protocol-test = { path = "../../build/aws-sdk/sdk/aws-smithy-protocol-test"} bytes = "1.0.0" http = "0.2.0" -tokio = { version = "1.8.4", 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/glacier/tests/custom-headers.rs b/aws/sdk/integration-tests/glacier/tests/custom-headers.rs index 5ab708ef07..00163b0a81 100644 --- a/aws/sdk/integration-tests/glacier/tests/custom-headers.rs +++ b/aws/sdk/integration-tests/glacier/tests/custom-headers.rs @@ -3,8 +3,8 @@ * SPDX-License-Identifier: Apache-2.0 */ -use aws_sdk_glacier::types::ByteStream; -use aws_sdk_glacier::{Credentials, Region}; +use aws_sdk_glacier::config::{Credentials, Region}; +use aws_sdk_glacier::primitives::ByteStream; use aws_smithy_client::test_connection::capture_request; use aws_smithy_protocol_test::{assert_ok, validate_headers}; diff --git a/aws/sdk/integration-tests/iam/Cargo.toml b/aws/sdk/integration-tests/iam/Cargo.toml index ea3ff36ea0..9c7b6b7464 100644 --- a/aws/sdk/integration-tests/iam/Cargo.toml +++ b/aws/sdk/integration-tests/iam/Cargo.toml @@ -19,5 +19,5 @@ aws-smithy-client = { path = "../../build/aws-sdk/sdk/aws-smithy-client", featur aws-smithy-types = { path = "../../build/aws-sdk/sdk/aws-smithy-types" } bytes = "1.0.0" http = "0.2.0" -tokio = { version = "1.8.4", 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/iam/tests/resolve-global-endpoint.rs b/aws/sdk/integration-tests/iam/tests/resolve-global-endpoint.rs index 252cc51822..923bf568f6 100644 --- a/aws/sdk/integration-tests/iam/tests/resolve-global-endpoint.rs +++ b/aws/sdk/integration-tests/iam/tests/resolve-global-endpoint.rs @@ -3,7 +3,7 @@ * SPDX-License-Identifier: Apache-2.0 */ -use aws_sdk_iam::{Credentials, Region}; +use aws_sdk_iam::config::{Credentials, Region}; use aws_smithy_client::test_connection::capture_request; // this test is ignored because pseudoregions have been removed. This test should be re-enabled diff --git a/aws/sdk/integration-tests/kms/Cargo.toml b/aws/sdk/integration-tests/kms/Cargo.toml index bfe5ed5cd9..8820956d27 100644 --- a/aws/sdk/integration-tests/kms/Cargo.toml +++ b/aws/sdk/integration-tests/kms/Cargo.toml @@ -19,5 +19,5 @@ aws-smithy-http = { path = "../../build/aws-sdk/sdk/aws-smithy-http" } aws-smithy-types = { path = "../../build/aws-sdk/sdk/aws-smithy-types" } bytes = "1.0.0" http = "0.2.0" -tokio = { version = "1.8.4", 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 baa39ef6a4..8d17e13d48 100644 --- a/aws/sdk/integration-tests/kms/tests/integration.rs +++ b/aws/sdk/integration-tests/kms/tests/integration.rs @@ -6,14 +6,14 @@ 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_http::body::SdkBody; use http::header::AUTHORIZATION; use http::Uri; -use kms::operation::GenerateRandom; -use kms::Credentials; -use kms::{Config, Region}; +use kms::config::{Config, Credentials, Region}; +use kms::operation::generate_random::GenerateRandomInput; use std::time::{Duration, UNIX_EPOCH}; type Client = CoreClient; @@ -73,7 +73,7 @@ async fn generate_random() { .region(Region::new("us-east-1")) .credentials_provider(Credentials::for_tests()) .build(); - let mut op = GenerateRandom::builder() + let mut op = GenerateRandomInput::builder() .number_of_bytes(64) .build() .unwrap() @@ -111,7 +111,7 @@ async fn generate_random_malformed_response() { .region(Region::new("us-east-1")) .credentials_provider(Credentials::for_tests()) .build(); - let op = GenerateRandom::builder() + let op = GenerateRandomInput::builder() .number_of_bytes(64) .build() .unwrap() @@ -151,7 +151,7 @@ async fn generate_random_keystore_not_found() { .body(r#"{"__type":"CustomKeyStoreNotFoundException"}"#).unwrap()) ]); - let mut op = GenerateRandom::builder() + let mut op = GenerateRandomInput::builder() .number_of_bytes(64) .custom_key_store_id("does not exist") .build() diff --git a/aws/sdk/integration-tests/kms/tests/sensitive-it.rs b/aws/sdk/integration-tests/kms/tests/sensitive-it.rs index 5a97651d83..2de72f3279 100644 --- a/aws/sdk/integration-tests/kms/tests/sensitive-it.rs +++ b/aws/sdk/integration-tests/kms/tests/sensitive-it.rs @@ -12,19 +12,23 @@ use aws_smithy_http::result::SdkError; use aws_smithy_http::retry::ClassifyRetry; use aws_smithy_types::retry::{ErrorKind, RetryKind}; use bytes::Bytes; -use kms::error::CreateAliasError; -use kms::operation::{CreateAlias, GenerateRandom}; -use kms::output::GenerateRandomOutput; -use kms::types::Blob; +use kms::operation::create_alias::{CreateAlias, CreateAliasError, CreateAliasInput}; +use kms::operation::generate_random::{GenerateRandom, GenerateRandomOutput}; +use kms::primitives::Blob; #[test] 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 }" + ); let output = GenerateRandomOutput::builder() .plaintext(Blob::new("some output")) .build(); assert_eq!( format!("{:?}", output), - "GenerateRandomOutput { plaintext: \"*** Sensitive Data Redacted ***\" }" + "GenerateRandomOutput { plaintext: \"*** Sensitive Data Redacted ***\", _request_id: None }" ); } @@ -35,9 +39,9 @@ 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::(); + assert_send_sync::(); assert_send_sync::(); assert_send_sync::(); let conf = kms::Config::builder().build(); @@ -66,13 +70,13 @@ async fn client_is_clone() { #[test] fn types_are_debug() { assert_debug::(); - assert_debug::(); - assert_debug::(); + assert_debug::(); + assert_debug::(); } async fn create_alias_op() -> Parts { let conf = kms::Config::builder().build(); - let (_, parts) = CreateAlias::builder() + let (_, parts) = CreateAliasInput::builder() .build() .unwrap() .make_operation(&conf) diff --git a/aws/sdk/integration-tests/lambda/Cargo.toml b/aws/sdk/integration-tests/lambda/Cargo.toml index 5492fbc68a..169327eb75 100644 --- a/aws/sdk/integration-tests/lambda/Cargo.toml +++ b/aws/sdk/integration-tests/lambda/Cargo.toml @@ -19,5 +19,5 @@ bytes = "1.0.0" futures-core = "0.3.14" http = "0.2.0" serde_json = "1.0.0" -tokio = { version = "1.8.4", 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/lambda/tests/request_id.rs b/aws/sdk/integration-tests/lambda/tests/request_id.rs new file mode 100644 index 0000000000..2bc465835e --- /dev/null +++ b/aws/sdk/integration-tests/lambda/tests/request_id.rs @@ -0,0 +1,38 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +use aws_sdk_lambda::operation::list_functions::{ListFunctions, ListFunctionsError}; +use aws_sdk_lambda::operation::RequestId; +use aws_smithy_http::response::ParseHttpResponse; +use bytes::Bytes; + +#[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()); +} + +#[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()); +} diff --git a/aws/sdk/integration-tests/no-default-features/Cargo.toml b/aws/sdk/integration-tests/no-default-features/Cargo.toml index bb0ba9d02d..545837f66c 100644 --- a/aws/sdk/integration-tests/no-default-features/Cargo.toml +++ b/aws/sdk/integration-tests/no-default-features/Cargo.toml @@ -16,6 +16,7 @@ publish = false [dev-dependencies] 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" } futures = "0.3.25" -tokio = { version = "1.8.4", 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/no-default-features/tests/client-construction.rs b/aws/sdk/integration-tests/no-default-features/tests/client-construction.rs index 1a602dc985..8f250f9322 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 @@ -13,12 +13,22 @@ async fn test_clients_from_sdk_config() { } // This will fail due to lack of a connector when constructing the service client -#[test] +#[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." )] -fn test_clients_from_service_config() { - let config = aws_sdk_s3::Config::builder().build(); +async fn test_clients_from_service_config() { + #[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 { + todo!() + } + } + + let config = aws_sdk_s3::Config::builder() + .sleep_impl(std::sync::Arc::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/Cargo.toml b/aws/sdk/integration-tests/polly/Cargo.toml index b7b1a0b6f8..5412a3dcaf 100644 --- a/aws/sdk/integration-tests/polly/Cargo.toml +++ b/aws/sdk/integration-tests/polly/Cargo.toml @@ -18,5 +18,5 @@ aws-smithy-client = { path = "../../build/aws-sdk/sdk/aws-smithy-client", featur aws-smithy-http = { path = "../../build/aws-sdk/sdk/aws-smithy-http" } bytes = "1.0.0" http = "0.2.0" -tokio = { version = "1.8.4", features = ["full"]} +tokio = { version = "1.23.1", features = ["full"]} tracing-subscriber = { version = "0.3.15", features = ["env-filter"] } diff --git a/aws/sdk/integration-tests/polly/tests/presigning.rs b/aws/sdk/integration-tests/polly/tests/presigning.rs index 7dc40b8e5d..bfb8729164 100644 --- a/aws/sdk/integration-tests/polly/tests/presigning.rs +++ b/aws/sdk/integration-tests/polly/tests/presigning.rs @@ -4,19 +4,21 @@ */ use aws_sdk_polly as polly; -use aws_sdk_polly::model::{OutputFormat, VoiceId}; -use polly::presigning::config::PresigningConfig; +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> { - let config = polly::Config::builder() - .credentials_provider(polly::Credentials::for_tests()) - .region(polly::Region::new("us-east-1")) + let config = Config::builder() + .credentials_provider(Credentials::for_tests()) + .region(Region::new("us-east-1")) .build(); - let input = polly::input::SynthesizeSpeechInput::builder() + let input = SynthesizeSpeechInput::builder() .output_format(OutputFormat::Mp3) .text("hello, world") .voice_id(VoiceId::Joanna) diff --git a/aws/sdk/integration-tests/qldbsession/Cargo.toml b/aws/sdk/integration-tests/qldbsession/Cargo.toml index f6710cb575..ef09721a84 100644 --- a/aws/sdk/integration-tests/qldbsession/Cargo.toml +++ b/aws/sdk/integration-tests/qldbsession/Cargo.toml @@ -18,5 +18,5 @@ aws-smithy-client = { path = "../../build/aws-sdk/sdk/aws-smithy-client", featur aws-smithy-http = { path = "../../build/aws-sdk/sdk/aws-smithy-http" } aws-smithy-types = { path = "../../build/aws-sdk/sdk/aws-smithy-types" } http = "0.2.0" -tokio = { version = "1.8.4", features = ["full"]} +tokio = { version = "1.23.1", features = ["full"]} tracing-subscriber = { version = "0.3.15", features = ["env-filter"] } diff --git a/aws/sdk/integration-tests/qldbsession/tests/integration.rs b/aws/sdk/integration-tests/qldbsession/tests/integration.rs index 9e02690acb..680e1647d5 100644 --- a/aws/sdk/integration-tests/qldbsession/tests/integration.rs +++ b/aws/sdk/integration-tests/qldbsession/tests/integration.rs @@ -9,11 +9,10 @@ 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::model::StartSessionRequest; -use qldbsession::operation::SendCommand; -use qldbsession::Credentials; -use qldbsession::{Config, Region}; +use qldbsession::operation::send_command::SendCommandInput; +use qldbsession::types::StartSessionRequest; use std::time::{Duration, UNIX_EPOCH}; pub type Client = CoreClient; @@ -46,7 +45,7 @@ async fn signv4_use_correct_service_name() { .credentials_provider(Credentials::for_tests()) .build(); - let mut op = SendCommand::builder() + let mut op = SendCommandInput::builder() .start_session( StartSessionRequest::builder() .ledger_name("not-real-ledger") diff --git a/aws/sdk/integration-tests/s3/Cargo.toml b/aws/sdk/integration-tests/s3/Cargo.toml index 7da45d554a..88c608a0bc 100644 --- a/aws/sdk/integration-tests/s3/Cargo.toml +++ b/aws/sdk/integration-tests/s3/Cargo.toml @@ -26,15 +26,15 @@ aws-types = { path = "../../build/aws-sdk/sdk/aws-types" } bytes = "1" bytes-utils = "0.1.2" fastrand = "1.8.0" -futures-util = "0.3.25" +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.12" +hyper = "0.14.25" serde_json = "1" smol = "1.2" tempfile = "3" -tokio = { version = "1.8.4", features = ["macros", "test-util", "rt-multi-thread"] } +tokio = { version = "1.23.1", features = ["macros", "test-util", "rt-multi-thread"] } tracing = "0.1.37" tracing-appender = "0.2.2" tracing-subscriber = { version = "0.3.15", features = ["env-filter", "json"] } 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 a1e44b2c7a..209403de4a 100644 --- a/aws/sdk/integration-tests/s3/tests/alternative-async-runtime.rs +++ b/aws/sdk/integration-tests/s3/tests/alternative-async-runtime.rs @@ -5,11 +5,12 @@ use aws_config::retry::RetryConfig; use aws_credential_types::provider::SharedCredentialsProvider; -use aws_sdk_s3::model::{ +use aws_sdk_s3::config::{Credentials, Region}; +use aws_sdk_s3::types::{ CompressionType, CsvInput, CsvOutput, ExpressionType, FileHeaderInfo, InputSerialization, OutputSerialization, }; -use aws_sdk_s3::{Client, Config, Credentials, Region}; +use aws_sdk_s3::{Client, Config}; use aws_smithy_async::assert_elapsed; use aws_smithy_async::rt::sleep::{AsyncSleep, Sleep}; use aws_smithy_client::never::NeverConnector; diff --git a/aws/sdk/integration-tests/s3/tests/checksums.rs b/aws/sdk/integration-tests/s3/tests/checksums.rs index 79f00a2a3a..cb1e574e48 100644 --- a/aws/sdk/integration-tests/s3/tests/checksums.rs +++ b/aws/sdk/integration-tests/s3/tests/checksums.rs @@ -6,7 +6,9 @@ use aws_config::SdkConfig; use aws_credential_types::provider::SharedCredentialsProvider; use aws_http::user_agent::AwsUserAgent; -use aws_sdk_s3::{model::ChecksumAlgorithm, output::GetObjectOutput, Client, Credentials, Region}; +use aws_sdk_s3::config::{Credentials, Region}; +use aws_sdk_s3::Client; +use aws_sdk_s3::{operation::get_object::GetObjectOutput, types::ChecksumAlgorithm}; use aws_smithy_client::test_connection::{capture_request, TestConnection}; use aws_smithy_http::body::SdkBody; use http::header::AUTHORIZATION; @@ -69,7 +71,7 @@ async fn test_checksum_on_streaming_response( .get_object() .bucket("some-test-bucket") .key("test.txt") - .checksum_mode(aws_sdk_s3::model::ChecksumMode::Enabled) + .checksum_mode(aws_sdk_s3::types::ChecksumMode::Enabled) .customize() .await .unwrap() @@ -169,7 +171,7 @@ async fn test_checksum_on_streaming_request<'a>( use std::io::Write; file.write_all(body).unwrap(); - let body = aws_sdk_s3::types::ByteStream::read_from() + let body = aws_sdk_s3::primitives::ByteStream::read_from() .path(file.path()) .buffer_size(1024) .build() diff --git a/aws/sdk/integration-tests/s3/tests/custom-error-deserializer.rs b/aws/sdk/integration-tests/s3/tests/custom-error-deserializer.rs deleted file mode 100644 index 46b1fc50f7..0000000000 --- a/aws/sdk/integration-tests/s3/tests/custom-error-deserializer.rs +++ /dev/null @@ -1,38 +0,0 @@ -/* - * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. - * SPDX-License-Identifier: Apache-2.0 - */ - -use aws_sdk_s3::operation::GetObject; -use aws_sdk_s3::ErrorExt; -use aws_smithy_http::response::ParseHttpResponse; -use bytes::Bytes; - -#[test] -fn deserialize_extended_errors() { - let resp = http::Response::builder() - .header( - "x-amz-id-2", - "gyB+3jRPnrkN98ZajxHXr3u7EFM67bNgSAxexeEHndCX/7GRnfTXxReKUQF28IfP", - ) - .header("x-amz-request-id", "3B3C7C725673C630") - .status(404) - .body( - r#" - - NoSuchKey - The resource you requested does not exist - /mybucket/myfoto.jpg - 4442587FB7D0A2F9 -"#, - ) - .unwrap(); - let err = GetObject::new() - .parse_loaded(&resp.map(Bytes::from)) - .expect_err("status was 404, this is an error"); - assert_eq!( - err.meta().extended_request_id(), - Some("gyB+3jRPnrkN98ZajxHXr3u7EFM67bNgSAxexeEHndCX/7GRnfTXxReKUQF28IfP") - ); - assert_eq!(err.meta().request_id(), Some("4442587FB7D0A2F9")); -} diff --git a/aws/sdk/integration-tests/s3/tests/customizable-operation.rs b/aws/sdk/integration-tests/s3/tests/customizable-operation.rs index f0b584aa12..24b53523a9 100644 --- a/aws/sdk/integration-tests/s3/tests/customizable-operation.rs +++ b/aws/sdk/integration-tests/s3/tests/customizable-operation.rs @@ -6,7 +6,8 @@ use aws_config::SdkConfig; use aws_credential_types::provider::SharedCredentialsProvider; use aws_http::user_agent::AwsUserAgent; -use aws_sdk_s3::{Client, Credentials, Region}; +use aws_sdk_s3::config::{Credentials, Region}; +use aws_sdk_s3::Client; use aws_smithy_client::test_connection::capture_request; use std::convert::Infallible; diff --git a/aws/sdk/integration-tests/s3/tests/endpoints.rs b/aws/sdk/integration-tests/s3/tests/endpoints.rs index 93c37f5c87..357c142900 100644 --- a/aws/sdk/integration-tests/s3/tests/endpoints.rs +++ b/aws/sdk/integration-tests/s3/tests/endpoints.rs @@ -6,7 +6,8 @@ use aws_config::SdkConfig; use aws_credential_types::provider::SharedCredentialsProvider; use aws_sdk_s3::config::Builder; -use aws_sdk_s3::{Client, Credentials, Region}; +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}; @@ -16,7 +17,7 @@ fn test_client(update_builder: fn(Builder) -> Builder) -> (CaptureRequestReceive let sdk_config = SdkConfig::builder() .credentials_provider(SharedCredentialsProvider::new(Credentials::for_tests())) .region(Region::new("us-west-4")) - .http_connector(conn.clone()) + .http_connector(conn) .build(); let client = Client::from_conf(update_builder(Builder::from(&sdk_config)).build()); (captured_request, client) 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 e2f850cb68..f4e8704360 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 @@ -5,7 +5,7 @@ use aws_credential_types::provider::SharedCredentialsProvider; use aws_http::user_agent::AwsUserAgent; -use aws_sdk_s3::{model::ObjectAttributes, Client, Credentials, Region}; +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; diff --git a/aws/sdk/integration-tests/s3/tests/make-connector-override.rs b/aws/sdk/integration-tests/s3/tests/make-connector-override.rs new file mode 100644 index 0000000000..fc816b8188 --- /dev/null +++ b/aws/sdk/integration-tests/s3/tests/make-connector-override.rs @@ -0,0 +1,99 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +use aws_credential_types::provider::SharedCredentialsProvider; +use aws_credential_types::Credentials; +use aws_smithy_async::rt::sleep::{AsyncSleep, TokioSleep}; + +use aws_smithy_client::http_connector::{ConnectorSettings, HttpConnector}; +use aws_smithy_client::test_connection; + +use aws_smithy_http::result::SdkError; +use aws_smithy_types::timeout::TimeoutConfig; +use aws_types::region::Region; +use aws_types::SdkConfig; +use std::sync::atomic::{AtomicUsize, Ordering}; +use std::sync::Arc; +use std::time::Duration; +use tokio::time::Instant; + +/// Verify that `make_connector_fn` isn't called per request +#[tokio::test] +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>| { + connector_sentinel.fetch_add(1, Ordering::Relaxed); + Some(test_connection::infallible_connection_fn(|_req| { + http::Response::builder().status(200).body("ok!").unwrap() + })) + }, + )); + let sdk_config = SdkConfig::builder() + .http_connector(connector_with_counter) + .credentials_provider(SharedCredentialsProvider::new(Credentials::for_tests())) + .region(Region::from_static("us-east-1")) + .build(); + let client = aws_sdk_s3::Client::new(&sdk_config); + assert_eq!(sentinel.load(Ordering::Relaxed), 1); + for _ in 0..10 { + let _ = client + .get_object() + .bucket("foo") + .key("bar") + .send() + .await + .expect("test connector replies with 200"); + } + assert_eq!(sentinel.load(Ordering::Relaxed), 1); + // but creating another client creates another connector + let _client_2 = aws_sdk_s3::Client::new(&sdk_config); + assert_eq!(sentinel.load(Ordering::Relaxed), 2); +} + +/// Use a 5 second operation timeout on SdkConfig and a 0ms connect timeout on the service config +#[tokio::test] +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())) + .timeout_config( + TimeoutConfig::builder() + .operation_timeout(Duration::from_secs(5)) + .build(), + ) + // ip that + .endpoint_url( + // Emulate a connect timeout error by hitting an unroutable IP + "http://172.255.255.0:18104", + ) + .build(); + let config = aws_sdk_s3::config::Builder::from(&sdk_config) + .timeout_config( + TimeoutConfig::builder() + .connect_timeout(Duration::from_secs(0)) + .build(), + ) + .build(); + let client = aws_sdk_s3::Client::from_conf(config); + let start = Instant::now(); + let err = client + .get_object() + .key("foo") + .bucket("bar") + .send() + .await + .expect_err("unroutable IP should timeout"); + match err { + SdkError::DispatchFailure(err) => assert!(err.is_timeout()), + // if the connect timeout is not respected, this times out after 1 second because of the operation timeout with `SdkError::Timeout` + _other => panic!("unexpected error: {:?}", _other), + } + // there should be a 0ms timeout, we gotta set some stuff up. Just want to make sure + // it's shorter than the 5 second timeout if the test is broken + assert!(start.elapsed() < Duration::from_millis(500)); +} 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 fd537a539a..eef5b0b14b 100644 --- a/aws/sdk/integration-tests/s3/tests/naughty-string-metadata.rs +++ b/aws/sdk/integration-tests/s3/tests/naughty-string-metadata.rs @@ -5,7 +5,7 @@ use aws_credential_types::provider::SharedCredentialsProvider; use aws_http::user_agent::AwsUserAgent; -use aws_sdk_s3::{types::ByteStream, Client, Credentials, Region}; +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; 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 d039064cbc..1f903abb7a 100644 --- a/aws/sdk/integration-tests/s3/tests/normalize-uri-path.rs +++ b/aws/sdk/integration-tests/s3/tests/normalize-uri-path.rs @@ -6,8 +6,8 @@ use aws_config::SdkConfig; use aws_credential_types::provider::SharedCredentialsProvider; use aws_http::user_agent::AwsUserAgent; -use aws_sdk_s3::types::ByteStream; -use aws_sdk_s3::{Client, Credentials, Region}; +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}; diff --git a/aws/sdk/integration-tests/s3/tests/presigning.rs b/aws/sdk/integration-tests/s3/tests/presigning.rs index 341c7f3e0c..6f8a33d571 100644 --- a/aws/sdk/integration-tests/s3/tests/presigning.rs +++ b/aws/sdk/integration-tests/s3/tests/presigning.rs @@ -4,10 +4,14 @@ */ use aws_sdk_s3 as s3; -use aws_sdk_s3::presigning::request::PresignedRequest; use http::header::{CONTENT_LENGTH, CONTENT_TYPE}; use http::{HeaderMap, HeaderValue}; -use s3::presigning::config::PresigningConfig; +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::presigning::{PresignedRequest, PresigningConfig}; use std::error::Error; use std::time::{Duration, SystemTime}; @@ -15,10 +19,10 @@ use std::time::{Duration, SystemTime}; /// Assumes that that input has a `presigned` method on it. macro_rules! presign_input { ($input:expr) => {{ - let creds = s3::Credentials::for_tests(); + let creds = Credentials::for_tests(); let config = s3::Config::builder() .credentials_provider(creds) - .region(s3::Region::new("us-east-1")) + .region(Region::new("us-east-1")) .build(); let req: PresignedRequest = $input @@ -37,7 +41,7 @@ macro_rules! presign_input { #[tokio::test] async fn test_presigning() -> Result<(), Box> { - let presigned = presign_input!(s3::input::GetObjectInput::builder() + let presigned = presign_input!(GetObjectInput::builder() .bucket("test-bucket") .key("test-key") .build()?); @@ -74,7 +78,7 @@ async fn test_presigning() -> Result<(), Box> { #[tokio::test] async fn test_presigning_with_payload_headers() -> Result<(), Box> { - let presigned = presign_input!(s3::input::PutObjectInput::builder() + let presigned = presign_input!(PutObjectInput::builder() .bucket("test-bucket") .key("test-key") .content_length(12345) @@ -117,7 +121,7 @@ async fn test_presigning_with_payload_headers() -> Result<(), Box> { #[tokio::test] async fn test_presigned_upload_part() -> Result<(), Box> { - let presigned = presign_input!(s3::input::UploadPartInput::builder() + let presigned = presign_input!(UploadPartInput::builder() .content_length(12345) .bucket("bucket") .key("key") @@ -130,3 +134,30 @@ async fn test_presigned_upload_part() -> Result<(), Box> { ); 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()); + // 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(()) +} + +#[tokio::test] +async fn test_presigned_head_object() -> Result<(), Box> { + let presigned = presign_input!(HeadObjectInput::builder() + .bucket("bucket") + .key("key") + .build()?); + + assert_eq!("HEAD", presigned.method().as_str()); + assert_eq!( + presigned.uri().to_string(), + "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", + ); + 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 858d013841..03393a5a74 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 @@ -6,7 +6,8 @@ use aws_config::SdkConfig; use aws_credential_types::provider::SharedCredentialsProvider; use aws_http::user_agent::AwsUserAgent; -use aws_sdk_s3::{Client, Credentials, Region}; +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}; @@ -71,7 +72,7 @@ async fn test_s3_signer_query_string_with_all_valid_chars() { #[tokio::test] #[ignore] async fn test_query_strings_are_correctly_encoded() { - use aws_sdk_s3::error::{ListObjectsV2Error, ListObjectsV2ErrorKind}; + use aws_sdk_s3::operation::list_objects_v2::ListObjectsV2Error; use aws_smithy_http::result::SdkError; tracing_subscriber::fmt::init(); @@ -92,22 +93,19 @@ async fn test_query_strings_are_correctly_encoded() { .send() .await; if let Err(SdkError::ServiceError(context)) = res { - let ListObjectsV2Error { kind, .. } = context.err(); - match kind { - ListObjectsV2ErrorKind::Unhandled(e) + match context.err() { + ListObjectsV2Error::Unhandled(e) if e.to_string().contains("SignatureDoesNotMatch") => { chars_that_break_signing.push(byte); } - ListObjectsV2ErrorKind::Unhandled(e) if e.to_string().contains("InvalidUri") => { + ListObjectsV2Error::Unhandled(e) if e.to_string().contains("InvalidUri") => { chars_that_break_uri_parsing.push(byte); } - ListObjectsV2ErrorKind::Unhandled(e) - if e.to_string().contains("InvalidArgument") => - { + ListObjectsV2Error::Unhandled(e) if e.to_string().contains("InvalidArgument") => { chars_that_are_invalid_arguments.push(byte); } - ListObjectsV2ErrorKind::Unhandled(e) if e.to_string().contains("InvalidToken") => { + ListObjectsV2Error::Unhandled(e) if e.to_string().contains("InvalidToken") => { panic!("refresh your credentials and run this test again"); } e => todo!("unexpected error: {:?}", e), diff --git a/aws/sdk/integration-tests/s3/tests/reconnects.rs b/aws/sdk/integration-tests/s3/tests/reconnects.rs new file mode 100644 index 0000000000..85afcd40a9 --- /dev/null +++ b/aws/sdk/integration-tests/s3/tests/reconnects.rs @@ -0,0 +1,99 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +use aws_credential_types::provider::SharedCredentialsProvider; +use aws_credential_types::Credentials; +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() { + let mock = WireLevelTestConnection::spinup(vec![ + ReplayedEvent::status(503), + ReplayedEvent::status(503), + ReplayedEvent::with_body("here-is-your-object"), + ]) + .await; + + let sdk_config = SdkConfig::builder() + .region(Region::from_static("us-east-2")) + .credentials_provider(SharedCredentialsProvider::new(Credentials::for_tests())) + .sleep_impl(Arc::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 resp = client + .get_object() + .bucket("bucket") + .key("key") + .send() + .await + .expect("succeeds after retries"); + assert_eq!( + resp.body.collect().await.unwrap().to_vec(), + b"here-is-your-object" + ); + match_events!( + ev!(dns), + ev!(connect), + ev!(http(503)), + ev!(http(503)), + ev!(http(200)) + )(&mock.events()); +} + +#[tokio::test] +async fn reconnect_on_503() { + let mock = WireLevelTestConnection::spinup(vec![ + ReplayedEvent::status(503), + ReplayedEvent::status(503), + ReplayedEvent::with_body("here-is-your-object"), + ]) + .await; + + let sdk_config = SdkConfig::builder() + .region(Region::from_static("us-east-2")) + .credentials_provider(SharedCredentialsProvider::new(Credentials::for_tests())) + .sleep_impl(Arc::new(TokioSleep::new())) + .endpoint_url(mock.endpoint_url()) + .http_connector(mock.http_connector()) + .retry_config(RetryConfig::standard()) + .build(); + let client = aws_sdk_s3::Client::new(&sdk_config); + let resp = client + .get_object() + .bucket("bucket") + .key("key") + .send() + .await + .expect("succeeds after retries"); + assert_eq!( + resp.body.collect().await.unwrap().to_vec(), + b"here-is-your-object" + ); + match_events!( + ev!(dns), + ev!(connect), + ev!(http(503)), + ev!(dns), + ev!(connect), + ev!(http(503)), + ev!(dns), + ev!(connect), + ev!(http(200)) + )(&mock.events()); +} diff --git a/aws/sdk/integration-tests/s3/tests/recursion-detection.rs b/aws/sdk/integration-tests/s3/tests/recursion-detection.rs index c4d7394475..f0aa974d8e 100644 --- a/aws/sdk/integration-tests/s3/tests/recursion-detection.rs +++ b/aws/sdk/integration-tests/s3/tests/recursion-detection.rs @@ -5,7 +5,8 @@ use aws_config::SdkConfig; use aws_credential_types::provider::SharedCredentialsProvider; -use aws_sdk_s3::{Client, Credentials, Region}; +use aws_sdk_s3::config::{Credentials, Region}; +use aws_sdk_s3::Client; use aws_smithy_client::test_connection::capture_request; use http::HeaderValue; diff --git a/aws/sdk/integration-tests/s3/tests/request_id.rs b/aws/sdk/integration-tests/s3/tests/request_id.rs new file mode 100644 index 0000000000..67d9523fb5 --- /dev/null +++ b/aws/sdk/integration-tests/s3/tests/request_id.rs @@ -0,0 +1,148 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * 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::{RequestId, RequestIdExt}; +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(_))); + assert_eq!(Some("correct-request-id"), err.request_id()); + assert_eq!(Some("correct-request-id"), err.meta().request_id()); + assert_eq!( + Some("correct-extended-request-id"), + err.extended_request_id() + ); + assert_eq!( + Some("correct-extended-request-id"), + err.meta().extended_request_id() + ); +} + +#[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"); + assert!(matches!(err, GetObjectError::Unhandled(_))); + assert_eq!(Some("correct-request-id"), err.request_id()); + assert_eq!(Some("correct-request-id"), err.meta().request_id()); + assert_eq!( + Some("correct-extended-request-id"), + err.extended_request_id() + ); + assert_eq!( + Some("correct-extended-request-id"), + err.meta().extended_request_id() + ); +} + +#[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)) + .expect("valid successful response"); + assert_eq!(Some("correct-request-id"), output.request_id()); + assert_eq!( + Some("correct-extended-request-id"), + output.extended_request_id() + ); +} + +#[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) + .expect("valid successful response"); + assert_eq!(Some("correct-request-id"), output.request_id()); + assert_eq!( + Some("correct-extended-request-id"), + output.extended_request_id() + ); +} + +// 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)) + .expect_err("status was 404, this is an error"); + + let service_error: aws_sdk_s3::Error = err.into(); + assert_eq!(Some("correct-request-id"), service_error.request_id()); + assert_eq!( + Some("correct-extended-request-id"), + service_error.extended_request_id() + ); +} 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 05124af0a3..7cf6238da0 100644 --- a/aws/sdk/integration-tests/s3/tests/required-query-params.rs +++ b/aws/sdk/integration-tests/s3/tests/required-query-params.rs @@ -3,8 +3,8 @@ * SPDX-License-Identifier: Apache-2.0 */ -use aws_sdk_s3::operation::AbortMultipartUpload; -use aws_sdk_s3::Region; +use aws_sdk_s3::config::Region; +use aws_sdk_s3::operation::abort_multipart_upload::AbortMultipartUploadInput; use aws_smithy_http::operation::error::BuildError; #[tokio::test] @@ -13,7 +13,7 @@ async fn test_error_when_required_query_param_is_unset() { .region(Region::new("us-east-1")) .build(); - let err = AbortMultipartUpload::builder() + let err = AbortMultipartUploadInput::builder() .bucket("test-bucket") .key("test.txt") .build() @@ -33,7 +33,7 @@ async fn test_error_when_required_query_param_is_set_but_empty() { let conf = aws_sdk_s3::Config::builder() .region(Region::new("us-east-1")) .build(); - let err = AbortMultipartUpload::builder() + let err = AbortMultipartUploadInput::builder() .bucket("test-bucket") .key("test.txt") .upload_id("") diff --git a/aws/sdk/integration-tests/s3/tests/select-object-content.rs b/aws/sdk/integration-tests/s3/tests/select-object-content.rs index d68af2f68b..eb4d7a055c 100644 --- a/aws/sdk/integration-tests/s3/tests/select-object-content.rs +++ b/aws/sdk/integration-tests/s3/tests/select-object-content.rs @@ -5,11 +5,12 @@ use aws_config::SdkConfig; use aws_credential_types::provider::SharedCredentialsProvider; -use aws_sdk_s3::model::{ +use aws_sdk_s3::config::{Credentials, Region}; +use aws_sdk_s3::types::{ CompressionType, CsvInput, CsvOutput, ExpressionType, FileHeaderInfo, InputSerialization, OutputSerialization, SelectObjectContentEventStream, }; -use aws_sdk_s3::{Client, Credentials, Region}; +use aws_sdk_s3::Client; use aws_smithy_client::dvr::{Event, ReplayingConnection}; use aws_smithy_protocol_test::{assert_ok, validate_body, MediaType}; use std::error::Error; diff --git a/aws/sdk/integration-tests/s3/tests/signing-it.rs b/aws/sdk/integration-tests/s3/tests/signing-it.rs index 0efdc9a5f6..6e20e187e2 100644 --- a/aws/sdk/integration-tests/s3/tests/signing-it.rs +++ b/aws/sdk/integration-tests/s3/tests/signing-it.rs @@ -6,7 +6,8 @@ use aws_config::SdkConfig; use aws_credential_types::provider::SharedCredentialsProvider; use aws_http::user_agent::AwsUserAgent; -use aws_sdk_s3::{Client, Credentials, Region}; +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; diff --git a/aws/sdk/integration-tests/s3/tests/size-type.rs b/aws/sdk/integration-tests/s3/tests/size-type.rs index 986daaaaf8..d790b0a622 100644 --- a/aws/sdk/integration-tests/s3/tests/size-type.rs +++ b/aws/sdk/integration-tests/s3/tests/size-type.rs @@ -3,7 +3,7 @@ * SPDX-License-Identifier: Apache-2.0 */ -use aws_sdk_s3::model::Object; +use aws_sdk_s3::types::Object; // Tests that `com.amazonaws.s3#Size` is correctly customized to be a long instead of an int. #[test] diff --git a/aws/sdk/integration-tests/s3/tests/streaming-response.rs b/aws/sdk/integration-tests/s3/tests/streaming-response.rs index 8ff6d6eb37..50acb13b33 100644 --- a/aws/sdk/integration-tests/s3/tests/streaming-response.rs +++ b/aws/sdk/integration-tests/s3/tests/streaming-response.rs @@ -5,8 +5,9 @@ use aws_config::SdkConfig; use aws_credential_types::provider::SharedCredentialsProvider; -use aws_sdk_s3::{Client, Credentials, Region}; -use aws_smithy_types::error::display::DisplayErrorContext; +use aws_sdk_s3::config::{Credentials, Region}; +use aws_sdk_s3::error::DisplayErrorContext; +use aws_sdk_s3::Client; use bytes::BytesMut; use std::future::Future; use std::net::SocketAddr; diff --git a/aws/sdk/integration-tests/s3/tests/timeouts.rs b/aws/sdk/integration-tests/s3/tests/timeouts.rs index 5d1cacc5aa..e4d28b72f4 100644 --- a/aws/sdk/integration-tests/s3/tests/timeouts.rs +++ b/aws/sdk/integration-tests/s3/tests/timeouts.rs @@ -5,11 +5,12 @@ use aws_config::SdkConfig; use aws_credential_types::provider::SharedCredentialsProvider; -use aws_sdk_s3::model::{ +use aws_sdk_s3::config::{Credentials, Region}; +use aws_sdk_s3::types::{ CompressionType, CsvInput, CsvOutput, ExpressionType, FileHeaderInfo, InputSerialization, OutputSerialization, }; -use aws_sdk_s3::{Client, Credentials, Region}; +use aws_sdk_s3::Client; use aws_smithy_async::assert_elapsed; use aws_smithy_async::rt::sleep::{default_async_sleep, TokioSleep}; use aws_smithy_client::never::NeverConnector; diff --git a/aws/sdk/integration-tests/s3/tests/user-agent-app-name.rs b/aws/sdk/integration-tests/s3/tests/user-agent-app-name.rs index 8d72c52878..2dfea8d37b 100644 --- a/aws/sdk/integration-tests/s3/tests/user-agent-app-name.rs +++ b/aws/sdk/integration-tests/s3/tests/user-agent-app-name.rs @@ -5,7 +5,8 @@ use aws_config::SdkConfig; use aws_credential_types::provider::SharedCredentialsProvider; -use aws_sdk_s3::{AppName, Client, Credentials, Region}; +use aws_sdk_s3::config::{AppName, Credentials, Region}; +use aws_sdk_s3::Client; use aws_smithy_client::test_connection::capture_request; #[tokio::test] diff --git a/aws/sdk/integration-tests/s3control/Cargo.toml b/aws/sdk/integration-tests/s3control/Cargo.toml index ada6d95b41..beeb8e9177 100644 --- a/aws/sdk/integration-tests/s3control/Cargo.toml +++ b/aws/sdk/integration-tests/s3control/Cargo.toml @@ -20,5 +20,5 @@ aws-types = { path = "../../build/aws-sdk/sdk/aws-types" } bytes = "1.0.0" http = "0.2.0" serde_json = "1.0.0" -tokio = { version = "1.8.4", 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/s3control/tests/signing-it.rs b/aws/sdk/integration-tests/s3control/tests/signing-it.rs index 4a9857b17a..f5c583bbcd 100644 --- a/aws/sdk/integration-tests/s3control/tests/signing-it.rs +++ b/aws/sdk/integration-tests/s3control/tests/signing-it.rs @@ -5,7 +5,8 @@ use aws_credential_types::provider::SharedCredentialsProvider; use aws_http::user_agent::AwsUserAgent; -use aws_sdk_s3control::{Client, Credentials, Region}; +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; diff --git a/aws/sdk/integration-tests/sts/Cargo.toml b/aws/sdk/integration-tests/sts/Cargo.toml index 63c91c4639..a5e50b83cf 100644 --- a/aws/sdk/integration-tests/sts/Cargo.toml +++ b/aws/sdk/integration-tests/sts/Cargo.toml @@ -16,5 +16,5 @@ aws-sdk-sts = { path = "../../build/aws-sdk/sdk/sts" } 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" } -tokio = { version = "1.8.4", 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/sts/tests/retry_idp_comms_err.rs b/aws/sdk/integration-tests/sts/tests/retry_idp_comms_err.rs index 6fe9895cd3..5242785717 100644 --- a/aws/sdk/integration-tests/sts/tests/retry_idp_comms_err.rs +++ b/aws/sdk/integration-tests/sts/tests/retry_idp_comms_err.rs @@ -4,24 +4,22 @@ */ use aws_sdk_sts as sts; -use aws_smithy_types::error::Error as ErrorMeta; +use aws_smithy_types::error::ErrorMetadata; use aws_smithy_types::retry::{ErrorKind, ProvideErrorKind}; -use sts::error::{ - AssumeRoleWithWebIdentityError, AssumeRoleWithWebIdentityErrorKind, - IdpCommunicationErrorException, -}; +use sts::operation::assume_role_with_web_identity::AssumeRoleWithWebIdentityError; +use sts::types::error::IdpCommunicationErrorException; #[tokio::test] async fn idp_comms_err_retryable() { - let error = AssumeRoleWithWebIdentityError::new( - AssumeRoleWithWebIdentityErrorKind::IdpCommunicationErrorException( - IdpCommunicationErrorException::builder() - .message("test") - .build(), - ), - ErrorMeta::builder() - .code("IDPCommunicationError") + let error = AssumeRoleWithWebIdentityError::IdpCommunicationErrorException( + IdpCommunicationErrorException::builder() .message("test") + .meta( + ErrorMetadata::builder() + .code("IDPCommunicationError") + .message("test") + .build(), + ) .build(), ); assert_eq!( diff --git a/aws/sdk/integration-tests/sts/tests/signing-it.rs b/aws/sdk/integration-tests/sts/tests/signing-it.rs index 12c4d78084..f2c80bd9f7 100644 --- a/aws/sdk/integration-tests/sts/tests/signing-it.rs +++ b/aws/sdk/integration-tests/sts/tests/signing-it.rs @@ -3,7 +3,7 @@ * SPDX-License-Identifier: Apache-2.0 */ -use aws_sdk_sts::{Credentials, Region}; +use aws_sdk_sts::config::{Credentials, Region}; use aws_smithy_client::test_connection::capture_request; #[tokio::test] diff --git a/aws/sdk/integration-tests/transcribestreaming/Cargo.toml b/aws/sdk/integration-tests/transcribestreaming/Cargo.toml index 1c19e73f9f..38b1018cc5 100644 --- a/aws/sdk/integration-tests/transcribestreaming/Cargo.toml +++ b/aws/sdk/integration-tests/transcribestreaming/Cargo.toml @@ -21,5 +21,5 @@ futures-core = "0.3.14" hound = "3.4.0" http = "0.2.0" serde_json = "1.0.0" -tokio = { version = "1.8.4", 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/transcribestreaming/tests/test.rs b/aws/sdk/integration-tests/transcribestreaming/tests/test.rs index f48515038a..62654ebd82 100644 --- a/aws/sdk/integration-tests/transcribestreaming/tests/test.rs +++ b/aws/sdk/integration-tests/transcribestreaming/tests/test.rs @@ -4,15 +4,15 @@ */ use async_stream::stream; -use aws_sdk_transcribestreaming::error::{ - AudioStreamError, TranscriptResultStreamError, TranscriptResultStreamErrorKind, -}; -use aws_sdk_transcribestreaming::model::{ +use aws_sdk_transcribestreaming::config::{Credentials, Region}; +use aws_sdk_transcribestreaming::error::SdkError; +use aws_sdk_transcribestreaming::operation::start_stream_transcription::StartStreamTranscriptionOutput; +use aws_sdk_transcribestreaming::primitives::Blob; +use aws_sdk_transcribestreaming::types::error::{AudioStreamError, TranscriptResultStreamError}; +use aws_sdk_transcribestreaming::types::{ AudioEvent, AudioStream, LanguageCode, MediaEncoding, TranscriptResultStream, }; -use aws_sdk_transcribestreaming::output::StartStreamTranscriptionOutput; -use aws_sdk_transcribestreaming::types::{Blob, SdkError}; -use aws_sdk_transcribestreaming::{Client, Config, Credentials, Region}; +use aws_sdk_transcribestreaming::{Client, Config}; use aws_smithy_client::dvr::{Event, ReplayingConnection}; use aws_smithy_eventstream::frame::{DecodedFrame, HeaderValue, Message, MessageFrameDecoder}; use bytes::BufMut; @@ -76,10 +76,7 @@ async fn test_error() { match output.transcript_result_stream.recv().await { Err(SdkError::ServiceError(context)) => match context.err() { - TranscriptResultStreamError { - kind: TranscriptResultStreamErrorKind::BadRequestException(err), - .. - } => { + TranscriptResultStreamError::BadRequestException(err) => { assert_eq!( Some("A complete signal was sent without the preceding empty frame."), err.message() 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 new file mode 100644 index 0000000000..3642d7ba24 --- /dev/null +++ b/aws/sdk/integration-tests/using-native-tls-instead-of-rustls/Cargo.toml @@ -0,0 +1,20 @@ +[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 new file mode 100644 index 0000000000..1df97865db --- /dev/null +++ b/aws/sdk/integration-tests/using-native-tls-instead-of-rustls/tests/no-rustls-in-dependency.rs @@ -0,0 +1,52 @@ +/* + * 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/sra-test/.gitignore b/aws/sra-test/.gitignore new file mode 100644 index 0000000000..388d181b4d --- /dev/null +++ b/aws/sra-test/.gitignore @@ -0,0 +1 @@ +/smithy-build.json diff --git a/aws/sra-test/build.gradle.kts b/aws/sra-test/build.gradle.kts new file mode 100644 index 0000000000..d7cdeaa691 --- /dev/null +++ b/aws/sra-test/build.gradle.kts @@ -0,0 +1,88 @@ +/* + * 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/" + +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") +} + +val allCodegenTests = listOf( + CodegenTest( + "com.amazonaws.dynamodb#DynamoDB_20120810", + "aws-sdk-dynamodb", + imports = listOf("../sdk/aws-models/dynamodb.json"), + extraConfig = """ + , + "codegen": { + "includeFluentClient": false, + "enableNewSmithyRuntime": true + }, + "customizationConfig": { + "awsSdk": { + "generateReadme": false + } + } + """, + ), + CodegenTest( + "com.amazonaws.s3#AmazonS3", + "aws-sdk-s3", + imports = listOf("../sdk/aws-models/s3.json", "../sdk/aws-models/s3-tests.smithy"), + 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") } diff --git a/build.gradle.kts b/build.gradle.kts index 2c06af6725..a2d6385db4 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -14,9 +14,7 @@ buildscript { } } -plugins { - kotlin("jvm") version "1.3.72" apply false -} +plugins { } allprojects { repositories { @@ -61,7 +59,7 @@ tasks.register("ktlint") { group = "Verification" classpath = configurations.getByName("ktlint") mainClass.set("com.pinterest.ktlint.Main") - args = listOf("--verbose", "--relative", "--") + lintPaths + args = listOf("--log-level=info", "--relative", "--") + lintPaths // https://github.com/pinterest/ktlint/issues/1195#issuecomment-1009027802 jvmArgs("--add-opens", "java.base/java.lang=ALL-UNNAMED") } @@ -71,7 +69,7 @@ tasks.register("ktlintFormat") { group = "formatting" classpath = configurations.getByName("ktlint") mainClass.set("com.pinterest.ktlint.Main") - args = listOf("--verbose", "--relative", "--format", "--") + lintPaths + args = listOf("--log-level=info", "--relative", "--format", "--") + lintPaths // https://github.com/pinterest/ktlint/issues/1195#issuecomment-1009027802 jvmArgs("--add-opens", "java.base/java.lang=ALL-UNNAMED") } diff --git a/buildSrc/src/main/kotlin/CodegenTestCommon.kt b/buildSrc/src/main/kotlin/CodegenTestCommon.kt index d975d1a521..ad02ea63af 100644 --- a/buildSrc/src/main/kotlin/CodegenTestCommon.kt +++ b/buildSrc/src/main/kotlin/CodegenTestCommon.kt @@ -37,6 +37,9 @@ private fun generateSmithyBuild(projectDir: String, pluginName: String, tests: L "runtimeConfig": { "relativePath": "$projectDir/rust-runtime" }, + "codegen": { + "enableNewCrateOrganizationScheme": true + }, "service": "${it.service}", "module": "${it.module}", "moduleVersion": "0.0.1", @@ -62,7 +65,7 @@ enum class Cargo(val toString: String) { CHECK("cargoCheck"), TEST("cargoTest"), DOCS("cargoDoc"), - CLIPPY("cargoClippy"); + CLIPPY("cargoClippy"), } private fun generateCargoWorkspace(pluginName: String, tests: List) = @@ -86,7 +89,9 @@ private fun codegenTests(properties: PropertyRetriever, allTests: List { AllCargoCommands } require(ret.isNotEmpty()) { - "None of the provided cargo commands (`$cargoCommandsOverride`) are valid cargo commands (`${AllCargoCommands.map { it.toString }}`)" + "None of the provided cargo commands (`$cargoCommandsOverride`) are valid cargo commands (`${AllCargoCommands.map { + it.toString + }}`)" } return ret } @@ -175,7 +182,7 @@ fun Project.registerGenerateCargoConfigTomlTask( this.tasks.register("generateCargoConfigToml") { description = "generate `.cargo/config.toml`" doFirst { - outputDir.resolve(".cargo").mkdir() + outputDir.resolve(".cargo").mkdirs() outputDir.resolve(".cargo/config.toml") .writeText( """ diff --git a/buildSrc/src/main/kotlin/CrateSet.kt b/buildSrc/src/main/kotlin/CrateSet.kt index c915649f61..47ff406b71 100644 --- a/buildSrc/src/main/kotlin/CrateSet.kt +++ b/buildSrc/src/main/kotlin/CrateSet.kt @@ -17,14 +17,17 @@ object CrateSet { private val SMITHY_RUNTIME_COMMON = listOf( "aws-smithy-async", - "aws-smithy-client", "aws-smithy-checksums", + "aws-smithy-client", "aws-smithy-eventstream", "aws-smithy-http", + "aws-smithy-http-auth", "aws-smithy-http-tower", "aws-smithy-json", "aws-smithy-protocol-test", "aws-smithy-query", + "aws-smithy-runtime", + "aws-smithy-runtime-api", "aws-smithy-types", "aws-smithy-types-convert", "aws-smithy-xml", diff --git a/buildSrc/src/main/kotlin/aws/sdk/DocsLandingPage.kt b/buildSrc/src/main/kotlin/aws/sdk/DocsLandingPage.kt index d522a02f4a..715917c4db 100644 --- a/buildSrc/src/main/kotlin/aws/sdk/DocsLandingPage.kt +++ b/buildSrc/src/main/kotlin/aws/sdk/DocsLandingPage.kt @@ -44,7 +44,9 @@ fun Project.docsLandingPage(awsServices: AwsServices, outputPath: File) { /** * Generate a link to the examples for a given service */ -private fun examplesLink(service: AwsService, project: Project) = service.examplesUri(project)?.let { "([examples]($it))" } +private fun examplesLink(service: AwsService, project: Project) = service.examplesUri(project)?.let { + "([examples]($it))" +} /** * Generate a link to the docs diff --git a/buildSrc/src/main/kotlin/aws/sdk/ModelMetadata.kt b/buildSrc/src/main/kotlin/aws/sdk/ModelMetadata.kt index a70aa7a0d6..95d65fd106 100644 --- a/buildSrc/src/main/kotlin/aws/sdk/ModelMetadata.kt +++ b/buildSrc/src/main/kotlin/aws/sdk/ModelMetadata.kt @@ -11,7 +11,7 @@ import java.io.File enum class ChangeType { UNCHANGED, FEATURE, - DOCUMENTATION + DOCUMENTATION, } /** Model metadata toml file */ diff --git a/buildSrc/src/main/kotlin/aws/sdk/ServiceLoader.kt b/buildSrc/src/main/kotlin/aws/sdk/ServiceLoader.kt index fdc6448b0c..44510d1b11 100644 --- a/buildSrc/src/main/kotlin/aws/sdk/ServiceLoader.kt +++ b/buildSrc/src/main/kotlin/aws/sdk/ServiceLoader.kt @@ -135,9 +135,9 @@ fun Project.discoverServices(awsModelsPath: String?, serviceMembership: Membersh serviceMembership.exclusions.forEach { disabledService -> check(baseModules.contains(disabledService)) { "Service $disabledService was explicitly disabled but no service was generated with that name. Generated:\n ${ - baseModules.joinToString( - "\n ", - ) + baseModules.joinToString( + "\n ", + ) }" } } @@ -206,7 +206,9 @@ fun parseMembership(rawList: String): Membership { } val conflictingMembers = inclusions.intersect(exclusions) - require(conflictingMembers.isEmpty()) { "$conflictingMembers specified both for inclusion and exclusion in $rawList" } + require(conflictingMembers.isEmpty()) { + "$conflictingMembers specified both for inclusion and exclusion in $rawList" + } return Membership(inclusions, exclusions) } diff --git a/codegen-client-test/build.gradle.kts b/codegen-client-test/build.gradle.kts index 434bcef65f..f3796a6911 100644 --- a/codegen-client-test/build.gradle.kts +++ b/codegen-client-test/build.gradle.kts @@ -79,6 +79,11 @@ val allCodegenTests = "../codegen-core/common-test-models".let { commonModels -> """.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", diff --git a/codegen-client/build.gradle.kts b/codegen-client/build.gradle.kts index ba6ac6ac1b..62d543beb4 100644 --- a/codegen-client/build.gradle.kts +++ b/codegen-client/build.gradle.kts @@ -28,6 +28,10 @@ dependencies { implementation("software.amazon.smithy:smithy-protocol-test-traits:$smithyVersion") implementation("software.amazon.smithy:smithy-waiters:$smithyVersion") implementation("software.amazon.smithy:smithy-rules-engine:$smithyVersion") + + // `smithy.framework#ValidationException` is defined here, which is used in event stream +// marshalling/unmarshalling tests. + testImplementation("software.amazon.smithy:smithy-validation-model:$smithyVersion") } tasks.compileKotlin { 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 38567aa0cd..e0ec0afb6e 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 @@ -11,6 +11,7 @@ import software.amazon.smithy.model.shapes.ShapeId import software.amazon.smithy.rust.codegen.client.smithy.customize.ClientCodegenDecorator 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 /** @@ -22,6 +23,7 @@ import software.amazon.smithy.rust.codegen.core.smithy.RustSymbolProvider data class ClientCodegenContext( override val model: Model, override val symbolProvider: RustSymbolProvider, + override val moduleDocProvider: ModuleDocProvider?, override val serviceShape: ServiceShape, override val protocol: ShapeId, override val settings: ClientRustSettings, @@ -29,5 +31,5 @@ data class ClientCodegenContext( // decorator val rootDecorator: ClientCodegenDecorator, ) : CodegenContext( - model, symbolProvider, serviceShape, protocol, settings, CodegenTarget.CLIENT, + model, symbolProvider, moduleDocProvider, serviceShape, protocol, settings, CodegenTarget.CLIENT, ) 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 44099cceb1..5a97b1ede5 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 @@ -6,6 +6,7 @@ package software.amazon.smithy.rust.codegen.client.smithy import software.amazon.smithy.build.PluginContext +import software.amazon.smithy.codegen.core.Symbol import software.amazon.smithy.model.Model import software.amazon.smithy.model.knowledge.NullableIndex import software.amazon.smithy.model.shapes.OperationShape @@ -16,38 +17,43 @@ 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.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.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.protocols.ClientProtocolLoader import software.amazon.smithy.rust.codegen.client.smithy.transformers.AddErrorMessage import software.amazon.smithy.rust.codegen.client.smithy.transformers.RemoveEventStreamOperations +import software.amazon.smithy.rust.codegen.core.rustlang.EscapeFor import software.amazon.smithy.rust.codegen.core.rustlang.RustModule +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.implBlock import software.amazon.smithy.rust.codegen.core.smithy.DirectedWalker 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.SymbolVisitorConfig +import software.amazon.smithy.rust.codegen.core.smithy.RustSymbolProviderConfig +import software.amazon.smithy.rust.codegen.core.smithy.contextName import software.amazon.smithy.rust.codegen.core.smithy.generators.BuilderGenerator -import software.amazon.smithy.rust.codegen.core.smithy.generators.EnumGenerator import software.amazon.smithy.rust.codegen.core.smithy.generators.StructureGenerator import software.amazon.smithy.rust.codegen.core.smithy.generators.UnionGenerator -import software.amazon.smithy.rust.codegen.core.smithy.generators.error.OperationErrorGenerator -import software.amazon.smithy.rust.codegen.core.smithy.generators.error.eventStreamErrorSymbol -import software.amazon.smithy.rust.codegen.core.smithy.generators.implBlock +import software.amazon.smithy.rust.codegen.core.smithy.module import software.amazon.smithy.rust.codegen.core.smithy.protocols.ProtocolGeneratorFactory -import software.amazon.smithy.rust.codegen.core.smithy.traits.SyntheticInputTrait 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.smithy.transformers.eventStreamErrors -import software.amazon.smithy.rust.codegen.core.smithy.transformers.operationErrors import software.amazon.smithy.rust.codegen.core.util.CommandFailed 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.isEventStream import software.amazon.smithy.rust.codegen.core.util.letIf import software.amazon.smithy.rust.codegen.core.util.runCommand +import software.amazon.smithy.rust.codegen.core.util.serviceNameOrDefault +import software.amazon.smithy.rust.codegen.core.util.toSnakeCase import java.util.logging.Logger /** @@ -57,7 +63,6 @@ class ClientCodegenVisitor( context: PluginContext, private val codegenDecorator: ClientCodegenDecorator, ) : ShapeVisitor.Default() { - private val logger = Logger.getLogger(javaClass.name) private val settings = ClientRustSettings.from(context.model, context.settings) @@ -65,17 +70,26 @@ class ClientCodegenVisitor( private val rustCrate: RustCrate private val fileManifest = context.fileManifest private val model: Model - private val codegenContext: ClientCodegenContext + private var codegenContext: ClientCodegenContext private val protocolGeneratorFactory: ProtocolGeneratorFactory private val protocolGenerator: ClientProtocolGenerator init { - val symbolVisitorConfig = - SymbolVisitorConfig( - runtimeConfig = settings.runtimeConfig, - renameExceptions = settings.codegenConfig.renameExceptions, - nullabilityCheckMode = NullableIndex.CheckMode.CLIENT_ZERO_VALUE_V1, - ) + val rustSymbolProviderConfig = RustSymbolProviderConfig( + runtimeConfig = settings.runtimeConfig, + renameExceptions = settings.codegenConfig.renameExceptions, + nullabilityCheckMode = NullableIndex.CheckMode.CLIENT_ZERO_VALUE_V1, + moduleProvider = when (settings.codegenConfig.enableNewCrateOrganizationScheme) { + true -> ClientModuleProvider + else -> OldModuleSchemeClientModuleProvider + }, + nameBuilderFor = { symbol -> + when (settings.codegenConfig.enableNewCrateOrganizationScheme) { + true -> "${symbol.name}Builder" + else -> "Builder" + } + }, + ) val baseModel = baselineTransform(context.model) val untransformedService = settings.getService(baseModel) val (protocol, generator) = ClientProtocolLoader( @@ -85,14 +99,30 @@ class ClientCodegenVisitor( model = codegenDecorator.transformModel(untransformedService, baseModel) // the model transformer _might_ change the service shape val service = settings.getService(model) - symbolProvider = RustClientCodegenPlugin.baseSymbolProvider(model, service, symbolVisitorConfig) + symbolProvider = RustClientCodegenPlugin.baseSymbolProvider(settings, model, service, rustSymbolProviderConfig, codegenDecorator) + + codegenContext = ClientCodegenContext( + model, + symbolProvider, + null, + service, + protocol, + settings, + codegenDecorator, + ) - codegenContext = ClientCodegenContext(model, symbolProvider, service, protocol, settings, codegenDecorator) + codegenContext = codegenContext.copy( + moduleDocProvider = codegenDecorator.moduleDocumentationCustomization( + codegenContext, + ClientModuleDocProvider(codegenContext, service.serviceNameOrDefault("the service")), + ), + ) rustCrate = RustCrate( context.fileManifest, symbolProvider, codegenContext.settings.codegenConfig, + codegenContext.expectModuleDocProvider(), ) protocolGenerator = protocolGeneratorFactory.buildProtocolGenerator(codegenContext) } @@ -108,14 +138,14 @@ class ClientCodegenVisitor( // Add errors attached at the service level to the models .let { ModelTransformer.create().copyServiceErrorsToOperations(it, settings.getService(it)) } // Add `Box` to recursive shapes as necessary - .let(RecursiveShapeBoxer::transform) + .let(RecursiveShapeBoxer()::transform) // Normalize the `message` field on errors when enabled in settings (default: true) .letIf(settings.codegenConfig.addMessageToErrors, AddErrorMessage::transform) // NormalizeOperations by ensuring every operation has an input & output shape .let(OperationNormalizer::transform) // Drop unsupported event stream operations from the model .let { RemoveEventStreamOperations.transform(it, settings) } - // - Normalize event stream operations + // Normalize event stream operations .let(EventStreamNormalizer::transform) /** @@ -177,6 +207,29 @@ class ClientCodegenVisitor( override fun getDefault(shape: Shape?) { } + // TODO(CrateReorganization): Remove this function when cleaning up `enableNewCrateOrganizationScheme` + private fun RustCrate.maybeInPrivateModuleWithReexport( + privateModule: RustModule.LeafModule, + symbol: Symbol, + writer: Writable, + ) { + if (codegenContext.settings.codegenConfig.enableNewCrateOrganizationScheme) { + inPrivateModuleWithReexport(privateModule, symbol, writer) + } else { + withModule(symbol.module(), writer) + } + } + + private fun privateModule(shape: Shape): RustModule.LeafModule = + RustModule.private(privateModuleName(shape), parent = symbolProvider.moduleForShape(shape)) + + private fun privateModuleName(shape: Shape): String = + shape.contextName(codegenContext.serviceShape).let(this::privateModuleName) + + private fun privateModuleName(name: String): String = + // Add the underscore to avoid colliding with public module names + "_" + RustReservedWords.escapeIfNeeded(name.toSnakeCase(), EscapeFor.ModuleName) + /** * Structure Shape Visitor * @@ -187,17 +240,50 @@ class ClientCodegenVisitor( * This function _does not_ generate any serializers */ override fun structureShape(shape: StructureShape) { - logger.fine("generating a structure...") - rustCrate.useShapeWriter(shape) { - StructureGenerator(model, symbolProvider, this, shape).render() - if (!shape.hasTrait()) { - val builderGenerator = BuilderGenerator(codegenContext.model, codegenContext.symbolProvider, shape) - builderGenerator.render(this) - this.implBlock(shape, symbolProvider) { - builderGenerator.renderConvenienceMethod(this) + val (renderStruct, renderBuilder) = when (val errorTrait = shape.getTrait()) { + null -> { + val struct: Writable = { + StructureGenerator( + model, + symbolProvider, + this, + shape, + codegenDecorator.structureCustomizations(codegenContext, emptyList()), + ).render() + + implBlock(symbolProvider.toSymbol(shape)) { + BuilderGenerator.renderConvenienceMethod(this, symbolProvider, shape) + } + } + val builder: Writable = { + BuilderGenerator( + codegenContext.model, + codegenContext.symbolProvider, + shape, + codegenDecorator.builderCustomizations(codegenContext, emptyList()), + ).render(this) } + struct to builder + } + else -> { + val errorGenerator = ErrorGenerator( + model, + symbolProvider, + shape, + errorTrait, + codegenDecorator.errorImplCustomizations(codegenContext, emptyList()), + ) + errorGenerator::renderStruct to errorGenerator::renderBuilder } } + + val privateModule = privateModule(shape) + rustCrate.maybeInPrivateModuleWithReexport(privateModule, symbolProvider.toSymbol(shape)) { + renderStruct(this) + } + rustCrate.maybeInPrivateModuleWithReexport(privateModule, symbolProvider.symbolForBuilder(shape)) { + renderBuilder(this) + } } /** @@ -206,9 +292,10 @@ class ClientCodegenVisitor( * Although raw strings require no code generation, enums are actually `EnumTrait` applied to string shapes. */ override fun stringShape(shape: StringShape) { - shape.getTrait()?.also { enum -> - rustCrate.useShapeWriter(shape) { - EnumGenerator(model, symbolProvider, this, shape, enum).render() + if (shape.hasTrait()) { + val privateModule = privateModule(shape) + rustCrate.maybeInPrivateModuleWithReexport(privateModule, symbolProvider.toSymbol(shape)) { + ClientEnumGenerator(codegenContext, shape).render(this) } } } @@ -221,17 +308,17 @@ class ClientCodegenVisitor( * Note: this does not generate serializers */ override fun unionShape(shape: UnionShape) { - rustCrate.useShapeWriter(shape) { + rustCrate.maybeInPrivateModuleWithReexport(privateModule(shape), symbolProvider.toSymbol(shape)) { UnionGenerator(model, symbolProvider, this, shape, renderUnknownVariant = true).render() } if (shape.isEventStream()) { - rustCrate.withModule(RustModule.Error) { - val symbol = symbolProvider.toSymbol(shape) - val errors = shape.eventStreamErrors() - .map { model.expectShape(it.asMemberShape().get().target, StructureShape::class.java) } - val errorSymbol = shape.eventStreamErrorSymbol(symbolProvider) - OperationErrorGenerator(model, symbolProvider, symbol, errors) - .renderErrors(this, errorSymbol, symbol) + rustCrate.withModule(symbolProvider.moduleForEventStreamError(shape)) { + OperationErrorGenerator( + model, + symbolProvider, + shape, + codegenDecorator.errorCustomizations(codegenContext, emptyList()), + ).render(this) } } } @@ -240,13 +327,12 @@ class ClientCodegenVisitor( * Generate errors for operation shapes */ override fun operationShape(shape: OperationShape) { - rustCrate.withModule(RustModule.Error) { - val operationSymbol = symbolProvider.toSymbol(shape) + rustCrate.withModule(symbolProvider.moduleForOperationError(shape)) { OperationErrorGenerator( model, symbolProvider, - operationSymbol, - shape.operationErrors(model).map { it.asStructureShape().get() }, + shape, + codegenDecorator.errorCustomizations(codegenContext, emptyList()), ).render(this) } } diff --git a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/ClientReservedWords.kt b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/ClientReservedWords.kt new file mode 100644 index 0000000000..add9f53f6d --- /dev/null +++ b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/ClientReservedWords.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 software.amazon.smithy.rust.codegen.core.rustlang.RustReservedWordConfig +import software.amazon.smithy.rust.codegen.core.smithy.generators.StructureGenerator +import software.amazon.smithy.rust.codegen.core.smithy.generators.UnionGenerator + +val ClientReservedWords = RustReservedWordConfig( + structureMemberMap = StructureGenerator.structureMemberNameMap + + mapOf( + "send" to "send_value", + // To avoid conflicts with the `make_operation` and `presigned` functions on generated inputs + "make_operation" to "make_operation_value", + "presigned" to "presigned_value", + "customize" to "customize_value", + // To avoid conflicts with the error metadata `meta` field + "meta" to "meta_value", + ), + unionMemberMap = mapOf( + // Unions contain an `Unknown` variant. This exists to support parsing data returned from the server + // that represent union variants that have been added since this SDK was generated. + UnionGenerator.UnknownVariantName to "${UnionGenerator.UnknownVariantName}Value", + "${UnionGenerator.UnknownVariantName}Value" to "${UnionGenerator.UnknownVariantName}Value_", + ), + enumMemberMap = mapOf( + // Unknown is used as the name of the variant containing unexpected values + "Unknown" to "UnknownValue", + // Real models won't end in `_` so it's safe to stop here + "UnknownValue" to "UnknownValue_", + ), +) 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 new file mode 100644 index 0000000000..69b7212236 --- /dev/null +++ b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/ClientRustModule.kt @@ -0,0 +1,303 @@ +/* + * 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 software.amazon.smithy.codegen.core.Symbol +import software.amazon.smithy.model.Model +import software.amazon.smithy.model.shapes.OperationShape +import software.amazon.smithy.model.shapes.Shape +import software.amazon.smithy.model.shapes.StructureShape +import software.amazon.smithy.model.shapes.UnionShape +import software.amazon.smithy.model.traits.ErrorTrait +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.core.rustlang.CargoDependency +import software.amazon.smithy.rust.codegen.core.rustlang.EscapeFor +import software.amazon.smithy.rust.codegen.core.rustlang.RustModule +import software.amazon.smithy.rust.codegen.core.rustlang.RustReservedWords +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.docs +import software.amazon.smithy.rust.codegen.core.rustlang.docsTemplate +import software.amazon.smithy.rust.codegen.core.rustlang.escape +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.RustSymbolProvider +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 +import software.amazon.smithy.rust.codegen.core.smithy.traits.SyntheticOutputTrait +import software.amazon.smithy.rust.codegen.core.util.PANIC +import software.amazon.smithy.rust.codegen.core.util.UNREACHABLE +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.toSnakeCase + +/** + * Modules for code generated client crates. + */ +object ClientRustModule { + /** crate */ + val root = RustModule.LibRs + + /** crate::client */ + val client = Client.self + object Client { + /** crate::client */ + val self = RustModule.public("client") + + /** crate::client::customize */ + val customize = RustModule.public("customize", parent = self) + } + + val Config = RustModule.public("config") + val Error = RustModule.public("error") + val Endpoint = RustModule.public("endpoint") + val Operation = RustModule.public("operation") + val Meta = RustModule.public("meta") + val Input = RustModule.public("input") + val Output = RustModule.public("output") + val Primitives = RustModule.public("primitives") + + /** crate::types */ + val types = Types.self + object Types { + /** crate::types */ + val self = RustModule.public("types") + + /** crate::types::error */ + val Error = RustModule.public("error", parent = self) + } + + // TODO(CrateReorganization): Remove this module when cleaning up `enableNewCrateOrganizationScheme` + val Model = RustModule.public("model") +} + +class ClientModuleDocProvider( + private val codegenContext: ClientCodegenContext, + private val serviceName: String, +) : ModuleDocProvider { + private val config: ClientCodegenConfig = codegenContext.settings.codegenConfig + + override fun docsWriter(module: RustModule.LeafModule): Writable? { + val strDoc: (String) -> Writable = { str -> writable { docs(escape(str)) } } + return when (config.enableNewCrateOrganizationScheme) { + true -> when (module) { + ClientRustModule.client -> clientModuleDoc() + ClientRustModule.Client.customize -> customizeModuleDoc() + ClientRustModule.Config -> strDoc("Configuration for $serviceName.") + 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.") + ClientRustModule.Meta -> strDoc("Information about this crate.") + ClientRustModule.Input -> PANIC("this module shouldn't exist in the new scheme") + ClientRustModule.Output -> PANIC("this module shouldn't exist in the new scheme") + ClientRustModule.Primitives -> strDoc("Primitives such as `Blob` or `DateTime` used by other types.") + ClientRustModule.types -> strDoc("Data structures used by operation inputs/outputs.") + ClientRustModule.Types.Error -> strDoc("Error types that $serviceName can respond with.") + ClientRustModule.Model -> PANIC("this module shouldn't exist in the new scheme") + else -> TODO("Document this module: $module") + } + + else -> strDoc( + when (module) { + ClientRustModule.client -> "Client and fluent builders for calling $serviceName." + ClientRustModule.Client.customize -> "Operation customization and supporting types." + ClientRustModule.Config -> "Configuration for $serviceName." + ClientRustModule.Error -> "All error types that operations can return. Documentation on these types is copied from the model." + ClientRustModule.Endpoint -> "Endpoint resolution functionality." + ClientRustModule.Operation -> "All operations that this crate can perform." + ClientRustModule.Meta -> PANIC("this module shouldn't exist in the old scheme") + ClientRustModule.Input -> "Input structures for operations. Documentation on these types is copied from the model." + ClientRustModule.Output -> "Output structures for operations. Documentation on these types is copied from the model." + ClientRustModule.Primitives -> PANIC("this module shouldn't exist in the old scheme") + ClientRustModule.types -> "Data primitives referenced by other data types." + ClientRustModule.Types.Error -> PANIC("this module shouldn't exist in the old scheme") + ClientRustModule.Model -> "Data structures used by operation inputs/outputs." + else -> TODO("Document this module: $module") + }, + ) + } + } + + private fun clientModuleDoc(): Writable = writable { + val genericClientConstructionDocs = FluentClientDocs.clientConstructionDocs(codegenContext) + val writeClientConstructionDocs = codegenContext.rootDecorator + .clientConstructionDocs(codegenContext, genericClientConstructionDocs) + + writeClientConstructionDocs(this) + FluentClientDocs.clientUsageDocs(codegenContext)(this) + } + + private fun customizeModuleDoc(): Writable = writable { + val model = codegenContext.model + docs("Operation customization and supporting types.\n") + if (codegenContext.serviceShape.operations.isNotEmpty()) { + val opFnName = FluentClientGenerator.clientOperationFnName( + codegenContext.serviceShape.operations.minOf { it } + .let { model.expectShape(it, OperationShape::class.java) }, + codegenContext.symbolProvider, + ) + val moduleUseName = codegenContext.moduleUseName() + docsTemplate( + """ + The underlying HTTP requests made during an operation can be customized + by calling the `customize()` method on the builder returned from a client + operation call. For example, this can be used to add an additional HTTP header: + + ```ignore + ## async fn wrapper() -> Result<(), $moduleUseName::Error> { + ## let client: $moduleUseName::Client = unimplemented!(); + use #{http}::header::{HeaderName, HeaderValue}; + + let result = client.$opFnName() + .customize() + .await? + .mutate_request(|req| { + // Add `x-example-header` with value + req.headers_mut() + .insert( + HeaderName::from_static("x-example-header"), + HeaderValue::from_static("1"), + ); + }) + .send() + .await; + ## } + ``` + """.trimIndent(), + "http" to CargoDependency.Http.toDevDependency().toType(), + ) + } + } +} + +object ClientModuleProvider : ModuleProvider { + override fun moduleForShape(context: ModuleProviderContext, shape: Shape): RustModule.LeafModule = when (shape) { + is OperationShape -> perOperationModule(context, shape) + is StructureShape -> when { + shape.hasTrait() -> ClientRustModule.Types.Error + shape.hasTrait() -> perOperationModule(context, shape) + shape.hasTrait() -> perOperationModule(context, shape) + else -> ClientRustModule.types + } + + else -> ClientRustModule.types + } + + override fun moduleForOperationError( + context: ModuleProviderContext, + operation: OperationShape, + ): RustModule.LeafModule = perOperationModule(context, operation) + + override fun moduleForEventStreamError( + context: ModuleProviderContext, + eventStream: UnionShape, + ): RustModule.LeafModule = ClientRustModule.Types.Error + + override fun moduleForBuilder(context: ModuleProviderContext, shape: Shape, symbol: Symbol): RustModule.LeafModule = + RustModule.public("builders", parent = symbol.module(), documentationOverride = "Builders") + + private fun Shape.findOperation(model: Model): OperationShape { + val inputTrait = getTrait() + val outputTrait = getTrait() + return when { + this is OperationShape -> this + inputTrait != null -> model.expectShape(inputTrait.operation, OperationShape::class.java) + outputTrait != null -> model.expectShape(outputTrait.operation, OperationShape::class.java) + else -> UNREACHABLE("this is only called with compatible shapes") + } + } + + private fun perOperationModule(context: ModuleProviderContext, shape: Shape): RustModule.LeafModule { + val operationShape = shape.findOperation(context.model) + val contextName = operationShape.contextName(context.serviceShape) + val operationModuleName = + RustReservedWords.escapeIfNeeded(contextName.toSnakeCase(), EscapeFor.ModuleName) + return RustModule.public( + operationModuleName, + parent = ClientRustModule.Operation, + documentationOverride = "Types for the `$contextName` operation.", + ) + } +} + +// TODO(CrateReorganization): Remove this provider +object OldModuleSchemeClientModuleProvider : ModuleProvider { + override fun moduleForShape(context: ModuleProviderContext, shape: Shape): RustModule.LeafModule = when (shape) { + is OperationShape -> ClientRustModule.Operation + is StructureShape -> when { + shape.hasTrait() -> ClientRustModule.Error + shape.hasTrait() -> ClientRustModule.Input + shape.hasTrait() -> ClientRustModule.Output + else -> ClientRustModule.Model + } + + else -> ClientRustModule.Model + } + + override fun moduleForOperationError( + context: ModuleProviderContext, + operation: OperationShape, + ): RustModule.LeafModule = ClientRustModule.Error + + override fun moduleForEventStreamError( + context: ModuleProviderContext, + eventStream: UnionShape, + ): RustModule.LeafModule = ClientRustModule.Error + + override fun moduleForBuilder(context: ModuleProviderContext, shape: Shape, symbol: Symbol): RustModule.LeafModule { + val builderNamespace = RustReservedWords.escapeIfNeeded(symbol.name.toSnakeCase()) + return RustModule.new( + builderNamespace, + visibility = Visibility.PUBLIC, + parent = symbol.module(), + inline = true, + documentationOverride = "See [`${symbol.name}`](${symbol.module().fullyQualifiedPath()}::${symbol.name}).", + ) + } +} + +// TODO(CrateReorganization): Remove when cleaning up `enableNewCrateOrganizationScheme` +fun ClientCodegenContext.featureGatedConfigModule() = when (settings.codegenConfig.enableNewCrateOrganizationScheme) { + true -> ClientRustModule.Config + else -> ClientRustModule.root +} + +// TODO(CrateReorganization): Remove when cleaning up `enableNewCrateOrganizationScheme` +fun ClientCodegenContext.featureGatedCustomizeModule() = when (settings.codegenConfig.enableNewCrateOrganizationScheme) { + true -> ClientRustModule.Client.customize + else -> RustModule.public( + "customize", + parent = ClientRustModule.Operation, + documentationOverride = "Operation customization and supporting types", + ) +} + +// TODO(CrateReorganization): Remove when cleaning up `enableNewCrateOrganizationScheme` +fun ClientCodegenContext.featureGatedMetaModule() = when (settings.codegenConfig.enableNewCrateOrganizationScheme) { + true -> ClientRustModule.Meta + else -> ClientRustModule.root +} + +// TODO(CrateReorganization): Remove when cleaning up `enableNewCrateOrganizationScheme` +fun ClientCodegenContext.featureGatedPaginatorModule(symbolProvider: RustSymbolProvider, operation: OperationShape) = + when (settings.codegenConfig.enableNewCrateOrganizationScheme) { + true -> RustModule.public( + "paginator", + parent = symbolProvider.moduleForShape(operation), + documentationOverride = "Paginator for this operation", + ) + else -> RustModule.public("paginator", documentationOverride = "Paginators for the service") + } + +// TODO(CrateReorganization): Remove when cleaning up `enableNewCrateOrganizationScheme` +fun ClientCodegenContext.featureGatedPrimitivesModule() = when (settings.codegenConfig.enableNewCrateOrganizationScheme) { + true -> ClientRustModule.Primitives + else -> ClientRustModule.types +} 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 5fb6eb1d1b..dad5bf9d37 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 @@ -86,6 +86,10 @@ 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(CrateReorganization): Remove this once we commit to the breaking change + val enableNewCrateOrganizationScheme: Boolean = defaultEnableNewCrateOrganizationScheme, + // TODO(SmithyRuntime): Remove this once we commit to switch to aws-smithy-runtime and aws-smithy-runtime-api + val enableNewSmithyRuntime: Boolean = defaultEnableNewSmithyRuntime, ) : CoreCodegenConfig( formatTimeoutSeconds, debugMode, ) { @@ -94,6 +98,8 @@ data class ClientCodegenConfig( private const val defaultIncludeFluentClient = true private const val defaultAddMessageToErrors = true private val defaultEventStreamAllowList: Set = emptySet() + private const val defaultEnableNewCrateOrganizationScheme = true + private const val defaultEnableNewSmithyRuntime = false fun fromCodegenConfigAndNode(coreCodegenConfig: CoreCodegenConfig, node: Optional) = if (node.isPresent) { @@ -106,12 +112,13 @@ data class ClientCodegenConfig( renameExceptions = node.get().getBooleanMemberOrDefault("renameErrors", defaultRenameExceptions), includeFluentClient = node.get().getBooleanMemberOrDefault("includeFluentClient", defaultIncludeFluentClient), addMessageToErrors = node.get().getBooleanMemberOrDefault("addMessageToErrors", defaultAddMessageToErrors), + enableNewCrateOrganizationScheme = node.get().getBooleanMemberOrDefault("enableNewCrateOrganizationScheme", defaultEnableNewCrateOrganizationScheme), + enableNewSmithyRuntime = node.get().getBooleanMemberOrDefault("enableNewSmithyRuntime", defaultEnableNewSmithyRuntime), ) } else { ClientCodegenConfig( formatTimeoutSeconds = coreCodegenConfig.formatTimeoutSeconds, debugMode = coreCodegenConfig.debugMode, - eventStreamAllowList = defaultEventStreamAllowList, ) } } 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 1d3ffabbb7..5f900a8e71 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 @@ -9,6 +9,7 @@ import software.amazon.smithy.build.PluginContext 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.client.smithy.customizations.ApiKeyAuthDecorator import software.amazon.smithy.rust.codegen.client.smithy.customizations.ClientCustomizations import software.amazon.smithy.rust.codegen.client.smithy.customize.ClientCodegenDecorator import software.amazon.smithy.rust.codegen.client.smithy.customize.CombinedClientCodegenDecorator @@ -17,16 +18,16 @@ import software.amazon.smithy.rust.codegen.client.smithy.customize.RequiredCusto import software.amazon.smithy.rust.codegen.client.smithy.customize.SerdeDecorator 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.DecoratableBuildPlugin +import software.amazon.smithy.rust.codegen.client.testutil.ClientDecoratableBuildPlugin import software.amazon.smithy.rust.codegen.core.rustlang.Attribute.Companion.NonExhaustive 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.StreamingShapeMetadataProvider import software.amazon.smithy.rust.codegen.core.smithy.StreamingShapeSymbolProvider import software.amazon.smithy.rust.codegen.core.smithy.SymbolVisitor -import software.amazon.smithy.rust.codegen.core.smithy.SymbolVisitorConfig import java.util.logging.Level import java.util.logging.Logger @@ -37,7 +38,7 @@ import java.util.logging.Logger * `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 Smithy plugin context + models. */ -class RustClientCodegenPlugin : DecoratableBuildPlugin() { +class RustClientCodegenPlugin : ClientDecoratableBuildPlugin() { override fun getName(): String = "rust-client-codegen" override fun executeWithDecorator( @@ -60,6 +61,7 @@ class RustClientCodegenPlugin : DecoratableBuildPlugin() { FluentClientDecorator(), EndpointsDecorator(), NoOpEventStreamSigningDecorator(), + ApiKeyAuthDecorator(), *decorator, ) @@ -68,24 +70,32 @@ class RustClientCodegenPlugin : DecoratableBuildPlugin() { } companion object { - /** SymbolProvider + /** * 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 + * 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(model: Model, serviceShape: ServiceShape, symbolVisitorConfig: SymbolVisitorConfig) = - SymbolVisitor(model, serviceShape = serviceShape, config = symbolVisitorConfig) + fun baseSymbolProvider( + settings: ClientRustSettings, + model: Model, + serviceShape: ServiceShape, + rustSymbolProviderConfig: RustSymbolProviderConfig, + codegenDecorator: ClientCodegenDecorator, + ) = + SymbolVisitor(settings, model, serviceShape = serviceShape, config = rustSymbolProviderConfig) // Generate different types for EventStream shapes (e.g. transcribe streaming) - .let { EventStreamSymbolProvider(symbolVisitorConfig.runtimeConfig, it, model, CodegenTarget.CLIENT) } + .let { EventStreamSymbolProvider(rustSymbolProviderConfig.runtimeConfig, it, CodegenTarget.CLIENT) } // Generate `ByteStream` instead of `Blob` for streaming binary shapes (e.g. S3 GetObject) - .let { StreamingShapeSymbolProvider(it, model) } + .let { StreamingShapeSymbolProvider(it) } // Add Rust attributes (like `#[derive(PartialEq)]`) to generated shapes - .let { BaseSymbolMetadataProvider(it, model, additionalAttributes = listOf(NonExhaustive)) } + .let { BaseSymbolMetadataProvider(it, additionalAttributes = listOf(NonExhaustive)) } // Streaming shapes need different derives (e.g. they cannot derive `PartialEq`) - .let { StreamingShapeMetadataProvider(it, model) } + .let { StreamingShapeMetadataProvider(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, model) } + .let { RustReservedWordSymbolProvider(it, ClientReservedWords) } + // Allows decorators to inject a custom symbol provider + .let { codegenDecorator.symbolProvider(it) } } } 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 new file mode 100644 index 0000000000..0d9f5bde46 --- /dev/null +++ b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/customizations/ApiKeyAuthDecorator.kt @@ -0,0 +1,206 @@ +/* + * 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.OptionalAuthTrait +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.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.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.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.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 + +/** + * Inserts a ApiKeyAuth configuration into the operation + */ +class ApiKeyAuthDecorator : ClientCodegenDecorator { + override val name: String = "ApiKeyAuth" + override val order: Byte = 10 + + private fun applies(codegenContext: ClientCodegenContext) = + isSupportedApiKeyAuth(codegenContext) + + override fun configCustomizations( + codegenContext: ClientCodegenContext, + baseCustomizations: List, + ): List { + return baseCustomizations.letIf(applies(codegenContext)) { customizations -> + customizations + ApiKeyConfigCustomization(codegenContext.runtimeConfig) + } + } + + override fun operationCustomizations( + codegenContext: ClientCodegenContext, + operation: OperationShape, + baseCustomizations: List, + ): List { + if (applies(codegenContext) && hasApiKeyAuthScheme(codegenContext, operation)) { + val service = codegenContext.serviceShape + val authDefinition: HttpApiKeyAuthTrait = service.expectTrait(HttpApiKeyAuthTrait::class.java) + return baseCustomizations + ApiKeyOperationCustomization(codegenContext.runtimeConfig, authDefinition) + } + return baseCustomizations + } + + override fun extras(codegenContext: ClientCodegenContext, rustCrate: RustCrate) { + if (applies(codegenContext)) { + rustCrate.withModule(ClientRustModule.Config) { + rust("pub use #T;", apiKey(codegenContext.runtimeConfig)) + } + } + } +} + +/** + * Returns if the service supports the httpApiKeyAuth trait. + * + * @param codegenContext Codegen context that includes the model and service shape + * @return if the httpApiKeyAuth trait is used by the service + */ +private fun isSupportedApiKeyAuth(codegenContext: ClientCodegenContext): Boolean { + return ServiceIndex.of(codegenContext.model).getAuthSchemes(codegenContext.serviceShape).containsKey(HttpApiKeyAuthTrait.ID) +} + +/** + * Returns if the service and operation have the httpApiKeyAuthTrait. + * + * @param codegenContext codegen context that includes the model and service shape + * @param operation operation shape + * @return if the service and operation have the httpApiKeyAuthTrait + */ +private fun hasApiKeyAuthScheme(codegenContext: ClientCodegenContext, operation: OperationShape): Boolean { + val auth: Map = ServiceIndex.of(codegenContext.model).getEffectiveAuthSchemes(codegenContext.serviceShape.getId(), operation.getId()) + return auth.containsKey(HttpApiKeyAuthTrait.ID) && !operation.hasTrait(OptionalAuthTrait.ID) +} + +private class ApiKeyOperationCustomization(private val runtimeConfig: RuntimeConfig, private val authDefinition: HttpApiKeyAuthTrait) : OperationCustomization() { + override fun section(section: OperationSection): Writable = when (section) { + is OperationSection.MutateRequest -> writable { + rustBlock("if let Some(api_key_config) = ${section.config}.api_key()") { + rust( + """ + ${section.request}.properties_mut().insert(api_key_config.clone()); + let api_key = api_key_config.api_key(); + """, + ) + val definitionName = authDefinition.getName() + if (authDefinition.getIn() == HttpApiKeyAuthTrait.Location.QUERY) { + rustTemplate( + """ + let auth_definition = #{http_auth_definition}::query( + "$definitionName".to_owned(), + ); + let name = auth_definition.name(); + let mut query = #{query_writer}::new(${section.request}.http().uri()); + query.insert(name, api_key); + *${section.request}.http_mut().uri_mut() = query.build_uri(); + """, + "http_auth_definition" to + RuntimeType.smithyHttpAuth(runtimeConfig).resolve("definition::HttpAuthDefinition"), + "query_writer" to RuntimeType.smithyHttp(runtimeConfig).resolve("query_writer::QueryWriter"), + ) + } else { + val definitionScheme: String = authDefinition.getScheme() + .map { scheme -> + "Some(\"" + scheme + "\".to_owned())" + } + .orElse("None") + rustTemplate( + """ + let auth_definition = #{http_auth_definition}::header( + "$definitionName".to_owned(), + $definitionScheme, + ); + let name = auth_definition.name(); + let value = match auth_definition.scheme() { + Some(value) => format!("{value} {api_key}"), + None => api_key.to_owned(), + }; + ${section.request} + .http_mut() + .headers_mut() + .insert( + #{http_header}::HeaderName::from_bytes(name.as_bytes()).expect("valid header name for api key auth"), + #{http_header}::HeaderValue::from_bytes(value.as_bytes()).expect("valid header value for api key auth") + ); + """, + "http_auth_definition" to + RuntimeType.smithyHttpAuth(runtimeConfig).resolve("definition::HttpAuthDefinition"), + "http_header" to RuntimeType.Http.resolve("header"), + ) + } + } + } + else -> emptySection + } +} + +private class ApiKeyConfigCustomization(runtimeConfig: RuntimeConfig) : ConfigCustomization() { + private val codegenScope = arrayOf( + "ApiKey" to apiKey(runtimeConfig), + ) + + override fun section(section: ServiceConfig): Writable = + when (section) { + is ServiceConfig.BuilderStruct -> writable { + rustTemplate("api_key: Option<#{ApiKey}>,", *codegenScope) + } + is ServiceConfig.BuilderImpl -> writable { + rustTemplate( + """ + /// Sets the API key that will be used by the client. + pub fn api_key(mut self, api_key: #{ApiKey}) -> Self { + self.set_api_key(Some(api_key)); + self + } + + /// Sets the API key that will be used by the client. + pub fn set_api_key(&mut self, api_key: Option<#{ApiKey}>) -> &mut Self { + self.api_key = api_key; + self + } + """, + *codegenScope, + ) + } + is ServiceConfig.BuilderBuild -> writable { + rust("api_key: self.api_key,") + } + is ServiceConfig.ConfigStruct -> writable { + 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, + ) + } + else -> emptySection + } +} + +private fun apiKey(runtimeConfig: RuntimeConfig) = RuntimeType.smithyHttpAuth(runtimeConfig).resolve("api_key::AuthApiKey") diff --git a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/customizations/ClientCustomizations.kt b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/customizations/ClientCustomizations.kt index b668752785..3f2685c1cf 100644 --- a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/customizations/ClientCustomizations.kt +++ b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/customizations/ClientCustomizations.kt @@ -19,5 +19,5 @@ class ClientCustomizations : ClientCodegenDecorator { override fun libRsCustomizations( codegenContext: ClientCodegenContext, baseCustomizations: List, - ): List = baseCustomizations + ClientDocsGenerator() + ): List = baseCustomizations + ClientDocsGenerator(codegenContext) } diff --git a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/customizations/ClientDocsGenerator.kt b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/customizations/ClientDocsGenerator.kt index 8b8d419ad0..c5bccd08c3 100644 --- a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/customizations/ClientDocsGenerator.kt +++ b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/customizations/ClientDocsGenerator.kt @@ -5,23 +5,63 @@ package software.amazon.smithy.rust.codegen.client.smithy.customizations +import software.amazon.smithy.model.traits.TitleTrait +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.containerDocs 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.smithy.generators.ModuleDocSection +import software.amazon.smithy.rust.codegen.core.util.getTrait -class ClientDocsGenerator : LibRsCustomization() { +class ClientDocsGenerator(private val codegenContext: ClientCodegenContext) : LibRsCustomization() { override fun section(section: LibRsSection): Writable { return when (section) { - is LibRsSection.ModuleDocumentation -> if (section.subsection == LibRsSection.CrateOrganization) { - crateLayout() - } else emptySection + is LibRsSection.ModuleDoc -> if (section.subsection is ModuleDocSection.CrateOrganization) { + when (codegenContext.settings.codegenConfig.enableNewCrateOrganizationScheme) { + true -> crateLayout() + else -> oldCrateLayout() + } + } else { + emptySection + } else -> emptySection } } private fun crateLayout(): Writable = + writable { + val serviceName = codegenContext.serviceShape?.getTrait()?.value ?: "the service" + containerDocs( + """ + The entry point for most customers will be [`Client`], which exposes one method for each API + offered by $serviceName. The return value of each of these methods is a "fluent builder", + where the different inputs for that API are added by builder-style function call chaining, + followed by calling `send()` to get a [`Future`](std::future::Future) that will result in + either a successful output or a [`SdkError`](crate::error::SdkError). + + Some of these API inputs may be structs or enums to provide more complex structured information. + These structs and enums live in [`types`](crate::types). There are some simpler types for + representing data such as date times or binary blobs that live in [`primitives`](crate::primitives). + + All types required to configure a client via the [`Config`](crate::Config) struct live + in [`config`](crate::config). + + The [`operation`](crate::operation) module has a submodule for every API, and in each submodule + is the input, output, and error type for that API, as well as builders to construct each of those. + + There is a top-level [`Error`](crate::Error) type that encompasses all the errors that the + client can return. Any other error type can be converted to this `Error` type via the + [`From`](std::convert::From) trait. + + The other modules within this crate are not required for normal usage. + """.trimEnd(), + ) + } + + // TODO(CrateReorganization): Delete this function when removing `enableNewCrateOrganizationScheme` + private fun oldCrateLayout(): Writable = writable { containerDocs( """ 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 84a6d5f38a..a52c4a81d6 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,9 +5,9 @@ package software.amazon.smithy.rust.codegen.client.smithy.customizations +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.core.rustlang.RustModule 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 @@ -235,7 +235,7 @@ class ResiliencyConfigCustomization(codegenContext: CodegenContext) : ConfigCust class ResiliencyReExportCustomization(private val runtimeConfig: RuntimeConfig) { fun extras(rustCrate: RustCrate) { - rustCrate.withModule(RustModule.Config) { + rustCrate.withModule(ClientRustModule.Config) { rustTemplate( """ pub use #{sleep}::{AsyncSleep, Sleep}; 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 f4034c1b9b..6949fc8b5d 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 @@ -11,7 +11,9 @@ import software.amazon.smithy.model.shapes.ShapeId 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.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.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 @@ -40,9 +42,22 @@ interface ClientCodegenDecorator : CoreCodegenDecorator { baseCustomizations: List, ): List = baseCustomizations + /** + * Hook to customize generated errors. + */ + fun errorCustomizations( + codegenContext: ClientCodegenContext, + baseCustomizations: List, + ): List = baseCustomizations + fun protocols(serviceId: ShapeId, currentProtocols: ClientProtocolMap): ClientProtocolMap = currentProtocols fun endpointCustomizations(codegenContext: ClientCodegenContext): List = listOf() + + /** + * Hook to customize client construction documentation. + */ + fun clientConstructionDocs(codegenContext: ClientCodegenContext, baseDocs: Writable): Writable = baseDocs } /** @@ -72,6 +87,13 @@ open class CombinedClientCodegenDecorator(decorators: List, + ): List = combineCustomizations(baseCustomizations) { decorator, customizations -> + decorator.errorCustomizations(codegenContext, customizations) + } + override fun protocols(serviceId: ShapeId, currentProtocols: ClientProtocolMap): ClientProtocolMap = combineCustomizations(currentProtocols) { decorator, protocolMap -> decorator.protocols(serviceId, protocolMap) @@ -80,6 +102,11 @@ open class CombinedClientCodegenDecorator(decorators: List = addCustomizations { decorator -> decorator.endpointCustomizations(codegenContext) } + override fun clientConstructionDocs(codegenContext: ClientCodegenContext, baseDocs: Writable): Writable = + combineCustomizations(baseDocs) { decorator, customizations -> + decorator.clientConstructionDocs(codegenContext, customizations) + } + companion object { fun fromClasspath( context: PluginContext, 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 f65e042d44..c88af4c2bd 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 @@ -7,18 +7,22 @@ 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.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.ResiliencyConfigCustomization import software.amazon.smithy.rust.codegen.client.smithy.customizations.ResiliencyReExportCustomization +import software.amazon.smithy.rust.codegen.client.smithy.featureGatedMetaModule +import software.amazon.smithy.rust.codegen.client.smithy.featureGatedPrimitivesModule 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 import software.amazon.smithy.rust.codegen.core.smithy.customizations.AllowLintsCustomization import software.amazon.smithy.rust.codegen.core.smithy.customizations.CrateVersionCustomization -import software.amazon.smithy.rust.codegen.core.smithy.customizations.pubUseSmithyTypes +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 @@ -54,7 +58,7 @@ class RequiredCustomizations : ClientCodegenDecorator { codegenContext: ClientCodegenContext, baseCustomizations: List, ): List = - baseCustomizations + CrateVersionCustomization() + AllowLintsCustomization() + baseCustomizations + AllowLintsCustomization() override fun extras(codegenContext: ClientCodegenContext, rustCrate: RustCrate) { // Add rt-tokio feature for `ByteStream::from_path` @@ -65,6 +69,22 @@ class RequiredCustomizations : ClientCodegenDecorator { // Re-export resiliency types ResiliencyReExportCustomization(codegenContext.runtimeConfig).extras(rustCrate) - pubUseSmithyTypes(codegenContext.runtimeConfig, codegenContext.model, rustCrate) + rustCrate.withModule(codegenContext.featureGatedPrimitivesModule()) { + pubUseSmithyPrimitives(codegenContext, codegenContext.model)(this) + if (!codegenContext.settings.codegenConfig.enableNewCrateOrganizationScheme) { + pubUseSmithyErrorTypes(codegenContext)(this) + } + } + if (codegenContext.settings.codegenConfig.enableNewCrateOrganizationScheme) { + rustCrate.withModule(ClientRustModule.Error) { + pubUseSmithyErrorTypes(codegenContext)(this) + } + } + + codegenContext.featureGatedMetaModule().also { metaModule -> + rustCrate.withModule(metaModule) { + CrateVersionCustomization.extras(rustCrate, metaModule) + } + } } } diff --git a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/endpoint/ClientContextParamDecorator.kt b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/endpoint/ClientContextConfigCustomization.kt similarity index 100% rename from codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/endpoint/ClientContextParamDecorator.kt rename to codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/endpoint/ClientContextConfigCustomization.kt 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 4d44c5602c..d6bb2cccc7 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 @@ -6,7 +6,7 @@ package software.amazon.smithy.rust.codegen.client.smithy.endpoint import software.amazon.smithy.rust.codegen.client.smithy.ClientCodegenContext -import software.amazon.smithy.rust.codegen.client.smithy.endpoint.generators.EndpointsModule +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.core.rustlang.Writable @@ -90,7 +90,9 @@ internal class EndpointConfigCustomization( /// let config = $moduleUseName::Config::builder().endpoint_resolver(prefix_resolver); /// ``` """ - } else "" + } else { + "" + } rustTemplate( """ /// Sets the endpoint resolver to use when making requests. @@ -126,21 +128,22 @@ internal class EndpointConfigCustomization( "DefaultResolver" to defaultResolver, ) } else { - val alwaysFailsResolver = RuntimeType.forInlineFun("MissingResolver", EndpointsModule) { - 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.")) + 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"), - ) - } + """, + "ResolveEndpoint" to types.resolveEndpoint, + "ResolveEndpointError" to types.resolveEndpointError, + "Result" to types.smithyHttpEndpointModule.resolve("Result"), + ) + } // 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()` rustTemplate( 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 3b8edb69ad..4cfaafd2b9 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 @@ -14,11 +14,11 @@ import software.amazon.smithy.rulesengine.language.syntax.parameters.Parameter 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.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.EndpointsModule import software.amazon.smithy.rust.codegen.client.smithy.endpoint.rulesgen.SmithyEndpointsStdLib import software.amazon.smithy.rust.codegen.client.smithy.generators.config.ConfigCustomization import software.amazon.smithy.rust.codegen.core.rustlang.Writable @@ -133,8 +133,8 @@ class EndpointsDecorator : ClientCodegenDecorator { override fun extras(codegenContext: ClientCodegenContext, rustCrate: RustCrate) { val generator = EndpointTypesGenerator.fromContext(codegenContext) - rustCrate.withModule(EndpointsModule) { - withInlineModule(EndpointTests) { + rustCrate.withModule(ClientRustModule.Endpoint) { + withInlineModule(EndpointTests, rustCrate.moduleDocProvider) { generator.testGenerator()(this) } } 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 5e5b3d7b2f..ed283bc6de 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 @@ -10,7 +10,7 @@ import software.amazon.smithy.rulesengine.language.syntax.Identifier import software.amazon.smithy.rulesengine.language.syntax.parameters.Parameter import software.amazon.smithy.rulesengine.language.syntax.parameters.ParameterType import software.amazon.smithy.rulesengine.traits.ContextParamTrait -import software.amazon.smithy.rust.codegen.client.smithy.endpoint.generators.EndpointsStdLib +import software.amazon.smithy.rust.codegen.client.smithy.endpoint.generators.EndpointStdLib import software.amazon.smithy.rust.codegen.client.smithy.endpoint.generators.FunctionRegistry import software.amazon.smithy.rust.codegen.core.rustlang.InlineDependency import software.amazon.smithy.rust.codegen.core.rustlang.RustDependency @@ -38,7 +38,7 @@ fun Identifier.rustName(): String { internal fun endpointsLib(name: String, vararg additionalDependency: RustDependency) = InlineDependency.forRustFile( RustModule.pubCrate( name, - parent = EndpointsStdLib, + parent = EndpointStdLib, ), "/inlineable/src/endpoint_lib/$name.rs", *additionalDependency, 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 1e5059830d..286f34443d 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,12 +8,12 @@ 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.ClientRustModule import software.amazon.smithy.rust.codegen.client.smithy.endpoint.memberName 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.core.rustlang.Attribute import software.amazon.smithy.rust.codegen.core.rustlang.Attribute.Companion.derive -import software.amazon.smithy.rust.codegen.core.rustlang.RustMetadata 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.RustWriter @@ -35,34 +35,19 @@ import software.amazon.smithy.rust.codegen.core.smithy.rustType import software.amazon.smithy.rust.codegen.core.util.dq import software.amazon.smithy.rust.codegen.core.util.orNull -/** - * The module containing all endpoint resolution machinery. Module layout: - * ``` - * crate::endpoints:: - * struct Params // Endpoint parameter struct - * struct ParamsBuilder // Builder for Params - * enum InvalidParams - * DefaultResolver // struct implementing the endpoint resolver based on the provided rules for the service - * internal // private module containing the endpoints library functions, the private version of the default resolver - * endpoints_lib::{endpoints_fn*, ...} - * fn default_resolver(params: &Params, partition_metadata: &PartitionMetadata, error_collector: &mut ErrorCollector) - * ``` - */ -val EndpointsModule = RustModule.public("endpoint", "Endpoint resolution functionality") - // internals contains the actual resolver function -val EndpointsImpl = RustModule.private("internals", "Endpoints internals", parent = EndpointsModule) +val EndpointImpl = RustModule.private("internals", parent = ClientRustModule.Endpoint) val EndpointTests = RustModule.new( "test", visibility = Visibility.PRIVATE, - documentation = "Generated endpoint tests", - parent = EndpointsModule, + parent = ClientRustModule.Endpoint, inline = true, -).copy(rustMetadata = RustMetadata.TestModule) + documentationOverride = "", +).cfgTest() // stdlib is isolated because it contains code generated names of stdlib functions–we want to ensure we avoid clashing -val EndpointsStdLib = RustModule.private("endpoint_lib", "Endpoints standard library functions") +val EndpointStdLib = RustModule.private("endpoint_lib") /** Endpoint Parameters generator. * @@ -129,15 +114,15 @@ internal class EndpointParamsGenerator(private val parameters: Parameters) { fun setterName(parameterName: String) = "set_${memberName(parameterName)}" } - fun paramsStruct(): RuntimeType = RuntimeType.forInlineFun("Params", EndpointsModule) { + fun paramsStruct(): RuntimeType = RuntimeType.forInlineFun("Params", ClientRustModule.Endpoint) { generateEndpointsStruct(this) } - private fun endpointsBuilder(): RuntimeType = RuntimeType.forInlineFun("ParamsBuilder", EndpointsModule) { + private fun endpointsBuilder(): RuntimeType = RuntimeType.forInlineFun("ParamsBuilder", ClientRustModule.Endpoint) { generateEndpointParamsBuilder(this) } - private fun paramsError(): RuntimeType = RuntimeType.forInlineFun("InvalidParams", EndpointsModule) { + private fun paramsError(): RuntimeType = RuntimeType.forInlineFun("InvalidParams", ClientRustModule.Endpoint) { rust( """ /// An error that occurred during endpoint resolution 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 d4f3fe32f0..d85282f16e 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 @@ -10,11 +10,11 @@ import software.amazon.smithy.rulesengine.language.EndpointRuleSet import software.amazon.smithy.rulesengine.language.eval.Type import software.amazon.smithy.rulesengine.language.syntax.expr.Expression import software.amazon.smithy.rulesengine.language.syntax.expr.Reference -import software.amazon.smithy.rulesengine.language.syntax.fn.Function 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.ClientRustModule import software.amazon.smithy.rust.codegen.client.smithy.endpoint.Context import software.amazon.smithy.rust.codegen.client.smithy.endpoint.Types import software.amazon.smithy.rust.codegen.client.smithy.endpoint.endpointsLib @@ -164,7 +164,7 @@ 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", EndpointsModule) { + return RuntimeType.forInlineFun("DefaultResolver", ClientRustModule.Endpoint) { rustTemplate( """ /// The default endpoint resolver @@ -202,7 +202,7 @@ internal class EndpointResolverGenerator(stdlib: List, ru endpointRuleSet: EndpointRuleSet, fnsUsed: List, ): RuntimeType { - return RuntimeType.forInlineFun("resolve_endpoint", EndpointsImpl) { + return RuntimeType.forInlineFun("resolve_endpoint", EndpointImpl) { Attribute(allow(allowLintsForResolver)).render(this) rustTemplate( """ @@ -288,9 +288,7 @@ internal class EndpointResolverGenerator(stdlib: List, ru val target = generator.generate(fn) val next = generateRuleInternal(rule, rest) when { - fn.type() is Type.Option || - // TODO(https://github.com/awslabs/smithy/pull/1504): ReterminusCore bug: substring should return `Option`: - (fn as Function).name == "substring" -> { + fn.type() is Type.Option -> { Attribute.AllowUnusedVariables.render(this) rustTemplate( "if let Some($resultName) = #{target:W} { #{next:W} }", 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 183e25d33e..c4d6efc327 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 @@ -14,7 +14,6 @@ import software.amazon.smithy.rust.codegen.client.smithy.endpoint.EndpointCustom 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.core.rustlang.CargoDependency 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 @@ -48,8 +47,7 @@ internal class EndpointTestGenerator( "Error" to types.resolveEndpointError, "Document" to RuntimeType.document(runtimeConfig), "HashMap" to RuntimeType.HashMap, - "capture_request" to CargoDependency.smithyClient(runtimeConfig) - .withFeature("test-util").toType().resolve("test_connection::capture_request"), + "capture_request" to RuntimeType.captureRequest(runtimeConfig), ) private val instantiator = clientInstantiator(codegenContext) diff --git a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/endpoint/rulesgen/ExpressionGenerator.kt b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/endpoint/rulesgen/ExpressionGenerator.kt index f0bad1aac2..b0d45366ed 100644 --- a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/endpoint/rulesgen/ExpressionGenerator.kt +++ b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/endpoint/rulesgen/ExpressionGenerator.kt @@ -65,7 +65,14 @@ class ExpressionGenerator( getAttr.path.toList().forEach { part -> when (part) { is GetAttr.Part.Key -> rust(".${part.key().rustName()}()") - is GetAttr.Part.Index -> rust(".get(${part.index()}).cloned()") // we end up with Option<&&T>, we need to get to Option<&T> + is GetAttr.Part.Index -> { + if (part.index() == 0) { + // In this case, `.first()` is more idiomatic and `.get(0)` triggers lint warnings + rust(".first().cloned()") + } else { + rust(".get(${part.index()}).cloned()") // we end up with Option<&&T>, we need to get to Option<&T> + } + } } } if (ownership == Ownership.Owned && getAttr.type() != Type.bool()) { diff --git a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/endpoint/rulesgen/StdLib.kt b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/endpoint/rulesgen/StdLib.kt index d2d43f2858..2c16a51bb7 100644 --- a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/endpoint/rulesgen/StdLib.kt +++ b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/endpoint/rulesgen/StdLib.kt @@ -74,10 +74,9 @@ class AwsPartitionResolver(runtimeConfig: RuntimeConfig, private val partitionsD ) override fun structFieldInit() = writable { + val json = Node.printJson(partitionsDotJson).dq() rustTemplate( - """partition_resolver: #{PartitionResolver}::new_from_json(b${ - Node.printJson(partitionsDotJson).dq() - }).expect("valid JSON")""", + """partition_resolver: #{PartitionResolver}::new_from_json(b$json).expect("valid JSON")""", *codegenScope, ) } 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 new file mode 100644 index 0000000000..a217b0f5f0 --- /dev/null +++ b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/ClientEnumGenerator.kt @@ -0,0 +1,176 @@ +/* + * 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.StringShape +import software.amazon.smithy.rust.codegen.client.smithy.ClientCodegenContext +import software.amazon.smithy.rust.codegen.client.smithy.featureGatedPrimitivesModule +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 +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.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.generators.EnumGenerator +import software.amazon.smithy.rust.codegen.core.smithy.generators.EnumGeneratorContext +import software.amazon.smithy.rust.codegen.core.smithy.generators.EnumMemberModel +import software.amazon.smithy.rust.codegen.core.smithy.generators.EnumType +import software.amazon.smithy.rust.codegen.core.util.dq + +/** Infallible enums have an `Unknown` variant and can't fail to parse */ +data class InfallibleEnumType( + val unknownVariantModule: RustModule, +) : EnumType() { + companion object { + /** Name of the generated unknown enum member name for enums with named members. */ + const val UnknownVariant = "Unknown" + + /** Name of the opaque struct that is inner data for the generated [UnknownVariant]. */ + const val UnknownVariantValue = "UnknownVariantValue" + } + + override fun implFromForStr(context: EnumGeneratorContext): Writable = writable { + rustTemplate( + """ + impl #{From}<&str> for ${context.enumName} { + fn from(s: &str) -> Self { + match s { + #{matchArms} + } + } + } + """, + "From" to RuntimeType.From, + "matchArms" to writable { + context.sortedMembers.forEach { member -> + rust("${member.value.dq()} => ${context.enumName}::${member.derivedName()},") + } + rust( + "other => ${context.enumName}::$UnknownVariant(#T(other.to_owned()))", + unknownVariantValue(context), + ) + }, + ) + } + + override fun implFromStr(context: EnumGeneratorContext): Writable = writable { + rust( + """ + 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)) + } + } + """, + ) + } + + override fun additionalDocs(context: EnumGeneratorContext): Writable = writable { + renderForwardCompatibilityNote(context.enumName, context.sortedMembers, UnknownVariant, UnknownVariantValue) + } + + override fun additionalEnumMembers(context: EnumGeneratorContext): Writable = writable { + docs("`$UnknownVariant` contains new variants that have been added since this code was generated.") + rust("$UnknownVariant(#T)", unknownVariantValue(context)) + } + + override fun additionalAsStrMatchArms(context: EnumGeneratorContext): Writable = writable { + rust("${context.enumName}::$UnknownVariant(value) => value.as_str()") + } + + private fun unknownVariantValue(context: EnumGeneratorContext): RuntimeType { + return RuntimeType.forInlineFun(UnknownVariantValue, unknownVariantModule) { + docs( + """ + Opaque struct used as inner data for the `Unknown` variant defined in enums in + the crate + + While this is not intended to be used directly, it is marked as `pub` because it is + part of the enums that are public interface. + """.trimIndent(), + ) + context.enumMeta.render(this) + rust("struct $UnknownVariantValue(pub(crate) String);") + 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") { + rust("&self.0") + } + } + } + } + + /** + * Generate the rustdoc describing how to write a match expression against a generated enum in a + * forward-compatible way. + */ + private fun RustWriter.renderForwardCompatibilityNote( + enumName: String, sortedMembers: List, + unknownVariant: String, unknownVariantValue: String, + ) { + docs( + """ + When writing a match expression against `$enumName`, it is important to ensure + your code is forward-compatible. That is, if a match arm handles a case for a + feature that is supported by the service but has not been represented as an enum + variant in a current version of SDK, your code should continue to work when you + upgrade SDK to a future version in which the enum does include a variant for that + feature. + """.trimIndent(), + ) + docs("") + docs("Here is an example of how you can make a match expression forward-compatible:") + docs("") + docs("```text") + rust("/// ## let ${enumName.lowercase()} = unimplemented!();") + rust("/// match ${enumName.lowercase()} {") + sortedMembers.mapNotNull { it.name() }.forEach { member -> + rust("/// $enumName::${member.name} => { /* ... */ },") + } + rust("""/// other @ _ if other.as_str() == "NewFeature" => { /* handles a case for `NewFeature` */ },""") + rust("/// _ => { /* ... */ },") + rust("/// }") + docs("```") + docs( + """ + The above code demonstrates that when `${enumName.lowercase()}` represents + `NewFeature`, the execution path will lead to the second last match arm, + even though the enum does not contain a variant `$enumName::NewFeature` + in the current version of SDK. The reason is that the variable `other`, + created by the `@` operator, is bound to + `$enumName::$unknownVariant($unknownVariantValue("NewFeature".to_owned()))` + and calling `as_str` on it yields `"NewFeature"`. + This match expression is forward-compatible when executed with a newer + version of SDK where the variant `$enumName::NewFeature` is defined. + Specifically, when `${enumName.lowercase()}` represents `NewFeature`, + the execution path will hit the second last match arm as before by virtue of + calling `as_str` on `$enumName::NewFeature` also yielding `"NewFeature"`. + """.trimIndent(), + ) + docs("") + docs( + """ + Explicitly matching on the `$unknownVariant` variant should + be avoided for two reasons: + - The inner data `$unknownVariantValue` is opaque, and no further information can be extracted. + - It might inadvertently shadow other intended match arms. + """.trimIndent(), + ) + } +} + +class ClientEnumGenerator(codegenContext: ClientCodegenContext, shape: StringShape) : + EnumGenerator( + codegenContext.model, + codegenContext.symbolProvider, + shape, + InfallibleEnumType(codegenContext.featureGatedPrimitivesModule()), + ) 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 9095438d6c..2c752814be 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 @@ -13,16 +13,17 @@ 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.RustSymbolProvider 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 -import software.amazon.smithy.rust.codegen.core.smithy.protocols.lensName +import software.amazon.smithy.rust.codegen.core.smithy.protocols.nestedAccessorName /** Generator for accessing nested fields through optional values **/ -class NestedAccessorGenerator(private val symbolProvider: RustSymbolProvider) { - private val module = RustModule.private("lens", "Generated accessors for nested fields") +class NestedAccessorGenerator(private val codegenContext: CodegenContext) { + private val symbolProvider = codegenContext.symbolProvider + private val module = RustModule.private("lens") /** * Generate an accessor on [root] that consumes [root] and returns an `Option` for the nested item @@ -30,7 +31,7 @@ class NestedAccessorGenerator(private val symbolProvider: RustSymbolProvider) { fun generateOwnedAccessor(root: StructureShape, path: List): RuntimeType { check(path.isNotEmpty()) { "must not be called on an empty path" } val baseType = symbolProvider.toSymbol(path.last()) - val fnName = symbolProvider.lensName("", root, path) + val fnName = symbolProvider.nestedAccessorName(codegenContext.serviceShape, "", root, path) return RuntimeType.forInlineFun(fnName, module) { rustTemplate( """ @@ -49,7 +50,7 @@ class NestedAccessorGenerator(private val symbolProvider: RustSymbolProvider) { fun generateBorrowingAccessor(root: StructureShape, path: List): RuntimeType { check(path.isNotEmpty()) { "must not be called on an empty path" } val baseType = symbolProvider.toSymbol(path.last()).makeOptional() - val fnName = symbolProvider.lensName("ref", root, path) + val fnName = symbolProvider.nestedAccessorName(codegenContext.serviceShape, "ref", root, path) val referencedType = baseType.mapRustType { (it as RustType.Option).referenced(lifetime = null) } return RuntimeType.forInlineFun(fnName, module) { rustTemplate( 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 54982643e8..b34163b51e 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 @@ -8,11 +8,11 @@ package software.amazon.smithy.rust.codegen.client.smithy.generators import software.amazon.smithy.model.Model import software.amazon.smithy.model.knowledge.PaginatedIndex import software.amazon.smithy.model.shapes.OperationShape -import software.amazon.smithy.model.shapes.ServiceShape import software.amazon.smithy.model.traits.IdempotencyTokenTrait import software.amazon.smithy.model.traits.PaginatedTrait +import software.amazon.smithy.rust.codegen.client.smithy.ClientCodegenContext +import software.amazon.smithy.rust.codegen.client.smithy.featureGatedPaginatorModule import software.amazon.smithy.rust.codegen.client.smithy.generators.client.FluentClientGenerics -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 @@ -20,11 +20,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.stripOuter 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 -import software.amazon.smithy.rust.codegen.core.smithy.generators.builderSymbol -import software.amazon.smithy.rust.codegen.core.smithy.generators.error.errorSymbol 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 @@ -40,25 +36,21 @@ fun OperationShape.isPaginated(model: Model) = .findMemberWithTrait(model) == null class PaginatorGenerator private constructor( - private val model: Model, - private val symbolProvider: RustSymbolProvider, - service: ServiceShape, + private val codegenContext: ClientCodegenContext, operation: OperationShape, private val generics: FluentClientGenerics, retryClassifier: RuntimeType, ) { companion object { fun paginatorType( - codegenContext: CodegenContext, + codegenContext: ClientCodegenContext, generics: FluentClientGenerics, operationShape: OperationShape, retryClassifier: RuntimeType, ): RuntimeType? { return if (operationShape.isPaginated(codegenContext.model)) { PaginatorGenerator( - codegenContext.model, - codegenContext.symbolProvider, - codegenContext.serviceShape, + codegenContext, operationShape, generics, retryClassifier, @@ -69,17 +61,19 @@ class PaginatorGenerator private constructor( } } + private val model = codegenContext.model + private val symbolProvider = codegenContext.symbolProvider + private val runtimeConfig = codegenContext.runtimeConfig private val paginatorName = "${operation.id.name.toPascalCase()}Paginator" - private val runtimeConfig = symbolProvider.config().runtimeConfig private val idx = PaginatedIndex.of(model) - private val paginationInfo = - idx.getPaginationInfo(service, operation).orNull() ?: PANIC("failed to load pagination info") - private val module = RustModule.public("paginator", "Paginators for the service") + private val paginationInfo = idx.getPaginationInfo(codegenContext.serviceShape, operation).orNull() + ?: PANIC("failed to load pagination info") + private val module = codegenContext.featureGatedPaginatorModule(symbolProvider, operation) private val inputType = symbolProvider.toSymbol(operation.inputShape(model)) private val outputShape = operation.outputShape(model) private val outputType = symbolProvider.toSymbol(outputShape) - private val errorType = operation.errorSymbol(symbolProvider) + private val errorType = symbolProvider.symbolForOperationError(operation) private fun paginatorType(): RuntimeType = RuntimeType.forInlineFun( paginatorName, @@ -103,7 +97,7 @@ class PaginatorGenerator private constructor( "Input" to inputType, "Output" to outputType, "Error" to errorType, - "Builder" to operation.inputShape(model).builderSymbol(symbolProvider), + "Builder" to symbolProvider.symbolForBuilder(operation.inputShape(model)), // SDK Types "SdkError" to RuntimeType.sdkError(runtimeConfig), @@ -117,7 +111,7 @@ class PaginatorGenerator private constructor( /** Generate the paginator struct & impl **/ private fun generate() = writable { - val outputTokenLens = NestedAccessorGenerator(symbolProvider).generateBorrowingAccessor( + val outputTokenLens = NestedAccessorGenerator(codegenContext).generateBorrowingAccessor( outputShape, paginationInfo.outputTokenMemberPath, ) @@ -272,7 +266,7 @@ class PaginatorGenerator private constructor( } """, - "extract_items" to NestedAccessorGenerator(symbolProvider).generateOwnedAccessor( + "extract_items" to NestedAccessorGenerator(codegenContext).generateOwnedAccessor( outputShape, paginationInfo.itemsMemberPath, ), 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 6710ac9c3d..c495c623f5 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 @@ -7,14 +7,14 @@ package software.amazon.smithy.rust.codegen.client.smithy.generators 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.generators.config.ServiceConfigGenerator +import software.amazon.smithy.rust.codegen.client.smithy.generators.error.ServiceErrorGenerator 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.Attribute -import software.amazon.smithy.rust.codegen.core.rustlang.RustModule import software.amazon.smithy.rust.codegen.core.smithy.RustCrate -import software.amazon.smithy.rust.codegen.core.smithy.generators.error.ServiceErrorGenerator import software.amazon.smithy.rust.codegen.core.smithy.generators.protocol.ProtocolSupport import software.amazon.smithy.rust.codegen.core.util.inputShape @@ -56,9 +56,13 @@ class ServiceGenerator( } } - ServiceErrorGenerator(clientCodegenContext, operations).render(rustCrate) + ServiceErrorGenerator( + clientCodegenContext, + operations, + decorator.errorCustomizations(clientCodegenContext, emptyList()), + ).render(rustCrate) - rustCrate.withModule(RustModule.Config) { + rustCrate.withModule(ClientRustModule.Config) { ServiceConfigGenerator.withBaseBehavior( clientCodegenContext, extraCustomizations = decorator.configCustomizations(clientCodegenContext, listOf()), 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 31a86d07ac..31ca1eae9f 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 @@ -5,14 +5,13 @@ package software.amazon.smithy.rust.codegen.client.smithy.generators.client +import software.amazon.smithy.rust.codegen.client.smithy.ClientCodegenContext +import software.amazon.smithy.rust.codegen.client.smithy.featureGatedCustomizeModule 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.smithy.RuntimeConfig import software.amazon.smithy.rust.codegen.core.smithy.RuntimeType import software.amazon.smithy.rust.codegen.core.smithy.RustCrate @@ -21,28 +20,28 @@ import software.amazon.smithy.rust.codegen.core.smithy.RustCrate * fluent client builders. */ class CustomizableOperationGenerator( - private val runtimeConfig: RuntimeConfig, + private val codegenContext: ClientCodegenContext, private val generics: FluentClientGenerics, - private val includeFluentClient: Boolean, ) { - - companion object { - val CustomizeModule = RustModule.public("customize", "Operation customization and supporting types", parent = RustModule.operation(Visibility.PUBLIC)) - } - + 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(CustomizeModule) { + crate.withModule(codegenContext.featureGatedCustomizeModule()) { rustTemplate( """ pub use #{Operation}; + pub use #{Request}; + pub use #{Response}; pub use #{ClassifyRetry}; pub use #{RetryKind}; """, "Operation" to smithyHttp.resolve("operation::Operation"), - "ClassifyRetry" to smithyHttp.resolve("retry::ClassifyRetry"), + "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) @@ -67,6 +66,7 @@ class CustomizableOperationGenerator( "handle_generics_bounds" to handleGenerics.bounds(), "operation_generics_decl" to operationGenerics.declaration(), "combined_generics_decl" to combinedGenerics.declaration(), + "customize_module" to codegenContext.featureGatedCustomizeModule(), ) writer.rustTemplate( @@ -81,7 +81,7 @@ class CustomizableOperationGenerator( /// 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()`][crate::operation::customize::CustomizableOperation::send] method. + /// by calling its [`.send()`][#{customize_module}::CustomizableOperation::send] method. ##[derive(Debug)] pub struct CustomizableOperation#{combined_generics_decl:W} { pub(crate) handle: Arc, @@ -150,6 +150,9 @@ class CustomizableOperationGenerator( "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), + "SdkError" to RuntimeType.sdkError(runtimeConfig), ) writer.rustTemplate( @@ -164,6 +167,7 @@ class CustomizableOperationGenerator( 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 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 b6fced279f..bc959d4a74 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 @@ -5,22 +5,22 @@ package software.amazon.smithy.rust.codegen.client.smithy.generators.client +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.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.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.RustCrate 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.LibRsCustomization import software.amazon.smithy.rust.codegen.core.smithy.generators.LibRsSection +import software.amazon.smithy.rust.codegen.core.util.serviceNameOrDefault class FluentClientDecorator : ClientCodegenDecorator { override val name: String = "FluentClient" @@ -65,7 +65,7 @@ sealed class FluentClientSection(name: String) : Section(name) { /** Write custom code into an operation fluent builder's impl block */ data class FluentBuilderImpl( val operationShape: OperationShape, - val operationErrorType: RuntimeType, + val operationErrorType: Symbol, ) : FluentClientSection("FluentBuilderImpl") /** Write custom code into the docs */ @@ -74,171 +74,22 @@ sealed class FluentClientSection(name: String) : Section(name) { abstract class FluentClientCustomization : NamedCustomization() -class GenericFluentClient(codegenContext: CodegenContext) : FluentClientCustomization() { - private val moduleUseName = codegenContext.moduleUseName() - private val codegenScope = arrayOf("client" to RuntimeType.smithyClient(codegenContext.runtimeConfig)) +class GenericFluentClient(private val codegenContext: ClientCodegenContext) : FluentClientCustomization() { override fun section(section: FluentClientSection): Writable { return when (section) { is FluentClientSection.FluentClientDocs -> writable { - val humanName = section.serviceShape.id.name - rust( + val serviceName = codegenContext.serviceShape.serviceNameOrDefault("the service") + docs( """ - /// An ergonomic service client for `$humanName`. - /// - /// This client allows ergonomic access to a `$humanName`-shaped service. - /// Each method corresponds to an endpoint defined in the service's Smithy model, - /// and the request and response shapes are auto-generated from that same model. - /// """, - ) - rustTemplate( - """ - /// ## Constructing a Client - /// - /// To construct a client, you need a few different things: - /// - /// - A [`Config`](crate::Config) that specifies additional configuration - /// required by the service. - /// - A connector (`C`) that specifies how HTTP requests are translated - /// into HTTP responses. This will typically be an HTTP client (like - /// `hyper`), though you can also substitute in your own, like a mock - /// mock connector for testing. - /// - A "middleware" (`M`) that modifies requests prior to them being - /// sent to the request. Most commonly, middleware will decide what - /// endpoint the requests should be sent to, as well as perform - /// authentication and authorization of requests (such as SigV4). - /// You can also have middleware that performs request/response - /// tracing, throttling, or other middleware-like tasks. - /// - A retry policy (`R`) that dictates the behavior for requests that - /// fail and should (potentially) be retried. The default type is - /// generally what you want, as it implements a well-vetted retry - /// policy implemented in [`RetryMode::Standard`](aws_smithy_types::retry::RetryMode::Standard). - /// - /// To construct a client, you will generally want to call - /// [`Client::with_config`], which takes a [`#{client}::Client`] (a - /// Smithy client that isn't specialized to a particular service), - /// and a [`Config`](crate::Config). Both of these are constructed using - /// the [builder pattern] where you first construct a `Builder` type, - /// then configure it with the necessary parameters, and then call - /// `build` to construct the finalized output type. The - /// [`#{client}::Client`] builder is re-exported in this crate as - /// [`Builder`] for convenience. - /// - /// In _most_ circumstances, you will want to use the following pattern - /// to construct a client: - /// - /// ``` - /// use $moduleUseName::{Builder, Client, Config}; - /// - /// let smithy_client = Builder::new() - /// .dyn_https_connector(Default::default()) - /// ## /* - /// .middleware(/* discussed below */) - /// ## */ - /// ## .middleware_fn(|r| r) - /// .build(); - /// let config = Config::builder().endpoint_resolver("https://www.myurl.com").build(); - /// let client = Client::with_config(smithy_client, config); - /// ``` - /// - /// For the middleware, you'll want to use whatever matches the - /// routing, authentication and authorization required by the target - /// service. For example, for the standard AWS SDK which uses - /// [SigV4-signed requests], the middleware looks like this: - /// - // Ignored as otherwise we'd need to pull in all these dev-dependencies. - /// ```rust,ignore - /// use aws_endpoint::AwsEndpointStage; - /// use aws_http::auth::CredentialsStage; - /// use aws_http::recursion_detection::RecursionDetectionStage; - /// use aws_http::user_agent::UserAgentStage; - /// use aws_sig_auth::middleware::SigV4SigningStage; - /// use aws_sig_auth::signer::SigV4Signer; - /// use aws_smithy_client::retry::Config as RetryConfig; - /// use aws_smithy_http_tower::map_request::{AsyncMapRequestLayer, MapRequestLayer}; - /// use std::fmt::Debug; - /// use tower::layer::util::{Identity, Stack}; - /// use tower::ServiceBuilder; - /// - /// type AwsMiddlewareStack = Stack< - /// MapRequestLayer, - /// Stack< - /// MapRequestLayer, - /// Stack< - /// AsyncMapRequestLayer, - /// Stack< - /// MapRequestLayer, - /// Stack, Identity>, - /// >, - /// >, - /// >, - /// >; - /// - /// /// AWS Middleware Stack - /// /// - /// /// This implements the middleware stack for this service. It will: - /// /// 1. Load credentials asynchronously into the property bag - /// /// 2. Sign the request with SigV4 - /// /// 3. Resolve an Endpoint for the request - /// /// 4. Add a user agent to the request - /// ##[derive(Debug, Default, Clone)] - /// ##[non_exhaustive] - /// pub struct AwsMiddleware; - /// - /// impl AwsMiddleware { - /// /// Create a new `AwsMiddleware` stack - /// /// - /// /// Note: `AwsMiddleware` holds no state. - /// pub fn new() -> Self { - /// AwsMiddleware::default() - /// } - /// } - /// - /// // define the middleware stack in a non-generic location to reduce code bloat. - /// fn base() -> ServiceBuilder { - /// let credential_provider = AsyncMapRequestLayer::for_mapper(CredentialsStage::new()); - /// let signer = MapRequestLayer::for_mapper(SigV4SigningStage::new(SigV4Signer::new())); - /// let endpoint_resolver = MapRequestLayer::for_mapper(AwsEndpointStage); - /// let user_agent = MapRequestLayer::for_mapper(UserAgentStage::new()); - /// let recursion_detection = MapRequestLayer::for_mapper(RecursionDetectionStage::new()); - /// // These layers can be considered as occurring in order, that is: - /// // 1. Resolve an endpoint - /// // 2. Add a user agent - /// // 3. Acquire credentials - /// // 4. Sign with credentials - /// // (5. Dispatch over the wire) - /// ServiceBuilder::new() - /// .layer(endpoint_resolver) - /// .layer(user_agent) - /// .layer(credential_provider) - /// .layer(signer) - /// .layer(recursion_detection) - /// } - /// - /// impl tower::Layer for AwsMiddleware { - /// type Service = >::Service; - /// - /// fn layer(&self, inner: S) -> Self::Service { - /// base().service(inner) - /// } - /// } - /// ``` - ///""", - *codegenScope, - ) - rust( - """ - /// ## Using a Client - /// - /// Once you have a client set up, you can access the service's endpoints - /// by calling the appropriate method on [`Client`]. Each such method - /// returns a request builder for that endpoint, with methods for setting - /// the various fields of the request. Once your request is complete, use - /// the `send` method to send the request. `send` returns a future, which - /// you then have to `.await` to get the service's response. - /// - /// [builder pattern]: https://rust-lang.github.io/api-guidelines/type-safety.html##c-builder - /// [SigV4-signed requests]: https://docs.aws.amazon.com/general/latest/gr/signature-version-4.html""", + An ergonomic client for $serviceName. + + This client allows ergonomic access to $serviceName. + Each method corresponds to an API defined in the service's Smithy model, + and the request and response shapes are auto-generated from that same model. + """, ) + FluentClientDocs.clientConstructionDocs(codegenContext)(this) + FluentClientDocs.clientUsageDocs(codegenContext)(this) } else -> emptySection } 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 new file mode 100644 index 0000000000..3e8c66f64b --- /dev/null +++ b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/client/FluentClientDocs.kt @@ -0,0 +1,224 @@ +/* + * 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.model.shapes.OperationShape +import software.amazon.smithy.model.shapes.StringShape +import software.amazon.smithy.rust.codegen.client.smithy.ClientCodegenContext +import software.amazon.smithy.rust.codegen.core.rustlang.CargoDependency +import software.amazon.smithy.rust.codegen.core.rustlang.docsTemplate +import software.amazon.smithy.rust.codegen.core.rustlang.writable +import software.amazon.smithy.rust.codegen.core.util.inputShape +import software.amazon.smithy.rust.codegen.core.util.serviceNameOrDefault + +object FluentClientDocs { + fun clientConstructionDocs(codegenContext: ClientCodegenContext) = writable { + val serviceName = codegenContext.serviceShape.serviceNameOrDefault("the service") + val moduleUseName = codegenContext.moduleUseName() + docsTemplate( + """ + Client for calling $serviceName. + + #### Client types and purposes + + Clients generated by smithy-rs have two separate client layers: + + 1. The Smithy [`Client`](#{aws_smithy_client}::Client): A _lower level client_ that is not tied + to the service and directly operates on operation structs. A Smithy client is composed of a + connector, middleware, and retry strategy. + 2. The "fluent" [`Client`]: A _higher level client_ that is convenient to use. + + The vast majority of use-cases don't require using the Smithy client, so it is usually only relevant + for client construction. Because of this, this documentation will refer to the fluent client as simply + the "client". The Smithy client is only relevant when customizing the connector, middleware, or + retry strategy, or for even more advanced use cases. + + #### Constructing a `Client` + + A fluent [`Client`] is composed of a Smithy client and service configuration. In order to + construct one, a Smithy client must be created first, and this is done using + the [`client::Builder`](crate::client::Builder) struct: + + ```rust,no_run + let smithy_client = $moduleUseName::client::Builder::new() + // Use the default HTTPS connector + .dyn_https_connector(Default::default()) + // Use a no-op middleware + .middleware_fn(|request| request) + // Build a type-erased Smithy client + .build_dyn(); + ``` + + The client builder has generics `C`, `M`, and `R` representing the connector, middleware, and + retry strategy: + + - A connector (`C`) specifies how HTTP requests are translated into HTTP responses. This will typically be + an HTTP client (like `hyper`), but you can also provide your own, like a mock connector for testing. + - Middleware (`M`) modifies requests prior to them being sent to the service. Most commonly, middleware decide + what endpoint the requests should be sent to, as well as perform authentication and authorization (such + as HTTP basic auth or AWS SigV4). You can also have middleware that performs request/response tracing, + throttling, or other middleware-like tasks. + - A retry strategy (`R`) dictates the behavior for requests that fail. The default, + [`RetryMode::Standard`](aws_smithy_types::retry::RetryMode::Standard) is generally what you want since + it provides a well-vetted exponential backoff retry implementation. + + Once the Smithy client is created, a service config and fluent client can be created. Generally, you + want to call [`Client::with_config`], which takes a Smithy client and the service [`Config`](crate::Config). + The config is constructed using the [builder pattern], and has several builder methods to further + customize the client. + + In _most_ circumstances, you will want to use the following pattern to construct a client: + + ```rust,no_run + let smithy_client = $moduleUseName::client::Builder::new() + .dyn_https_connector(Default::default()) + ## /* + .middleware(/* discussed below */) + ## */ + ## .middleware_fn(|r| r) + .build_dyn(); + + let config = $moduleUseName::Config::builder().build(); + let client = $moduleUseName::Client::with_config(smithy_client, config); + ``` + + _Note:_ Client construction is expensive due to connection thread pool initialization, and should be done + once at application start-up. + + For middleware, you'll want to use whatever matches the routing, authentication, and authorization + required by the target service. For example, for the AWS SDK which uses [SigV4-signed requests], the + middleware looks like this: + + ```rust,ignore + use aws_endpoint::AwsEndpointStage; + use aws_http::auth::CredentialsStage; + use aws_http::recursion_detection::RecursionDetectionStage; + use aws_http::user_agent::UserAgentStage; + use aws_sig_auth::middleware::SigV4SigningStage; + use aws_sig_auth::signer::SigV4Signer; + use aws_smithy_client::retry::Config as RetryConfig; + use aws_smithy_http_tower::map_request::{AsyncMapRequestLayer, MapRequestLayer}; + use std::fmt::Debug; + use tower::layer::util::{Identity, Stack}; + use tower::ServiceBuilder; + + type AwsMiddlewareStack = Stack< + MapRequestLayer, + Stack< + MapRequestLayer, + Stack< + AsyncMapRequestLayer, + Stack< + MapRequestLayer, + Stack, Identity>, + >, + >, + >, + >; + + /// AWS Middleware Stack + /// + /// This implements the middleware stack for this service. It will: + /// 1. Load credentials asynchronously into the property bag + /// 2. Sign the request with SigV4 + /// 3. Resolve an Endpoint for the request + /// 4. Add a user agent to the request + ##[derive(Debug, Default, Clone)] + ##[non_exhaustive] + pub struct AwsMiddleware; + + impl AwsMiddleware { + /// Create a new `AwsMiddleware` stack + /// + /// Note: `AwsMiddleware` holds no state. + pub fn new() -> Self { + AwsMiddleware::default() + } + } + + // define the middleware stack in a non-generic location to reduce code bloat. + fn base() -> ServiceBuilder { + let credential_provider = AsyncMapRequestLayer::for_mapper(CredentialsStage::new()); + let signer = MapRequestLayer::for_mapper(SigV4SigningStage::new(SigV4Signer::new())); + let endpoint_resolver = MapRequestLayer::for_mapper(AwsEndpointStage); + let user_agent = MapRequestLayer::for_mapper(UserAgentStage::new()); + let recursion_detection = MapRequestLayer::for_mapper(RecursionDetectionStage::new()); + // These layers can be considered as occurring in order, that is: + // 1. Resolve an endpoint + // 2. Add a user agent + // 3. Acquire credentials + // 4. Sign with credentials + // (5. Dispatch over the wire) + ServiceBuilder::new() + .layer(endpoint_resolver) + .layer(user_agent) + .layer(credential_provider) + .layer(signer) + .layer(recursion_detection) + } + + impl tower::Layer for AwsMiddleware { + type Service = >::Service; + + fn layer(&self, inner: S) -> Self::Service { + base().service(inner) + } + } + ``` + + [builder pattern]: https://rust-lang.github.io/api-guidelines/type-safety.html##builders-enable-construction-of-complex-values-c-builder + [SigV4-signed requests]: https://docs.aws.amazon.com/general/latest/gr/signature-version-4.html""${'"'}, + """.trimIndent(), + "aws_smithy_client" to CargoDependency.smithyClient(codegenContext.runtimeConfig).toDevDependency().toType(), + ) + } + + fun clientUsageDocs(codegenContext: ClientCodegenContext) = writable { + val model = codegenContext.model + val symbolProvider = codegenContext.symbolProvider + if (model.operationShapes.isNotEmpty()) { + // Find an operation with a simple string member shape + val (operation, member) = codegenContext.serviceShape.operations + .map { id -> + val operationShape = model.expectShape(id, OperationShape::class.java) + val member = operationShape.inputShape(model) + .members() + .firstOrNull { model.expectShape(it.target) is StringShape } + operationShape to member + } + .sortedBy { it.first.id } + .firstOrNull { (_, member) -> member != null } ?: (null to null) + if (operation != null && member != null) { + val operationSymbol = symbolProvider.toSymbol(operation) + val memberSymbol = symbolProvider.toSymbol(member) + val operationFnName = FluentClientGenerator.clientOperationFnName(operation, symbolProvider) + docsTemplate( + """ + ## Using the `Client` + + 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 + returns a result, as illustrated below: + + ```rust,ignore + let result = client.$operationFnName() + .${memberSymbol.name}("example") + .call() + .await; + ``` + + The underlying HTTP requests that get made by this can be modified with the `customize_operation` + function on the fluent builder. See the [`customize`](crate::client::customize) module for more + information. + """.trimIndent(), + "operation" to operationSymbol, + ) + } + } + } +} 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 3f3c1d2501..0a4861447d 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 @@ -13,15 +13,17 @@ import software.amazon.smithy.model.shapes.OperationShape import software.amazon.smithy.model.shapes.StructureShape import software.amazon.smithy.model.traits.DocumentationTrait 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.featureGatedCustomizeModule import software.amazon.smithy.rust.codegen.client.smithy.generators.PaginatorGenerator import software.amazon.smithy.rust.codegen.client.smithy.generators.isPaginated 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.EscapeFor import software.amazon.smithy.rust.codegen.core.rustlang.RustModule import software.amazon.smithy.rust.codegen.core.rustlang.RustReservedWords 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.Visibility import software.amazon.smithy.rust.codegen.core.rustlang.asArgumentType import software.amazon.smithy.rust.codegen.core.rustlang.asOptional import software.amazon.smithy.rust.codegen.core.rustlang.deprecatedShape @@ -32,7 +34,6 @@ import software.amazon.smithy.rust.codegen.core.rustlang.escape 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 @@ -43,8 +44,6 @@ 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.builderSymbol -import software.amazon.smithy.rust.codegen.core.smithy.generators.error.errorSymbol 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 @@ -54,6 +53,7 @@ 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, @@ -66,11 +66,11 @@ class FluentClientGenerator( companion object { fun clientOperationFnName(operationShape: OperationShape, symbolProvider: RustSymbolProvider): String = RustReservedWords.escapeIfNeeded(symbolProvider.toSymbol(operationShape).name.toSnakeCase()) - - val clientModule = RustModule.public( - "client", - "Client and fluent builders for calling the service.", - ) + fun clientOperationModuleName(operationShape: OperationShape, symbolProvider: RustSymbolProvider): String = + RustReservedWords.escapeIfNeeded( + symbolProvider.toSymbol(operationShape).name.toSnakeCase(), + EscapeFor.ModuleName, + ) } private val serviceShape = codegenContext.serviceShape @@ -82,242 +82,142 @@ class FluentClientGenerator( private val core = FluentClientCore(model) fun render(crate: RustCrate) { - crate.withModule(clientModule) { - renderFluentClient(this) + renderFluentClient(crate) + + operations.forEach { operation -> + crate.withModule(operation.fluentBuilderModule(codegenContext, symbolProvider)) { + renderFluentBuilder(operation) + } } - CustomizableOperationGenerator( - runtimeConfig, - generics, - codegenContext.settings.codegenConfig.includeFluentClient, - ).render(crate) + CustomizableOperationGenerator(codegenContext, generics).render(crate) } - private fun renderFluentClient(writer: RustWriter) { - writer.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 + private fun renderFluentClient(crate: RustCrate) { + crate.withModule(ClientRustModule.client) { + if (!codegenContext.settings.codegenConfig.enableNewCrateOrganizationScheme || reexportSmithyClientBuilder) { + rustTemplate( + """ + ##[doc(inline)] + pub use #{client}::Builder; + """, + "client" to RuntimeType.smithyClient(runtimeConfig), + ) } - - impl${generics.inst} std::clone::Clone for Client${generics.inst} { - fn clone(&self) -> Self { - Self { handle: self.handle.clone() } + rustTemplate( + """ + ##[derive(Debug)] + pub(crate) struct Handle#{generics_decl:W} { + pub(crate) client: #{client}::Client#{smithy_inst:W}, + pub(crate) conf: crate::Config, } - } - ##[doc(inline)] - pub use #{client}::Builder; - - 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()) + #{client_docs:W} + ##[derive(std::fmt::Debug)] + pub struct Client#{generics_decl:W} { + handle: std::sync::Arc } - } - 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, - }) + impl${generics.inst} std::clone::Clone for Client${generics.inst} { + fn clone(&self) -> Self { + Self { handle: self.handle.clone() } } } - /// Returns the client's configuration. - pub fn conf(&self) -> &crate::Config { - &self.handle.conf - } - } - """, - "generics_decl" to generics.decl, - "smithy_inst" to generics.smithyInst, - "client" to RuntimeType.smithyClient(runtimeConfig), - "client_docs" to writable - { - customizations.forEach { - it.section( - FluentClientSection.FluentClientDocs( - serviceShape, - ), - )(this) - } - }, - ) - writer.rustBlockTemplate( - "impl${generics.inst} Client${generics.inst} #{bounds:W}", - "client" to RuntimeType.smithyClient(runtimeConfig), - "bounds" to generics.bounds, - ) { - operations.forEach { operation -> - val name = symbolProvider.toSymbol(operation).name - val fullPath = operation.fullyQualifiedFluentBuilder(symbolProvider) - val maybePaginated = if (operation.isPaginated(model)) { - "\n/// This operation supports pagination; See [`into_paginator()`]($fullPath::into_paginator)." - } else "" - - val output = operation.outputShape(model) - val operationOk = symbolProvider.toSymbol(output) - val operationErr = operation.errorSymbol(symbolProvider).toSymbol() - - val inputFieldsBody = - generateOperationShapeDocs(writer, symbolProvider, operation, model).joinToString("\n") { - "/// - $it" + 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()) } - - val inputFieldsHead = if (inputFieldsBody.isNotEmpty()) { - "The fluent builder is configurable:" - } else { - "The fluent builder takes no input, just [`send`]($fullPath::send) it." } - val outputFieldsBody = - generateShapeMemberDocs(writer, symbolProvider, output, model).joinToString("\n") { - "/// - $it" + 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, + }) + } } - var outputFieldsHead = "On success, responds with [`${operationOk.name}`]($operationOk)" - if (outputFieldsBody.isNotEmpty()) { - outputFieldsHead += " with field(s):" - } - - writer.rustTemplate( - """ - /// Constructs a fluent builder for the [`$name`]($fullPath) operation.$maybePaginated - /// - /// - $inputFieldsHead - $inputFieldsBody - /// - $outputFieldsHead - $outputFieldsBody - /// - On failure, responds with [`SdkError<${operationErr.name}>`]($operationErr) - """, - ) - - writer.rust( - """ - pub fn ${ - clientOperationFnName( - operation, - symbolProvider, - ) - }(&self) -> fluent_builders::$name${generics.inst} { - fluent_builders::$name::new(self.handle.clone()) + /// Returns the client's configuration. + pub fn conf(&self) -> &crate::Config { + &self.handle.conf } - """, - ) - } - } - writer.withInlineModule(RustModule.new("fluent_builders", visibility = Visibility.PUBLIC, inline = true)) { - docs( - """ - Utilities to ergonomically construct a request to the service. - - Fluent builders are created through the [`Client`](crate::client::Client) by calling - one if its operation methods. After parameters are set using the builder methods, - the `send` method can be called to initiate the request. - """.trim(), - newlinePrefix = "//! ", + } + """, + "generics_decl" to generics.decl, + "smithy_inst" to generics.smithyInst, + "client" to RuntimeType.smithyClient(runtimeConfig), + "client_docs" to writable + { + customizations.forEach { + it.section( + FluentClientSection.FluentClientDocs( + serviceShape, + ), + )(this) + } + }, ) - operations.forEach { 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 - val derives = baseDerives.filter { it == RuntimeType.Clone } + RuntimeType.Debug - rust( - """ - /// Fluent builder constructing a request to `${operationSymbol.name}`. - /// - """, - ) + } - documentShape(operation, model, autoSuppressMissingDocs = false) - deprecatedShape(operation) - Attribute(derive(derives.toSet())).render(this) - rustTemplate( - """ - pub struct ${operationSymbol.name}#{generics:W} { - handle: std::sync::Arc, - inner: #{Inner} - } - """, - "Inner" to input.builderSymbol(symbolProvider), - "client" to RuntimeType.smithyClient(runtimeConfig), - "generics" to generics.decl, - "operation" to operationSymbol, - ) + operations.forEach { operation -> + val name = symbolProvider.toSymbol(operation).name + val fnName = clientOperationFnName(operation, symbolProvider) + val moduleName = clientOperationModuleName(operation, symbolProvider) + val privateModule = RustModule.private(moduleName, parent = ClientRustModule.client) + crate.withModule(privateModule) { rustBlockTemplate( - "impl${generics.inst} ${operationSymbol.name}${generics.inst} #{bounds:W}", + "impl${generics.inst} super::Client${generics.inst} #{bounds:W}", "client" to RuntimeType.smithyClient(runtimeConfig), "bounds" to generics.bounds, ) { - val outputType = symbolProvider.toSymbol(operation.outputShape(model)) - val errorType = operation.errorSymbol(symbolProvider) - val operationFnName = clientOperationFnName( - operation, + + val fullPath = operation.fullyQualifiedFluentBuilder(codegenContext, symbolProvider) + val maybePaginated = if (operation.isPaginated(model)) { + "\n/// This operation supports pagination; See [`into_paginator()`]($fullPath::into_paginator)." + } else { + "" + } + + val output = operation.outputShape(model) + val operationOk = symbolProvider.toSymbol(output) + val operationErr = symbolProvider.symbolForOperationError(operation) + + val inputFieldsBody = generateOperationShapeDocs( + this, + codegenContext, symbolProvider, - ) - // 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() } - } + operation, + model, + ).joinToString("\n") { "/// - $it" } - /// 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< - crate::operation::customize::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(crate::operation::customize::CustomizableOperation { handle, operation }) - } + val inputFieldsHead = if (inputFieldsBody.isNotEmpty()) { + "The fluent builder is configurable:\n" + } else { + "The fluent builder takes no input, just [`send`]($fullPath::send) it." + } - ##[#{Unstable}] - /// This function replaces the parameter with new one. - /// It is useful when you want to replace the existing data with de-serialized data. - /// ```rust - /// let deserialized_parameters: #{InputBuilderType} = serde_json::from_str(parameters_written_in_json).unwrap(); - /// let outcome: #{OperationOutput} = client.$operationFnName().set_fields(&deserialized_parameters).send().await; - /// ``` - pub fn set_fields(mut self, data: #{InputBuilderType}) -> Self { - self.inner = data; - self + val outputFieldsBody = + generateShapeMemberDocs(this, symbolProvider, output, model).joinToString("\n") { + "/// - $it" } - /// Sends the request and returns the response. - /// - /// If an error occurs, an `SdkError` will be returned with additional details that - /// can be matched against. + var outputFieldsHead = "On success, responds with [`${operationOk.name}`]($operationOk)" + if (outputFieldsBody.isNotEmpty()) { + outputFieldsHead += " with field(s):\n" + } + + rustTemplate( + """ + /// Constructs a fluent builder for the [`$name`]($fullPath) operation.$maybePaginated /// - /// 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 - } + /// - $inputFieldsHead$inputFieldsBody + /// - $outputFieldsHead$outputFieldsBody + /// - On failure, responds with [`SdkError<${operationErr.name}>`]($operationErr) """, "Unstable" to Attribute.AwsSdkUnstableAttribute.inner, "InputBuilderType" to input.builderSymbol(symbolProvider), @@ -333,42 +233,158 @@ class FluentClientGenerator( generics.toRustGenerics(), ), ) - 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( - operation, - operation.errorSymbol(symbolProvider), - ), - ) - input.members().forEach { member -> - val memberName = symbolProvider.toMemberName(member) - // All fields in the builder are optional - val memberSymbol = symbolProvider.toSymbol(member) - val outerType = memberSymbol.rustType() - when (val coreType = outerType.stripOuter()) { - is RustType.Vec -> with(core) { renderVecHelper(member, memberName, coreType) } - is RustType.HashMap -> with(core) { renderMapHelper(member, memberName, coreType) } - else -> with(core) { renderInputHelper(member, memberName, coreType) } + + // Write a deprecation notice if this operation is deprecated. + deprecatedShape(operation) + + rustTemplate( + """ + pub fn $fnName(&self) -> #{FluentBuilder}${generics.inst} { + #{FluentBuilder}::new(self.handle.clone()) } - // pure setter - val setterName = member.setterName() - val optionalInputType = outerType.asOptional() - with(core) { renderInputHelper(member, setterName, optionalInputType) } + """, + "FluentBuilder" to operation.fluentBuilderType(codegenContext, symbolProvider), + ) + } + } + } + } + + private fun RustWriter.renderFluentBuilder(operation: OperationShape) { + 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 + val derives = baseDerives.filter { it == RuntimeType.Clone } + RuntimeType.Debug + docs("Fluent builder constructing a request to `${operationSymbol.name}`.\n") + + val builderName = operation.fluentBuilderType(codegenContext, symbolProvider).name + 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), + "generics" to generics.decl, + "operation" to operationSymbol, + ) + + 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) + val operationFnName = clientOperationFnName( + operation, + symbolProvider, + ) + // 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() } + } + + /// 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 }) + } + + ##[#{Unstable}] + /// This function replaces the parameter with new one. + /// It is useful when you want to replace the existing data with de-serialized data. + /// ```rust + /// let deserialized_parameters: #{InputBuilderType} = serde_json::from_str(parameters_written_in_json).unwrap(); + /// let outcome: #{OperationOutput} = client.$operationFnName().set_fields(&deserialized_parameters).send().await; + /// ``` + pub fn set_fields(mut self, data: #{InputBuilderType}) -> Self { + self.inner = data; + self + } + + /// 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 codegenContext.featureGatedCustomizeModule().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(), + ), + ) + 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( + operation, + symbolProvider.symbolForOperationError(operation), + ), + ) + input.members().forEach { member -> + val memberName = symbolProvider.toMemberName(member) + // All fields in the builder are optional + val memberSymbol = symbolProvider.toSymbol(member) + val outerType = memberSymbol.rustType() + when (val coreType = outerType.stripOuter()) { + is RustType.Vec -> with(core) { renderVecHelper(member, memberName, coreType) } + is RustType.HashMap -> with(core) { renderMapHelper(member, memberName, coreType) } + else -> with(core) { renderInputHelper(member, memberName, coreType) } } + // pure setter + val setterName = member.setterName() + val optionalInputType = outerType.asOptional() + with(core) { renderInputHelper(member, setterName, optionalInputType) } } } } @@ -382,12 +398,13 @@ class FluentClientGenerator( */ private fun generateOperationShapeDocs( writer: RustWriter, - symbolProvider: SymbolProvider, + codegenContext: ClientCodegenContext, + symbolProvider: RustSymbolProvider, operation: OperationShape, model: Model, ): List { val input = operation.inputShape(model) - val fluentBuilderFullyQualifiedName = operation.fullyQualifiedFluentBuilder(symbolProvider) + val fluentBuilderFullyQualifiedName = operation.fullyQualifiedFluentBuilder(codegenContext, symbolProvider) return input.members().map { memberShape -> val builderInputDoc = memberShape.asFluentBuilderInputDoc(symbolProvider) val builderInputLink = docLink("$fluentBuilderFullyQualifiedName::${symbolProvider.toMemberName(memberShape)}") @@ -430,17 +447,46 @@ private fun generateShapeMemberDocs( } } +private fun OperationShape.fluentBuilderModule( + codegenContext: ClientCodegenContext, + symbolProvider: RustSymbolProvider, +) = when (codegenContext.settings.codegenConfig.enableNewCrateOrganizationScheme) { + true -> symbolProvider.moduleForBuilder(this) + else -> RustModule.public( + "fluent_builders", + parent = ClientRustModule.client, + documentationOverride = """ + Utilities to ergonomically construct a request to the service. + + Fluent builders are created through the [`Client`](crate::client::Client) by calling + one if its operation methods. After parameters are set using the builder methods, + the `send` method can be called to initiate the request. + """.trimIndent(), + ) +} + +internal fun OperationShape.fluentBuilderType( + codegenContext: ClientCodegenContext, + symbolProvider: RustSymbolProvider, +): RuntimeType = fluentBuilderModule(codegenContext, symbolProvider).toType() + .resolve( + symbolProvider.toSymbol(this).name + + when (codegenContext.settings.codegenConfig.enableNewCrateOrganizationScheme) { + true -> "FluentBuilder" + else -> "" + }, + ) + /** * Generate a valid fully-qualified Type for a fluent builder e.g. - * `OperationShape(AssumeRole)` -> `"crate::client::fluent_builders::AssumeRole"` + * `OperationShape(AssumeRole)` -> `"crate::operations::assume_role::AssumeRoleFluentBuilder"` * * * _NOTE: This function generates the links that appear under **"The fluent builder is configurable:"**_ */ -private fun OperationShape.fullyQualifiedFluentBuilder(symbolProvider: SymbolProvider): String { - val operationName = symbolProvider.toSymbol(this).name - - return "crate::client::fluent_builders::$operationName" -} +private fun OperationShape.fullyQualifiedFluentBuilder( + codegenContext: ClientCodegenContext, + symbolProvider: RustSymbolProvider, +): String = fluentBuilderType(codegenContext, symbolProvider).fullyQualifiedName() /** * Generate a string that looks like a Rust function pointer for documenting a fluent builder method e.g. 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 b3229051e6..399085d5e5 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 @@ -28,7 +28,7 @@ interface FluentClientGenerics { val bounds: Writable /** Bounds for generated `send()` functions */ - fun sendBounds(operation: Symbol, operationOutput: Symbol, operationError: RuntimeType, retryClassifier: RuntimeType): Writable + fun sendBounds(operation: Symbol, operationOutput: Symbol, operationError: Symbol, retryClassifier: RuntimeType): Writable /** Convert this `FluentClientGenerics` into the more general `RustGenerics` */ fun toRustGenerics(): RustGenerics @@ -70,7 +70,7 @@ data class FlexibleClientGenerics( } /** Bounds for generated `send()` functions */ - override fun sendBounds(operation: Symbol, operationOutput: Symbol, operationError: RuntimeType, retryClassifier: RuntimeType): Writable = writable { + override fun sendBounds(operation: Symbol, operationOutput: Symbol, operationError: Symbol, retryClassifier: RuntimeType): Writable = writable { rustTemplate( """ where 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 7cda821957..75b075dc4d 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 @@ -63,7 +63,9 @@ class IdempotencyTokenProviderCustomization : NamedCustomization( rust("make_token: self.make_token.unwrap_or_else(#T::default_provider),", RuntimeType.IdempotencyToken) } - is ServiceConfig.DefaultForTests -> writable { rust("""${section.configBuilderRef}.set_make_token(Some("00000000-0000-4000-8000-000000000000".into()));""") } + is ServiceConfig.DefaultForTests -> writable { + rust("""${section.configBuilderRef}.set_make_token(Some("00000000-0000-4000-8000-000000000000".into()));""") + } else -> writable { } } diff --git a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/error/ErrorCustomization.kt b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/error/ErrorCustomization.kt new file mode 100644 index 0000000000..d275c9b17d --- /dev/null +++ b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/error/ErrorCustomization.kt @@ -0,0 +1,25 @@ +/* + * 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.error + +import software.amazon.smithy.codegen.core.Symbol +import software.amazon.smithy.model.shapes.StructureShape +import software.amazon.smithy.rust.codegen.core.smithy.customize.NamedCustomization +import software.amazon.smithy.rust.codegen.core.smithy.customize.Section + +/** Error customization sections */ +sealed class ErrorSection(name: String) : Section(name) { + /** Use this section to add additional trait implementations to the generated operation errors */ + data class OperationErrorAdditionalTraitImpls(val errorSymbol: Symbol, val allErrors: List) : + ErrorSection("OperationErrorAdditionalTraitImpls") + + /** Use this section to add additional trait implementations to the generated service error */ + class ServiceErrorAdditionalTraitImpls(val allErrors: List) : + ErrorSection("ServiceErrorAdditionalTraitImpls") +} + +/** Customizations for generated errors */ +abstract class ErrorCustomization : NamedCustomization() diff --git a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/error/ErrorGenerator.kt b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/error/ErrorGenerator.kt new file mode 100644 index 0000000000..21d4f24aef --- /dev/null +++ b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/error/ErrorGenerator.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.rust.codegen.client.smithy.generators.error + +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.RustWriter +import software.amazon.smithy.rust.codegen.core.rustlang.Writable +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.rustlang.writable +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.errorMetadata +import software.amazon.smithy.rust.codegen.core.smithy.RustSymbolProvider +import software.amazon.smithy.rust.codegen.core.smithy.generators.BuilderCustomization +import software.amazon.smithy.rust.codegen.core.smithy.generators.BuilderGenerator +import software.amazon.smithy.rust.codegen.core.smithy.generators.BuilderSection +import software.amazon.smithy.rust.codegen.core.smithy.generators.StructureCustomization +import software.amazon.smithy.rust.codegen.core.smithy.generators.StructureGenerator +import software.amazon.smithy.rust.codegen.core.smithy.generators.StructureSection +import software.amazon.smithy.rust.codegen.core.smithy.generators.error.ErrorImplCustomization +import software.amazon.smithy.rust.codegen.core.smithy.generators.error.ErrorImplGenerator + +class ErrorGenerator( + private val model: Model, + private val symbolProvider: RustSymbolProvider, + private val shape: StructureShape, + private val error: ErrorTrait, + private val implCustomizations: List, +) { + private val runtimeConfig = symbolProvider.config.runtimeConfig + private val symbol = symbolProvider.toSymbol(shape) + + fun renderStruct(writer: RustWriter) { + writer.apply { + StructureGenerator( + model, symbolProvider, this, shape, + listOf( + object : StructureCustomization() { + override fun section(section: StructureSection): Writable = writable { + when (section) { + is StructureSection.AdditionalFields -> { + rust("pub(crate) meta: #T,", errorMetadata(runtimeConfig)) + } + + is StructureSection.AdditionalDebugFields -> { + rust("""${section.formatterName}.field("meta", &self.meta);""") + } + + else -> {} + } + } + }, + ), + ).render() + + ErrorImplGenerator( + model, + symbolProvider, + this, + shape, + error, + implCustomizations, + ).render(CodegenTarget.CLIENT) + + rustBlock("impl #T for ${symbol.name}", RuntimeType.provideErrorMetadataTrait(runtimeConfig)) { + rust("fn meta(&self) -> &#T { &self.meta }", errorMetadata(runtimeConfig)) + } + + implBlock(symbol) { + BuilderGenerator.renderConvenienceMethod(this, symbolProvider, shape) + } + } + } + + fun renderBuilder(writer: RustWriter) { + writer.apply { + BuilderGenerator( + model, symbolProvider, shape, + listOf( + object : BuilderCustomization() { + override fun section(section: BuilderSection): Writable = writable { + when (section) { + is BuilderSection.AdditionalFields -> { + rust("meta: std::option::Option<#T>,", errorMetadata(runtimeConfig)) + } + + is BuilderSection.AdditionalMethods -> { + rustTemplate( + """ + /// Sets error metadata + pub fn meta(mut self, meta: #{error_metadata}) -> Self { + self.meta = Some(meta); + self + } + + /// Sets error metadata + pub fn set_meta(&mut self, meta: std::option::Option<#{error_metadata}>) -> &mut Self { + self.meta = meta; + self + } + """, + "error_metadata" to errorMetadata(runtimeConfig), + ) + } + + is BuilderSection.AdditionalFieldsInBuild -> { + rust("meta: self.meta.unwrap_or_default(),") + } + + else -> {} + } + } + }, + ), + ).render(this) + } + } +} 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 new file mode 100644 index 0000000000..26d926a78d --- /dev/null +++ b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/error/OperationErrorGenerator.kt @@ -0,0 +1,268 @@ +/* + * 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.error + +import software.amazon.smithy.codegen.core.Symbol +import software.amazon.smithy.model.Model +import software.amazon.smithy.model.shapes.OperationShape +import software.amazon.smithy.model.shapes.Shape +import software.amazon.smithy.model.shapes.StructureShape +import software.amazon.smithy.model.shapes.UnionShape +import software.amazon.smithy.model.traits.RetryableTrait +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.RustWriter +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.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.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.errorMetadata +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 +import software.amazon.smithy.rust.codegen.core.smithy.customize.writeCustomizations +import software.amazon.smithy.rust.codegen.core.smithy.transformers.eventStreamErrors +import software.amazon.smithy.rust.codegen.core.smithy.transformers.operationErrors +import software.amazon.smithy.rust.codegen.core.util.UNREACHABLE +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.toSnakeCase + +/** + * Generates a unified error enum for [operation]. [ErrorGenerator] handles generating the individual variants, + * but we must still combine those variants into an enum covering all possible errors for a given operation. + * + * This generator also generates errors for event streams. + */ +class OperationErrorGenerator( + private val model: Model, + private val symbolProvider: RustSymbolProvider, + private val operationOrEventStream: Shape, + private val customizations: List, +) { + private val runtimeConfig = symbolProvider.config.runtimeConfig + private val errorMetadata = errorMetadata(symbolProvider.config.runtimeConfig) + private val createUnhandledError = + RuntimeType.smithyHttp(runtimeConfig).resolve("result::CreateUnhandledError") + + private fun operationErrors(): List = + (operationOrEventStream as OperationShape).operationErrors(model).map { it.asStructureShape().get() } + private fun eventStreamErrors(): List = + (operationOrEventStream as UnionShape).eventStreamErrors() + .map { model.expectShape(it.asMemberShape().get().target, StructureShape::class.java) } + + fun render(writer: RustWriter) { + val (errorSymbol, errors) = when (operationOrEventStream) { + is OperationShape -> symbolProvider.symbolForOperationError(operationOrEventStream) to operationErrors() + is UnionShape -> symbolProvider.symbolForEventStreamError(operationOrEventStream) to eventStreamErrors() + else -> UNREACHABLE("OperationErrorGenerator only supports operation or event stream shapes") + } + + val meta = RustMetadata( + derives = setOf(RuntimeType.Debug), + additionalAttributes = listOf(Attribute.NonExhaustive), + visibility = Visibility.PUBLIC, + ) + + // TODO(deprecated): Remove this temporary alias. This was added so that the compiler + // points customers in the right direction when they are upgrading. Unfortunately there's no + // way to provide better backwards compatibility on this change. + val kindDeprecationMessage = "Operation `*Error/*ErrorKind` types were combined into a single `*Error` enum. " + + "The `.kind` field on `*Error` no longer exists and isn't needed anymore (you can just match on the " + + "error directly since it's an enum now)." + writer.rust( + """ + /// Do not use this. + /// + /// $kindDeprecationMessage + ##[deprecated(note = ${kindDeprecationMessage.dq()})] + pub type ${errorSymbol.name}Kind = ${errorSymbol.name}; + """, + ) + + writer.rust("/// Error type for the `${errorSymbol.name}` operation.") + meta.render(writer) + writer.rustBlock("enum ${errorSymbol.name}") { + errors.forEach { errorVariant -> + documentShape(errorVariant, model) + deprecatedShape(errorVariant) + val errorVariantSymbol = symbolProvider.toSymbol(errorVariant) + write("${errorVariantSymbol.name}(#T),", errorVariantSymbol) + } + rust( + """ + /// An unexpected error occurred (e.g., invalid JSON returned by the service or an unknown error code). + Unhandled(#T), + """, + unhandledError(runtimeConfig), + ) + } + writer.rustBlock("impl #T for ${errorSymbol.name}", createUnhandledError) { + rustBlock( + """ + fn create_unhandled_error( + source: Box, + meta: std::option::Option<#T> + ) -> Self + """, + errorMetadata, + ) { + rust( + """ + Self::Unhandled({ + let mut builder = #T::builder().source(source); + builder.set_meta(meta); + builder.build() + }) + """, + unhandledError(runtimeConfig), + ) + } + } + writer.rustBlock("impl #T for ${errorSymbol.name}", RuntimeType.Display) { + rustBlock("fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result") { + delegateToVariants(errors) { + writable { rust("_inner.fmt(f)") } + } + } + } + + val errorMetadataTrait = RuntimeType.provideErrorMetadataTrait(runtimeConfig) + writer.rustBlock("impl #T for ${errorSymbol.name}", errorMetadataTrait) { + rustBlock("fn meta(&self) -> &#T", errorMetadata(runtimeConfig)) { + delegateToVariants(errors) { + writable { rust("#T::meta(_inner)", errorMetadataTrait) } + } + } + } + + writer.writeCustomizations(customizations, ErrorSection.OperationErrorAdditionalTraitImpls(errorSymbol, errors)) + + val retryErrorKindT = RuntimeType.retryErrorKind(symbolProvider.config.runtimeConfig) + writer.rustBlock( + "impl #T for ${errorSymbol.name}", + RuntimeType.provideErrorKind(symbolProvider.config.runtimeConfig), + ) { + rustBlock("fn code(&self) -> std::option::Option<&str>") { + rust("#T::code(self)", RuntimeType.provideErrorMetadataTrait(runtimeConfig)) + } + + rustBlock("fn retryable_error_kind(&self) -> std::option::Option<#T>", retryErrorKindT) { + val retryableVariants = errors.filter { it.hasTrait() } + if (retryableVariants.isEmpty()) { + rust("None") + } else { + rustBlock("match self") { + retryableVariants.forEach { + val errorVariantSymbol = symbolProvider.toSymbol(it) + rust("Self::${errorVariantSymbol.name}(inner) => Some(inner.retryable_error_kind()),") + } + rust("_ => None") + } + } + } + } + + writer.rustBlock("impl ${errorSymbol.name}") { + writer.rustTemplate( + """ + /// Creates the `${errorSymbol.name}::Unhandled` variant from any error type. + pub fn unhandled(err: impl Into>) -> Self { + Self::Unhandled(#{Unhandled}::builder().source(err).build()) + } + + /// Creates the `${errorSymbol.name}::Unhandled` variant from a `#{error_metadata}`. + pub fn generic(err: #{error_metadata}) -> Self { + Self::Unhandled(#{Unhandled}::builder().source(err.clone()).meta(err).build()) + } + """, + "error_metadata" to errorMetadata, + "std_error" to RuntimeType.StdError, + "Unhandled" to unhandledError(runtimeConfig), + ) + writer.docs( + """ + Returns error metadata, which includes the error code, message, + request ID, and potentially additional information. + """, + ) + writer.rustBlock("pub fn meta(&self) -> &#T", errorMetadata) { + rust("use #T;", RuntimeType.provideErrorMetadataTrait(runtimeConfig)) + rustBlock("match self") { + errors.forEach { error -> + val errorVariantSymbol = symbolProvider.toSymbol(error) + rust("Self::${errorVariantSymbol.name}(e) => e.meta(),") + } + rust("Self::Unhandled(e) => e.meta(),") + } + } + errors.forEach { error -> + val errorVariantSymbol = symbolProvider.toSymbol(error) + val fnName = errorVariantSymbol.name.toSnakeCase() + writer.rust("/// Returns `true` if the error kind is `${errorSymbol.name}::${errorVariantSymbol.name}`.") + writer.rustBlock("pub fn is_$fnName(&self) -> bool") { + rust("matches!(self, Self::${errorVariantSymbol.name}(_))") + } + } + } + + writer.rustBlock("impl #T for ${errorSymbol.name}", RuntimeType.StdError) { + rustBlock("fn source(&self) -> std::option::Option<&(dyn #T + 'static)>", RuntimeType.StdError) { + delegateToVariants(errors) { + writable { + rust("Some(_inner)") + } + } + } + } + } + + sealed class VariantMatch(name: String) : Section(name) { + object Unhandled : VariantMatch("Unhandled") + data class Modeled(val symbol: Symbol, val shape: Shape) : VariantMatch("Modeled") + } + + /** + * Generates code to delegate behavior to the variants, for example: + * + * ```rust + * match self { + * Self::InvalidGreeting(_inner) => inner.fmt(f), + * Self::ComplexError(_inner) => inner.fmt(f), + * Self::FooError(_inner) => inner.fmt(f), + * Self::Unhandled(_inner) => _inner.fmt(f), + * } + * ``` + * + * [handler] is passed an instance of [VariantMatch]—a [writable] should be returned containing the content to be + * written for this variant. + * + * The field will always be bound as `_inner`. + */ + fun RustWriter.delegateToVariants( + errors: List, + handler: (VariantMatch) -> Writable, + ) { + rustBlock("match self") { + errors.forEach { + val errorSymbol = symbolProvider.toSymbol(it) + rust("""Self::${errorSymbol.name}(_inner) => """) + handler(VariantMatch.Modeled(errorSymbol, it))(this) + write(",") + } + val unhandledHandler = handler(VariantMatch.Unhandled) + rustBlock("Self::Unhandled(_inner) =>") { + unhandledHandler(this) + } + } + } +} diff --git a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/generators/error/ServiceErrorGenerator.kt b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/error/ServiceErrorGenerator.kt similarity index 71% rename from codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/generators/error/ServiceErrorGenerator.kt rename to codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/error/ServiceErrorGenerator.kt index c03a30b6cc..f461d59e05 100644 --- a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/generators/error/ServiceErrorGenerator.kt +++ b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/error/ServiceErrorGenerator.kt @@ -3,8 +3,9 @@ * SPDX-License-Identifier: Apache-2.0 */ -package software.amazon.smithy.rust.codegen.core.smithy.generators.error +package software.amazon.smithy.rust.codegen.client.smithy.generators.error +import software.amazon.smithy.codegen.core.Symbol import software.amazon.smithy.model.shapes.OperationShape import software.amazon.smithy.model.shapes.ShapeId import software.amazon.smithy.model.shapes.StructureShape @@ -23,7 +24,9 @@ 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.CodegenTarget import software.amazon.smithy.rust.codegen.core.smithy.RuntimeType +import software.amazon.smithy.rust.codegen.core.smithy.RuntimeType.Companion.unhandledError import software.amazon.smithy.rust.codegen.core.smithy.RustCrate +import software.amazon.smithy.rust.codegen.core.smithy.customize.writeCustomizations import software.amazon.smithy.rust.codegen.core.smithy.transformers.allErrors import software.amazon.smithy.rust.codegen.core.smithy.transformers.eventStreamErrors @@ -42,11 +45,17 @@ import software.amazon.smithy.rust.codegen.core.smithy.transformers.eventStreamE * } * ``` */ -class ServiceErrorGenerator(private val codegenContext: CodegenContext, private val operations: List) { +class ServiceErrorGenerator( + private val codegenContext: CodegenContext, + private val operations: List, + private val customizations: List, +) { private val symbolProvider = codegenContext.symbolProvider private val model = codegenContext.model - private val allErrors = operations.flatMap { it.allErrors(model) }.map { it.id }.distinctBy { it.getName(codegenContext.serviceShape) } + private val allErrors = operations.flatMap { + it.allErrors(model) + }.map { it.id }.distinctBy { it.getName(codegenContext.serviceShape) } .map { codegenContext.model.expectShape(it, StructureShape::class.java) } .sortedBy { it.id.getName(codegenContext.serviceShape) } @@ -59,7 +68,7 @@ class ServiceErrorGenerator(private val codegenContext: CodegenContext, private // Every operation error can be converted into service::Error operations.forEach { operationShape -> // operation errors - renderImplFrom(operationShape.errorSymbol(symbolProvider), operationShape.errors) + renderImplFrom(symbolProvider.symbolForOperationError(operationShape), operationShape.errors) } // event stream errors operations.map { it.eventStreamErrors(codegenContext.model) } @@ -67,11 +76,12 @@ class ServiceErrorGenerator(private val codegenContext: CodegenContext, private .associate { it.key to it.value } .forEach { (unionShape, errors) -> renderImplFrom( - unionShape.eventStreamErrorSymbol(symbolProvider), + symbolProvider.symbolForEventStreamError(unionShape), errors.map { it.id }, ) } rust("impl #T for Error {}", RuntimeType.StdError) + writeCustomizations(customizations, ErrorSection.ServiceErrorAdditionalTraitImpls(allErrors)) } crate.lib { rust("pub use error_meta::Error;") } } @@ -89,7 +99,7 @@ class ServiceErrorGenerator(private val codegenContext: CodegenContext, private } } - private fun RustWriter.renderImplFrom(errorSymbol: RuntimeType, errors: List) { + private fun RustWriter.renderImplFrom(errorSymbol: Symbol, errors: List) { if (errors.isNotEmpty() || CodegenTarget.CLIENT == codegenContext.target) { val operationErrors = errors.map { model.expectShape(it) } rustBlock( @@ -104,25 +114,36 @@ class ServiceErrorGenerator(private val codegenContext: CodegenContext, private ) { rustBlock("match err") { rust("#T::ServiceError(context) => Self::from(context.into_err()),", sdkError) - rust("_ => Error::Unhandled(#T::new(err.into())),", unhandledError()) + rustTemplate( + """ + _ => Error::Unhandled( + #{Unhandled}::builder() + .meta(#{ProvideErrorMetadata}::meta(&err).clone()) + .source(err) + .build() + ), + """, + "Unhandled" to unhandledError(codegenContext.runtimeConfig), + "ProvideErrorMetadata" to RuntimeType.provideErrorMetadataTrait(codegenContext.runtimeConfig), + ) } } } rustBlock("impl From<#T> for Error", errorSymbol) { rustBlock("fn from(err: #T) -> Self", errorSymbol) { - rustBlock("match err.kind") { + rustBlock("match err") { operationErrors.forEach { errorShape -> val errSymbol = symbolProvider.toSymbol(errorShape) rust( - "#TKind::${errSymbol.name}(inner) => Error::${errSymbol.name}(inner),", + "#T::${errSymbol.name}(inner) => Error::${errSymbol.name}(inner),", errorSymbol, ) } rustTemplate( - "#{errorSymbol}Kind::Unhandled(inner) => Error::Unhandled(#{unhandled}::new(inner.into())),", + "#{errorSymbol}::Unhandled(inner) => Error::Unhandled(inner),", "errorSymbol" to errorSymbol, - "unhandled" to unhandledError(), + "unhandled" to unhandledError(codegenContext.runtimeConfig), ) } } @@ -143,8 +164,8 @@ class ServiceErrorGenerator(private val codegenContext: CodegenContext, private val sym = symbolProvider.toSymbol(error) rust("${sym.name}(#T),", sym) } - docs(UNHANDLED_ERROR_DOCS) - rust("Unhandled(#T)", unhandledError()) + docs("An unexpected error occurred (e.g., invalid JSON returned by the service or an unknown error code).") + rust("Unhandled(#T)", unhandledError(codegenContext.runtimeConfig)) } } } 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 9ec7173d03..1d3f38a884 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 @@ -5,7 +5,6 @@ package software.amazon.smithy.rust.codegen.client.smithy.generators.http -import software.amazon.smithy.codegen.core.Symbol import software.amazon.smithy.model.knowledge.HttpBinding import software.amazon.smithy.model.knowledge.HttpBindingIndex import software.amazon.smithy.model.pattern.SmithyPattern @@ -13,7 +12,6 @@ import software.amazon.smithy.model.shapes.MapShape 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 import software.amazon.smithy.model.traits.EnumTrait import software.amazon.smithy.model.traits.HttpTrait import software.amazon.smithy.rust.codegen.core.rustlang.Attribute @@ -26,7 +24,6 @@ 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.OperationBuildError -import software.amazon.smithy.rust.codegen.core.smithy.generators.builderSymbol import software.amazon.smithy.rust.codegen.core.smithy.generators.http.HttpBindingGenerator import software.amazon.smithy.rust.codegen.core.smithy.generators.operationBuildError import software.amazon.smithy.rust.codegen.core.smithy.isOptional @@ -68,9 +65,8 @@ class RequestBindingGenerator( private val symbolProvider = codegenContext.symbolProvider private val runtimeConfig = codegenContext.runtimeConfig private val httpTrait = protocol.httpBindingResolver.httpTrait(operationShape) - private fun builderSymbol(shape: StructureShape): Symbol = shape.builderSymbol(symbolProvider) private val httpBindingGenerator = - HttpBindingGenerator(protocol, codegenContext, codegenContext.symbolProvider, operationShape, ::builderSymbol) + HttpBindingGenerator(protocol, codegenContext, codegenContext.symbolProvider, operationShape) private val index = HttpBindingIndex.of(model) private val encoder = RuntimeType.smithyTypes(runtimeConfig).resolve("primitive::Encoder") @@ -131,7 +127,7 @@ class RequestBindingGenerator( val combinedArgs = listOf(formatString, *args.toTypedArray()) writer.addImport(RuntimeType.stdFmt.resolve("Write").toSymbol(), null) writer.rustBlockTemplate( - "fn uri_base(_input: &#{Input}, output: &mut String) -> Result<(), #{BuildError}>", + "fn uri_base(_input: &#{Input}, output: &mut String) -> std::result::Result<(), #{BuildError}>", *codegenScope, ) { httpTrait.uri.labels.map { label -> @@ -277,7 +273,7 @@ class RequestBindingGenerator( target.isTimestampShape -> { val timestampFormat = index.determineTimestampFormat(member, HttpBinding.Location.QUERY, protocol.defaultTimestampFormat) - val timestampFormatType = RuntimeType.timestampFormat(runtimeConfig, timestampFormat) + val timestampFormatType = RuntimeType.serializeTimestampFormat(runtimeConfig, timestampFormat) val func = writer.format(RuntimeType.queryFormat(runtimeConfig, "fmt_timestamp")) "&$func($targetName, ${writer.format(timestampFormatType)})?" } @@ -318,7 +314,7 @@ class RequestBindingGenerator( target.isTimestampShape -> { val timestampFormat = index.determineTimestampFormat(member, HttpBinding.Location.LABEL, protocol.defaultTimestampFormat) - val timestampFormatType = RuntimeType.timestampFormat(runtimeConfig, timestampFormat) + val timestampFormatType = RuntimeType.serializeTimestampFormat(runtimeConfig, timestampFormat) val func = format(RuntimeType.labelFormat(runtimeConfig, "fmt_timestamp")) rust("let $outputVar = $func($input, ${format(timestampFormatType)})?;") } diff --git a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/http/ResponseBindingGenerator.kt b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/http/ResponseBindingGenerator.kt index 7becd9df01..911028ea5a 100644 --- a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/http/ResponseBindingGenerator.kt +++ b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/http/ResponseBindingGenerator.kt @@ -7,24 +7,20 @@ package software.amazon.smithy.rust.codegen.client.smithy.generators.http import software.amazon.smithy.codegen.core.Symbol 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.smithy.CodegenContext import software.amazon.smithy.rust.codegen.core.smithy.RuntimeType -import software.amazon.smithy.rust.codegen.core.smithy.generators.builderSymbol import software.amazon.smithy.rust.codegen.core.smithy.generators.http.HttpBindingGenerator import software.amazon.smithy.rust.codegen.core.smithy.protocols.HttpBindingDescriptor import software.amazon.smithy.rust.codegen.core.smithy.protocols.Protocol class ResponseBindingGenerator( protocol: Protocol, - private val codegenContext: CodegenContext, + codegenContext: CodegenContext, operationShape: OperationShape, ) { - private fun builderSymbol(shape: StructureShape): Symbol = shape.builderSymbol(codegenContext.symbolProvider) - private val httpBindingGenerator = - HttpBindingGenerator(protocol, codegenContext, codegenContext.symbolProvider, operationShape, ::builderSymbol) + HttpBindingGenerator(protocol, codegenContext, codegenContext.symbolProvider, operationShape) fun generateDeserializeHeaderFn(binding: HttpBindingDescriptor): RuntimeType = httpBindingGenerator.generateDeserializeHeaderFn(binding) @@ -34,11 +30,7 @@ class ResponseBindingGenerator( fun generateDeserializePayloadFn( binding: HttpBindingDescriptor, - errorT: RuntimeType, + errorSymbol: Symbol, payloadParser: RustWriter.(String) -> Unit, - ): RuntimeType = httpBindingGenerator.generateDeserializePayloadFn( - binding, - errorT, - payloadParser, - ) + ): RuntimeType = httpBindingGenerator.generateDeserializePayloadFn(binding, errorSymbol, payloadParser) } 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 index b8d6a652e8..8cd5fd9338 100644 --- 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 @@ -6,27 +6,29 @@ package software.amazon.smithy.rust.codegen.client.smithy.generators.protocol 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.client.smithy.generators.client.fluentBuilderType 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.docLink +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.CodegenContext +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.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.implBlock import software.amazon.smithy.rust.codegen.core.smithy.generators.protocol.ProtocolGenerator import software.amazon.smithy.rust.codegen.core.smithy.generators.protocol.ProtocolTraitImplGenerator import software.amazon.smithy.rust.codegen.core.smithy.protocols.Protocol import software.amazon.smithy.rust.codegen.core.util.inputShape open class ClientProtocolGenerator( - codegenContext: CodegenContext, + 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 @@ -50,40 +52,78 @@ open class ClientProtocolGenerator( customizations: List, ) { val inputShape = operationShape.inputShape(model) - val builderGenerator = BuilderGenerator(model, symbolProvider, operationShape.inputShape(model)) - builderGenerator.render(inputWriter) // impl OperationInputShape { ... } - val operationName = symbolProvider.toSymbol(operationShape).name - inputWriter.implBlock(inputShape, symbolProvider) { + inputWriter.implBlock(symbolProvider.toSymbol(inputShape)) { writeCustomizations( customizations, OperationSection.InputImpl(customizations, operationShape, inputShape, protocol), ) makeOperationGenerator.generateMakeOperation(this, operationShape, customizations) + } - // pub fn builder() -> ... { } - builderGenerator.renderConvenienceMethod(this) + when (codegenContext.settings.codegenConfig.enableNewCrateOrganizationScheme) { + true -> renderOperationStruct(operationWriter, operationShape, customizations) + else -> oldRenderOperationStruct(operationWriter, operationShape, inputShape, customizations) } + } + + private fun renderOperationStruct( + operationWriter: RustWriter, + operationShape: OperationShape, + customizations: List, + ) { + val operationName = symbolProvider.toSymbol(operationShape).name // pub struct Operation { ... } - val fluentBuilderName = FluentClientGenerator.clientOperationFnName(operationShape, symbolProvider) 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) + } + + // TODO(CrateReorganization): Remove this function when removing `enableNewCrateOrganizationScheme` + private fun oldRenderOperationStruct( + operationWriter: RustWriter, + operationShape: OperationShape, + inputShape: StructureShape, + customizations: List, + ) { + val operationName = symbolProvider.toSymbol(operationShape).name + + // pub struct Operation { ... } + val fluentBuilderName = FluentClientGenerator.clientOperationFnName(operationShape, symbolProvider) + operationWriter.rustTemplate( """ /// Operation shape for `$operationName`. /// /// This is usually constructed for you using the the fluent builder returned by - /// [`$fluentBuilderName`](${docLink("crate::client::Client::$fluentBuilderName")}). + /// [`$fluentBuilderName`](#{fluentBuilder}). /// - /// See [`crate::client::fluent_builders::$operationName`] for more details about the operation. + /// `ParseStrictResponse` impl for `$operationName`. """, + "fluentBuilder" to operationShape.fluentBuilderType(codegenContext, symbolProvider), ) Attribute(derive(RuntimeType.Clone, RuntimeType.Default, RuntimeType.Debug)).render(operationWriter) operationWriter.rustBlock("pub struct $operationName") { write("_private: ()") } - operationWriter.implBlock(operationShape, symbolProvider) { - builderGenerator.renderConvenienceMethod(this) + operationWriter.implBlock(symbolProvider.toSymbol(operationShape)) { + BuilderGenerator.renderConvenienceMethod(this, symbolProvider, inputShape) rust("/// Creates a new `$operationName` operation.") rustBlock("pub fn new() -> Self") { 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 541af1ceb5..0ac4f727c3 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 @@ -8,6 +8,7 @@ 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.http.RequestBindingGenerator import software.amazon.smithy.rust.codegen.core.rustlang.Attribute import software.amazon.smithy.rust.codegen.core.rustlang.RustWriter @@ -54,7 +55,7 @@ open class MakeOperationGenerator( ?: codegenContext.serviceShape.id.getName(codegenContext.serviceShape) private val codegenScope = arrayOf( - "config" to RuntimeType.Config, + "config" to ClientRustModule.Config, "header_util" to RuntimeType.smithyHttp(runtimeConfig).resolve("header"), "http" to RuntimeType.Http, "HttpRequestBuilder" to RuntimeType.HttpRequestBuilder, @@ -87,6 +88,7 @@ open class MakeOperationGenerator( Attribute.AllowClippyLetAndReturn.render(implBlockWriter) // Allows builders that don’t consume the input borrow Attribute.AllowClippyNeedlessBorrow.render(implBlockWriter) + implBlockWriter.rustBlockTemplate( "$fnType $functionName($self, _config: &#{config}::Config) -> $returnType", *codegenScope, 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 476e67ef5a..5a1c4993b0 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 @@ -19,13 +19,12 @@ import software.amazon.smithy.protocoltests.traits.HttpRequestTestsTrait 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.core.rustlang.Attribute import software.amazon.smithy.rust.codegen.core.rustlang.Attribute.Companion.allow -import software.amazon.smithy.rust.codegen.core.rustlang.RustMetadata 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.Writable import software.amazon.smithy.rust.codegen.core.rustlang.escape import software.amazon.smithy.rust.codegen.core.rustlang.rust @@ -34,7 +33,6 @@ 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.error.errorSymbol 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 @@ -91,14 +89,13 @@ class ProtocolTestGenerator( if (allTests.isNotEmpty()) { val operationName = operationSymbol.name val testModuleName = "${operationName.toSnakeCase()}_request_test" - val moduleMeta = RustMetadata( - visibility = Visibility.PRIVATE, - additionalAttributes = listOf( - Attribute.CfgTest, - Attribute(allow("unreachable_code", "unused_variables")), - ), + val additionalAttributes = listOf( + Attribute(allow("unreachable_code", "unused_variables")), ) - writer.withInlineModule(RustModule.LeafModule(testModuleName, moduleMeta, inline = true)) { + writer.withInlineModule( + RustModule.inlineTests(testModuleName, additionalAttributes = additionalAttributes), + null, + ) { renderAllTestCases(allTests) } } @@ -175,12 +172,12 @@ class ProtocolTestGenerator( } ?: writable { } rustTemplate( """ - let builder = #{Config}::Config::builder().with_test_defaults().endpoint_resolver("https://example.com"); + let builder = #{config}::Config::builder().with_test_defaults().endpoint_resolver("https://example.com"); #{customParams} let config = builder.build(); """, - "Config" to RuntimeType.Config, + "config" to ClientRustModule.Config, "customParams" to customParams, ) writeInline("let input =") @@ -217,9 +214,9 @@ class ProtocolTestGenerator( checkQueryParams(this, httpRequestTestCase.queryParams) checkForbidQueryParams(this, httpRequestTestCase.forbidQueryParams) checkRequiredQueryParams(this, httpRequestTestCase.requireQueryParams) - checkHeaders(this, "&http_request.headers()", httpRequestTestCase.headers) - checkForbidHeaders(this, "&http_request.headers()", httpRequestTestCase.forbidHeaders) - checkRequiredHeaders(this, "&http_request.headers()", httpRequestTestCase.requireHeaders) + checkHeaders(this, "http_request.headers()", httpRequestTestCase.headers) + checkForbidHeaders(this, "http_request.headers()", httpRequestTestCase.forbidHeaders) + checkRequiredHeaders(this, "http_request.headers()", httpRequestTestCase.requireHeaders) if (protocolSupport.requestBodySerialization) { // "If no request body is defined, then no assertions are made about the body of the message." httpRequestTestCase.body.orNull()?.also { body -> @@ -253,10 +250,10 @@ class ProtocolTestGenerator( expectedShape: StructureShape, ) { if (!protocolSupport.responseDeserialization || ( - !protocolSupport.errorDeserialization && expectedShape.hasTrait( + !protocolSupport.errorDeserialization && expectedShape.hasTrait( ErrorTrait::class.java, ) - ) + ) ) { rust("/* test case disabled for this protocol (not yet supported) */") return @@ -296,49 +293,53 @@ class ProtocolTestGenerator( "parse_http_response" to RuntimeType.parseHttpResponse(codegenContext.runtimeConfig), ) if (expectedShape.hasTrait()) { - val errorSymbol = operationShape.errorSymbol(codegenContext.symbolProvider) + val errorSymbol = codegenContext.symbolProvider.symbolForOperationError(operationShape) val errorVariant = codegenContext.symbolProvider.toSymbol(expectedShape).name rust("""let parsed = parsed.expect_err("should be error response");""") - rustBlock("if let #TKind::$errorVariant(actual_error) = parsed.kind", errorSymbol) { - rustTemplate("#{AssertEq}(expected_output, actual_error);", *codegenScope) + rustBlock("if let #T::$errorVariant(parsed) = parsed", errorSymbol) { + compareMembers(expectedShape) } rustBlock("else") { rust("panic!(\"wrong variant: Got: {:?}. Expected: {:?}\", parsed, expected_output);") } } else { rust("let parsed = parsed.unwrap();") - outputShape.members().forEach { member -> - val memberName = codegenContext.symbolProvider.toMemberName(member) - if (member.isStreaming(codegenContext.model)) { - rustTemplate( - """ - #{AssertEq}( - parsed.$memberName.collect().await.unwrap().into_bytes(), - expected_output.$memberName.collect().await.unwrap().into_bytes() - ); - """, - *codegenScope, - ) - } else { - when (codegenContext.model.expectShape(member.target)) { - is DoubleShape, is FloatShape -> { - addUseImports( - RuntimeType.protocolTest(codegenContext.runtimeConfig, "FloatEquals").toSymbol(), - ) - rust( - """ - assert!(parsed.$memberName.float_equals(&expected_output.$memberName), - "Unexpected value for `$memberName` {:?} vs. {:?}", expected_output.$memberName, parsed.$memberName); - """, - ) - } - - else -> - rustTemplate( - """#{AssertEq}(parsed.$memberName, expected_output.$memberName, "Unexpected value for `$memberName`");""", - *codegenScope, - ) + compareMembers(outputShape) + } + } + + private fun RustWriter.compareMembers(shape: StructureShape) { + shape.members().forEach { member -> + val memberName = codegenContext.symbolProvider.toMemberName(member) + if (member.isStreaming(codegenContext.model)) { + rustTemplate( + """ + #{AssertEq}( + parsed.$memberName.collect().await.unwrap().into_bytes(), + expected_output.$memberName.collect().await.unwrap().into_bytes() + ); + """, + *codegenScope, + ) + } else { + when (codegenContext.model.expectShape(member.target)) { + is DoubleShape, is FloatShape -> { + addUseImports( + RuntimeType.protocolTest(codegenContext.runtimeConfig, "FloatEquals").toSymbol(), + ) + rust( + """ + assert!(parsed.$memberName.float_equals(&expected_output.$memberName), + "Unexpected value for `$memberName` {:?} vs. {:?}", expected_output.$memberName, parsed.$memberName); + """, + ) } + + else -> + rustTemplate( + """#{AssertEq}(parsed.$memberName, expected_output.$memberName, "Unexpected value for `$memberName`");""", + *codegenScope, + ) } } } @@ -359,7 +360,7 @@ class ProtocolTestGenerator( assertOk(rustWriter) { rustWriter.write( "#T(&body, ${ - rustWriter.escape(body).dq() + rustWriter.escape(body).dq() }, #T::from(${(mediaType ?: "unknown").dq()}))", RuntimeType.protocolTest(codegenContext.runtimeConfig, "validate_body"), RuntimeType.protocolTest(codegenContext.runtimeConfig, "MediaType"), 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 ecfea77ff5..a194dc98a2 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 @@ -7,16 +7,19 @@ package software.amazon.smithy.rust.codegen.client.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.AwsQueryCompatibleTrait import software.amazon.smithy.aws.traits.protocols.AwsQueryTrait import software.amazon.smithy.aws.traits.protocols.Ec2QueryTrait 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.core.smithy.CodegenContext import software.amazon.smithy.rust.codegen.core.smithy.generators.protocol.ProtocolSupport 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.AwsQueryCompatible import software.amazon.smithy.rust.codegen.core.smithy.protocols.AwsQueryProtocol import software.amazon.smithy.rust.codegen.core.smithy.protocols.Ec2QueryProtocol import software.amazon.smithy.rust.codegen.core.smithy.protocols.Protocol @@ -25,6 +28,7 @@ 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.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) { @@ -57,12 +61,20 @@ private val CLIENT_PROTOCOL_SUPPORT = ProtocolSupport( private class ClientAwsJsonFactory(private val version: AwsJsonVersion) : ProtocolGeneratorFactory { - override fun protocol(codegenContext: ClientCodegenContext): Protocol = AwsJson(codegenContext, version) + override fun protocol(codegenContext: ClientCodegenContext): Protocol = + if (compatibleWithAwsQuery(codegenContext.serviceShape, version)) { + AwsQueryCompatible(codegenContext, AwsJson(codegenContext, version)) + } else { + AwsJson(codegenContext, version) + } override fun buildProtocolGenerator(codegenContext: ClientCodegenContext): HttpBoundProtocolGenerator = HttpBoundProtocolGenerator(codegenContext, protocol(codegenContext)) override fun support(): ProtocolSupport = CLIENT_PROTOCOL_SUPPORT + + private fun compatibleWithAwsQuery(serviceShape: ServiceShape, version: AwsJsonVersion) = + serviceShape.hasTrait() && version == AwsJsonVersion.Json10 } private class ClientAwsQueryFactory : ProtocolGeneratorFactory { 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 0592d91cd9..18a2f0b32b 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 @@ -9,11 +9,12 @@ import software.amazon.smithy.codegen.core.Symbol 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.http.ResponseBindingGenerator import software.amazon.smithy.rust.codegen.client.smithy.generators.protocol.ClientProtocolGenerator import software.amazon.smithy.rust.codegen.client.smithy.generators.protocol.MakeOperationGenerator import software.amazon.smithy.rust.codegen.core.rustlang.Attribute -import software.amazon.smithy.rust.codegen.core.rustlang.RustModule +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.assignment @@ -23,20 +24,18 @@ 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.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.builderSymbol -import software.amazon.smithy.rust.codegen.core.smithy.generators.error.errorSymbol import software.amazon.smithy.rust.codegen.core.smithy.generators.protocol.ProtocolTraitImplGenerator import software.amazon.smithy.rust.codegen.core.smithy.generators.setterName 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.parse.StructuredDataParserGenerator import software.amazon.smithy.rust.codegen.core.smithy.transformers.operationErrors import software.amazon.smithy.rust.codegen.core.util.UNREACHABLE @@ -46,10 +45,9 @@ 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.core.util.outputShape -import software.amazon.smithy.rust.codegen.core.util.toSnakeCase class HttpBoundProtocolGenerator( - codegenContext: CodegenContext, + codegenContext: ClientCodegenContext, protocol: Protocol, ) : ClientProtocolGenerator( codegenContext, @@ -65,14 +63,14 @@ class HttpBoundProtocolGenerator( ) class HttpBoundProtocolTraitImplGenerator( - private val codegenContext: CodegenContext, + private val codegenContext: ClientCodegenContext, private val protocol: Protocol, ) : ProtocolTraitImplGenerator { private val symbolProvider = codegenContext.symbolProvider private val model = codegenContext.model private val runtimeConfig = codegenContext.runtimeConfig private val httpBindingResolver = protocol.httpBindingResolver - private val operationDeserModule = RustModule.private("operation_deser") + private val protocolFunctions = ProtocolFunctions(codegenContext) private val codegenScope = arrayOf( "ParseStrict" to RuntimeType.parseStrictResponse(runtimeConfig), @@ -80,7 +78,27 @@ class HttpBoundProtocolTraitImplGenerator( "http" to RuntimeType.Http, "operation" to RuntimeType.operationModule(runtimeConfig), "Bytes" to RuntimeType.Bytes, + "SdkBody" to RuntimeType.sdkBody(runtimeConfig), ) + private val orchestratorCodegenScope by lazy { + val interceptorContext = + CargoDependency.smithyRuntimeApi(runtimeConfig).toType().resolve("client::interceptors::context") + val orchestrator = + CargoDependency.smithyRuntimeApi(runtimeConfig).toType().resolve("client::orchestrator") + arrayOf( + "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"), + "SdkBody" to RuntimeType.sdkBody(runtimeConfig), + "SdkError" to RuntimeType.sdkError(runtimeConfig), + "TypedBox" to CargoDependency.smithyRuntimeApi(runtimeConfig).toType().resolve("type_erasure::TypedBox"), + "debug_span" to RuntimeType.Tracing.resolve("debug_span"), + "type_erase_result" to typeEraseResult(), + ) + } override fun generateTraitImpls( operationWriter: RustWriter, @@ -93,17 +111,136 @@ class HttpBoundProtocolTraitImplGenerator( // For streaming response bodies, we need to generate a different implementation of the parse traits. // These will first offer the streaming input to the parser & potentially read the body into memory // if an error occurred or if the streaming parser indicates that it needs the full data to proceed. - if (operationShape.outputShape(model).hasStreamingMember(model)) { - with(operationWriter) { - renderStreamingTraits(operationName, outputSymbol, operationShape, customizations) - } + val streaming = operationShape.outputShape(model).hasStreamingMember(model) + if (streaming) { + operationWriter.renderStreamingTraits(operationName, outputSymbol, operationShape, customizations) } else { - with(operationWriter) { - renderNonStreamingTraits(operationName, outputSymbol, operationShape, customizations) - } + operationWriter.renderNonStreamingTraits(operationName, outputSymbol, operationShape, customizations) } + + if (codegenContext.settings.codegenConfig.enableNewSmithyRuntime) { + operationWriter.renderRuntimeTraits(operationName, outputSymbol, operationShape, customizations, streaming) + } + } + + private fun typeEraseResult(): RuntimeType = ProtocolFunctions.crossOperationFn("type_erase_result") { fnName -> + rustTemplate( + """ + pub(crate) fn $fnName(result: Result) -> Result<#{Output}, #{Error}> + where + O: Send + Sync + 'static, + E: Send + Sync + 'static, + { + result.map(|output| #{TypedBox}::new(output).erase()) + .map_err(|error| #{TypedBox}::new(error).erase()) + } + """, + *orchestratorCodegenScope, + ) + } + + private fun RustWriter.renderRuntimeTraits( + operationName: String?, + outputSymbol: Symbol, + operationShape: OperationShape, + customizations: List, + streaming: Boolean, + ) { + rustTemplate( + """ + impl #{ResponseDeserializer} for $operationName { + #{deserialize_streaming} + + fn deserialize_nonstreaming(&self, response: &#{HttpResponse}) -> #{OutputOrError} { + #{deserialize_nonstreaming} + } + } + """, + *orchestratorCodegenScope, + "O" to outputSymbol, + "E" to symbolProvider.symbolForOperationError(operationShape), + "deserialize_streaming" to writable { + if (streaming) { + deserializeStreaming(operationShape, customizations) + } + }, + "deserialize_nonstreaming" to writable { + when (streaming) { + true -> deserializeStreamingError(operationShape, customizations) + else -> deserializeNonStreaming(operationShape, customizations) + } + }, + ) + } + + private fun RustWriter.deserializeStreaming( + operationShape: OperationShape, + customizations: List, + ) { + val successCode = httpBindingResolver.httpTrait(operationShape).code + rustTemplate( + """ + 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; + } + Some(#{type_erase_result}(#{parse_streaming_response}(response))) + } + """, + *orchestratorCodegenScope, + "parse_streaming_response" to parseStreamingResponse(operationShape, customizations), + "BeforeParseResponse" to writable { + writeCustomizations(customizations, OperationSection.BeforeParseResponse(customizations, "response")) + }, + ) + } + + private fun RustWriter.deserializeStreamingError( + operationShape: OperationShape, + customizations: List, + ) { + rustTemplate( + """ + // For streaming operations, we only hit this case if its an error + let body = response.body().bytes().expect("body loaded"); + #{type_erase_result}(#{parse_error}(response.status().as_u16(), response.headers(), body)) + """, + *orchestratorCodegenScope, + "parse_error" to parseError(operationShape, customizations), + ) } + private fun RustWriter.deserializeNonStreaming( + operationShape: OperationShape, + customizations: List, + ) { + val successCode = httpBindingResolver.httpTrait(operationShape).code + rustTemplate( + """ + let (success, status) = (response.status().is_success(), response.status().as_u16()); + let headers = response.headers(); + let body = response.body().bytes().expect("body loaded"); + #{BeforeParseResponse} + let parse_result = if !success && status != $successCode { + #{parse_error}(status, headers, body) + } else { + #{parse_response}(status, headers, body) + }; + #{type_erase_result}(parse_result) + """, + *orchestratorCodegenScope, + "parse_error" to parseError(operationShape, customizations), + "parse_response" to parseResponse(operationShape, customizations), + "BeforeParseResponse" to writable { + writeCustomizations(customizations, OperationSection.BeforeParseResponse(customizations, "response")) + }, + ) + } + + // TODO(enableNewSmithyRuntime): Delete this when cleaning up `enableNewSmithyRuntime` private fun RustWriter.renderNonStreamingTraits( operationName: String?, outputSymbol: Symbol, @@ -111,26 +248,37 @@ class HttpBoundProtocolTraitImplGenerator( customizations: List, ) { val successCode = httpBindingResolver.httpTrait(operationShape).code + val localScope = arrayOf( + "O" to outputSymbol, + "E" to symbolProvider.symbolForOperationError(operationShape), + "parse_error" to parseError(operationShape, customizations), + "parse_response" to parseResponse(operationShape, customizations), + "BeforeParseResponse" to writable { + writeCustomizations(customizations, OperationSection.BeforeParseResponse(customizations, "response")) + }, + ) rustTemplate( """ impl #{ParseStrict} for $operationName { type Output = std::result::Result<#{O}, #{E}>; fn parse(&self, response: &#{http}::Response<#{Bytes}>) -> Self::Output { - if !response.status().is_success() && response.status().as_u16() != $successCode { - #{parse_error}(response) + let (success, status) = (response.status().is_success(), response.status().as_u16()); + let headers = response.headers(); + let body = response.body().as_ref(); + #{BeforeParseResponse} + if !success && status != $successCode { + #{parse_error}(status, headers, body) } else { - #{parse_response}(response) + #{parse_response}(status, headers, body) } } }""", *codegenScope, - "O" to outputSymbol, - "E" to operationShape.errorSymbol(symbolProvider), - "parse_error" to parseError(operationShape), - "parse_response" to parseResponse(operationShape, customizations), + *localScope, ) } + // TODO(enableNewSmithyRuntime): Delete this when cleaning up `enableNewSmithyRuntime` private fun RustWriter.renderStreamingTraits( operationName: String, outputSymbol: Symbol, @@ -143,6 +291,7 @@ class HttpBoundProtocolTraitImplGenerator( impl #{ParseResponse} for $operationName { type Output = std::result::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; @@ -151,36 +300,49 @@ class HttpBoundProtocolTraitImplGenerator( } fn parse_loaded(&self, response: &#{http}::Response<#{Bytes}>) -> Self::Output { // if streaming, we only hit this case if its an error - #{parse_error}(response) + #{parse_error}(response.status().as_u16(), response.headers(), response.body().as_ref()) } } """, "O" to outputSymbol, - "E" to operationShape.errorSymbol(symbolProvider), - "parse_streaming_response" to parseStreamingResponse(operationShape, customizations), - "parse_error" to parseError(operationShape), + "E" to symbolProvider.symbolForOperationError(operationShape), + "parse_streaming_response" to parseStreamingResponseNoRt(operationShape, customizations), + "parse_error" to parseError(operationShape, customizations), + "BeforeParseResponse" to writable { + writeCustomizations(customizations, OperationSection.BeforeParseResponse(customizations, "response")) + }, *codegenScope, ) } - private fun parseError(operationShape: OperationShape): RuntimeType { - val fnName = "parse_${operationShape.id.name.toSnakeCase()}_error" + private fun parseError(operationShape: OperationShape, customizations: List): RuntimeType { val outputShape = operationShape.outputShape(model) val outputSymbol = symbolProvider.toSymbol(outputShape) - val errorSymbol = operationShape.errorSymbol(symbolProvider) - return RuntimeType.forInlineFun(fnName, operationDeserModule) { + val errorSymbol = symbolProvider.symbolForOperationError(operationShape) + return protocolFunctions.deserializeFn(operationShape, fnNameSuffix = "http_error") { fnName -> Attribute.AllowClippyUnnecessaryWraps.render(this) rustBlockTemplate( - "pub fn $fnName(response: &#{http}::Response<#{Bytes}>) -> std::result::Result<#{O}, #{E}>", + "pub fn $fnName(_response_status: u16, _response_headers: &#{http}::header::HeaderMap, _response_body: &[u8]) -> std::result::Result<#{O}, #{E}>", *codegenScope, "O" to outputSymbol, "E" to errorSymbol, ) { + Attribute.AllowUnusedMut.render(this) rust( - "let generic = #T(response).map_err(#T::unhandled)?;", - protocol.parseHttpGenericError(operationShape), + "let mut generic_builder = #T(_response_status, _response_headers, _response_body).map_err(#T::unhandled)?;", + protocol.parseHttpErrorMetadata(operationShape), errorSymbol, ) + writeCustomizations( + customizations, + OperationSection.PopulateErrorMetadataExtras( + customizations, + "generic_builder", + "_response_status", + "_response_headers", + ), + ) + rust("let generic = generic_builder.build();") if (operationShape.operationErrors(model).isNotEmpty()) { rustTemplate( """ @@ -200,8 +362,8 @@ class HttpBoundProtocolTraitImplGenerator( val variantName = symbolProvider.toSymbol(model.expectShape(error.id)).name val errorCode = httpBindingResolver.errorCode(errorShape).dq() withBlock( - "$errorCode => #1T { meta: generic, kind: #1TKind::$variantName({", - "})},", + "$errorCode => #1T::$variantName({", + "}),", errorSymbol, ) { Attribute.AllowUnusedMut.render(this) @@ -212,7 +374,14 @@ class HttpBoundProtocolTraitImplGenerator( errorShape, httpBindingResolver.errorResponseBindings(errorShape), errorSymbol, - listOf(), + listOf(object : OperationCustomization() { + override fun section(section: OperationSection): Writable = writable { + if (section is OperationSection.MutateOutput) { + rust("let output = output.meta(generic);") + } + } + }, + ), ) } } @@ -238,11 +407,47 @@ class HttpBoundProtocolTraitImplGenerator( } private fun parseStreamingResponse(operationShape: OperationShape, customizations: List): RuntimeType { - val fnName = "parse_${operationShape.id.name.toSnakeCase()}" val outputShape = operationShape.outputShape(model) val outputSymbol = symbolProvider.toSymbol(outputShape) - val errorSymbol = operationShape.errorSymbol(symbolProvider) - return RuntimeType.forInlineFun(fnName, operationDeserModule) { + val errorSymbol = symbolProvider.symbolForOperationError(operationShape) + return protocolFunctions.deserializeFn(operationShape, fnNameSuffix = "http_response") { fnName -> + Attribute.AllowClippyUnnecessaryWraps.render(this) + rustBlockTemplate( + "pub fn $fnName(response: &mut #{http}::Response<#{SdkBody}>) -> std::result::Result<#{O}, #{E}>", + *codegenScope, + "O" to outputSymbol, + "E" to errorSymbol, + ) { + rustTemplate( + """ + let mut _response_body = #{SdkBody}::taken(); + std::mem::swap(&mut _response_body, response.body_mut()); + let _response_body = &mut _response_body; + + let _response_status = response.status().as_u16(); + let _response_headers = response.headers(); + """, + *codegenScope, + ) + withBlock("Ok({", "})") { + renderShapeParser( + operationShape, + outputShape, + httpBindingResolver.responseBindings(operationShape), + errorSymbol, + customizations, + ) + } + } + } + } + + // TODO(enableNewSmithyRuntime): Delete this when cleaning up `enableNewSmithyRuntime` + private fun parseStreamingResponseNoRt(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 = "http_response_") { fnName -> Attribute.AllowClippyUnnecessaryWraps.render(this) rustBlockTemplate( "pub fn $fnName(op_response: &mut #{operation}::Response) -> std::result::Result<#{O}, #{E}>", @@ -253,6 +458,17 @@ class HttpBoundProtocolTraitImplGenerator( // Not all implementations will use the property bag, but some will Attribute.AllowUnusedVariables.render(this) rust("let (response, properties) = op_response.parts_mut();") + rustTemplate( + """ + let mut _response_body = #{SdkBody}::taken(); + std::mem::swap(&mut _response_body, response.body_mut()); + let _response_body = &mut _response_body; + + let _response_status = response.status().as_u16(); + let _response_headers = response.headers(); + """, + *codegenScope, + ) withBlock("Ok({", "})") { renderShapeParser( operationShape, @@ -267,14 +483,13 @@ class HttpBoundProtocolTraitImplGenerator( } private fun parseResponse(operationShape: OperationShape, customizations: List): RuntimeType { - val fnName = "parse_${operationShape.id.name.toSnakeCase()}_response" val outputShape = operationShape.outputShape(model) val outputSymbol = symbolProvider.toSymbol(outputShape) - val errorSymbol = operationShape.errorSymbol(symbolProvider) - return RuntimeType.forInlineFun(fnName, operationDeserModule) { + val errorSymbol = symbolProvider.symbolForOperationError(operationShape) + return protocolFunctions.deserializeFn(operationShape, fnNameSuffix = "http_response") { fnName -> Attribute.AllowClippyUnnecessaryWraps.render(this) rustBlockTemplate( - "pub fn $fnName(response: &#{http}::Response<#{Bytes}>) -> std::result::Result<#{O}, #{E}>", + "pub fn $fnName(_response_status: u16, _response_headers: &#{http}::header::HeaderMap, _response_body: &[u8]) -> std::result::Result<#{O}, #{E}>", *codegenScope, "O" to outputSymbol, "E" to errorSymbol, @@ -296,19 +511,17 @@ class HttpBoundProtocolTraitImplGenerator( operationShape: OperationShape, outputShape: StructureShape, bindings: List, - errorSymbol: RuntimeType, + errorSymbol: Symbol, customizations: List, ) { val httpBindingGenerator = ResponseBindingGenerator(protocol, codegenContext, operationShape) val structuredDataParser = protocol.structuredDataParser(operationShape) Attribute.AllowUnusedMut.render(this) - rust("let mut output = #T::default();", outputShape.builderSymbol(symbolProvider)) - // avoid non-usage warnings for response - rust("let _ = response;") + rust("let mut output = #T::default();", symbolProvider.symbolForBuilder(outputShape)) if (outputShape.id == operationShape.output.get()) { structuredDataParser.operationParser(operationShape)?.also { parser -> rust( - "output = #T(response.body().as_ref(), output).map_err(#T::unhandled)?;", + "output = #T(_response_body, output).map_err(#T::unhandled)?;", parser, errorSymbol, ) @@ -317,7 +530,7 @@ class HttpBoundProtocolTraitImplGenerator( check(outputShape.hasTrait()) { "should only be called on outputs or errors $outputShape" } structuredDataParser.errorParser(outputShape)?.also { parser -> rust( - "output = #T(response.body().as_ref(), output).map_err(#T::unhandled)?;", + "output = #T(_response_body, output).map_err(#T::unhandled)?;", parser, errorSymbol, ) } @@ -334,9 +547,14 @@ class HttpBoundProtocolTraitImplGenerator( val err = if (BuilderGenerator.hasFallibleBuilder(outputShape, symbolProvider)) { ".map_err(${format(errorSymbol)}::unhandled)?" - } else "" + } else { + "" + } - writeCustomizations(customizations, OperationSection.MutateOutput(customizations, operationShape)) + writeCustomizations( + customizations, + OperationSection.MutateOutput(customizations, operationShape, "_response_headers"), + ) rust("output.build()$err") } @@ -352,14 +570,14 @@ class HttpBoundProtocolTraitImplGenerator( httpBindingGenerator: ResponseBindingGenerator, structuredDataParser: StructuredDataParserGenerator, ): Writable? { - val errorSymbol = operationShape.errorSymbol(symbolProvider) + val errorSymbol = symbolProvider.symbolForOperationError(operationShape) val member = binding.member return when (binding.location) { HttpLocation.HEADER -> writable { val fnName = httpBindingGenerator.generateDeserializeHeaderFn(binding) rust( """ - #T(response.headers()) + #T(_response_headers) .map_err(|_|#T::unhandled("Failed to parse ${member.memberName} from header `${binding.locationName}"))? """, fnName, errorSymbol, @@ -379,20 +597,20 @@ class HttpBoundProtocolTraitImplGenerator( payloadParser = payloadParser, ) return if (binding.member.isStreaming(model)) { - writable { rust("Some(#T(response.body_mut())?)", deserializer) } + writable { rust("Some(#T(_response_body)?)", deserializer) } } else { - writable { rust("#T(response.body().as_ref())?", deserializer) } + writable { rust("#T(_response_body)?", deserializer) } } } HttpLocation.RESPONSE_CODE -> writable { - rust("Some(response.status().as_u16() as _)") + rust("Some(_response_status as _)") } HttpLocation.PREFIX_HEADERS -> { val sym = httpBindingGenerator.generateDeserializePrefixHeaderFn(binding) writable { rustTemplate( """ - #{deser}(response.headers()) + #{deser}(_response_headers) .map_err(|_| #{err}::unhandled("Failed to parse ${member.memberName} from prefix header `${binding.locationName}") )? diff --git a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/transformers/AddErrorMessage.kt b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/transformers/AddErrorMessage.kt index 7e69ce6995..02d61d9905 100644 --- a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/transformers/AddErrorMessage.kt +++ b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/transformers/AddErrorMessage.kt @@ -19,7 +19,7 @@ import java.util.logging.Logger * * Not all errors are modeled with an error message field. However, in many cases, the server can still send an error. * If an error, specifically, a structure shape with the error trait does not have a member `message` or `Message`, - * this transformer will add a `message` member targeting a string. + * this transformer will add a `Message` member targeting a string. * * This ensures that we always generate a modeled error message field enabling end users to easily extract the error * message when present. @@ -37,7 +37,7 @@ object AddErrorMessage { val addMessageField = shape.hasTrait() && shape is StructureShape && shape.errorMessageMember() == null if (addMessageField && shape is StructureShape) { logger.info("Adding message field to ${shape.id}") - shape.toBuilder().addMember("message", ShapeId.from("smithy.api#String")).build() + shape.toBuilder().addMember("Message", ShapeId.from("smithy.api#String")).build() } else { shape } diff --git a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/testutil/ClientCodegenIntegrationTest.kt b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/testutil/ClientCodegenIntegrationTest.kt new file mode 100644 index 0000000000..138f43cd26 --- /dev/null +++ b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/testutil/ClientCodegenIntegrationTest.kt @@ -0,0 +1,56 @@ +/* + * 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.build.PluginContext +import software.amazon.smithy.build.SmithyBuildPlugin +import software.amazon.smithy.model.Model +import software.amazon.smithy.rust.codegen.client.smithy.ClientCodegenContext +import software.amazon.smithy.rust.codegen.client.smithy.RustClientCodegenPlugin +import software.amazon.smithy.rust.codegen.client.smithy.customize.ClientCodegenDecorator +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.codegenIntegrationTest +import java.nio.file.Path + +fun clientIntegrationTest( + model: Model, + params: IntegrationTestParams = IntegrationTestParams(), + additionalDecorators: List = listOf(), + test: (ClientCodegenContext, RustCrate) -> Unit = { _, _ -> }, +): Path { + fun invokeRustCodegenPlugin(ctx: PluginContext) { + val codegenDecorator = object : ClientCodegenDecorator { + override val name: String = "Add tests" + override val order: Byte = 0 + + override fun classpathDiscoverable(): Boolean = false + + override fun extras(codegenContext: ClientCodegenContext, rustCrate: RustCrate) { + test(codegenContext, rustCrate) + } + } + RustClientCodegenPlugin().executeWithDecorator(ctx, codegenDecorator, *additionalDecorators.toTypedArray()) + } + return codegenIntegrationTest(model, params, invokePlugin = ::invokeRustCodegenPlugin) +} + +/** + * A `SmithyBuildPlugin` that accepts an additional decorator. + * + * This exists to allow tests to easily customize the _real_ build without needing to list out customizations + * or attempt to manually discover them from the path. + */ +abstract class ClientDecoratableBuildPlugin : SmithyBuildPlugin { + abstract fun executeWithDecorator( + context: PluginContext, + vararg decorator: ClientCodegenDecorator, + ) + + override fun execute(context: PluginContext) { + executeWithDecorator(context) + } +} diff --git a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/testutil/CodegenIntegrationTest.kt b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/testutil/CodegenIntegrationTest.kt deleted file mode 100644 index c764e29059..0000000000 --- a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/testutil/CodegenIntegrationTest.kt +++ /dev/null @@ -1,103 +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.testutil - -import software.amazon.smithy.build.PluginContext -import software.amazon.smithy.build.SmithyBuildPlugin -import software.amazon.smithy.model.Model -import software.amazon.smithy.model.node.ObjectNode -import software.amazon.smithy.rust.codegen.client.smithy.ClientCodegenContext -import software.amazon.smithy.rust.codegen.client.smithy.RustClientCodegenPlugin -import software.amazon.smithy.rust.codegen.client.smithy.customize.ClientCodegenDecorator -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.testutil.generatePluginContext -import software.amazon.smithy.rust.codegen.core.testutil.printGeneratedFiles -import software.amazon.smithy.rust.codegen.core.util.runCommand -import java.io.File -import java.nio.file.Path - -/** - * Run cargo test on a true, end-to-end, codegen product of a given model. - * - * For test purposes, additional codegen decorators can also be composed. - */ -fun clientIntegrationTest( - model: Model, - additionalDecorators: List = listOf(), - addModuleToEventStreamAllowList: Boolean = false, - service: String? = null, - runtimeConfig: RuntimeConfig? = null, - additionalSettings: ObjectNode = ObjectNode.builder().build(), - command: ((Path) -> Unit)? = null, - test: (ClientCodegenContext, RustCrate) -> Unit = { _, _ -> }, -): Path { - return codegenIntegrationTest( - model, - RustClientCodegenPlugin(), - additionalDecorators, - addModuleToEventStreamAllowList = addModuleToEventStreamAllowList, - service = service, - runtimeConfig = runtimeConfig, - additionalSettings = additionalSettings, - test = test, - command = command, - ) -} - -/** - * A Smithy BuildPlugin that accepts an additional decorator - * - * This exists to allow tests to easily customize the _real_ build without needing to list out customizations - * or attempt to manually discover them from the path - */ -abstract class DecoratableBuildPlugin : SmithyBuildPlugin { - abstract fun executeWithDecorator( - context: PluginContext, - vararg decorator: ClientCodegenDecorator, - ) - - override fun execute(context: PluginContext) { - executeWithDecorator(context) - } -} - -// TODO(https://github.com/awslabs/smithy-rs/issues/1864): move to core once CodegenDecorator is in core -private fun codegenIntegrationTest( - model: Model, - buildPlugin: DecoratableBuildPlugin, - additionalDecorators: List, - additionalSettings: ObjectNode = ObjectNode.builder().build(), - addModuleToEventStreamAllowList: Boolean = false, - service: String? = null, - runtimeConfig: RuntimeConfig? = null, - overrideTestDir: File? = null, test: (ClientCodegenContext, RustCrate) -> Unit, - command: ((Path) -> Unit)? = null, -): Path { - val (ctx, testDir) = generatePluginContext( - model, - additionalSettings, - addModuleToEventStreamAllowList, - service, - runtimeConfig, - overrideTestDir, - ) - - val codegenDecorator = object : ClientCodegenDecorator { - override val name: String = "Add tests" - override val order: Byte = 0 - - override fun classpathDiscoverable(): Boolean = false - - override fun extras(codegenContext: ClientCodegenContext, rustCrate: RustCrate) { - test(codegenContext, rustCrate) - } - } - buildPlugin.executeWithDecorator(ctx, codegenDecorator, *additionalDecorators.toTypedArray()) - ctx.fileManifest.printGeneratedFiles() - command?.invoke(testDir) ?: "cargo test".runCommand(testDir, environment = mapOf("RUSTFLAGS" to "-D warnings")) - return testDir -} 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 da2362a195..cb06688225 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,10 +5,10 @@ package software.amazon.smithy.rust.codegen.client.testutil +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.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.writable @@ -74,7 +74,7 @@ fun validateConfigCustomizations( fun stubConfigProject(customization: ConfigCustomization, project: TestWriterDelegator): TestWriterDelegator { val customizations = listOf(stubConfigCustomization("a")) + customization + stubConfigCustomization("b") val generator = ServiceConfigGenerator(customizations = customizations.toList()) - project.withModule(RustModule.Config) { + 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 1b3f813959..8cc56a8350 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 @@ -6,22 +6,25 @@ package software.amazon.smithy.rust.codegen.client.testutil import software.amazon.smithy.model.Model +import software.amazon.smithy.model.knowledge.NullableIndex import software.amazon.smithy.model.node.ObjectNode import software.amazon.smithy.model.shapes.ServiceShape import software.amazon.smithy.model.shapes.ShapeId import software.amazon.smithy.rust.codegen.client.smithy.ClientCodegenConfig +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.OldModuleSchemeClientModuleProvider import software.amazon.smithy.rust.codegen.client.smithy.RustClientCodegenPlugin -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.CoreRustSettings +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 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.testutil.TestModuleDocProvider import software.amazon.smithy.rust.codegen.core.testutil.TestRuntimeConfig -import software.amazon.smithy.rust.codegen.core.testutil.TestSymbolVisitorConfig -import software.amazon.smithy.rust.codegen.core.testutil.testRustSettings +import software.amazon.smithy.rust.codegen.core.testutil.TestWriterDelegator -fun clientTestRustSettings( +fun testClientRustSettings( service: ShapeId = ShapeId.from("notrelevant#notrelevant"), moduleName: String = "test-module", moduleVersion: String = "0.0.1", @@ -47,25 +50,48 @@ fun clientTestRustSettings( customizationConfig, ) +val TestClientRustSymbolProviderConfig = RustSymbolProviderConfig( + runtimeConfig = TestRuntimeConfig, + renameExceptions = true, + nullabilityCheckMode = NullableIndex.CheckMode.CLIENT_ZERO_VALUE_V1, + moduleProvider = OldModuleSchemeClientModuleProvider, +) + +private class ClientTestCodegenDecorator : ClientCodegenDecorator { + override val name = "test" + override val order: Byte = 0 +} + fun testSymbolProvider(model: Model, serviceShape: ServiceShape? = null): RustSymbolProvider = RustClientCodegenPlugin.baseSymbolProvider( + testClientRustSettings(), model, serviceShape ?: ServiceShape.builder().version("test").id("test#Service").build(), - TestSymbolVisitorConfig, + TestClientRustSymbolProviderConfig, + ClientTestCodegenDecorator(), ) -fun testCodegenContext( +fun testClientCodegenContext( model: Model, + symbolProvider: RustSymbolProvider? = null, serviceShape: ServiceShape? = null, - settings: CoreRustSettings = testRustSettings(), - codegenTarget: CodegenTarget = CodegenTarget.CLIENT, -): CodegenContext = CodegenContext( + settings: ClientRustSettings = testClientRustSettings(), + rootDecorator: ClientCodegenDecorator? = null, +): ClientCodegenContext = ClientCodegenContext( model, - testSymbolProvider(model), + symbolProvider ?: testSymbolProvider(model), + TestModuleDocProvider, serviceShape ?: model.serviceShapes.firstOrNull() ?: ServiceShape.builder().version("test").id("test#Service").build(), ShapeId.from("test#Protocol"), settings, - codegenTarget, + rootDecorator ?: CombinedClientCodegenDecorator(emptyList()), ) + +fun TestWriterDelegator.clientRustSettings() = + testClientRustSettings( + service = ShapeId.from("fake#Fake"), + moduleName = "test_${baseDir.toFile().nameWithoutExtension}", + codegenConfig = codegenConfig as ClientCodegenConfig, + ) 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/customizations/ApiKeyAuthDecoratorTest.kt new file mode 100644 index 0000000000..b44e427777 --- /dev/null +++ b/codegen-client/src/test/kotlin/software/amazon/smithy/rust/codegen/client/customizations/ApiKeyAuthDecoratorTest.kt @@ -0,0 +1,175 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +package software.amazon.smithy.rust.codegen.client.customizations + +import org.junit.jupiter.api.Test +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 + +internal class ApiKeyAuthDecoratorTest { + private val modelQuery = """ + 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() + + @Test + fun `set an api key in query parameter`() { + val testDir = clientIntegrationTest( + modelQuery, + // just run integration tests + IntegrationTestParams(command = { "cargo test --test *".runWithWarnings(it) }), + ) { clientCodegenContext, rustCrate -> + rustCrate.integrationTest("api_key_present_in_property_bag") { + val moduleName = clientCodegenContext.moduleUseName() + Attribute.TokioTest.render(this) + rust( + """ + async fn api_key_present_in_property_bag() { + use aws_smithy_http_auth::api_key::AuthApiKey; + let api_key_value = "some-api-key"; + let conf = $moduleName::Config::builder() + .api_key(AuthApiKey::new(api_key_value)) + .build(); + let operation = $moduleName::operation::some_operation::SomeOperationInput::builder() + .build() + .expect("input is valid") + .make_operation(&conf) + .await + .expect("valid operation"); + let props = operation.properties(); + let api_key_config = props.get::().expect("api key in the bag"); + assert_eq!( + api_key_config, + &AuthApiKey::new(api_key_value), + ); + } + """, + ) + } + + rustCrate.integrationTest("api_key_auth_is_set_in_query") { + val moduleName = clientCodegenContext.moduleUseName() + Attribute.TokioTest.render(this) + rust( + """ + async fn api_key_auth_is_set_in_query() { + use aws_smithy_http_auth::api_key::AuthApiKey; + let api_key_value = "some-api-key"; + let conf = $moduleName::Config::builder() + .api_key(AuthApiKey::new(api_key_value)) + .build(); + let operation = $moduleName::operation::some_operation::SomeOperationInput::builder() + .build() + .expect("input is valid") + .make_operation(&conf) + .await + .expect("valid operation"); + assert_eq!( + operation.request().uri().query(), + Some("api_key=some-api-key"), + ); + } + """, + ) + } + } + "cargo clippy".runWithWarnings(testDir) + } + + private val modelHeader = """ + 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() + + @Test + fun `set an api key in http header`() { + val testDir = clientIntegrationTest( + modelHeader, + // just run integration tests + IntegrationTestParams(command = { "cargo test --test *".runWithWarnings(it) }), + ) { clientCodegenContext, rustCrate -> + rustCrate.integrationTest("api_key_auth_is_set_in_http_header") { + val moduleName = clientCodegenContext.moduleUseName() + Attribute.TokioTest.render(this) + rust( + """ + async fn api_key_auth_is_set_in_http_header() { + use aws_smithy_http_auth::api_key::AuthApiKey; + let api_key_value = "some-api-key"; + let conf = $moduleName::Config::builder() + .api_key(AuthApiKey::new(api_key_value)) + .build(); + let operation = $moduleName::operation::some_operation::SomeOperationInput::builder() + .build() + .expect("input is valid") + .make_operation(&conf) + .await + .expect("valid operation"); + let props = operation.properties(); + let api_key_config = props.get::().expect("api key in the bag"); + assert_eq!( + api_key_config, + &AuthApiKey::new(api_key_value), + ); + assert_eq!( + operation.request().headers().contains_key("authorization"), + true, + ); + } + """, + ) + } + } + "cargo clippy".runWithWarnings(testDir) + } +} 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/customizations/HttpVersionListGeneratorTest.kt index 0c57ba622a..7536629578 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/customizations/HttpVersionListGeneratorTest.kt @@ -19,6 +19,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.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 @@ -61,7 +62,7 @@ internal class HttpVersionListGeneratorTest { """ async fn test_http_version_list_defaults() { let conf = $moduleName::Config::builder().build(); - let op = $moduleName::operation::SayHello::builder() + let op = $moduleName::operation::say_hello::SayHelloInput::builder() .greeting("hello") .build().expect("valid operation") .make_operation(&conf).await.expect("hello is a valid prefix"); @@ -112,7 +113,7 @@ internal class HttpVersionListGeneratorTest { """ async fn test_http_version_list_defaults() { let conf = $moduleName::Config::builder().build(); - let op = $moduleName::operation::SayHello::builder() + let op = $moduleName::operation::say_hello::SayHelloInput::builder() .greeting("hello") .build().expect("valid operation") .make_operation(&conf).await.expect("hello is a valid prefix"); @@ -170,8 +171,8 @@ internal class HttpVersionListGeneratorTest { clientIntegrationTest( model, - listOf(FakeSigningDecorator()), - addModuleToEventStreamAllowList = true, + IntegrationTestParams(addModuleToEventStreamAllowList = true), + additionalDecorators = listOf(FakeSigningDecorator()), ) { clientCodegenContext, rustCrate -> val moduleName = clientCodegenContext.moduleUseName() rustCrate.integrationTest("validate_eventstream_http") { @@ -180,7 +181,7 @@ internal class HttpVersionListGeneratorTest { """ async fn test_http_version_list_defaults() { let conf = $moduleName::Config::builder().build(); - let op = $moduleName::operation::SayHello::builder() + let op = $moduleName::operation::say_hello::SayHelloInput::builder() .build().expect("valid operation") .make_operation(&conf).await.unwrap(); let properties = op.properties(); @@ -204,7 +205,9 @@ class FakeSigningDecorator : ClientCodegenDecorator { codegenContext: ClientCodegenContext, baseCustomizations: List, ): List { - return baseCustomizations.filterNot { it is EventStreamSigningConfig } + FakeSigningConfig(codegenContext.runtimeConfig) + return baseCustomizations.filterNot { + it is EventStreamSigningConfig + } + FakeSigningConfig(codegenContext.runtimeConfig) } } 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/customizations/ResiliencyConfigCustomizationTest.kt index 2d2cf76916..612bf8e333 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/customizations/ResiliencyConfigCustomizationTest.kt @@ -6,16 +6,17 @@ package software.amazon.smithy.rust.codegen.client.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.testutil.clientRustSettings import software.amazon.smithy.rust.codegen.client.testutil.stubConfigProject -import software.amazon.smithy.rust.codegen.client.testutil.testCodegenContext +import software.amazon.smithy.rust.codegen.client.testutil.testClientCodegenContext 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.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.rustSettings internal class ResiliencyConfigCustomizationTest { private val baseModel = """ @@ -36,9 +37,9 @@ internal class ResiliencyConfigCustomizationTest { @Test fun `generates a valid config`() { - val model = RecursiveShapeBoxer.transform(OperationNormalizer.transform(baseModel)) - val project = TestWorkspace.testProject() - val codegenContext = testCodegenContext(model, settings = project.rustSettings()) + val model = RecursiveShapeBoxer().transform(OperationNormalizer.transform(baseModel)) + val project = TestWorkspace.testProject(model, ClientCodegenConfig()) + val codegenContext = testClientCodegenContext(model, settings = project.clientRustSettings()) stubConfigProject(ResiliencyConfigCustomization(codegenContext), project) ResiliencyReExportCustomization(codegenContext.runtimeConfig).extras(project) diff --git a/codegen-client/src/test/kotlin/software/amazon/smithy/rust/codegen/client/endpoint/ClientContextParamsDecoratorTest.kt b/codegen-client/src/test/kotlin/software/amazon/smithy/rust/codegen/client/endpoint/ClientContextConfigCustomizationTest.kt similarity index 94% rename from codegen-client/src/test/kotlin/software/amazon/smithy/rust/codegen/client/endpoint/ClientContextParamsDecoratorTest.kt rename to codegen-client/src/test/kotlin/software/amazon/smithy/rust/codegen/client/endpoint/ClientContextConfigCustomizationTest.kt index cb96490209..8c3a4122e9 100644 --- a/codegen-client/src/test/kotlin/software/amazon/smithy/rust/codegen/client/endpoint/ClientContextParamsDecoratorTest.kt +++ b/codegen-client/src/test/kotlin/software/amazon/smithy/rust/codegen/client/endpoint/ClientContextConfigCustomizationTest.kt @@ -7,14 +7,14 @@ 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.testCodegenContext +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 ClientContextParamsDecoratorTest { +class ClientContextConfigCustomizationTest { val model = """ namespace test use smithy.rules#clientContextParams @@ -52,6 +52,6 @@ class ClientContextParamsDecoratorTest { """, ) } - validateConfigCustomizations(ClientContextConfigCustomization(testCodegenContext(model)), project) + validateConfigCustomizations(ClientContextConfigCustomization(testClientCodegenContext(model)), project) } } 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/endpoint/EndpointResolverGeneratorTest.kt index ccd028ebcb..179cbbc944 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/endpoint/EndpointResolverGeneratorTest.kt @@ -23,7 +23,7 @@ import software.amazon.smithy.rust.codegen.client.smithy.endpoint.generators.End import software.amazon.smithy.rust.codegen.client.smithy.endpoint.generators.EndpointTestGenerator import software.amazon.smithy.rust.codegen.client.smithy.endpoint.rulesgen.SmithyEndpointsStdLib import software.amazon.smithy.rust.codegen.client.smithy.endpoint.rulesgen.awsStandardLib -import software.amazon.smithy.rust.codegen.client.testutil.testCodegenContext +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.TestRuntimeConfig import software.amazon.smithy.rust.codegen.core.testutil.TestWorkspace @@ -64,7 +64,7 @@ class EndpointResolverGeneratorTest { paramsType = EndpointParamsGenerator(suite.ruleSet().parameters).paramsStruct(), resolverType = ruleset, suite.ruleSet().parameters, - codegenContext = testCodegenContext(model = Model.builder().build()), + codegenContext = testClientCodegenContext(model = Model.builder().build()), endpointCustomizations = listOf(), ) testGenerator.generate()(this) @@ -90,7 +90,7 @@ class EndpointResolverGeneratorTest { paramsType = EndpointParamsGenerator(suite.ruleSet().parameters).paramsStruct(), resolverType = ruleset, suite.ruleSet().parameters, - codegenContext = testCodegenContext(Model.builder().build()), + codegenContext = testClientCodegenContext(Model.builder().build()), endpointCustomizations = listOf(), ) testGenerator.generate()(this) 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/endpoint/EndpointsDecoratorTest.kt index d05f3b21de..fc564c2696 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/endpoint/EndpointsDecoratorTest.kt @@ -11,6 +11,7 @@ import org.junit.jupiter.api.Test 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 @@ -123,8 +124,8 @@ class EndpointsDecoratorTest { fun `set an endpoint in the property bag`() { val testDir = clientIntegrationTest( model, - // just run integration tests - command = { "cargo test --test *".runWithWarnings(it) }, + // Just run integration tests. + IntegrationTestParams(command = { "cargo test --test *".runWithWarnings(it) }), ) { clientCodegenContext, rustCrate -> rustCrate.integrationTest("endpoint_params_test") { val moduleName = clientCodegenContext.moduleUseName() @@ -133,7 +134,7 @@ class EndpointsDecoratorTest { """ async fn endpoint_params_are_set() { let conf = $moduleName::Config::builder().a_string_param("hello").a_bool_param(false).build(); - let operation = $moduleName::operation::TestOperation::builder() + let operation = $moduleName::operation::test_operation::TestOperationInput::builder() .bucket("bucket-name").build().expect("input is valid") .make_operation(&conf).await.expect("valid operation"); use $moduleName::endpoint::{Params}; 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 5fb38e58e3..42663f2756 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 @@ -10,6 +10,8 @@ import org.junit.jupiter.api.Test import software.amazon.smithy.model.shapes.MemberShape import software.amazon.smithy.model.shapes.ServiceShape import software.amazon.smithy.model.shapes.ShapeId +import software.amazon.smithy.rust.codegen.client.testutil.TestClientRustSymbolProviderConfig +import software.amazon.smithy.rust.codegen.client.testutil.testClientRustSettings 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 @@ -17,7 +19,6 @@ 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 import software.amazon.smithy.rust.codegen.core.testutil.TestRuntimeConfig -import software.amazon.smithy.rust.codegen.core.testutil.TestSymbolVisitorConfig import software.amazon.smithy.rust.codegen.core.testutil.asSmithyModel class EventStreamSymbolProviderTest { @@ -46,7 +47,11 @@ class EventStreamSymbolProviderTest { ) val service = model.expectShape(ShapeId.from("test#TestService")) as ServiceShape - val provider = EventStreamSymbolProvider(TestRuntimeConfig, SymbolVisitor(model, service, TestSymbolVisitorConfig), model, CodegenTarget.CLIENT) + val provider = EventStreamSymbolProvider( + TestRuntimeConfig, + SymbolVisitor(testClientRustSettings(), model, service, TestClientRustSymbolProviderConfig), + CodegenTarget.CLIENT, + ) // Look up the synthetic input/output rather than the original input/output val inputStream = model.expectShape(ShapeId.from("test.synthetic#TestOperationInput\$inputStream")) as MemberShape @@ -82,7 +87,11 @@ class EventStreamSymbolProviderTest { ) val service = model.expectShape(ShapeId.from("test#TestService")) as ServiceShape - val provider = EventStreamSymbolProvider(TestRuntimeConfig, SymbolVisitor(model, service, TestSymbolVisitorConfig), model, CodegenTarget.CLIENT) + val provider = EventStreamSymbolProvider( + TestRuntimeConfig, + SymbolVisitor(testClientRustSettings(), model, service, TestClientRustSymbolProviderConfig), + CodegenTarget.CLIENT, + ) // Look up the synthetic input/output rather than the original input/output val inputStream = model.expectShape(ShapeId.from("test.synthetic#TestOperationInput\$inputStream")) as MemberShape 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 6c0c3cdadf..a2e233c719 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 @@ -9,8 +9,10 @@ import io.kotest.matchers.shouldBe import org.junit.jupiter.api.Test import software.amazon.smithy.model.shapes.MemberShape import software.amazon.smithy.rust.codegen.client.testutil.testSymbolProvider +import software.amazon.smithy.rust.codegen.core.rustlang.RustType import software.amazon.smithy.rust.codegen.core.smithy.Default import software.amazon.smithy.rust.codegen.core.smithy.defaultValue +import software.amazon.smithy.rust.codegen.core.smithy.rustType 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 @@ -38,8 +40,18 @@ internal class StreamingShapeSymbolProviderTest { // "doing the right thing" val modelWithOperationTraits = OperationNormalizer.transform(model) val symbolProvider = testSymbolProvider(modelWithOperationTraits) - symbolProvider.toSymbol(modelWithOperationTraits.lookup("test.synthetic#GenerateSpeechOutput\$data")).name shouldBe ("ByteStream") - symbolProvider.toSymbol(modelWithOperationTraits.lookup("test.synthetic#GenerateSpeechInput\$data")).name shouldBe ("ByteStream") + 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") + } + } + 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") + } + } } @Test diff --git a/codegen-client/src/test/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/ClientEnumGeneratorTest.kt b/codegen-client/src/test/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/ClientEnumGeneratorTest.kt new file mode 100644 index 0000000000..4354033485 --- /dev/null +++ b/codegen-client/src/test/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/ClientEnumGeneratorTest.kt @@ -0,0 +1,160 @@ +/* + * 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.model.Model +import software.amazon.smithy.model.shapes.StringShape +import software.amazon.smithy.rust.codegen.client.testutil.testClientCodegenContext +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.compileAndTest +import software.amazon.smithy.rust.codegen.core.testutil.unitTest +import software.amazon.smithy.rust.codegen.core.util.lookup + +class ClientEnumGeneratorTest { + @Test + fun `matching on enum should be forward-compatible`() { + fun expectMatchExpressionCompiles(model: Model, shapeId: String, enumToMatchOn: String) { + val shape = model.lookup(shapeId) + val context = testClientCodegenContext(model) + val project = TestWorkspace.testProject(context.symbolProvider) + project.moduleFor(shape) { + ClientEnumGenerator(context, shape).render(this) + unitTest( + "matching_on_enum_should_be_forward_compatible", + """ + match $enumToMatchOn { + SomeEnum::Variant1 => panic!("expected `Variant3` but got `Variant1`"), + SomeEnum::Variant2 => panic!("expected `Variant3` but got `Variant2`"), + other @ _ if other.as_str() == "Variant3" => {}, + _ => panic!("expected `Variant3` but got `_`"), + } + """, + ) + } + project.compileAndTest() + } + + val modelV1 = """ + namespace test + + @enum([ + { name: "Variant1", value: "Variant1" }, + { name: "Variant2", value: "Variant2" }, + ]) + string SomeEnum + """.asSmithyModel() + val variant3AsUnknown = """SomeEnum::from("Variant3")""" + expectMatchExpressionCompiles(modelV1, "test#SomeEnum", variant3AsUnknown) + + val modelV2 = """ + namespace test + + @enum([ + { name: "Variant1", value: "Variant1" }, + { name: "Variant2", value: "Variant2" }, + { name: "Variant3", value: "Variant3" }, + ]) + string SomeEnum + """.asSmithyModel() + val variant3AsVariant3 = "SomeEnum::Variant3" + expectMatchExpressionCompiles(modelV2, "test#SomeEnum", variant3AsVariant3) + } + + @Test + fun `impl debug for non-sensitive enum should implement the derived debug trait`() { + val model = """ + namespace test + @enum([ + { name: "Foo", value: "Foo" }, + { name: "Bar", value: "Bar" }, + ]) + string SomeEnum + """.asSmithyModel() + + val shape = model.lookup("test#SomeEnum") + val context = testClientCodegenContext(model) + val project = TestWorkspace.testProject(context.symbolProvider) + project.moduleFor(shape) { + ClientEnumGenerator(context, shape).render(this) + unitTest( + "impl_debug_for_non_sensitive_enum_should_implement_the_derived_debug_trait", + """ + assert_eq!(format!("{:?}", SomeEnum::Foo), "Foo"); + assert_eq!(format!("{:?}", SomeEnum::Bar), "Bar"); + assert_eq!( + format!("{:?}", SomeEnum::from("Baz")), + "Unknown(UnknownVariantValue(\"Baz\"))" + ); + """, + ) + } + project.compileAndTest() + } + + @Test + fun `it escapes the Unknown variant if the enum has an unknown value in the model`() { + val model = """ + namespace test + @enum([ + { name: "Known", value: "Known" }, + { name: "Unknown", value: "Unknown" }, + { name: "UnknownValue", value: "UnknownValue" }, + ]) + string SomeEnum + """.asSmithyModel() + + val shape = model.lookup("test#SomeEnum") + val context = testClientCodegenContext(model) + val project = TestWorkspace.testProject(context.symbolProvider) + project.moduleFor(shape) { + ClientEnumGenerator(context, shape).render(this) + unitTest( + "it_escapes_the_unknown_variant_if_the_enum_has_an_unknown_value_in_the_model", + """ + assert_eq!(SomeEnum::from("Unknown"), SomeEnum::UnknownValue); + assert_eq!(SomeEnum::from("UnknownValue"), SomeEnum::UnknownValue_); + assert_eq!(SomeEnum::from("SomethingNew"), SomeEnum::Unknown(crate::primitives::UnknownVariantValue("SomethingNew".to_owned()))); + """, + ) + } + project.compileAndTest() + } + + @Test + fun `generated named enums can roundtrip between string and enum value on the unknown variant`() { + val model = """ + namespace test + @enum([ + { value: "t2.nano", name: "T2_NANO" }, + { value: "t2.micro", name: "T2_MICRO" }, + ]) + string InstanceType + """.asSmithyModel() + + val shape = model.lookup("test#InstanceType") + val context = testClientCodegenContext(model) + val project = TestWorkspace.testProject(context.symbolProvider) + project.moduleFor(shape) { + rust("##![allow(deprecated)]") + ClientEnumGenerator(context, shape).render(this) + unitTest( + "generated_named_enums_roundtrip", + """ + let instance = InstanceType::T2Micro; + assert_eq!(instance.as_str(), "t2.micro"); + assert_eq!(InstanceType::from("t2.nano"), InstanceType::T2Nano); + // round trip unknown variants: + assert_eq!(InstanceType::from("other"), InstanceType::Unknown(crate::primitives::UnknownVariantValue("other".to_owned()))); + assert_eq!(InstanceType::from("other").as_str(), "other"); + """, + ) + } + project.compileAndTest() + } +} 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 f507ba2d4c..abd497b49b 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 @@ -8,17 +8,14 @@ package software.amazon.smithy.rust.codegen.client.smithy.generators import org.junit.jupiter.api.Test import software.amazon.smithy.model.node.Node import software.amazon.smithy.model.shapes.StringShape -import software.amazon.smithy.rust.codegen.client.testutil.testCodegenContext -import software.amazon.smithy.rust.codegen.core.rustlang.RustModule +import software.amazon.smithy.rust.codegen.client.testutil.testClientCodegenContext 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.smithy.generators.EnumGenerator 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.dq -import software.amazon.smithy.rust.codegen.core.util.expectTrait import software.amazon.smithy.rust.codegen.core.util.lookup internal class ClientInstantiatorTest { @@ -44,7 +41,7 @@ internal class ClientInstantiatorTest { string NamedEnum """.asSmithyModel() - private val codegenContext = testCodegenContext(model) + private val codegenContext = testClientCodegenContext(model) private val symbolProvider = codegenContext.symbolProvider @Test @@ -53,9 +50,9 @@ internal class ClientInstantiatorTest { val sut = clientInstantiator(codegenContext) val data = Node.parse("t2.nano".dq()) - val project = TestWorkspace.testProject() - project.withModule(RustModule.Model) { - EnumGenerator(model, symbolProvider, this, shape, shape.expectTrait()).render() + val project = TestWorkspace.testProject(symbolProvider) + project.moduleFor(shape) { + ClientEnumGenerator(codegenContext, shape).render(this) unitTest("generate_named_enums") { withBlock("let result = ", ";") { sut.render(this, shape, data) @@ -72,9 +69,9 @@ internal class ClientInstantiatorTest { val sut = clientInstantiator(codegenContext) val data = Node.parse("t2.nano".dq()) - val project = TestWorkspace.testProject() - project.withModule(RustModule.Model) { - EnumGenerator(model, symbolProvider, this, shape, shape.expectTrait()).render() + val project = TestWorkspace.testProject(symbolProvider) + project.moduleFor(shape) { + ClientEnumGenerator(codegenContext, shape).render(this) unitTest("generate_unnamed_enums") { withBlock("let result = ", ";") { sut.render(this, shape, data) 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 2a9787f69d..841986cb91 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 @@ -13,10 +13,10 @@ 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.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.smithy.RuntimeType -import software.amazon.smithy.rust.codegen.core.smithy.generators.implBlock import software.amazon.smithy.rust.codegen.core.smithy.generators.operationBuildError import software.amazon.smithy.rust.codegen.core.testutil.TestRuntimeConfig import software.amazon.smithy.rust.codegen.core.testutil.TestWorkspace @@ -50,10 +50,10 @@ internal class EndpointTraitBindingsTest { } """.asSmithyModel() val operationShape: OperationShape = model.lookup("test#GetStatus") - val sym = testSymbolProvider(model) + val symbolProvider = testSymbolProvider(model) val endpointBindingGenerator = EndpointTraitBindings( model, - sym, + symbolProvider, TestRuntimeConfig, operationShape, operationShape.expectTrait(EndpointTrait::class.java), @@ -67,7 +67,7 @@ internal class EndpointTraitBindingsTest { } """, ) - implBlock(model.lookup("test#GetStatusInput"), sym) { + implBlock(symbolProvider.toSymbol(model.lookup("test#GetStatusInput"))) { rustBlock( "fn endpoint_prefix(&self) -> std::result::Result<#T::endpoint::EndpointPrefix, #T>", RuntimeType.smithyHttp(TestRuntimeConfig), @@ -145,10 +145,10 @@ internal class EndpointTraitBindingsTest { """ async fn test_endpoint_prefix() { let conf = $moduleName::Config::builder().build(); - $moduleName::operation::SayHello::builder() + $moduleName::operation::say_hello::SayHelloInput::builder() .greeting("hey there!").build().expect("input is valid") .make_operation(&conf).await.expect_err("no spaces or exclamation points in ep prefixes"); - let op = $moduleName::operation::SayHello::builder() + let op = $moduleName::operation::say_hello::SayHelloInput::builder() .greeting("hello") .build().expect("valid operation") .make_operation(&conf).await.expect("hello is a valid prefix"); 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 f45f56a1ca..96eff3ad79 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 @@ -72,7 +72,7 @@ internal class PaginatorGeneratorTest { clientIntegrationTest(model) { clientCodegenContext, rustCrate -> rustCrate.integrationTest("paginators_generated") { Attribute.AllowUnusedImports.render(this) - rust("use ${clientCodegenContext.moduleUseName()}::paginator::PaginatedListPaginator;") + 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/config/ServiceConfigGeneratorTest.kt b/codegen-client/src/test/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/config/ServiceConfigGeneratorTest.kt index 366ff370dc..2aaa3e21dd 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 @@ -8,8 +8,8 @@ package software.amazon.smithy.rust.codegen.client.smithy.generators.config import io.kotest.matchers.shouldBe import org.junit.jupiter.api.Test import software.amazon.smithy.model.shapes.ServiceShape +import software.amazon.smithy.rust.codegen.client.smithy.ClientRustModule import software.amazon.smithy.rust.codegen.client.testutil.testSymbolProvider -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.writable @@ -94,7 +94,9 @@ internal class ServiceConfigGeneratorTest { } ServiceConfig.BuilderStruct -> writable { rust("config_field: Option") } ServiceConfig.BuilderImpl -> emptySection - ServiceConfig.BuilderBuild -> writable { rust("config_field: self.config_field.unwrap_or_default(),") } + ServiceConfig.BuilderBuild -> writable { + rust("config_field: self.config_field.unwrap_or_default(),") + } else -> emptySection } } @@ -102,7 +104,7 @@ internal class ServiceConfigGeneratorTest { val sut = ServiceConfigGenerator(listOf(ServiceCustomizer())) val symbolProvider = testSymbolProvider("namespace empty".asSmithyModel()) val project = TestWorkspace.testProject(symbolProvider) - project.withModule(RustModule.Config) { + project.withModule(ClientRustModule.Config) { sut.render(this) unitTest( "set_config_fields", diff --git a/codegen-client/src/test/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/error/ErrorGeneratorTest.kt b/codegen-client/src/test/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/error/ErrorGeneratorTest.kt new file mode 100644 index 0000000000..57f5589293 --- /dev/null +++ b/codegen-client/src/test/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/error/ErrorGeneratorTest.kt @@ -0,0 +1,60 @@ +/* + * 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.error + +import org.junit.jupiter.api.Test +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 + +class ErrorGeneratorTest { + val model = + """ + namespace com.test + use aws.protocols#awsJson1_1 + + @awsJson1_1 + service TestService { + operations: [TestOp] + } + + operation TestOp { + errors: [MyError] + } + + @error("server") + @retryable + structure MyError { + message: String + } + """.asSmithyModel() + + @Test + fun `generate error structure and builder`() { + clientIntegrationTest(model) { _, rustCrate -> + rustCrate.withFile("src/types/error.rs") { + rust( + """ + ##[test] + fn test_error_generator() { + use aws_smithy_types::error::metadata::{ErrorMetadata, ProvideErrorMetadata}; + use aws_smithy_types::retry::ErrorKind; + + let err = MyError::builder() + .meta(ErrorMetadata::builder().code("test").message("testmsg").build()) + .message("testmsg") + .build(); + assert_eq!(err.retryable_error_kind(), ErrorKind::ServerError); + assert_eq!("test", err.meta().code().unwrap()); + assert_eq!("testmsg", err.meta().message().unwrap()); + assert_eq!("testmsg", err.message().unwrap()); + } + """, + ) + } + } + } +} diff --git a/codegen-client/src/test/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/error/OperationErrorGeneratorTest.kt b/codegen-client/src/test/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/error/OperationErrorGeneratorTest.kt new file mode 100644 index 0000000000..590b7acb9c --- /dev/null +++ b/codegen-client/src/test/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/error/OperationErrorGeneratorTest.kt @@ -0,0 +1,92 @@ +/* + * 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.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.testutil.asSmithyModel +import software.amazon.smithy.rust.codegen.core.testutil.unitTest +import software.amazon.smithy.rust.codegen.core.util.lookup + +class OperationErrorGeneratorTest { + private val model = """ + namespace error + + @aws.protocols#awsJson1_0 + service TestService { + operations: [Greeting], + } + + operation Greeting { + errors: [InvalidGreeting, ComplexError, FooException, Deprecated] + } + + @error("client") + @retryable + structure InvalidGreeting { + message: String, + } + + @error("server") + structure FooException { } + + @error("server") + structure ComplexError { + abc: String, + other: Integer + } + + @error("server") + @deprecated + structure Deprecated { } + """.asSmithyModel() + + @Test + fun `generates combined error enums`() { + clientIntegrationTest(model) { _, rustCrate -> + rustCrate.moduleFor(model.lookup("error#FooException")) { + unitTest( + name = "generates_combined_error_enums", + test = """ + use crate::operation::greeting::GreetingError; + + let error = GreetingError::InvalidGreeting( + InvalidGreeting::builder() + .message("an error") + .meta(aws_smithy_types::Error::builder().code("InvalidGreeting").message("an error").build()) + .build() + ); + assert_eq!(format!("{}", error), "InvalidGreeting: an error"); + assert_eq!(error.meta().message(), Some("an error")); + assert_eq!(error.meta().code(), Some("InvalidGreeting")); + use aws_smithy_types::retry::ProvideErrorKind; + assert_eq!(error.retryable_error_kind(), Some(aws_smithy_types::retry::ErrorKind::ClientError)); + + // Generate is_xyz methods for errors. + assert_eq!(error.is_invalid_greeting(), true); + assert_eq!(error.is_complex_error(), false); + + // Unhandled variants properly delegate message. + let error = GreetingError::generic(aws_smithy_types::Error::builder().message("hello").build()); + assert_eq!(error.meta().message(), Some("hello")); + + let error = GreetingError::unhandled("some other error"); + assert_eq!(error.meta().message(), None); + assert_eq!(error.meta().code(), None); + + // Indicate the original name in the display output. + let error = FooError::builder().build(); + assert_eq!(format!("{}", error), "FooError [FooException]"); + + let error = Deprecated::builder().build(); + assert_eq!(error.to_string(), "Deprecated"); + """, + ) + } + } + } +} 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 new file mode 100644 index 0000000000..1cbb274cfb --- /dev/null +++ b/codegen-client/src/test/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/error/ServiceErrorGeneratorTest.kt @@ -0,0 +1,63 @@ +/* + * 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.error + +import org.junit.jupiter.api.Test +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 + +internal class ServiceErrorGeneratorTest { + @Test + fun `top level errors are send + sync`() { + val model = """ + namespace com.example + + use aws.protocols#restJson1 + + @restJson1 + service HelloService { + operations: [SayHello], + version: "1" + } + + @http(uri: "/", method: "POST") + operation SayHello { + input: EmptyStruct, + output: EmptyStruct, + errors: [SorryBusy, CanYouRepeatThat, MeDeprecated] + } + + structure EmptyStruct { } + + @error("server") + structure SorryBusy { } + + @error("client") + structure CanYouRepeatThat { } + + @error("client") + @deprecated + structure MeDeprecated { } + """.asSmithyModel() + + clientIntegrationTest(model) { codegenContext, rustCrate -> + rustCrate.integrationTest("validate_errors") { + rust( + """ + fn check_send_sync() {} + + ##[test] + fn service_errors_are_send_sync() { + check_send_sync::<${codegenContext.moduleUseName()}::Error>() + } + """, + ) + } + } + } +} diff --git a/codegen-client/src/test/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/http/RequestBindingGeneratorTest.kt b/codegen-client/src/test/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/http/RequestBindingGeneratorTest.kt index 6f214e3540..be42a9647c 100644 --- a/codegen-client/src/test/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/http/RequestBindingGeneratorTest.kt +++ b/codegen-client/src/test/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/http/RequestBindingGeneratorTest.kt @@ -11,13 +11,13 @@ 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.model.traits.HttpTrait -import software.amazon.smithy.rust.codegen.client.testutil.testCodegenContext +import software.amazon.smithy.rust.codegen.client.smithy.ClientRustModule +import software.amazon.smithy.rust.codegen.client.testutil.testClientCodegenContext import software.amazon.smithy.rust.codegen.client.testutil.testSymbolProvider -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.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.RustCrate import software.amazon.smithy.rust.codegen.core.smithy.generators.operationBuildError import software.amazon.smithy.rust.codegen.core.smithy.protocols.RestJson import software.amazon.smithy.rust.codegen.core.smithy.transformers.OperationNormalizer @@ -127,45 +127,47 @@ class RequestBindingGeneratorTest { private val operationShape = model.expectShape(ShapeId.from("smithy.example#PutObject"), OperationShape::class.java) private val inputShape = model.expectShape(operationShape.input.get(), StructureShape::class.java) - private fun renderOperation(writer: RustWriter) { - inputShape.renderWithModelBuilder(model, symbolProvider, writer) - val codegenContext = testCodegenContext(model) - val bindingGen = RequestBindingGenerator( - codegenContext, - // Any protocol is fine for this test. - RestJson(codegenContext), - operationShape, - ) - writer.rustBlock("impl PutObjectInput") { - // RequestBindingGenerator's functions expect to be rendered inside a function, - // but the unit test needs to call some of these functions individually. This generates - // some wrappers that can be called directly from the tests. The functions will get duplicated, - // but that's not a problem. - - rustBlock( - "pub fn test_uri_query(&self, mut output: &mut String) -> Result<(), #T>", - TestRuntimeConfig.operationBuildError(), - ) { - bindingGen.renderUpdateHttpBuilder(this) - rust("uri_query(self, output)") - } - - rustBlock( - "pub fn test_uri_base(&self, mut output: &mut String) -> Result<(), #T>", - TestRuntimeConfig.operationBuildError(), - ) { - bindingGen.renderUpdateHttpBuilder(this) - rust("uri_base(self, output)") - } - - rustBlock( - "pub fn test_request_builder_base(&self) -> Result<#T, #T>", - RuntimeType.HttpRequestBuilder, - TestRuntimeConfig.operationBuildError(), - ) { - bindingGen.renderUpdateHttpBuilder(this) - rust("let builder = #T::new();", RuntimeType.HttpRequestBuilder) - rust("update_http_builder(self, builder)") + private fun renderOperation(rustCrate: RustCrate) { + inputShape.renderWithModelBuilder(model, symbolProvider, rustCrate) + rustCrate.withModule(ClientRustModule.Input) { + val codegenContext = testClientCodegenContext(model) + val bindingGen = RequestBindingGenerator( + codegenContext, + // Any protocol is fine for this test. + RestJson(codegenContext), + operationShape, + ) + rustBlock("impl PutObjectInput") { + // RequestBindingGenerator's functions expect to be rendered inside a function, + // but the unit test needs to call some of these functions individually. This generates + // some wrappers that can be called directly from the tests. The functions will get duplicated, + // but that's not a problem. + + rustBlock( + "pub fn test_uri_query(&self, mut output: &mut String) -> Result<(), #T>", + TestRuntimeConfig.operationBuildError(), + ) { + bindingGen.renderUpdateHttpBuilder(this) + rust("uri_query(self, output)") + } + + rustBlock( + "pub fn test_uri_base(&self, mut output: &mut String) -> Result<(), #T>", + TestRuntimeConfig.operationBuildError(), + ) { + bindingGen.renderUpdateHttpBuilder(this) + rust("uri_base(self, output)") + } + + rustBlock( + "pub fn test_request_builder_base(&self) -> Result<#T, #T>", + RuntimeType.HttpRequestBuilder, + TestRuntimeConfig.operationBuildError(), + ) { + bindingGen.renderUpdateHttpBuilder(this) + rust("let builder = #T::new();", RuntimeType.HttpRequestBuilder) + rust("update_http_builder(self, builder)") + } } } } @@ -179,9 +181,8 @@ class RequestBindingGeneratorTest { @Test fun `generates valid request bindings`() { val project = TestWorkspace.testProject(symbolProvider) - project.withModule(RustModule.public("input")) { // Currently rendering the operation renders the protocols—I want to separate that at some point. - renderOperation(this) - + renderOperation(project) + project.withModule(ClientRustModule.Input) { // Currently rendering the operation renders the protocols—I want to separate that at some point. unitTest( name = "generate_uris", test = """ diff --git a/codegen-client/src/test/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/http/ResponseBindingGeneratorTest.kt b/codegen-client/src/test/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/http/ResponseBindingGeneratorTest.kt index 19ab23102f..5264b4ea6f 100644 --- a/codegen-client/src/test/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/http/ResponseBindingGeneratorTest.kt +++ b/codegen-client/src/test/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/http/ResponseBindingGeneratorTest.kt @@ -8,12 +8,11 @@ package software.amazon.smithy.rust.codegen.client.smithy.generators.http import org.junit.jupiter.api.Test import software.amazon.smithy.model.shapes.OperationShape import software.amazon.smithy.model.shapes.ShapeId -import software.amazon.smithy.rust.codegen.client.testutil.testCodegenContext -import software.amazon.smithy.rust.codegen.core.rustlang.RustModule -import software.amazon.smithy.rust.codegen.core.rustlang.RustWriter +import software.amazon.smithy.rust.codegen.client.smithy.ClientRustModule +import software.amazon.smithy.rust.codegen.client.testutil.testClientCodegenContext 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.CodegenContext +import software.amazon.smithy.rust.codegen.core.smithy.RustCrate import software.amazon.smithy.rust.codegen.core.smithy.protocols.HttpLocation import software.amazon.smithy.rust.codegen.core.smithy.protocols.HttpTraitHttpBindingResolver import software.amazon.smithy.rust.codegen.core.smithy.protocols.ProtocolContentTypes @@ -67,23 +66,25 @@ class ResponseBindingGeneratorTest { """.asSmithyModel() private val model = OperationNormalizer.transform(baseModel) private val operationShape = model.expectShape(ShapeId.from("smithy.example#PutObject"), OperationShape::class.java) - private val codegenContext: CodegenContext = testCodegenContext(model) + private val codegenContext = testClientCodegenContext(model) private val symbolProvider = codegenContext.symbolProvider - private fun RustWriter.renderOperation() { + private fun RustCrate.renderOperation() { operationShape.outputShape(model).renderWithModelBuilder(model, symbolProvider, this) - rustBlock("impl PutObjectOutput") { - val bindings = HttpTraitHttpBindingResolver(model, ProtocolContentTypes.consistent("dont-care")) - .responseBindings(operationShape) - .filter { it.location == HttpLocation.HEADER } - bindings.forEach { binding -> - val runtimeType = ResponseBindingGenerator( - RestJson(codegenContext), - codegenContext, - operationShape, - ).generateDeserializeHeaderFn(binding) - // little hack to force these functions to be generated - rust("// use #T;", runtimeType) + withModule(ClientRustModule.Output) { + rustBlock("impl PutObjectOutput") { + val bindings = HttpTraitHttpBindingResolver(model, ProtocolContentTypes.consistent("dont-care")) + .responseBindings(operationShape) + .filter { it.location == HttpLocation.HEADER } + bindings.forEach { binding -> + val runtimeType = ResponseBindingGenerator( + RestJson(codegenContext), + codegenContext, + operationShape, + ).generateDeserializeHeaderFn(binding) + // little hack to force these functions to be generated + rust("// use #T;", runtimeType) + } } } } @@ -91,12 +92,12 @@ class ResponseBindingGeneratorTest { @Test fun deserializeHeadersIntoOutputShape() { val testProject = TestWorkspace.testProject(symbolProvider) - testProject.withModule(RustModule.public("output")) { - renderOperation() + testProject.renderOperation() + testProject.withModule(ClientRustModule.Output) { unitTest( "http_header_deser", """ - use crate::http_serde; + use crate::protocol_serde::shape_put_object_output::*; let resp = http::Response::builder() .header("X-Ints", "1,2,3") .header("X-Ints", "4,5,6") @@ -104,9 +105,9 @@ class ResponseBindingGeneratorTest { .header("X-Dates", "Mon, 16 Dec 2019 23:48:18 GMT") .header("X-Dates", "Mon, 16 Dec 2019 23:48:18 GMT,Tue, 17 Dec 2019 23:48:18 GMT") .body(()).expect("valid request"); - assert_eq!(http_serde::deser_header_put_object_put_object_output_int_list(resp.headers()).unwrap(), Some(vec![1,2,3,4,5,6])); - assert_eq!(http_serde::deser_header_put_object_put_object_output_media_type(resp.headers()).expect("valid").unwrap(), "smithy-rs"); - assert_eq!(http_serde::deser_header_put_object_put_object_output_date_header_list(resp.headers()).unwrap().unwrap().len(), 3); + assert_eq!(de_int_list_header(resp.headers()).unwrap(), Some(vec![1,2,3,4,5,6])); + assert_eq!(de_media_type_header(resp.headers()).expect("valid").unwrap(), "smithy-rs"); + assert_eq!(de_date_header_list_header(resp.headers()).unwrap().unwrap().len(), 3); """, ) } 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 1da709422d..5413cc7679 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 @@ -21,7 +21,6 @@ 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.error.errorSymbol 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.generators.protocol.ProtocolTraitImplGenerator @@ -62,10 +61,11 @@ private class TestProtocolTraitImplGenerator( 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 operationShape.errorSymbol(symbolProvider), + "Error" to symbolProvider.symbolForOperationError(operationShape), "Response" to RuntimeType.HttpResponse, "Bytes" to RuntimeType.Bytes, ) @@ -92,7 +92,7 @@ private class TestProtocolMakeOperationGenerator( // A stubbed test protocol to do enable testing intentionally broken protocols private class TestProtocolGenerator( - codegenContext: CodegenContext, + codegenContext: ClientCodegenContext, protocol: Protocol, httpRequestBuilder: String, body: String, @@ -220,7 +220,7 @@ class ProtocolTestGeneratorTest { private fun testService( httpRequestBuilder: String, body: String = "${correctBody.dq()}.to_string()", - correctResponse: String = """Ok(crate::output::SayHelloOutput::builder().value("hey there!").build())""", + correctResponse: String = """Ok(crate::operation::say_hello::SayHelloOutput::builder().value("hey there!").build())""", ): Path { val codegenDecorator = object : ClientCodegenDecorator { override val name: String = "mock" @@ -256,7 +256,7 @@ class ProtocolTestGeneratorTest { .header("X-Greeting", "Hi") .method("POST") """, - correctResponse = "Ok(crate::output::SayHelloOutput::builder().build())", + correctResponse = "Ok(crate::operation::say_hello::SayHelloOutput::builder().build())", ) } 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 new file mode 100644 index 0000000000..c5b2470dd9 --- /dev/null +++ b/codegen-client/src/test/kotlin/software/amazon/smithy/rust/codegen/client/smithy/protocols/AwsQueryCompatibleTest.kt @@ -0,0 +1,152 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +package software.amazon.smithy.rust.codegen.client.smithy.protocols + +import org.junit.jupiter.api.Test +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 + +class AwsQueryCompatibleTest { + @Test + fun `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) { clientCodegenContext, rustCrate -> + val moduleName = clientCodegenContext.moduleUseName() + rustCrate.integrationTest("should_parse_code_and_type_fields") { + rust( + """ + ##[test] + fn should_parse_code_and_type_fields() { + use aws_smithy_http::response::ParseStrictResponse; + + let response = http::Response::builder() + .header( + "x-amzn-query-error", + http::HeaderValue::from_static("AWS.SimpleQueueService.NonExistentQueue;Sender"), + ) + .status(400) + .body( + r##"{ + "__type": "com.amazonaws.sqs##QueueDoesNotExist", + "message": "Some user-visible message" + }"##, + ) + .unwrap(); + let some_operation = $moduleName::operation::some_operation::SomeOperation::new(); + let error = some_operation + .parse(&response.map(bytes::Bytes::from)) + .err() + .unwrap(); + assert_eq!( + Some("AWS.SimpleQueueService.NonExistentQueue"), + error.meta().code(), + ); + assert_eq!(Some("Sender"), error.meta().extra("type")); + } + """, + ) + } + } + } + + @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) { clientCodegenContext, rustCrate -> + val moduleName = clientCodegenContext.moduleUseName() + rustCrate.integrationTest("should_parse_code_from_payload") { + rust( + """ + ##[test] + fn should_parse_code_from_payload() { + use aws_smithy_http::response::ParseStrictResponse; + + let response = http::Response::builder() + .status(400) + .body( + r##"{ + "__type": "com.amazonaws.sqs##QueueDoesNotExist", + "message": "Some user-visible message" + }"##, + ) + .unwrap(); + let some_operation = $moduleName::operation::some_operation::SomeOperation::new(); + let error = some_operation + .parse(&response.map(bytes::Bytes::from)) + .err() + .unwrap(); + assert_eq!(Some("QueueDoesNotExist"), error.meta().code()); + assert_eq!(None, error.meta().extra("type")); + } + """, + ) + } + } + } +} diff --git a/codegen-client/src/test/kotlin/software/amazon/smithy/rust/codegen/client/smithy/protocols/eventstream/ClientEventStreamBaseRequirements.kt b/codegen-client/src/test/kotlin/software/amazon/smithy/rust/codegen/client/smithy/protocols/eventstream/ClientEventStreamBaseRequirements.kt deleted file mode 100644 index 1717dab2d6..0000000000 --- a/codegen-client/src/test/kotlin/software/amazon/smithy/rust/codegen/client/smithy/protocols/eventstream/ClientEventStreamBaseRequirements.kt +++ /dev/null @@ -1,72 +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.protocols.eventstream - -import org.junit.jupiter.api.extension.ExtensionContext -import org.junit.jupiter.params.provider.Arguments -import org.junit.jupiter.params.provider.ArgumentsProvider -import software.amazon.smithy.codegen.core.Symbol -import software.amazon.smithy.model.Model -import software.amazon.smithy.model.shapes.ServiceShape -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.CombinedClientCodegenDecorator -import software.amazon.smithy.rust.codegen.client.testutil.clientTestRustSettings -import software.amazon.smithy.rust.codegen.client.testutil.testSymbolProvider -import software.amazon.smithy.rust.codegen.core.rustlang.RustWriter -import software.amazon.smithy.rust.codegen.core.smithy.CodegenTarget -import software.amazon.smithy.rust.codegen.core.smithy.RustSymbolProvider -import software.amazon.smithy.rust.codegen.core.smithy.generators.BuilderGenerator -import software.amazon.smithy.rust.codegen.core.smithy.generators.error.OperationErrorGenerator -import software.amazon.smithy.rust.codegen.core.smithy.generators.implBlock -import software.amazon.smithy.rust.codegen.core.testutil.EventStreamTestModels -import software.amazon.smithy.rust.codegen.core.testutil.EventStreamTestRequirements -import java.util.stream.Stream - -class TestCasesProvider : ArgumentsProvider { - override fun provideArguments(context: ExtensionContext?): Stream = - EventStreamTestModels.TEST_CASES.map { Arguments.of(it) }.stream() -} - -abstract class ClientEventStreamBaseRequirements : EventStreamTestRequirements { - override fun createCodegenContext( - model: Model, - serviceShape: ServiceShape, - protocolShapeId: ShapeId, - codegenTarget: CodegenTarget, - ): ClientCodegenContext = ClientCodegenContext( - model, - testSymbolProvider(model), - serviceShape, - protocolShapeId, - clientTestRustSettings(), - CombinedClientCodegenDecorator(emptyList()), - ) - - override fun renderBuilderForShape( - writer: RustWriter, - codegenContext: ClientCodegenContext, - shape: StructureShape, - ) { - BuilderGenerator(codegenContext.model, codegenContext.symbolProvider, shape).apply { - render(writer) - writer.implBlock(shape, codegenContext.symbolProvider) { - renderConvenienceMethod(writer) - } - } - } - - override fun renderOperationError( - writer: RustWriter, - model: Model, - symbolProvider: RustSymbolProvider, - operationSymbol: Symbol, - errors: List, - ) { - OperationErrorGenerator(model, symbolProvider, operationSymbol, errors).render(writer) - } -} diff --git a/codegen-client/src/test/kotlin/software/amazon/smithy/rust/codegen/client/smithy/protocols/eventstream/ClientEventStreamMarshallerGeneratorTest.kt b/codegen-client/src/test/kotlin/software/amazon/smithy/rust/codegen/client/smithy/protocols/eventstream/ClientEventStreamMarshallerGeneratorTest.kt index 936d3b6324..a3dcfc9b40 100644 --- a/codegen-client/src/test/kotlin/software/amazon/smithy/rust/codegen/client/smithy/protocols/eventstream/ClientEventStreamMarshallerGeneratorTest.kt +++ b/codegen-client/src/test/kotlin/software/amazon/smithy/rust/codegen/client/smithy/protocols/eventstream/ClientEventStreamMarshallerGeneratorTest.kt @@ -5,42 +5,30 @@ package software.amazon.smithy.rust.codegen.client.smithy.protocols.eventstream +import org.junit.jupiter.api.extension.ExtensionContext import org.junit.jupiter.params.ParameterizedTest +import org.junit.jupiter.params.provider.Arguments +import org.junit.jupiter.params.provider.ArgumentsProvider import org.junit.jupiter.params.provider.ArgumentsSource -import software.amazon.smithy.rust.codegen.client.smithy.ClientCodegenContext -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.protocols.Protocol -import software.amazon.smithy.rust.codegen.core.smithy.protocols.serialize.EventStreamMarshallerGenerator +import software.amazon.smithy.rust.codegen.client.testutil.clientIntegrationTest +import software.amazon.smithy.rust.codegen.core.testutil.EventStreamMarshallTestCases.writeMarshallTestCases import software.amazon.smithy.rust.codegen.core.testutil.EventStreamTestModels -import software.amazon.smithy.rust.codegen.core.testutil.EventStreamTestTools -import software.amazon.smithy.rust.codegen.core.testutil.EventStreamTestVariety -import software.amazon.smithy.rust.codegen.core.testutil.TestEventStreamProject -import software.amazon.smithy.rust.codegen.core.testutil.TestRuntimeConfig +import software.amazon.smithy.rust.codegen.core.testutil.testModule +import java.util.stream.Stream class ClientEventStreamMarshallerGeneratorTest { @ParameterizedTest @ArgumentsSource(TestCasesProvider::class) fun test(testCase: EventStreamTestModels.TestCase) { - EventStreamTestTools.runTestCase( - testCase, - object : ClientEventStreamBaseRequirements() { - override fun renderGenerator( - codegenContext: ClientCodegenContext, - project: TestEventStreamProject, - protocol: Protocol, - ): RuntimeType = EventStreamMarshallerGenerator( - project.model, - CodegenTarget.CLIENT, - TestRuntimeConfig, - project.symbolProvider, - project.streamShape, - protocol.structuredDataSerializer(project.operationShape), - testCase.requestContentType, - ).render() - }, - CodegenTarget.CLIENT, - EventStreamTestVariety.Marshall, - ) + clientIntegrationTest(testCase.model) { codegenContext, rustCrate -> + rustCrate.testModule { + writeMarshallTestCases(codegenContext, testCase, optionalBuilderInputs = false) + } + } } } + +class TestCasesProvider : ArgumentsProvider { + override fun provideArguments(context: ExtensionContext?): Stream = + EventStreamTestModels.TEST_CASES.map { Arguments.of(it) }.stream() +} diff --git a/codegen-client/src/test/kotlin/software/amazon/smithy/rust/codegen/client/smithy/protocols/eventstream/ClientEventStreamUnmarshallerGeneratorTest.kt b/codegen-client/src/test/kotlin/software/amazon/smithy/rust/codegen/client/smithy/protocols/eventstream/ClientEventStreamUnmarshallerGeneratorTest.kt index f9be7b3bf4..d0118d1f84 100644 --- a/codegen-client/src/test/kotlin/software/amazon/smithy/rust/codegen/client/smithy/protocols/eventstream/ClientEventStreamUnmarshallerGeneratorTest.kt +++ b/codegen-client/src/test/kotlin/software/amazon/smithy/rust/codegen/client/smithy/protocols/eventstream/ClientEventStreamUnmarshallerGeneratorTest.kt @@ -7,43 +7,60 @@ package software.amazon.smithy.rust.codegen.client.smithy.protocols.eventstream import org.junit.jupiter.params.ParameterizedTest import org.junit.jupiter.params.provider.ArgumentsSource -import software.amazon.smithy.codegen.core.Symbol -import software.amazon.smithy.model.shapes.StructureShape -import software.amazon.smithy.rust.codegen.client.smithy.ClientCodegenContext -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.builderSymbol -import software.amazon.smithy.rust.codegen.core.smithy.protocols.Protocol -import software.amazon.smithy.rust.codegen.core.smithy.protocols.parse.EventStreamUnmarshallerGenerator +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.EventStreamTestModels -import software.amazon.smithy.rust.codegen.core.testutil.EventStreamTestTools -import software.amazon.smithy.rust.codegen.core.testutil.EventStreamTestVariety -import software.amazon.smithy.rust.codegen.core.testutil.TestEventStreamProject +import software.amazon.smithy.rust.codegen.core.testutil.EventStreamUnmarshallTestCases.writeUnmarshallTestCases +import software.amazon.smithy.rust.codegen.core.testutil.IntegrationTestParams +import software.amazon.smithy.rust.codegen.core.testutil.testModule +import software.amazon.smithy.rust.codegen.core.testutil.unitTest class ClientEventStreamUnmarshallerGeneratorTest { @ParameterizedTest @ArgumentsSource(TestCasesProvider::class) fun test(testCase: EventStreamTestModels.TestCase) { - EventStreamTestTools.runTestCase( - testCase, - object : ClientEventStreamBaseRequirements() { - override fun renderGenerator( - codegenContext: ClientCodegenContext, - project: TestEventStreamProject, - protocol: Protocol, - ): RuntimeType { - fun builderSymbol(shape: StructureShape): Symbol = shape.builderSymbol(codegenContext.symbolProvider) - return EventStreamUnmarshallerGenerator( - protocol, - codegenContext, - project.operationShape, - project.streamShape, - ::builderSymbol, - ).render() - } - }, - CodegenTarget.CLIENT, - EventStreamTestVariety.Unmarshall, - ) + clientIntegrationTest( + testCase.model, + IntegrationTestParams(service = "test#TestService", addModuleToEventStreamAllowList = true), + ) { codegenContext, rustCrate -> + val generator = "crate::event_stream_serde::TestStreamUnmarshaller" + + rustCrate.testModule { + rust("##![allow(unused_imports, dead_code)]") + writeUnmarshallTestCases(codegenContext, testCase, optionalBuilderInputs = false) + + unitTest( + "unknown_message", + """ + let message = msg("event", "NewUnmodeledMessageType", "application/octet-stream", b"hello, world!"); + let result = $generator::new().unmarshall(&message); + assert!(result.is_ok(), "expected ok, got: {:?}", result); + assert!(expect_event(result.unwrap()).is_unknown()); + """, + ) + + unitTest( + "generic_error", + """ + let message = msg( + "exception", + "UnmodeledError", + "${testCase.responseContentType}", + br#"${testCase.validUnmodeledError}"# + ); + let result = $generator::new().unmarshall(&message); + assert!(result.is_ok(), "expected ok, got: {:?}", result); + match expect_error(result.unwrap()) { + TestStreamError::Unhandled(err) => { + let message = format!("{}", crate::error::DisplayErrorContext(&err)); + let expected = "message: \"unmodeled error\""; + assert!(message.contains(expected), "Expected '{message}' to contain '{expected}'"); + } + kind => panic!("expected generic error, but got {:?}", kind), + } + """, + ) + } + } } } diff --git a/codegen-client/src/test/kotlin/software/amazon/smithy/rust/codegen/client/smithy/transformers/RemoveEventStreamOperationsTest.kt b/codegen-client/src/test/kotlin/software/amazon/smithy/rust/codegen/client/smithy/transformers/RemoveEventStreamOperationsTest.kt index 7873a74c2e..4cc9c594aa 100644 --- a/codegen-client/src/test/kotlin/software/amazon/smithy/rust/codegen/client/smithy/transformers/RemoveEventStreamOperationsTest.kt +++ b/codegen-client/src/test/kotlin/software/amazon/smithy/rust/codegen/client/smithy/transformers/RemoveEventStreamOperationsTest.kt @@ -11,7 +11,7 @@ import org.junit.jupiter.api.Test import software.amazon.smithy.model.shapes.Shape import software.amazon.smithy.model.shapes.ShapeId import software.amazon.smithy.rust.codegen.client.smithy.ClientCodegenConfig -import software.amazon.smithy.rust.codegen.client.testutil.clientTestRustSettings +import software.amazon.smithy.rust.codegen.client.testutil.testClientRustSettings import software.amazon.smithy.rust.codegen.core.testutil.asSmithyModel import java.util.Optional @@ -49,7 +49,7 @@ internal class RemoveEventStreamOperationsTest { fun `remove event stream ops from services that are not in the allow list`() { val transformed = RemoveEventStreamOperations.transform( model, - clientTestRustSettings( + testClientRustSettings( codegenConfig = ClientCodegenConfig(eventStreamAllowList = setOf("not-test-module")), ), ) @@ -61,7 +61,7 @@ internal class RemoveEventStreamOperationsTest { fun `keep event stream ops from services that are in the allow list`() { val transformed = RemoveEventStreamOperations.transform( model, - clientTestRustSettings( + testClientRustSettings( codegenConfig = ClientCodegenConfig(eventStreamAllowList = setOf("test-module")), ), ) diff --git a/codegen-core/common-test-models/malformed-range-extras.smithy b/codegen-core/common-test-models/malformed-range-extras.smithy deleted file mode 100644 index 8fd9d93c11..0000000000 --- a/codegen-core/common-test-models/malformed-range-extras.smithy +++ /dev/null @@ -1,662 +0,0 @@ -$version: "2.0" - -namespace aws.protocoltests.extras.restjson.validation - -use aws.api#service -use aws.protocols#restJson1 -use smithy.test#httpMalformedRequestTests -use smithy.framework#ValidationException - -/// A REST JSON service that sends JSON requests and responses with validation applied -@service(sdkId: "Rest Json Validation Protocol") -@restJson1 -service MalformedRangeValidation { - version: "2022-11-23", - operations: [ - MalformedRange, - MalformedRangeOverride, - ] -} - -@suppress(["UnstableTrait"]) -@http(uri: "/MalformedRange", method: "POST") -operation MalformedRange { - input: MalformedRangeInput, - errors: [ValidationException] -} - -@suppress(["UnstableTrait"]) -@http(uri: "/MalformedRangeOverride", method: "POST") -operation MalformedRangeOverride { - input: MalformedRangeOverrideInput, - errors: [ValidationException] -} - -apply MalformedRange @httpMalformedRequestTests([ - { - id: "RestJsonMalformedRangeShort", - documentation: """ - When a short member does not fit within range bounds, - the response should be a 400 ValidationException.""", - protocol: restJson1, - request: { - method: "POST", - uri: "/MalformedRange", - body: """ - { "short" : $value:L }""", - headers: { - "content-type": "application/json" - } - }, - response: { - code: 400, - headers: { - "x-amzn-errortype": "ValidationException" - }, - body: { - mediaType: "application/json", - assertion: { - contents: """ - { "message" : "1 validation error detected. Value $value:L at '/short' failed to satisfy constraint: Member must be between 2 and 8, inclusive", - "fieldList" : [{"message": "Value $value:L at '/short' failed to satisfy constraint: Member must be between 2 and 8, inclusive", "path": "/short"}]}""" - } - } - }, - testParameters: { - value: ["1", "9"] - } - }, - { - id: "RestJsonMalformedRangeMinShort", - documentation: """ - When a short member does not fit within range bounds, - the response should be a 400 ValidationException.""", - protocol: restJson1, - request: { - method: "POST", - uri: "/MalformedRange", - body: """ - { "minShort" : 1 }""", - headers: { - "content-type": "application/json" - } - }, - response: { - code: 400, - headers: { - "x-amzn-errortype": "ValidationException" - }, - body: { - mediaType: "application/json", - assertion: { - contents: """ - { "message" : "1 validation error detected. Value 1 at '/minShort' failed to satisfy constraint: Member must be greater than or equal to 2", - "fieldList" : [{"message": "Value 1 at '/minShort' failed to satisfy constraint: Member must be greater than or equal to 2", "path": "/minShort"}]}""" - } - } - } - }, - { - id: "RestJsonMalformedRangeMaxShort", - documentation: """ - When a short member does not fit within range bounds, - the response should be a 400 ValidationException.""", - protocol: restJson1, - request: { - method: "POST", - uri: "/MalformedRange", - body: """ - { "maxShort" : 9 }""", - headers: { - "content-type": "application/json" - } - }, - response: { - code: 400, - headers: { - "x-amzn-errortype": "ValidationException" - }, - body: { - mediaType: "application/json", - assertion: { - contents: """ - { "message" : "1 validation error detected. Value 9 at '/maxShort' failed to satisfy constraint: Member must be less than or equal to 8", - "fieldList" : [{"message": "Value 9 at '/maxShort' failed to satisfy constraint: Member must be less than or equal to 8", "path": "/maxShort"}]}""" - } - } - } - }, - { - id: "RestJsonMalformedRangeInteger", - documentation: """ - When a integer member does not fit within range bounds, - the response should be a 400 ValidationException.""", - protocol: restJson1, - request: { - method: "POST", - uri: "/MalformedRange", - body: """ - { "integer" : $value:L }""", - headers: { - "content-type": "application/json" - } - }, - response: { - code: 400, - headers: { - "x-amzn-errortype": "ValidationException" - }, - body: { - mediaType: "application/json", - assertion: { - contents: """ - { "message" : "1 validation error detected. Value $value:L at '/integer' failed to satisfy constraint: Member must be between 2 and 8, inclusive", - "fieldList" : [{"message": "Value $value:L at '/integer' failed to satisfy constraint: Member must be between 2 and 8, inclusive", "path": "/integer"}]}""" - } - } - }, - testParameters: { - value: ["1", "9"] - } - }, - { - id: "RestJsonMalformedRangeMinInteger", - documentation: """ - When a integer member does not fit within range bounds, - the response should be a 400 ValidationException.""", - protocol: restJson1, - request: { - method: "POST", - uri: "/MalformedRange", - body: """ - { "minInteger" : 1 }""", - headers: { - "content-type": "application/json" - } - }, - response: { - code: 400, - headers: { - "x-amzn-errortype": "ValidationException" - }, - body: { - mediaType: "application/json", - assertion: { - contents: """ - { "message" : "1 validation error detected. Value 1 at '/minInteger' failed to satisfy constraint: Member must be greater than or equal to 2", - "fieldList" : [{"message": "Value 1 at '/minInteger' failed to satisfy constraint: Member must be greater than or equal to 2", "path": "/minInteger"}]}""" - } - } - } - }, - { - id: "RestJsonMalformedRangeMaxInteger", - documentation: """ - When a integer member does not fit within range bounds, - the response should be a 400 ValidationException.""", - protocol: restJson1, - request: { - method: "POST", - uri: "/MalformedRange", - body: """ - { "maxInteger" : 9 }""", - headers: { - "content-type": "application/json" - } - }, - response: { - code: 400, - headers: { - "x-amzn-errortype": "ValidationException" - }, - body: { - mediaType: "application/json", - assertion: { - contents: """ - { "message" : "1 validation error detected. Value 9 at '/maxInteger' failed to satisfy constraint: Member must be less than or equal to 8", - "fieldList" : [{"message": "Value 9 at '/maxInteger' failed to satisfy constraint: Member must be less than or equal to 8", "path": "/maxInteger"}]}""" - } - } - } - }, - { - id: "RestJsonMalformedRangeLong", - documentation: """ - When a long member does not fit within range bounds, - the response should be a 400 ValidationException.""", - protocol: restJson1, - request: { - method: "POST", - uri: "/MalformedRange", - body: """ - { "long" : $value:L }""", - headers: { - "content-type": "application/json" - } - }, - response: { - code: 400, - headers: { - "x-amzn-errortype": "ValidationException" - }, - body: { - mediaType: "application/json", - assertion: { - contents: """ - { "message" : "1 validation error detected. Value $value:L at '/long' failed to satisfy constraint: Member must be between 2 and 8, inclusive", - "fieldList" : [{"message": "Value $value:L at '/long' failed to satisfy constraint: Member must be between 2 and 8, inclusive", "path": "/long"}]}""" - } - } - }, - testParameters: { - value: ["1", "9"] - } - }, - { - id: "RestJsonMalformedRangeMinLong", - documentation: """ - When a long member does not fit within range bounds, - the response should be a 400 ValidationException.""", - protocol: restJson1, - request: { - method: "POST", - uri: "/MalformedRange", - body: """ - { "minLong" : 1 }""", - headers: { - "content-type": "application/json" - } - }, - response: { - code: 400, - headers: { - "x-amzn-errortype": "ValidationException" - }, - body: { - mediaType: "application/json", - assertion: { - contents: """ - { "message" : "1 validation error detected. Value 1 at '/minLong' failed to satisfy constraint: Member must be greater than or equal to 2", - "fieldList" : [{"message": "Value 1 at '/minLong' failed to satisfy constraint: Member must be greater than or equal to 2", "path": "/minLong"}]}""" - } - } - } - }, - { - id: "RestJsonMalformedRangeMaxLong", - documentation: """ - When a long member does not fit within range bounds, - the response should be a 400 ValidationException.""", - protocol: restJson1, - request: { - method: "POST", - uri: "/MalformedRange", - body: """ - { "maxLong" : 9 }""", - headers: { - "content-type": "application/json" - } - }, - response: { - code: 400, - headers: { - "x-amzn-errortype": "ValidationException" - }, - body: { - mediaType: "application/json", - assertion: { - contents: """ - { "message" : "1 validation error detected. Value 9 at '/maxLong' failed to satisfy constraint: Member must be less than or equal to 8", - "fieldList" : [{"message": "Value 9 at '/maxLong' failed to satisfy constraint: Member must be less than or equal to 8", "path": "/maxLong"}]}""" - } - } - } - }, -]) - -// now repeat the above tests, but for the more specific constraints applied to the input member -apply MalformedRangeOverride @httpMalformedRequestTests([ - { - id: "RestJsonMalformedRangeShortOverride", - documentation: """ - When a short member does not fit within range bounds, - the response should be a 400 ValidationException.""", - protocol: restJson1, - request: { - method: "POST", - uri: "/MalformedRangeOverride", - body: """ - { "short" : $value:L }""", - headers: { - "content-type": "application/json" - } - }, - response: { - code: 400, - headers: { - "x-amzn-errortype": "ValidationException" - }, - body: { - mediaType: "application/json", - assertion: { - contents: """ - { "message" : "1 validation error detected. Value $value:L at '/short' failed to satisfy constraint: Member must be between 4 and 6, inclusive", - "fieldList" : [{"message": "Value $value:L at '/short' failed to satisfy constraint: Member must be between 4 and 6, inclusive", "path": "/short"}]}""" - } - } - }, - testParameters: { - value: ["3", "7"] - } - }, - { - id: "RestJsonMalformedRangeMinShortOverride", - documentation: """ - When a short member does not fit within range bounds, - the response should be a 400 ValidationException.""", - protocol: restJson1, - request: { - method: "POST", - uri: "/MalformedRangeOverride", - body: """ - { "minShort" : 3 }""", - headers: { - "content-type": "application/json" - } - }, - response: { - code: 400, - headers: { - "x-amzn-errortype": "ValidationException" - }, - body: { - mediaType: "application/json", - assertion: { - contents: """ - { "message" : "1 validation error detected. Value 3 at '/minShort' failed to satisfy constraint: Member must be greater than or equal to 4", - "fieldList" : [{"message": "Value 3 at '/minShort' failed to satisfy constraint: Member must be greater than or equal to 4", "path": "/minShort"}]}""" - } - } - } - }, - { - id: "RestJsonMalformedRangeMaxShortOverride", - documentation: """ - When a short member does not fit within range bounds, - the response should be a 400 ValidationException.""", - protocol: restJson1, - request: { - method: "POST", - uri: "/MalformedRangeOverride", - body: """ - { "maxShort" : 7 }""", - headers: { - "content-type": "application/json" - } - }, - response: { - code: 400, - headers: { - "x-amzn-errortype": "ValidationException" - }, - body: { - mediaType: "application/json", - assertion: { - contents: """ - { "message" : "1 validation error detected. Value 7 at '/maxShort' failed to satisfy constraint: Member must be less than or equal to 6", - "fieldList" : [{"message": "Value 7 at '/maxShort' failed to satisfy constraint: Member must be less than or equal to 6", "path": "/maxShort"}]}""" - } - } - } - }, - { - id: "RestJsonMalformedRangeIntegerOverride", - documentation: """ - When a integer member does not fit within range bounds, - the response should be a 400 ValidationException.""", - protocol: restJson1, - request: { - method: "POST", - uri: "/MalformedRangeOverride", - body: """ - { "integer" : $value:L }""", - headers: { - "content-type": "application/json" - } - }, - response: { - code: 400, - headers: { - "x-amzn-errortype": "ValidationException" - }, - body: { - mediaType: "application/json", - assertion: { - contents: """ - { "message" : "1 validation error detected. Value $value:L at '/integer' failed to satisfy constraint: Member must be between 4 and 6, inclusive", - "fieldList" : [{"message": "Value $value:L at '/integer' failed to satisfy constraint: Member must be between 4 and 6, inclusive", "path": "/integer"}]}""" - } - } - }, - testParameters: { - value: ["3", "7"] - } - }, - { - id: "RestJsonMalformedRangeMinIntegerOverride", - documentation: """ - When a integer member does not fit within range bounds, - the response should be a 400 ValidationException.""", - protocol: restJson1, - request: { - method: "POST", - uri: "/MalformedRangeOverride", - body: """ - { "minInteger" : 3 }""", - headers: { - "content-type": "application/json" - } - }, - response: { - code: 400, - headers: { - "x-amzn-errortype": "ValidationException" - }, - body: { - mediaType: "application/json", - assertion: { - contents: """ - { "message" : "1 validation error detected. Value 3 at '/minInteger' failed to satisfy constraint: Member must be greater than or equal to 4", - "fieldList" : [{"message": "Value 3 at '/minInteger' failed to satisfy constraint: Member must be greater than or equal to 4", "path": "/minInteger"}]}""" - } - } - } - }, - { - id: "RestJsonMalformedRangeMaxIntegerOverride", - documentation: """ - When a integer member does not fit within range bounds, - the response should be a 400 ValidationException.""", - protocol: restJson1, - request: { - method: "POST", - uri: "/MalformedRangeOverride", - body: """ - { "maxInteger" : 7 }""", - headers: { - "content-type": "application/json" - } - }, - response: { - code: 400, - headers: { - "x-amzn-errortype": "ValidationException" - }, - body: { - mediaType: "application/json", - assertion: { - contents: """ - { "message" : "1 validation error detected. Value 7 at '/maxInteger' failed to satisfy constraint: Member must be less than or equal to 6", - "fieldList" : [{"message": "Value 7 at '/maxInteger' failed to satisfy constraint: Member must be less than or equal to 6", "path": "/maxInteger"}]}""" - } - } - } - }, - { - id: "RestJsonMalformedRangeLongOverride", - documentation: """ - When a long member does not fit within range bounds, - the response should be a 400 ValidationException.""", - protocol: restJson1, - request: { - method: "POST", - uri: "/MalformedRangeOverride", - body: """ - { "long" : $value:L }""", - headers: { - "content-type": "application/json" - } - }, - response: { - code: 400, - headers: { - "x-amzn-errortype": "ValidationException" - }, - body: { - mediaType: "application/json", - assertion: { - contents: """ - { "message" : "1 validation error detected. Value $value:L at '/long' failed to satisfy constraint: Member must be between 4 and 6, inclusive", - "fieldList" : [{"message": "Value $value:L at '/long' failed to satisfy constraint: Member must be between 4 and 6, inclusive", "path": "/long"}]}""" - } - } - }, - testParameters: { - value: ["3", "7"] - } - }, - { - id: "RestJsonMalformedRangeMinLongOverride", - documentation: """ - When a long member does not fit within range bounds, - the response should be a 400 ValidationException.""", - protocol: restJson1, - request: { - method: "POST", - uri: "/MalformedRangeOverride", - body: """ - { "minLong" : 3 }""", - headers: { - "content-type": "application/json" - } - }, - response: { - code: 400, - headers: { - "x-amzn-errortype": "ValidationException" - }, - body: { - mediaType: "application/json", - assertion: { - contents: """ - { "message" : "1 validation error detected. Value 3 at '/minLong' failed to satisfy constraint: Member must be greater than or equal to 4", - "fieldList" : [{"message": "Value 3 at '/minLong' failed to satisfy constraint: Member must be greater than or equal to 4", "path": "/minLong"}]}""" - } - } - } - }, - { - id: "RestJsonMalformedRangeMaxLongOverride", - documentation: """ - When a long member does not fit within range bounds, - the response should be a 400 ValidationException.""", - protocol: restJson1, - request: { - method: "POST", - uri: "/MalformedRangeOverride", - body: """ - { "maxLong" : 7 }""", - headers: { - "content-type": "application/json" - } - }, - response: { - code: 400, - headers: { - "x-amzn-errortype": "ValidationException" - }, - body: { - mediaType: "application/json", - assertion: { - contents: """ - { "message" : "1 validation error detected. Value 7 at '/maxLong' failed to satisfy constraint: Member must be less than or equal to 6", - "fieldList" : [{"message": "Value 7 at '/maxLong' failed to satisfy constraint: Member must be less than or equal to 6", "path": "/maxLong"}]}""" - } - } - } - }, -]) - -structure MalformedRangeInput { - short: RangeShort, - minShort: MinShort, - maxShort: MaxShort, - - integer: RangeInteger, - minInteger: MinInteger, - maxInteger: MaxInteger, - - long: RangeLong, - minLong: MinLong, - maxLong: MaxLong, -} - -structure MalformedRangeOverrideInput { - @range(min: 4, max: 6) - short: RangeShort, - @range(min: 4) - minShort: MinShort, - @range(max: 6) - maxShort: MaxShort, - - @range(min: 4, max: 6) - integer: RangeInteger, - @range(min: 4) - minInteger: MinInteger, - @range(max: 6) - maxInteger: MaxInteger, - - @range(min: 4, max: 6) - long: RangeLong, - @range(min: 4) - minLong: MinLong, - @range(max: 6) - maxLong: MaxLong, -} - -@range(min: 2, max: 8) -short RangeShort - -@range(min: 2) -short MinShort - -@range(max: 8) -short MaxShort - -@range(min: 2, max: 8) -integer RangeInteger - -@range(min: 2) -integer MinInteger - -@range(max: 8) -integer MaxInteger - -@range(min: 2, max: 8) -long RangeLong - -@range(min: 2) -long MinLong - -@range(max: 8) -long MaxLong diff --git a/codegen-core/common-test-models/misc.smithy b/codegen-core/common-test-models/misc.smithy index 7185e5a3b9..f78bb6db7c 100644 --- a/codegen-core/common-test-models/misc.smithy +++ b/codegen-core/common-test-models/misc.smithy @@ -20,7 +20,6 @@ service MiscService { ResponseCodeRequiredOperation, ResponseCodeHttpFallbackOperation, ResponseCodeDefaultOperation, - AcceptHeaderStarService, ], } @@ -204,36 +203,6 @@ structure ResponseCodeRequiredOutput { responseCode: Integer, } -// TODO(https://github.com/awslabs/smithy/pull/1365): remove when these tests are in smithy -@http(method: "GET", uri: "/test-accept-header") -@httpRequestTests([ - { - id: "AcceptHeaderStarRequestTest", - protocol: "aws.protocols#restJson1", - uri: "/test-accept-header", - headers: { - "Accept": "application/*", - }, - params: {}, - body: "{}", - method: "GET", - appliesTo: "server", - }, - { - id: "AcceptHeaderStarStarRequestTest", - protocol: "aws.protocols#restJson1", - uri: "/test-accept-header", - headers: { - "Accept": "*/*", - }, - params: {}, - body: "{}", - method: "GET", - appliesTo: "server", - } -]) -operation AcceptHeaderStarService {} - @http(uri: "/required-header-collection-operation", method: "GET") operation RequiredHeaderCollectionOperation { input: RequiredHeaderCollectionOperationInputOutput, diff --git a/codegen-core/common-test-models/naming-obstacle-course-casing.smithy b/codegen-core/common-test-models/naming-obstacle-course-casing.smithy new file mode 100644 index 0000000000..fb80a46d48 --- /dev/null +++ b/codegen-core/common-test-models/naming-obstacle-course-casing.smithy @@ -0,0 +1,63 @@ +$version: "1.0" +namespace casing + +use aws.protocols#awsJson1_1 + +// TODO(https://github.com/awslabs/smithy-rs/issues/2340): The commented part of the model breaks the generator in a +// miriad of ways. Any solution to the linked issue must address this. + +/// Confounds model generation machinery with lots of problematic casing +@awsJson1_1 +service ACRONYMInside_Service { + operations: [ + DoNothing, + // ACRONYMInside_Op + // ACRONYM_InsideOp + ] +} + +operation DoNothing {} + +// operation ACRONYMInside_Op { +// input: Input, +// output: Output, +// errors: [Error], +// } + +// operation ACRONYM_InsideOp { +// input: Input, +// output: Output, +// errors: [Error], +// } + +// structure Input { +// ACRONYMInside_Member: ACRONYMInside_Struct, +// ACRONYM_Inside_Member: ACRONYM_InsideStruct, +// ACRONYM_InsideMember: ACRONYMInsideStruct +// } + +// structure Output { +// ACRONYMInside_Member: ACRONYMInside_Struct, +// ACRONYM_Inside_Member: ACRONYM_InsideStruct, +// ACRONYM_InsideMember: ACRONYMInsideStruct +// } + +// @error("client") +// structure Error { +// ACRONYMInside_Member: ACRONYMInside_Struct, +// ACRONYM_Inside_Member: ACRONYM_InsideStruct, +// ACRONYM_InsideMember: ACRONYMInsideStruct +// } + +// structure ACRONYMInside_Struct { +// ACRONYMInside_Member: ACRONYM_InsideStruct, +// ACRONYM_Inside_Member: Integer, +// } + +// structure ACRONYM_InsideStruct { +// ACRONYMInside_Member: Integer, +// } + +// structure ACRONYMInsideStruct { +// ACRONYMInside_Member: Integer, +// } diff --git a/codegen-core/common-test-models/simple.smithy b/codegen-core/common-test-models/simple.smithy index 43c4bc6aca..c7e58c8e4a 100644 --- a/codegen-core/common-test-models/simple.smithy +++ b/codegen-core/common-test-models/simple.smithy @@ -1,136 +1,22 @@ -$version: "1.0" +$version: "2.0" namespace com.amazonaws.simple use aws.protocols#restJson1 -use smithy.test#httpRequestTests -use smithy.test#httpResponseTests -use smithy.framework#ValidationException @restJson1 -@title("SimpleService") -@documentation("A simple service example, with a Service resource that can be registered and a readonly healthcheck") service SimpleService { - version: "2022-01-01", - resources: [ - Service, - ], operations: [ - Healthcheck, - StoreServiceBlob, - ], + Operation + ] } -@documentation("Id of the service that will be registered") -string ServiceId - -@documentation("Name of the service that will be registered") -string ServiceName - -@error("client") -@documentation( - """ - Returned when a new resource cannot be created because one already exists. - """ -) -structure ResourceAlreadyExists { - @required - message: String -} - -@documentation("A resource that can register services") -resource Service { - identifiers: { id: ServiceId }, - put: RegisterService, -} - -@idempotent -@http(method: "PUT", uri: "/service/{id}") -@documentation("Service register operation") -@httpRequestTests([ - { - id: "RegisterServiceRequestTest", - protocol: "aws.protocols#restJson1", - uri: "/service/1", - headers: { - "Content-Type": "application/json", - }, - params: { id: "1", name: "TestService" }, - body: "{\"name\":\"TestService\"}", - method: "PUT", - } -]) -@httpResponseTests([ - { - id: "RegisterServiceResponseTest", - protocol: "aws.protocols#restJson1", - params: { id: "1", name: "TestService" }, - body: "{\"id\":\"1\",\"name\":\"TestService\"}", - code: 200, - headers: { - "Content-Length": "31" - } - } -]) -operation RegisterService { - input: RegisterServiceInputRequest, - output: RegisterServiceOutputResponse, - errors: [ResourceAlreadyExists, ValidationException] -} - -@documentation("Service register input structure") -structure RegisterServiceInputRequest { - @required - @httpLabel - id: ServiceId, - name: ServiceName, -} - -@documentation("Service register output structure") -structure RegisterServiceOutputResponse { - @required - id: ServiceId, - name: ServiceName, -} - -@readonly -@http(uri: "/healthcheck", method: "GET") -@documentation("Read-only healthcheck operation") -operation Healthcheck { - input: HealthcheckInputRequest, - output: HealthcheckOutputResponse +@http(uri: "/operation", method: "POST") +operation Operation { + input: OperationInputOutput + output: OperationInputOutput } -@documentation("Service healthcheck output structure") -structure HealthcheckInputRequest { - -} - -@documentation("Service healthcheck input structure") -structure HealthcheckOutputResponse { - -} - -@readonly -@http(method: "POST", uri: "/service/{id}/blob") -@documentation("Stores a blob for a service id") -operation StoreServiceBlob { - input: StoreServiceBlobInput, - output: StoreServiceBlobOutput, - errors: [ValidationException] -} - -@documentation("Store a blob for a service id input structure") -structure StoreServiceBlobInput { - @required - @httpLabel - id: ServiceId, - @required - @httpPayload - content: Blob, -} - -@documentation("Store a blob for a service id output structure") -structure StoreServiceBlobOutput { - +structure OperationInputOutput { + message: String } 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 6083b73d72..810065e315 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 @@ -96,6 +96,13 @@ class InlineDependency( CargoDependency.Http, ) + fun awsQueryCompatibleErrors(runtimeConfig: RuntimeConfig) = + forInlineableRustFile( + "aws_query_compatible_errors", + CargoDependency.smithyJson(runtimeConfig), + CargoDependency.Http, + ) + fun idempotencyToken() = forInlineableRustFile("idempotency_token", CargoDependency.FastRand) @@ -126,6 +133,7 @@ data class CargoDependency( val scope: DependencyScope = DependencyScope.Compile, val optional: Boolean = false, val features: Set = emptySet(), + val defaultFeatures: Boolean = true, val rustName: String = name.replace("-", "_"), ) : RustDependency(name) { val key: Triple get() = Triple(name, location, scope) @@ -134,6 +142,8 @@ data class CargoDependency( return copy(features = features.toMutableSet().apply { add(feature) }) } + fun toDevDependency() = copy(scope = DependencyScope.Dev) + override fun version(): String = when (location) { is CratesIo -> location.version is Local -> "local" @@ -159,6 +169,9 @@ data class CargoDependency( if (optional) { attribs["optional"] = true } + if (!defaultFeatures) { + attribs["default-features"] = false + } return attribs } @@ -212,7 +225,7 @@ data class CargoDependency( 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) + 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 = @@ -221,7 +234,12 @@ data class CargoDependency( val Smol: CargoDependency = CargoDependency("smol", CratesIo("1.2.0"), DependencyScope.Dev) val TempFile: CargoDependency = CargoDependency("tempfile", CratesIo("3.2.0"), DependencyScope.Dev) val Tokio: CargoDependency = - CargoDependency("tokio", CratesIo("1.8.4"), DependencyScope.Dev, features = setOf("macros", "test-util", "rt-multi-thread")) + CargoDependency( + "tokio", + CratesIo("1.23.1"), + DependencyScope.Dev, + features = setOf("macros", "test-util", "rt-multi-thread"), + ) val TracingAppender: CargoDependency = CargoDependency( "tracing-appender", CratesIo("0.2.2"), @@ -237,13 +255,20 @@ data class CargoDependency( fun smithyAsync(runtimeConfig: RuntimeConfig) = runtimeConfig.smithyRuntimeCrate("smithy-async") fun smithyChecksums(runtimeConfig: RuntimeConfig) = runtimeConfig.smithyRuntimeCrate("smithy-checksums") fun smithyClient(runtimeConfig: RuntimeConfig) = runtimeConfig.smithyRuntimeCrate("smithy-client") + fun smithyClientTestUtil(runtimeConfig: RuntimeConfig) = + smithyClient(runtimeConfig).toDevDependency().withFeature("test-util") + fun smithyEventStream(runtimeConfig: RuntimeConfig) = runtimeConfig.smithyRuntimeCrate("smithy-eventstream") fun smithyHttp(runtimeConfig: RuntimeConfig) = runtimeConfig.smithyRuntimeCrate("smithy-http") + fun smithyHttpAuth(runtimeConfig: RuntimeConfig) = runtimeConfig.smithyRuntimeCrate("smithy-http-auth") fun smithyHttpTower(runtimeConfig: RuntimeConfig) = runtimeConfig.smithyRuntimeCrate("smithy-http-tower") fun smithyJson(runtimeConfig: RuntimeConfig) = runtimeConfig.smithyRuntimeCrate("smithy-json") fun smithyProtocolTestHelpers(runtimeConfig: RuntimeConfig) = runtimeConfig.smithyRuntimeCrate("smithy-protocol-test", scope = DependencyScope.Dev) + fun smithyQuery(runtimeConfig: RuntimeConfig) = runtimeConfig.smithyRuntimeCrate("smithy-query") + fun smithyRuntime(runtimeConfig: RuntimeConfig) = runtimeConfig.smithyRuntimeCrate("smithy-runtime") + fun smithyRuntimeApi(runtimeConfig: RuntimeConfig) = runtimeConfig.smithyRuntimeCrate("smithy-runtime-api") fun smithyTypes(runtimeConfig: RuntimeConfig) = runtimeConfig.smithyRuntimeCrate("smithy-types") fun smithyXml(runtimeConfig: RuntimeConfig) = runtimeConfig.smithyRuntimeCrate("smithy-xml") 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 6745e3b2ee..b8c3237e41 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 @@ -5,7 +5,9 @@ package software.amazon.smithy.rust.codegen.core.rustlang +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.util.PANIC /** * RustModule system. @@ -29,10 +31,13 @@ sealed class RustModule { data class LeafModule( val name: String, val rustMetadata: RustMetadata, - val documentation: String? = null, val parent: RustModule = LibRs, val inline: Boolean = false, + /* module is a cfg(test) module */ + val tests: Boolean = false, + val documentationOverride: String? = null, ) : RustModule() { + init { check(!name.contains("::")) { "Module names CANNOT contain `::`—modules must be nested with parent (name was: `$name`)" @@ -45,6 +50,12 @@ sealed class RustModule { "Module `$name` cannot be a module name—it is a reserved word." } } + + /** Convert a module into a module gated with `#[cfg(test)]` */ + fun cfgTest(): LeafModule = this.copy( + rustMetadata = rustMetadata.copy(additionalAttributes = rustMetadata.additionalAttributes + Attribute.CfgTest), + tests = true, + ) } companion object { @@ -53,49 +64,56 @@ sealed class RustModule { fun new( name: String, visibility: Visibility, - documentation: String? = null, inline: Boolean = false, parent: RustModule = LibRs, additionalAttributes: List = listOf(), + documentationOverride: String? = null, ): LeafModule { return LeafModule( RustReservedWords.escapeIfNeeded(name), RustMetadata(visibility = visibility, additionalAttributes = additionalAttributes), - documentation, inline = inline, parent = parent, + documentationOverride = documentationOverride, ) } /** Creates a new public module */ - fun public(name: String, documentation: String? = null, parent: RustModule = LibRs): LeafModule = - new(name, visibility = Visibility.PUBLIC, documentation = documentation, inline = false, parent = parent) - - /** Creates a new private module */ - fun private(name: String, documentation: String? = null, parent: RustModule = LibRs): LeafModule = - new(name, visibility = Visibility.PRIVATE, documentation = documentation, inline = false, parent = parent) - - fun pubCrate(name: String, documentation: String? = null, parent: RustModule): LeafModule = - new(name, visibility = Visibility.PUBCRATE, documentation = documentation, inline = false, parent = parent) - - /* Common modules used across client, server and tests */ - val Config = public("config", documentation = "Configuration for the service.") - val Error = public("error", documentation = "All error types that operations can return. Documentation on these types is copied from the model.") - val Model = public("model", documentation = "Data structures used by operation inputs/outputs. Documentation on these types is copied from the model.") - val Input = public("input", documentation = "Input structures for operations. Documentation on these types is copied from the model.") - val Output = public("output", documentation = "Output structures for operations. Documentation on these types is copied from the model.") - val Types = public("types", documentation = "Data primitives referenced by other data types.") - - /** - * Helper method to generate the `operation` Rust module. - * Its visibility depends on the generation context (client or server). - */ - fun operation(visibility: Visibility): RustModule = + fun public(name: String, parent: RustModule = LibRs, documentationOverride: String? = null): LeafModule = new( - "operation", - visibility = visibility, - documentation = "All operations that this crate can perform.", + name, + visibility = Visibility.PUBLIC, + inline = false, + parent = parent, + documentationOverride = documentationOverride, ) + + /** Creates a new private module */ + fun private(name: String, parent: RustModule = LibRs): LeafModule = + new(name, visibility = Visibility.PRIVATE, inline = false, parent = parent) + + fun pubCrate( + name: String, + parent: RustModule = LibRs, + additionalAttributes: List = emptyList(), + ): LeafModule = new( + name, visibility = Visibility.PUBCRATE, + inline = false, + parent = parent, + additionalAttributes = additionalAttributes, + ) + + fun inlineTests( + name: String = "test", + parent: RustModule = LibRs, + additionalAttributes: List = listOf(), + ) = new( + name, + Visibility.PRIVATE, + inline = true, + additionalAttributes = additionalAttributes, + parent = parent, + ).cfgTest() } fun isInline(): Boolean = when (this) { @@ -129,10 +147,13 @@ sealed class RustModule { * pub mod my_module_name * ``` */ - fun renderModStatement(writer: RustWriter) { + fun renderModStatement(writer: RustWriter, moduleDocProvider: ModuleDocProvider) { when (this) { is LeafModule -> { - documentation?.let { docs -> writer.docs(docs) } + if (name.startsWith("r#")) { + PANIC("Something went wrong with module name escaping (module named '$name'). This is a bug.") + } + ModuleDocProvider.writeDocs(moduleDocProvider, this, writer) rustMetadata.render(writer) writer.write("mod $name;") } diff --git a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/rustlang/RustReservedWords.kt b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/rustlang/RustReservedWords.kt index efe9ae7cc8..c9fdbafb13 100644 --- a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/rustlang/RustReservedWords.kt +++ b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/rustlang/RustReservedWords.kt @@ -8,53 +8,50 @@ package software.amazon.smithy.rust.codegen.core.rustlang import software.amazon.smithy.codegen.core.ReservedWordSymbolProvider import software.amazon.smithy.codegen.core.ReservedWords import software.amazon.smithy.codegen.core.Symbol -import software.amazon.smithy.model.Model +import software.amazon.smithy.model.shapes.EnumShape import software.amazon.smithy.model.shapes.MemberShape import software.amazon.smithy.model.shapes.Shape import software.amazon.smithy.model.shapes.StructureShape import software.amazon.smithy.model.shapes.UnionShape -import software.amazon.smithy.model.traits.EnumDefinition -import software.amazon.smithy.rust.codegen.core.smithy.MaybeRenamed +import software.amazon.smithy.model.traits.EnumTrait 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.generators.UnionGenerator import software.amazon.smithy.rust.codegen.core.smithy.renamedFrom +import software.amazon.smithy.rust.codegen.core.util.hasTrait import software.amazon.smithy.rust.codegen.core.util.letIf -import software.amazon.smithy.rust.codegen.core.util.orNull -import software.amazon.smithy.rust.codegen.core.util.toPascalCase -class RustReservedWordSymbolProvider(private val base: RustSymbolProvider, private val model: Model) : - WrappingSymbolProvider(base) { +data class RustReservedWordConfig( + /** Map of struct member names that should get renamed */ + val structureMemberMap: Map, + /** Map of union member names that should get renamed */ + val unionMemberMap: Map, + /** Map of enum member names that should get renamed */ + val enumMemberMap: Map, +) + +class RustReservedWordSymbolProvider( + private val base: RustSymbolProvider, + private val reservedWordConfig: RustReservedWordConfig, +) : WrappingSymbolProvider(base) { private val internal = - ReservedWordSymbolProvider.builder().symbolProvider(base).memberReservedWords(RustReservedWords).build() + ReservedWordSymbolProvider.builder().symbolProvider(base) + .nameReservedWords(RustReservedWords) + .memberReservedWords(RustReservedWords) + .build() override fun toMemberName(shape: MemberShape): String { - val baseName = internal.toMemberName(shape) - return when (val container = model.expectShape(shape.container)) { - is StructureShape -> when (baseName) { - "build" -> "build_value" - "builder" -> "builder_value" - "default" -> "default_value" - "send" -> "send_value" - // To avoid conflicts with the `make_operation` and `presigned` functions on generated inputs - "make_operation" -> "make_operation_value" - "presigned" -> "presigned_value" - "customize" -> "customize_value" - else -> baseName - } + val baseName = super.toMemberName(shape) + val reservedWordReplacedName = internal.toMemberName(shape) + val container = model.expectShape(shape.container) + return when { + container is StructureShape -> + reservedWordConfig.structureMemberMap.getOrDefault(baseName, reservedWordReplacedName) - is UnionShape -> when (baseName) { - // Unions contain an `Unknown` variant. This exists to support parsing data returned from the server - // that represent union variants that have been added since this SDK was generated. - UnionGenerator.UnknownVariantName -> "${UnionGenerator.UnknownVariantName}Value" - "${UnionGenerator.UnknownVariantName}Value" -> "${UnionGenerator.UnknownVariantName}Value_" - // Self cannot be used as a raw identifier, so we can't use the normal escaping strategy - // https://internals.rust-lang.org/t/raw-identifiers-dont-work-for-all-identifiers/9094/4 - "Self" -> "SelfValue" - // Real models won't end in `_` so it's safe to stop here - "SelfValue" -> "SelfValue_" - else -> baseName - } + container is UnionShape -> + reservedWordConfig.unionMemberMap.getOrDefault(baseName, reservedWordReplacedName) + + container is EnumShape || container.hasTrait() -> + reservedWordConfig.enumMemberMap.getOrDefault(baseName, reservedWordReplacedName) else -> error("unexpected container: $container") } @@ -67,46 +64,36 @@ class RustReservedWordSymbolProvider(private val base: RustSymbolProvider, priva * code generators to generate special docs. */ override fun toSymbol(shape: Shape): Symbol { + // Sanity check that the symbol provider stack is set up correctly + check(super.toSymbol(shape).renamedFrom() == null) { + "RustReservedWordSymbolProvider should only run once" + } + + var renamedSymbol = internal.toSymbol(shape) return when (shape) { is MemberShape -> { val container = model.expectShape(shape.container) - if (!(container is StructureShape || container is UnionShape)) { + val containerIsEnum = container is EnumShape || container.hasTrait() + if (container !is StructureShape && container !is UnionShape && !containerIsEnum) { return base.toSymbol(shape) } val previousName = base.toMemberName(shape) val escapedName = this.toMemberName(shape) - val baseSymbol = base.toSymbol(shape) // if the names don't match and it isn't a simple escaping with `r#`, record a rename - baseSymbol.letIf(escapedName != previousName && !escapedName.contains("r#")) { - it.toBuilder().renamedFrom(previousName).build() - } + renamedSymbol.toBuilder().name(escapedName) + .letIf(escapedName != previousName && !escapedName.contains("r#")) { + it.renamedFrom(previousName) + }.build() } - else -> base.toSymbol(shape) + else -> renamedSymbol } } +} - override fun toEnumVariantName(definition: EnumDefinition): MaybeRenamed? { - val baseName = base.toEnumVariantName(definition) ?: return null - check(definition.name.orNull()?.toPascalCase() == baseName.name) { - "Enum variants must already be in pascal case ${baseName.name} differed from ${baseName.name.toPascalCase()}. Definition: ${definition.name}" - } - check(baseName.renamedFrom == null) { - "definitions should only pass through the renamer once" - } - return when (baseName.name) { - // Self cannot be used as a raw identifier, so we can't use the normal escaping strategy - // https://internals.rust-lang.org/t/raw-identifiers-dont-work-for-all-identifiers/9094/4 - "Self" -> MaybeRenamed("SelfValue", "Self") - // Real models won't end in `_` so it's safe to stop here - "SelfValue" -> MaybeRenamed("SelfValue_", "SelfValue") - // Unknown is used as the name of the variant containing unexpected values - "Unknown" -> MaybeRenamed("UnknownValue", "Unknown") - // Real models won't end in `_` so it's safe to stop here - "UnknownValue" -> MaybeRenamed("UnknownValue_", "UnknownValue") - else -> baseName - } - } +enum class EscapeFor { + TypeName, + ModuleName, } object RustReservedWords : ReservedWords { @@ -166,17 +153,33 @@ object RustReservedWords : ReservedWords { "try", ) - private val cantBeRaw = setOf("self", "crate", "super") + // Some things can't be used as a raw identifier, so we can't use the normal escaping strategy + // https://internals.rust-lang.org/t/raw-identifiers-dont-work-for-all-identifiers/9094/4 + private val keywordEscapingMap = mapOf( + "crate" to "crate_", + "super" to "super_", + "self" to "self_", + "Self" to "SelfValue", + // Real models won't end in `_` so it's safe to stop here + "SelfValue" to "SelfValue_", + ) - override fun escape(word: String): String = when { - cantBeRaw.contains(word) -> "${word}_" - else -> "r##$word" - } + override fun escape(word: String): String = doEscape(word, EscapeFor.TypeName) - fun escapeIfNeeded(word: String): String = when (isReserved(word)) { - true -> escape(word) - else -> word - } + private fun doEscape(word: String, escapeFor: EscapeFor = EscapeFor.TypeName): String = + when (val mapped = keywordEscapingMap[word]) { + null -> when (escapeFor) { + EscapeFor.TypeName -> "r##$word" + EscapeFor.ModuleName -> "${word}_" + } + else -> mapped + } + + fun escapeIfNeeded(word: String, escapeFor: EscapeFor = EscapeFor.TypeName): String = + when (isReserved(word)) { + true -> doEscape(word, escapeFor) + else -> word + } - override fun isReserved(word: String): Boolean = RustKeywords.contains(word) + override fun isReserved(word: String): Boolean = RustKeywords.contains(word) || keywordEscapingMap.contains(word) } 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 d812b4909c..ec7d46a6f1 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 @@ -222,7 +222,9 @@ fun RustType.asArgument(name: String) = Argument( fun RustType.render(fullyQualified: Boolean = true): String { val namespace = if (fullyQualified) { this.namespace?.let { "$it::" } ?: "" - } else "" + } else { + "" + } val base = when (this) { is RustType.Unit -> this.name is RustType.Bool -> this.name @@ -325,6 +327,16 @@ fun RustType.isCopy(): Boolean = when (this) { else -> false } +/** Returns true if the type implements Eq */ +fun RustType.isEq(): Boolean = when (this) { + is RustType.Integer -> true + is RustType.Bool -> true + is RustType.String -> true + is RustType.Unit -> true + is RustType.Container -> this.member.isEq() + else -> false +} + enum class Visibility { PRIVATE, PUBCRATE, PUBLIC; @@ -416,7 +428,7 @@ enum class AttributeKind { /** * Outer attributes, written without the bang after the hash, apply to the thing that follows the attribute. */ - Outer + Outer, } /** @@ -464,10 +476,14 @@ class Attribute(val inner: Writable) { val AllowClippyUnnecessaryWraps = Attribute(allow("clippy::unnecessary_wraps")) val AllowClippyUselessConversion = Attribute(allow("clippy::useless_conversion")) val AllowClippyUnnecessaryLazyEvaluations = Attribute(allow("clippy::unnecessary_lazy_evaluations")) + val AllowClippyTooManyArguments = Attribute(allow("clippy::too_many_arguments")) val AllowDeadCode = Attribute(allow("dead_code")) val AllowDeprecated = Attribute(allow("deprecated")) val AllowIrrefutableLetPatterns = Attribute(allow("irrefutable_let_patterns")) + val AllowMissingDocs = Attribute(allow("missing_docs")) + val AllowNonSnakeCase = Attribute(allow("non_snake_case")) val AllowUnreachableCode = Attribute(allow("unreachable_code")) + val AllowUnreachablePatterns = Attribute(allow("unreachable_patterns")) val AllowUnusedImports = Attribute(allow("unused_imports")) val AllowUnusedMut = Attribute(allow("unused_mut")) val AllowUnusedVariables = Attribute(allow("unused_variables")) @@ -558,3 +574,10 @@ class Attribute(val inner: Writable) { } } } + +/** Render all attributes in this list, one after another */ +fun Collection.render(writer: RustWriter) { + for (attr in this) { + attr.render(writer) + } +} 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 2b4a17c193..7fd296db83 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 @@ -10,6 +10,7 @@ import org.jsoup.Jsoup import org.jsoup.nodes.Element import software.amazon.smithy.codegen.core.CodegenException import software.amazon.smithy.codegen.core.Symbol +import software.amazon.smithy.codegen.core.SymbolDependencyContainer import software.amazon.smithy.codegen.core.SymbolWriter import software.amazon.smithy.codegen.core.SymbolWriter.Factory import software.amazon.smithy.model.Model @@ -23,6 +24,7 @@ import software.amazon.smithy.model.shapes.ShapeId import software.amazon.smithy.model.traits.DeprecatedTrait 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.isOptional import software.amazon.smithy.rust.codegen.core.smithy.protocols.serialize.ValueExpression @@ -115,7 +117,7 @@ private fun , U> T.withTemplate( * This enables conditionally wrapping a block in a prefix/suffix, e.g. * * ``` - * writer.withBlock("Some(", ")", conditional = symbol.isOptional()) { + * writer.conditionalBlock("Some(", ")", conditional = symbol.isOptional()) { * write("symbolValue") * } * ``` @@ -166,9 +168,10 @@ private fun transformTemplate(template: String, scope: Array> T.docsOrFallback( note: String? = null, ): T { val htmlDocs: (T.() -> Unit)? = when (docString?.isNotBlank()) { - true -> { { docs(normalizeHtml(escape(docString))) } } + true -> { + { docs(normalizeHtml(escape(docString))) } + } + else -> null } return docsOrFallback(htmlDocs, autoSuppressMissingDocs, note) @@ -295,13 +301,23 @@ fun > T.docsOrFallback( return this } -/** Document the containing entity (e.g. module, crate, etc.) +/** + * Document the containing entity (e.g. module, crate, etc.) * Instead of prefixing lines with `///` lines are prefixed with `//!` */ -fun RustWriter.containerDocs(text: String, vararg args: Any): RustWriter { - return docs(text, newlinePrefix = "//! ", args = args) +fun RustWriter.containerDocs(text: String, vararg args: Any, trimStart: Boolean = true): RustWriter { + return docs(text, newlinePrefix = "//! ", args = args, trimStart = trimStart) } +/** + * Equivalent of [rustTemplate] for container docs. + */ +fun RustWriter.containerDocsTemplate( + text: String, + vararg args: Pair, + trimStart: Boolean = false, +): RustWriter = docsTemplate(text, newlinePrefix = "//! ", args = args, trimStart = trimStart) + /** * Write RustDoc-style docs into the writer * @@ -310,7 +326,12 @@ fun RustWriter.containerDocs(text: String, vararg args: Any): RustWriter { * - Tabs are replaced with spaces * - Empty newlines are removed */ -fun > T.docs(text: String, vararg args: Any, newlinePrefix: String = "/// "): T { +fun > T.docs( + text: String, + vararg args: Any, + newlinePrefix: String = "/// ", + trimStart: Boolean = true, +): T { // Because writing docs relies on the newline prefix, ensure that there was a new line written // before we write the docs this.ensureNewline() @@ -318,14 +339,28 @@ fun > T.docs(text: String, vararg args: Any, newlinePr setNewlinePrefix(newlinePrefix) val cleaned = text.lines() .joinToString("\n") { - // Rustdoc warns on tabs in documentation - it.trimStart().replace("\t", " ") + when (trimStart) { + true -> it.trimStart() + else -> it + }.replace("\t", " ") // Rustdoc warns on tabs in documentation } write(cleaned, *args) popState() return this } +/** + * [rustTemplate] equivalent for doc comments. + */ +fun > T.docsTemplate( + text: String, + vararg args: Pair, + newlinePrefix: String = "/// ", + trimStart: Boolean = false, +): T = withTemplate(text, args, trim = false) { template -> + docs(template, newlinePrefix = newlinePrefix, trimStart = trimStart) +} + /** * Writes a comment into the code * @@ -376,11 +411,26 @@ private fun Element.changeInto(tagName: String) { replaceWith(Element(tagName).also { elem -> elem.appendChildren(childNodesCopy()) }) } +/** Write an `impl` block for the given symbol */ +fun RustWriter.implBlock(symbol: Symbol, block: Writable) { + rustBlock("impl ${symbol.name}") { + block() + } +} + /** * Write _exactly_ the text as written into the code writer without newlines or formatting */ fun RustWriter.raw(text: String) = writeInline(escape(text)) +/** + * [rustTemplate] equivalent for `raw()`. Note: This function won't automatically escape formatter symbols. + */ +fun RustWriter.rawTemplate(text: String, vararg args: Pair) = + withTemplate(text, args, trim = false) { templated -> + writeInline(templated) + } + /** * Rustdoc doesn't support `r#` for raw identifiers. * This function adjusts doc links to refer to raw identifiers directly. @@ -394,6 +444,8 @@ class RustWriter private constructor( private val printWarning: Boolean = true, /** Insert comments indicating where code was generated */ private val debugMode: Boolean = false, + /** When true, automatically change all dependencies to be in the test scope */ + val devDependenciesOnly: Boolean = false, ) : SymbolWriter(UseDeclarations(namespace)) { companion object { @@ -407,8 +459,16 @@ class RustWriter private constructor( fun factory(debugMode: Boolean): Factory = Factory { fileName: String, namespace: String -> when { fileName.endsWith(".toml") -> RustWriter(fileName, namespace, "#", debugMode = debugMode) + fileName.endsWith(".py") -> RustWriter(fileName, namespace, "#", debugMode = debugMode) fileName.endsWith(".md") -> rawWriter(fileName, debugMode = debugMode) fileName == "LICENSE" -> rawWriter(fileName, debugMode = debugMode) + fileName.startsWith("tests/") -> RustWriter( + fileName, + namespace, + debugMode = debugMode, + devDependenciesOnly = true, + ) + else -> RustWriter(fileName, namespace, debugMode = debugMode) } } @@ -454,7 +514,9 @@ class RustWriter private constructor( init { expressionStart = '#' if (filename.endsWith(".rs")) { - require(namespace.startsWith("crate") || filename.startsWith("tests/")) { "We can only write into files in the crate (got $namespace)" } + require(namespace.startsWith("crate") || filename.startsWith("tests/")) { + "We can only write into files in the crate (got $namespace)" + } } putFormatter('T', formatter) putFormatter('D', RustDocLinker()) @@ -463,7 +525,9 @@ class RustWriter private constructor( fun module(): String? = if (filename.startsWith("src") && filename.endsWith(".rs")) { filename.removeSuffix(".rs").substringAfterLast(File.separatorChar) - } else null + } else { + null + } fun safeName(prefix: String = "var"): String { n += 1 @@ -474,6 +538,22 @@ class RustWriter private constructor( preamble.add(preWriter) } + private fun addDependencyTestAware(dependencyContainer: SymbolDependencyContainer): RustWriter { + if (!devDependenciesOnly) { + super.addDependency(dependencyContainer) + } else { + dependencyContainer.dependencies.forEach { dependency -> + super.addDependency( + when (val dep = RustDependency.fromSymbolDependency(dependency)) { + is CargoDependency -> dep.toDevDependency() + else -> dependencyContainer + }, + ) + } + } + return this + } + /** * Create an inline module. Instead of being in a new file, inline modules are written as a `mod { ... }` block * directly into the parent. @@ -481,7 +561,7 @@ class RustWriter private constructor( * Callers must take care to use [this] when writing to ensure code is written to the right place: * ```kotlin * val writer = RustWriter.forModule("model") - * writer.withModule(RustModule.public("nested")) { + * writer.withInlineModule(RustModule.public("nested")) { * Generator(...).render(this) // GOOD * Generator(...).render(writer) // WRONG! * } @@ -491,22 +571,29 @@ class RustWriter private constructor( */ fun withInlineModule( module: RustModule.LeafModule, + moduleDocProvider: ModuleDocProvider?, moduleWriter: Writable, ): RustWriter { check(module.isInline()) { "Only inline modules may be used with `withInlineModule`: $module" } + // In Rust, modules must specify their own imports—they don't have access to the parent scope. // To easily handle this, create a new inner writer to collect imports, then dump it // into an inline module. - val innerWriter = RustWriter(this.filename, "${this.namespace}::${module.name}", printWarning = false) + val innerWriter = RustWriter( + this.filename, + "${this.namespace}::${module.name}", + printWarning = false, + devDependenciesOnly = devDependenciesOnly || module.tests, + ) moduleWriter(innerWriter) - module.documentation?.let { docs -> docs(docs) } + ModuleDocProvider.writeDocs(moduleDocProvider, module, this) module.rustMetadata.render(this) rustBlock("mod ${module.name}") { writeWithNoFormatting(innerWriter.toString()) } - innerWriter.dependencies.forEach { addDependency(it) } + innerWriter.dependencies.forEach { addDependencyTestAware(it) } return this } @@ -605,15 +692,19 @@ class RustWriter private constructor( override fun toString(): String { val contents = super.toString() val preheader = if (preamble.isNotEmpty()) { - val prewriter = RustWriter(filename, namespace, printWarning = false) + val prewriter = RustWriter(filename, namespace, printWarning = false, devDependenciesOnly = devDependenciesOnly) preamble.forEach { it(prewriter) } prewriter.toString() - } else null + } else { + null + } // Hack to support TOML: the [commentCharacter] is overridden to support writing TOML. val header = if (printWarning) { "$commentCharacter Code generated by software.amazon.smithy.rust.codegen.smithy-rs. DO NOT EDIT." - } else null + } else { + null + } val useDecls = importContainer.toString().ifEmpty { null } @@ -623,7 +714,7 @@ class RustWriter private constructor( fun format(r: Any) = formatter.apply(r, "") fun addDepsRecursively(symbol: Symbol) { - addDependency(symbol) + addDependencyTestAware(symbol) symbol.references.forEach { addDepsRecursively(it.symbol) } } @@ -647,9 +738,9 @@ 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) + val innerWriter = RustWriter(filename, namespace, printWarning = false, devDependenciesOnly = devDependenciesOnly) func(innerWriter) - innerWriter.dependencies.forEach { addDependency(it) } + innerWriter.dependencies.forEach { addDependencyTestAware(it) } return innerWriter.toString().trimEnd() } } @@ -658,11 +749,15 @@ class RustWriter private constructor( override fun apply(t: Any, u: String): String { return when (t) { is RuntimeType -> { - t.dependency?.also { addDependency(it) } + t.dependency?.also { addDependencyTestAware(it) } // for now, use the fully qualified type name t.fullyQualifiedName() } + is RustModule -> { + t.fullyQualifiedPath() + } + is Symbol -> { addDepsRecursively(t) t.rustType().render(fullyQualified = true) @@ -676,9 +771,9 @@ 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) + val innerWriter = RustWriter(filename, namespace, printWarning = false, devDependenciesOnly = devDependenciesOnly) func(innerWriter) - innerWriter.dependencies.forEach { addDependency(it) } + innerWriter.dependencies.forEach { addDependencyTestAware(it) } return innerWriter.toString().trimEnd() } diff --git a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/CodegenContext.kt b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/CodegenContext.kt index 17a21c887c..3fa6f688e3 100644 --- a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/CodegenContext.kt +++ b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/CodegenContext.kt @@ -31,6 +31,11 @@ open class CodegenContext( */ open val symbolProvider: RustSymbolProvider, + /** + * Provider of documentation for generated Rust modules. + */ + open val moduleDocProvider: ModuleDocProvider?, + /** * Entrypoint service shape for code generation. */ @@ -79,4 +84,9 @@ open class CodegenContext( * it must be in snake-case. Call this method to get this crate's name in snake-case. */ fun moduleUseName() = moduleName.replace("-", "_") + + /** Return a ModuleDocProvider or panic if one wasn't configured */ + fun expectModuleDocProvider(): ModuleDocProvider = checkNotNull(moduleDocProvider) { + "A ModuleDocProvider must be set on the CodegenContext" + } } 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 768a24073b..157a6f2539 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 @@ -6,22 +6,57 @@ package software.amazon.smithy.rust.codegen.core.smithy import software.amazon.smithy.build.FileManifest +import software.amazon.smithy.codegen.core.Symbol import software.amazon.smithy.codegen.core.SymbolProvider 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.DependencyScope import software.amazon.smithy.rust.codegen.core.rustlang.Feature import software.amazon.smithy.rust.codegen.core.rustlang.InlineDependency import software.amazon.smithy.rust.codegen.core.rustlang.RustDependency 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.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.smithy.generators.CargoTomlGenerator import software.amazon.smithy.rust.codegen.core.smithy.generators.LibRsCustomization import software.amazon.smithy.rust.codegen.core.smithy.generators.LibRsGenerator import software.amazon.smithy.rust.codegen.core.smithy.generators.ManifestCustomizations +/** Provider of documentation for generated Rust modules */ +interface ModuleDocProvider { + companion object { + fun writeDocs(provider: ModuleDocProvider?, module: RustModule.LeafModule, writer: RustWriter) { + check( + provider != null || + module.documentationOverride != null || + module.rustMetadata.visibility != Visibility.PUBLIC, + ) { + "Documentation must be provided for public modules, either via ModuleDocumentationProvider, or " + + "by the module documentationOverride. Module: $module" + } + try { + when { + module.documentationOverride != null -> writer.docs(module.documentationOverride) + else -> provider?.docsWriter(module)?.also { writeTo -> writeTo(writer) } + } + } catch (e: NotImplementedError) { + // Catch `TODO()` and rethrow only if its a public module + if (module.rustMetadata.visibility == Visibility.PUBLIC) { + throw e + } + } + } + } + + /** Returns documentation for the given module */ + fun docsWriter(module: RustModule.LeafModule): Writable? +} + /** * RustCrate abstraction. * @@ -43,6 +78,7 @@ open class RustCrate( fileManifest: FileManifest, private val symbolProvider: SymbolProvider, coreCodegenConfig: CoreCodegenConfig, + val moduleDocProvider: ModuleDocProvider, ) { private val inner = WriterDelegator(fileManifest, symbolProvider, RustWriter.factory(coreCodegenConfig.debugMode)) private val features: MutableSet = mutableSetOf() @@ -146,21 +182,34 @@ open class RustCrate( is RustModule.LibRs -> lib { moduleWriter(this) } is RustModule.LeafModule -> { checkDups(module) - // Create a dependency which adds the mod statement for this module. This will be added to the writer - // so that _usage_ of this module will generate _exactly one_ `mod ` with the correct modifiers. - val modStatement = RuntimeType.forInlineFun("mod_" + module.fullyQualifiedPath(), module.parent) { - module.renderModStatement(this) - } - val path = module.fullyQualifiedPath().split("::").drop(1).joinToString("/") - inner.useFileWriter("src/$path.rs", module.fullyQualifiedPath()) { writer -> - moduleWriter(writer) - writer.addDependency(modStatement.dependency) + + if (module.isInline()) { + withModule(module.parent) { + withInlineModule(module, moduleDocProvider, moduleWriter) + } + } else { + // Create a dependency which adds the mod statement for this module. This will be added to the writer + // so that _usage_ of this module will generate _exactly one_ `mod ` with the correct modifiers. + val modStatement = RuntimeType.forInlineFun("mod_" + module.fullyQualifiedPath(), module.parent) { + module.renderModStatement(this, moduleDocProvider) + } + val path = module.fullyQualifiedPath().split("::").drop(1).joinToString("/") + inner.useFileWriter("src/$path.rs", module.fullyQualifiedPath()) { writer -> + moduleWriter(writer) + writer.addDependency(modStatement.dependency) + } } } } return this } + /** + * Returns the module for a given Shape. + */ + fun moduleFor(shape: Shape, moduleWriter: Writable): RustCrate = + withModule((symbolProvider as RustSymbolProvider).moduleForShape(shape), moduleWriter) + /** * Create a new file directly */ @@ -169,18 +218,27 @@ open class RustCrate( fileWriter(it) } } -} -val ErrorsModule = RustModule.public("error", documentation = "All error types that operations can return. Documentation on these types is copied from the model.") -val OperationsModule = RustModule.public("operation", documentation = "All operations that this crate can perform.") -val ModelsModule = RustModule.public("model", documentation = "Data structures used by operation inputs/outputs. Documentation on these types is copied from the model.") -val InputsModule = RustModule.public("input", documentation = "Input structures for operations. Documentation on these types is copied from the model.") -val OutputsModule = RustModule.public("output", documentation = "Output structures for operations. Documentation on these types is copied from the model.") + /** + * Render something in a private module and re-export it into the given symbol. + * + * @param privateModule: Private module to render into + * @param symbol: The symbol of the thing being rendered, which will be re-exported. This symbol + * should be the public-facing symbol rather than the private symbol. + */ + fun inPrivateModuleWithReexport(privateModule: RustModule.LeafModule, symbol: Symbol, writer: Writable) { + withModule(privateModule, writer) + privateModule.toType().resolve(symbol.name).toSymbol().also { privateSymbol -> + withModule(symbol.module()) { + rust("pub use #T;", privateSymbol) + } + } + } +} -val UnconstrainedModule = - RustModule.private("unconstrained", "Unconstrained types for constrained shapes.") -val ConstrainedModule = - RustModule.private("constrained", "Constrained types for constrained shapes.") +// TODO(https://github.com/awslabs/smithy-rs/issues/2341): Remove unconstrained/constrained from codegen-core +val UnconstrainedModule = RustModule.private("unconstrained") +val ConstrainedModule = RustModule.private("constrained") /** * Finalize all the writers by: @@ -198,10 +256,12 @@ fun WriterDelegator.finalize( this.useFileWriter("src/lib.rs", "crate::lib") { LibRsGenerator(settings, model, libRsCustomizations, requireDocs).render(it) } - val cargoDependencies = mergeDependencyFeatures( + val cargoDependencies = + this.dependencies.map { RustDependency.fromSymbolDependency(it) } - .filterIsInstance().distinct(), - ) + .filterIsInstance().distinct() + .mergeDependencyFeatures() + .mergeIdenticalTestDependencies() this.useFileWriter("Cargo.toml") { val cargoToml = CargoTomlGenerator( settings, @@ -219,13 +279,26 @@ private fun CargoDependency.mergeWith(other: CargoDependency): CargoDependency { check(key == other.key) return copy( features = features + other.features, + defaultFeatures = defaultFeatures || other.defaultFeatures, optional = optional && other.optional, ) } -fun mergeDependencyFeatures(cargoDependencies: List): List = - cargoDependencies.groupBy { it.key } +internal fun List.mergeDependencyFeatures(): List = + this.groupBy { it.key } .mapValues { group -> group.value.reduce { acc, next -> acc.mergeWith(next) } } .values .toList() .sortedBy { it.name } + +/** + * If the same dependency exists both in prod and test scope, remove it from the test scope. + */ +internal fun List.mergeIdenticalTestDependencies(): List { + val compileDeps = + this.filter { it.scope == DependencyScope.Compile }.toSet() + + return this.filterNot { + it.scope == DependencyScope.Dev && compileDeps.contains(it.copy(scope = DependencyScope.Compile)) + } +} diff --git a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/DirectedWalker.kt b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/DirectedWalker.kt index 51c0b4ecf8..f48b996045 100644 --- a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/DirectedWalker.kt +++ b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/DirectedWalker.kt @@ -19,11 +19,8 @@ import java.util.function.Predicate class DirectedWalker(model: Model) { private val inner = Walker(model) - fun walkShapes(shape: Shape): Set { - return walkShapes(shape) { _ -> true } - } + fun walkShapes(shape: Shape): Set = walkShapes(shape) { true } - fun walkShapes(shape: Shape, predicate: Predicate): Set { - return inner.walkShapes(shape) { rel -> predicate.test(rel) && rel.direction == RelationshipDirection.DIRECTED } - } + fun walkShapes(shape: Shape, predicate: Predicate): Set = + inner.walkShapes(shape) { rel -> predicate.test(rel) && rel.direction == RelationshipDirection.DIRECTED } } 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 1aff86f7d4..6eeab26d65 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 @@ -6,7 +6,6 @@ package software.amazon.smithy.rust.codegen.core.smithy import software.amazon.smithy.codegen.core.Symbol -import software.amazon.smithy.model.Model import software.amazon.smithy.model.shapes.MemberShape import software.amazon.smithy.model.shapes.OperationShape import software.amazon.smithy.model.shapes.Shape @@ -14,7 +13,6 @@ 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.generators.error.eventStreamErrorSymbol 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 @@ -29,7 +27,6 @@ import software.amazon.smithy.rust.codegen.core.util.isOutputEventStream class EventStreamSymbolProvider( private val runtimeConfig: RuntimeConfig, base: RustSymbolProvider, - private val model: Model, private val target: CodegenTarget, ) : WrappingSymbolProvider(base) { override fun toSymbol(shape: Shape): Symbol { @@ -49,7 +46,7 @@ class EventStreamSymbolProvider( val error = if (target == CodegenTarget.SERVER && unionShape.eventStreamErrors().isEmpty()) { RuntimeType.smithyHttp(runtimeConfig).resolve("event_stream::MessageStreamError").toSymbol() } else { - unionShape.eventStreamErrorSymbol(this).toSymbol() + symbolForEventStreamError(unionShape) } val errorFmt = error.rustType().render(fullyQualified = true) val innerFmt = initial.rustType().stripOuter().render(fullyQualified = true) 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 d49ae411b7..7064b57d70 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 @@ -80,10 +80,11 @@ data class RuntimeConfig( */ fun fromNode(maybeNode: Optional): RuntimeConfig { val node = maybeNode.orElse(Node.objectNode()) - val crateVersionMap = node.getObjectMember("versions").orElse(Node.objectNode()).members.entries.let { members -> - val map = members.associate { it.key.toString() to it.value.expectStringNode().value } - CrateVersionMap(map) - } + val crateVersionMap = + node.getObjectMember("versions").orElse(Node.objectNode()).members.entries.let { members -> + val map = members.associate { it.key.toString() to it.value.expectStringNode().value } + CrateVersionMap(map) + } val path = node.getStringMember("relativePath").orNull()?.value val runtimeCrateLocation = RuntimeCrateLocation(path = path, versions = crateVersionMap) return RuntimeConfig( @@ -95,7 +96,11 @@ data class RuntimeConfig( val crateSrcPrefix: String = cratePrefix.replace("-", "_") - fun smithyRuntimeCrate(runtimeCrateName: String, optional: Boolean = false, scope: DependencyScope = DependencyScope.Compile): CargoDependency { + fun smithyRuntimeCrate( + runtimeCrateName: String, + optional: Boolean = false, + scope: DependencyScope = DependencyScope.Compile, + ): CargoDependency { val crateName = "$cratePrefix-$runtimeCrateName" return CargoDependency( crateName, @@ -236,7 +241,6 @@ data class RuntimeType(val path: String, val dependency: RustDependency? = null) val Tracing = CargoDependency.Tracing.toType() // codegen types - val Config = RuntimeType("crate::config") val ConstrainedTrait = RuntimeType("crate::constrained::Constrained", InlineDependency.constrained()) val MaybeConstrained = RuntimeType("crate::constrained::MaybeConstrained", InlineDependency.constrained()) @@ -251,38 +255,79 @@ data class RuntimeType(val path: String, val dependency: RustDependency? = null) fun smithyClient(runtimeConfig: RuntimeConfig) = CargoDependency.smithyClient(runtimeConfig).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() + fun smithyHttpTower(runtimeConfig: RuntimeConfig) = CargoDependency.smithyHttpTower(runtimeConfig).toType() fun smithyJson(runtimeConfig: RuntimeConfig) = CargoDependency.smithyJson(runtimeConfig).toType() fun smithyQuery(runtimeConfig: RuntimeConfig) = CargoDependency.smithyQuery(runtimeConfig).toType() fun smithyTypes(runtimeConfig: RuntimeConfig) = CargoDependency.smithyTypes(runtimeConfig).toType() fun smithyXml(runtimeConfig: RuntimeConfig) = CargoDependency.smithyXml(runtimeConfig).toType() - private fun smithyProtocolTest(runtimeConfig: RuntimeConfig) = CargoDependency.smithyProtocolTestHelpers(runtimeConfig).toType() + private fun smithyProtocolTest(runtimeConfig: RuntimeConfig) = + CargoDependency.smithyProtocolTestHelpers(runtimeConfig).toType() // smithy runtime type members - fun base64Decode(runtimeConfig: RuntimeConfig): RuntimeType = smithyTypes(runtimeConfig).resolve("base64::decode") - fun base64Encode(runtimeConfig: RuntimeConfig): RuntimeType = smithyTypes(runtimeConfig).resolve("base64::encode") + fun base64Decode(runtimeConfig: RuntimeConfig): RuntimeType = + smithyTypes(runtimeConfig).resolve("base64::decode") + + fun base64Encode(runtimeConfig: RuntimeConfig): RuntimeType = + smithyTypes(runtimeConfig).resolve("base64::encode") + 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 errorKind(runtimeConfig: RuntimeConfig) = smithyTypes(runtimeConfig).resolve("retry::ErrorKind") + fun retryErrorKind(runtimeConfig: RuntimeConfig) = smithyTypes(runtimeConfig).resolve("retry::ErrorKind") fun eventStreamReceiver(runtimeConfig: RuntimeConfig): RuntimeType = smithyHttp(runtimeConfig).resolve("event_stream::Receiver") - fun genericError(runtimeConfig: RuntimeConfig) = smithyTypes(runtimeConfig).resolve("Error") + 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 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 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") - fun parseHttpResponse(runtimeConfig: RuntimeConfig) = smithyHttp(runtimeConfig).resolve("response::ParseHttpResponse") - fun parseStrictResponse(runtimeConfig: RuntimeConfig) = smithyHttp(runtimeConfig).resolve("response::ParseStrictResponse") - fun protocolTest(runtimeConfig: RuntimeConfig, func: String): RuntimeType = smithyProtocolTest(runtimeConfig).resolve(func) - fun provideErrorKind(runtimeConfig: RuntimeConfig) = smithyTypes(runtimeConfig).resolve("retry::ProvideErrorKind") + fun parseHttpResponse(runtimeConfig: RuntimeConfig) = + smithyHttp(runtimeConfig).resolve("response::ParseHttpResponse") + + fun parseStrictResponse(runtimeConfig: RuntimeConfig) = + smithyHttp(runtimeConfig).resolve("response::ParseStrictResponse") + + fun protocolTest(runtimeConfig: RuntimeConfig, func: String): RuntimeType = + smithyProtocolTest(runtimeConfig).resolve(func) + + fun provideErrorKind(runtimeConfig: RuntimeConfig) = + smithyTypes(runtimeConfig).resolve("retry::ProvideErrorKind") + fun queryFormat(runtimeConfig: RuntimeConfig, func: String) = smithyHttp(runtimeConfig).resolve("query::$func") fun sdkBody(runtimeConfig: RuntimeConfig): RuntimeType = smithyHttp(runtimeConfig).resolve("body::SdkBody") fun sdkError(runtimeConfig: RuntimeConfig): RuntimeType = smithyHttp(runtimeConfig).resolve("result::SdkError") - fun sdkSuccess(runtimeConfig: RuntimeConfig): RuntimeType = smithyHttp(runtimeConfig).resolve("result::SdkSuccess") - fun timestampFormat(runtimeConfig: RuntimeConfig, format: TimestampFormatTrait.Format): RuntimeType { + fun sdkSuccess(runtimeConfig: RuntimeConfig): RuntimeType = + smithyHttp(runtimeConfig).resolve("result::SdkSuccess") + + fun parseTimestampFormat( + codegenTarget: CodegenTarget, + runtimeConfig: RuntimeConfig, + format: TimestampFormatTrait.Format, + ): RuntimeType { val timestampFormat = when (format) { TimestampFormatTrait.Format.EPOCH_SECONDS -> "EpochSeconds" + // clients allow offsets, servers do nt + TimestampFormatTrait.Format.DATE_TIME -> codegenTarget.ifClient { "DateTimeWithOffset" } ?: "DateTime" + TimestampFormatTrait.Format.HTTP_DATE -> "HttpDate" + TimestampFormatTrait.Format.UNKNOWN -> TODO() + } + + return smithyTypes(runtimeConfig).resolve("date_time::Format::$timestampFormat") + } + + fun serializeTimestampFormat( + runtimeConfig: RuntimeConfig, + format: TimestampFormatTrait.Format, + ): RuntimeType { + val timestampFormat = when (format) { + TimestampFormatTrait.Format.EPOCH_SECONDS -> "EpochSeconds" + // clients allow offsets, servers do not TimestampFormatTrait.Format.DATE_TIME -> "DateTime" TimestampFormatTrait.Format.HTTP_DATE -> "HttpDate" TimestampFormatTrait.Format.UNKNOWN -> TODO() @@ -291,6 +336,9 @@ data class RuntimeType(val path: String, val dependency: RustDependency? = null) return smithyTypes(runtimeConfig).resolve("date_time::Format::$timestampFormat") } + fun captureRequest(runtimeConfig: RuntimeConfig) = + CargoDependency.smithyClientTestUtil(runtimeConfig).toType().resolve("test_connection::capture_request") + fun forInlineDependency(inlineDependency: InlineDependency) = RuntimeType("crate::${inlineDependency.name}", inlineDependency) fun forInlineFun(name: String, module: RustModule, func: Writable) = RuntimeType( @@ -301,10 +349,13 @@ data class RuntimeType(val path: String, val dependency: RustDependency? = null) // inlinable types fun ec2QueryErrors(runtimeConfig: RuntimeConfig) = forInlineDependency(InlineDependency.ec2QueryErrors(runtimeConfig)) + fun wrappedXmlErrors(runtimeConfig: RuntimeConfig) = forInlineDependency(InlineDependency.wrappedXmlErrors(runtimeConfig)) + fun unwrappedXmlErrors(runtimeConfig: RuntimeConfig) = forInlineDependency(InlineDependency.unwrappedXmlErrors(runtimeConfig)) + val IdempotencyToken by lazy { forInlineDependency(InlineDependency.idempotencyToken()) } } } diff --git a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/RustSymbolProvider.kt b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/RustSymbolProvider.kt new file mode 100644 index 0000000000..2314007aa9 --- /dev/null +++ b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/RustSymbolProvider.kt @@ -0,0 +1,101 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +package software.amazon.smithy.rust.codegen.core.smithy + +import software.amazon.smithy.codegen.core.Symbol +import software.amazon.smithy.codegen.core.SymbolProvider +import software.amazon.smithy.model.Model +import software.amazon.smithy.model.knowledge.NullableIndex +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.Shape +import software.amazon.smithy.model.shapes.UnionShape +import software.amazon.smithy.rust.codegen.core.rustlang.RustModule + +/** + * SymbolProvider interface that carries additional configuration and module/symbol resolution. + */ +interface RustSymbolProvider : SymbolProvider { + val model: Model + val moduleProviderContext: ModuleProviderContext + val config: RustSymbolProviderConfig + + fun moduleForShape(shape: Shape): RustModule.LeafModule = + config.moduleProvider.moduleForShape(moduleProviderContext, shape) + fun moduleForOperationError(operation: OperationShape): RustModule.LeafModule = + config.moduleProvider.moduleForOperationError(moduleProviderContext, operation) + fun moduleForEventStreamError(eventStream: UnionShape): RustModule.LeafModule = + config.moduleProvider.moduleForEventStreamError(moduleProviderContext, eventStream) + fun moduleForBuilder(shape: Shape): RustModule.LeafModule = + config.moduleProvider.moduleForBuilder(moduleProviderContext, shape, toSymbol(shape)) + + /** Returns the symbol for an operation error */ + fun symbolForOperationError(operation: OperationShape): Symbol + + /** Returns the symbol for an event stream error */ + fun symbolForEventStreamError(eventStream: UnionShape): Symbol + + /** Returns the symbol for a builder */ + fun symbolForBuilder(shape: Shape): Symbol +} + +/** + * Module providers can't use the full CodegenContext since they're invoked from + * inside the SymbolVisitor, which is created before CodegenContext is created. + */ +data class ModuleProviderContext( + val settings: CoreRustSettings, + val model: Model, + val serviceShape: ServiceShape?, +) + +fun CodegenContext.toModuleProviderContext(): ModuleProviderContext = + ModuleProviderContext(settings, model, serviceShape) + +/** + * Provider for RustModules so that the symbol provider knows where to organize things. + */ +interface ModuleProvider { + /** Returns the module for a shape */ + fun moduleForShape(context: ModuleProviderContext, shape: Shape): RustModule.LeafModule + + /** Returns the module for an operation error */ + fun moduleForOperationError(context: ModuleProviderContext, operation: OperationShape): RustModule.LeafModule + + /** Returns the module for an event stream error */ + fun moduleForEventStreamError(context: ModuleProviderContext, eventStream: UnionShape): RustModule.LeafModule + + /** Returns the module for a builder */ + fun moduleForBuilder(context: ModuleProviderContext, shape: Shape, symbol: Symbol): RustModule.LeafModule +} + +/** + * Configuration for symbol providers. + */ +data class RustSymbolProviderConfig( + val runtimeConfig: RuntimeConfig, + val renameExceptions: Boolean, + val nullabilityCheckMode: NullableIndex.CheckMode, + val moduleProvider: ModuleProvider, + val nameBuilderFor: (Symbol) -> String = { _ -> "Builder" }, +) + +/** + * Default delegator to enable easily decorating another symbol provider. + */ +open class WrappingSymbolProvider(private val base: RustSymbolProvider) : RustSymbolProvider { + override val model: Model get() = base.model + override val moduleProviderContext: ModuleProviderContext get() = base.moduleProviderContext + override val config: RustSymbolProviderConfig get() = base.config + + override fun toSymbol(shape: Shape): Symbol = base.toSymbol(shape) + override fun toMemberName(shape: MemberShape): String = base.toMemberName(shape) + override fun symbolForOperationError(operation: OperationShape): Symbol = base.symbolForOperationError(operation) + override fun symbolForEventStreamError(eventStream: UnionShape): Symbol = + base.symbolForEventStreamError(eventStream) + override fun symbolForBuilder(shape: Shape): Symbol = base.symbolForBuilder(shape) +} diff --git a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/StreamingTraitSymbolProvider.kt b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/StreamingTraitSymbolProvider.kt index 3e1d082627..051f3c3d1e 100644 --- a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/StreamingTraitSymbolProvider.kt +++ b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/StreamingTraitSymbolProvider.kt @@ -6,7 +6,6 @@ package software.amazon.smithy.rust.codegen.core.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.ListShape import software.amazon.smithy.model.shapes.MapShape @@ -26,8 +25,7 @@ import software.amazon.smithy.rust.codegen.core.util.isStreaming /** * Wrapping symbol provider to change `Blob` to `ByteStream` when it targets a streaming member */ -class StreamingShapeSymbolProvider(private val base: RustSymbolProvider, private val model: Model) : - WrappingSymbolProvider(base) { +class StreamingShapeSymbolProvider(private val base: RustSymbolProvider) : WrappingSymbolProvider(base) { override fun toSymbol(shape: Shape): Symbol { val initial = base.toSymbol(shape) // We are only targeting member shapes @@ -44,7 +42,7 @@ class StreamingShapeSymbolProvider(private val base: RustSymbolProvider, private // We are only targeting streaming blobs return if (target is BlobShape && shape.isStreaming(model)) { - RuntimeType.byteStream(config().runtimeConfig).toSymbol().toBuilder().setDefault(Default.RustDefault).build() + RuntimeType.byteStream(config.runtimeConfig).toSymbol().toBuilder().setDefault(Default.RustDefault).build() } else { base.toSymbol(shape) } @@ -59,22 +57,23 @@ class StreamingShapeSymbolProvider(private val base: RustSymbolProvider, private * * Note that since streaming members can only be used on the root shape, this can only impact input and output shapes. */ -class StreamingShapeMetadataProvider( - private val base: RustSymbolProvider, - private val model: Model, -) : SymbolMetadataProvider(base) { +class StreamingShapeMetadataProvider(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.Clone, RuntimeType.PartialEq) - } else baseMetadata + } else { + baseMetadata + } } override fun unionMeta(unionShape: UnionShape): RustMetadata { val baseMetadata = base.toSymbol(unionShape).expectRustMetadata() return if (unionShape.hasStreamingMember(model)) { baseMetadata.withoutDerives(RuntimeType.Clone, RuntimeType.PartialEq) - } else baseMetadata + } else { + baseMetadata + } } override fun memberMeta(memberShape: MemberShape) = base.toSymbol(memberShape).expectRustMetadata() diff --git a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/SymbolExt.kt b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/SymbolExt.kt new file mode 100644 index 0000000000..3b9307ab7e --- /dev/null +++ b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/SymbolExt.kt @@ -0,0 +1,138 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +package software.amazon.smithy.rust.codegen.core.smithy + +import software.amazon.smithy.codegen.core.Symbol +import software.amazon.smithy.model.shapes.Shape +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.stripOuter +import software.amazon.smithy.rust.codegen.core.util.orNull + +/** Set the symbolLocation for this symbol builder */ +fun Symbol.Builder.locatedIn(rustModule: RustModule.LeafModule): Symbol.Builder { + val currentRustType = this.build().rustType() + check(currentRustType is RustType.Opaque) { + "Only `RustType.Opaque` can have its namespace updated. Received $currentRustType." + } + val newRustType = currentRustType.copy(namespace = rustModule.fullyQualifiedPath()) + return this.definitionFile(rustModule.definitionFile()) + .namespace(rustModule.fullyQualifiedPath(), "::") + .rustType(newRustType) + .module(rustModule) +} + +/** + * Make the Rust type of a symbol optional (hold `Option`) + * + * This is idempotent and will have no change if the type is already optional. + */ +fun Symbol.makeOptional(): Symbol = + if (isOptional()) { + this + } else { + val rustType = RustType.Option(this.rustType()) + Symbol.builder() + .rustType(rustType) + .addReference(this) + .name(rustType.name) + .build() + } + +/** + * Make the Rust type of a symbol boxed (hold `Box`). + * + * This is idempotent and will have no change if the type is already boxed. + */ +fun Symbol.makeRustBoxed(): Symbol = + if (isRustBoxed()) { + this + } else { + val rustType = RustType.Box(this.rustType()) + Symbol.builder() + .rustType(rustType) + .addReference(this) + .name(rustType.name) + .build() + } + +/** + * Make the Rust type of a symbol wrapped in `MaybeConstrained`. (hold `MaybeConstrained`). + * + * This is idempotent and will have no change if the type is already `MaybeConstrained`. + */ +fun Symbol.makeMaybeConstrained(): Symbol = + if (this.rustType() is RustType.MaybeConstrained) { + this + } else { + val rustType = RustType.MaybeConstrained(this.rustType()) + Symbol.builder() + .rustType(rustType) + .addReference(this) + .name(rustType.name) + .build() + } + +/** + * Map the [RustType] of a symbol with [f]. + * + * WARNING: This function does not update any symbol references (e.g., `symbol.addReference()`) on the + * returned symbol. You will have to add those yourself if your logic relies on them. + **/ +fun Symbol.mapRustType(f: (RustType) -> RustType): Symbol { + val newType = f(this.rustType()) + return Symbol.builder().rustType(newType) + .name(newType.name) + .build() +} + +/** + * Type representing the default value for a given type (e.g. for Strings, this is `""`). + */ +sealed class Default { + /** + * This symbol has no default value. If the symbol is not optional, this will error during builder construction + */ + object NoDefault : Default() + + /** + * This symbol should use the Rust `std::default::Default` when unset + */ + object RustDefault : Default() +} + +/** + * Returns true when it's valid to use the default/0 value for [this] symbol during construction. + */ +fun Symbol.canUseDefault(): Boolean = this.defaultValue() != Default.NoDefault + +/** + * True when [this] is will be represented by Option in Rust + */ +fun Symbol.isOptional(): Boolean = when (this.rustType()) { + is RustType.Option -> true + else -> false +} + +fun Symbol.isRustBoxed(): Boolean = rustType().stripOuter() is RustType.Box + +private const val RUST_TYPE_KEY = "rusttype" +private const val SHAPE_KEY = "shape" +private const val RUST_MODULE_KEY = "rustmodule" +private const val RENAMED_FROM_KEY = "renamedfrom" +private const val SYMBOL_DEFAULT = "symboldefault" + +// Symbols should _always_ be created with a Rust type & shape attached +fun Symbol.rustType(): RustType = this.expectProperty(RUST_TYPE_KEY, RustType::class.java) +fun Symbol.Builder.rustType(rustType: RustType): Symbol.Builder = this.putProperty(RUST_TYPE_KEY, rustType) +fun Symbol.shape(): Shape = this.expectProperty(SHAPE_KEY, Shape::class.java) +fun Symbol.Builder.shape(shape: Shape?): Symbol.Builder = this.putProperty(SHAPE_KEY, shape) +fun Symbol.module(): RustModule.LeafModule = this.expectProperty(RUST_MODULE_KEY, RustModule.LeafModule::class.java) +fun Symbol.Builder.module(module: RustModule.LeafModule): Symbol.Builder = this.putProperty(RUST_MODULE_KEY, module) +fun Symbol.renamedFrom(): String? = this.getProperty(RENAMED_FROM_KEY, String::class.java).orNull() +fun Symbol.Builder.renamedFrom(name: String): Symbol.Builder = this.putProperty(RENAMED_FROM_KEY, name) +fun Symbol.defaultValue(): Default = this.getProperty(SYMBOL_DEFAULT, Default::class.java).orElse(Default.NoDefault) +fun Symbol.Builder.setDefault(default: Default): Symbol.Builder = this.putProperty(SYMBOL_DEFAULT, default) 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 a7017b504e..880ac0510a 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 @@ -18,7 +18,6 @@ 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.UnionShape -import software.amazon.smithy.model.traits.EnumDefinition import software.amazon.smithy.model.traits.EnumTrait import software.amazon.smithy.model.traits.SensitiveTrait import software.amazon.smithy.model.traits.StreamingTrait @@ -27,27 +26,6 @@ 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.util.hasTrait -/** - * Default delegator to enable easily decorating another symbol provider. - */ -open class WrappingSymbolProvider(private val base: RustSymbolProvider) : RustSymbolProvider { - override fun config(): SymbolVisitorConfig { - return base.config() - } - - override fun toEnumVariantName(definition: EnumDefinition): MaybeRenamed? { - return base.toEnumVariantName(definition) - } - - override fun toSymbol(shape: Shape): Symbol { - return base.toSymbol(shape) - } - - override fun toMemberName(shape: MemberShape): String { - return base.toMemberName(shape) - } -} - /** * Attach `meta` to symbols. `meta` is used by the generators (e.g. StructureGenerator) to configure the generated models. * @@ -92,7 +70,7 @@ fun containerDefaultMetadata( model: Model, additionalAttributes: List = emptyList(), ): RustMetadata { - val defaultDerives = setOf(RuntimeType.Debug, RuntimeType.PartialEq, RuntimeType.Clone) + val derives = mutableSetOf(RuntimeType.Debug, RuntimeType.PartialEq, RuntimeType.Clone) val isSensitive = shape.hasTrait() || // Checking the shape's direct members for the sensitive trait should suffice. @@ -101,27 +79,21 @@ fun containerDefaultMetadata( // shape; any sensitive descendant should still be printed as redacted. shape.members().any { it.getMemberTrait(model, SensitiveTrait::class.java).isPresent } - val setOfDerives = if (isSensitive) { - defaultDerives - RuntimeType.Debug - } else { - defaultDerives + if (isSensitive) { + derives.remove(RuntimeType.Debug) } - return RustMetadata( - setOfDerives, - additionalAttributes, - Visibility.PUBLIC, - ) + + return RustMetadata(derives, additionalAttributes, Visibility.PUBLIC) } /** * The base metadata supports a set of attributes that are used by generators to decorate code. * - * By default we apply `#[non_exhaustive]` in [additionalAttributes] only to client structures since breaking model + * By default, we apply `#[non_exhaustive]` in [additionalAttributes] only to client structures since breaking model * changes are fine when generating server code. */ class BaseSymbolMetadataProvider( base: RustSymbolProvider, - private val model: Model, private val additionalAttributes: List, ) : SymbolMetadataProvider(base) { @@ -142,6 +114,11 @@ class BaseSymbolMetadataProvider( } is UnionShape, is CollectionShape, is MapShape -> RustMetadata(visibility = Visibility.PUBLIC) + + // This covers strings with the enum trait for now, and can be removed once we're fully on EnumShape + // TODO(https://github.com/awslabs/smithy-rs/issues/1700): Remove this `is StringShape` match arm + is StringShape -> RustMetadata(visibility = Visibility.PUBLIC) + else -> TODO("Unrecognized container type: $container") } diff --git a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/SymbolVisitor.kt b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/SymbolVisitor.kt index c6a1b1dac6..b2426c602c 100644 --- a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/SymbolVisitor.kt +++ b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/SymbolVisitor.kt @@ -17,7 +17,9 @@ import software.amazon.smithy.model.shapes.BooleanShape import software.amazon.smithy.model.shapes.ByteShape import software.amazon.smithy.model.shapes.DocumentShape import software.amazon.smithy.model.shapes.DoubleShape +import software.amazon.smithy.model.shapes.EnumShape import software.amazon.smithy.model.shapes.FloatShape +import software.amazon.smithy.model.shapes.IntEnumShape import software.amazon.smithy.model.shapes.IntegerShape import software.amazon.smithy.model.shapes.ListShape import software.amazon.smithy.model.shapes.LongShape @@ -35,7 +37,6 @@ 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.model.traits.EnumDefinition import software.amazon.smithy.model.traits.EnumTrait import software.amazon.smithy.model.traits.ErrorTrait import software.amazon.smithy.rust.codegen.core.rustlang.Attribute @@ -43,14 +44,10 @@ import software.amazon.smithy.rust.codegen.core.rustlang.RustModule import software.amazon.smithy.rust.codegen.core.rustlang.RustReservedWords import software.amazon.smithy.rust.codegen.core.rustlang.RustType import software.amazon.smithy.rust.codegen.core.rustlang.Visibility -import software.amazon.smithy.rust.codegen.core.rustlang.stripOuter import software.amazon.smithy.rust.codegen.core.smithy.traits.RustBoxTrait -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.PANIC import software.amazon.smithy.rust.codegen.core.util.hasTrait import software.amazon.smithy.rust.codegen.core.util.letIf -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 import kotlin.reflect.KClass @@ -63,93 +60,11 @@ val SimpleShapes: Map, RustType> = mapOf( ByteShape::class to RustType.Integer(8), ShortShape::class to RustType.Integer(16), IntegerShape::class to RustType.Integer(32), + IntEnumShape::class to RustType.Integer(32), LongShape::class to RustType.Integer(64), StringShape::class to RustType.String, ) -data class SymbolVisitorConfig( - val runtimeConfig: RuntimeConfig, - val renameExceptions: Boolean, - val nullabilityCheckMode: CheckMode, -) - -/** - * Make the Rust type of a symbol optional (hold `Option`) - * - * This is idempotent and will have no change if the type is already optional. - */ -fun Symbol.makeOptional(): Symbol = - if (isOptional()) { - this - } else { - val rustType = RustType.Option(this.rustType()) - Symbol.builder() - .rustType(rustType) - .addReference(this) - .name(rustType.name) - .build() - } - -/** - * Make the Rust type of a symbol boxed (hold `Box`). - * - * This is idempotent and will have no change if the type is already boxed. - */ -fun Symbol.makeRustBoxed(): Symbol = - if (isRustBoxed()) { - this - } else { - val rustType = RustType.Box(this.rustType()) - Symbol.builder() - .rustType(rustType) - .addReference(this) - .name(rustType.name) - .build() - } - -/** - * Make the Rust type of a symbol wrapped in `MaybeConstrained`. (hold `MaybeConstrained`). - * - * This is idempotent and will have no change if the type is already `MaybeConstrained`. - */ -fun Symbol.makeMaybeConstrained(): Symbol = - if (this.rustType() is RustType.MaybeConstrained) { - this - } else { - val rustType = RustType.MaybeConstrained(this.rustType()) - Symbol.builder() - .rustType(rustType) - .addReference(this) - .name(rustType.name) - .build() - } - -/** - * Map the [RustType] of a symbol with [f]. - * - * WARNING: This function does not set any `SymbolReference`s on the returned symbol. You will have to add those - * yourself if your logic relies on them. - **/ -fun Symbol.mapRustType(f: (RustType) -> RustType): Symbol { - val newType = f(this.rustType()) - return Symbol.builder().rustType(newType) - .name(newType.name) - .build() -} - -/** Set the symbolLocation for this symbol builder */ -fun Symbol.Builder.locatedIn(rustModule: RustModule.LeafModule): Symbol.Builder { - val currentRustType = this.build().rustType() - check(currentRustType is RustType.Opaque) { - "Only `Opaque` can have their namespace updated" - } - val newRustType = currentRustType.copy(namespace = rustModule.fullyQualifiedPath()) - return this.definitionFile(rustModule.definitionFile()) - .namespace(rustModule.fullyQualifiedPath(), "::") - .rustType(newRustType) - .module(rustModule) -} - /** * Track both the past and current name of a symbol * @@ -161,23 +76,19 @@ fun Symbol.Builder.locatedIn(rustModule: RustModule.LeafModule): Symbol.Builder */ data class MaybeRenamed(val name: String, val renamedFrom: String?) -/** - * SymbolProvider interface that carries both the inner configuration and a function to produce an enum variant name. - */ -interface RustSymbolProvider : SymbolProvider { - fun config(): SymbolVisitorConfig - fun toEnumVariantName(definition: EnumDefinition): MaybeRenamed? -} - /** * Make the return [value] optional if the [member] symbol is as well optional. */ -fun SymbolProvider.wrapOptional(member: MemberShape, value: String): String = value.letIf(toSymbol(member).isOptional()) { "Some($value)" } +fun SymbolProvider.wrapOptional(member: MemberShape, value: String): String = value.letIf(toSymbol(member).isOptional()) { + "Some($value)" +} /** * Make the return [value] optional if the [member] symbol is not optional. */ -fun SymbolProvider.toOptional(member: MemberShape, value: String): String = value.letIf(!toSymbol(member).isOptional()) { "Some($value)" } +fun SymbolProvider.toOptional(member: MemberShape, value: String): String = value.letIf(!toSymbol(member).isOptional()) { + "Some($value)" +} /** * Services can rename their contained shapes. See https://awslabs.github.io/smithy/1.0/spec/core/model.html#service @@ -199,33 +110,42 @@ fun Shape.contextName(serviceShape: ServiceShape?): String { * derives for a given shape. */ open class SymbolVisitor( - private val model: Model, + settings: CoreRustSettings, + override val model: Model, private val serviceShape: ServiceShape?, - private val config: SymbolVisitorConfig, -) : RustSymbolProvider, - ShapeVisitor { + override val config: RustSymbolProviderConfig, +) : RustSymbolProvider, ShapeVisitor { + override val moduleProviderContext = ModuleProviderContext(settings, model, serviceShape) private val nullableIndex = NullableIndex.of(model) - override fun config(): SymbolVisitorConfig = config override fun toSymbol(shape: Shape): Symbol { return shape.accept(this) } - /** - * Return the name of a given `enum` variant. Note that this refers to `enum` in the Smithy context - * where enum is a trait that can be applied to [StringShape] and not in the Rust context of an algebraic data type. - * - * Because enum variants are not member shape, a separate handler is required. - */ - override fun toEnumVariantName(definition: EnumDefinition): MaybeRenamed? { - val baseName = definition.name.orNull()?.toPascalCase() ?: return null - return MaybeRenamed(baseName, null) + override fun symbolForOperationError(operation: OperationShape): Symbol = + toSymbol(operation).let { symbol -> + val module = moduleForOperationError(operation) + module.toType().resolve("${symbol.name}Error").toSymbol().toBuilder().locatedIn(module).build() + } + + override fun symbolForEventStreamError(eventStream: UnionShape): Symbol = + toSymbol(eventStream).let { symbol -> + val module = moduleForEventStreamError(eventStream) + module.toType().resolve("${symbol.name}Error").toSymbol().toBuilder().locatedIn(module).build() + } + + override fun symbolForBuilder(shape: Shape): Symbol = toSymbol(shape).let { symbol -> + val module = moduleForBuilder(shape) + module.toType().resolve(config.nameBuilderFor(symbol)).toSymbol().toBuilder().locatedIn(module).build() } - override fun toMemberName(shape: MemberShape): String = when (val container = model.expectShape(shape.container)) { - is StructureShape -> shape.memberName.toSnakeCase() - is UnionShape -> shape.memberName.toPascalCase() - else -> error("unexpected container shape: $container") + override fun toMemberName(shape: MemberShape): String { + val container = model.expectShape(shape.container) + return when { + container is StructureShape -> shape.memberName.toSnakeCase() + container is UnionShape || container is EnumShape || container.hasTrait() -> shape.memberName.toPascalCase() + else -> error("unexpected container shape: $container") + } } override fun blobShape(shape: BlobShape?): Symbol { @@ -244,7 +164,9 @@ open class SymbolVisitor( name(rustType.name) build() } - } else symbol + } else { + symbol + } } private fun simpleShape(shape: SimpleShape): Symbol { @@ -258,10 +180,12 @@ open class SymbolVisitor( override fun longShape(shape: LongShape): Symbol = simpleShape(shape) override fun floatShape(shape: FloatShape): Symbol = simpleShape(shape) override fun doubleShape(shape: DoubleShape): Symbol = simpleShape(shape) + + override fun intEnumShape(shape: IntEnumShape): Symbol = simpleShape(shape) override fun stringShape(shape: StringShape): Symbol { return if (shape.hasTrait()) { val rustType = RustType.Opaque(shape.contextName(serviceShape).toPascalCase()) - symbolBuilder(shape, rustType).locatedIn(ModelsModule).build() + symbolBuilder(shape, rustType).locatedIn(moduleForShape(shape)).build() } else { simpleShape(shape) } @@ -312,7 +236,7 @@ open class SymbolVisitor( .replaceFirstChar { it.uppercase() }, ), ) - .locatedIn(OperationsModule) + .locatedIn(moduleForShape(shape)) .build() } @@ -326,25 +250,15 @@ open class SymbolVisitor( override fun structureShape(shape: StructureShape): Symbol { val isError = shape.hasTrait() - val isInput = shape.hasTrait() - val isOutput = shape.hasTrait() val name = shape.contextName(serviceShape).toPascalCase().letIf(isError && config.renameExceptions) { it.replace("Exception", "Error") } - val builder = symbolBuilder(shape, RustType.Opaque(name)) - return when { - isError -> builder.locatedIn(ErrorsModule) - isInput -> builder.locatedIn(InputsModule) - isOutput -> builder.locatedIn(OutputsModule) - else -> builder.locatedIn(ModelsModule) - }.build() + return symbolBuilder(shape, RustType.Opaque(name)).locatedIn(moduleForShape(shape)).build() } override fun unionShape(shape: UnionShape): Symbol { val name = shape.contextName(serviceShape).toPascalCase() - val builder = symbolBuilder(shape, RustType.Opaque(name)).locatedIn(ModelsModule) - - return builder.build() + return symbolBuilder(shape, RustType.Opaque(name)).locatedIn(moduleForShape(shape)).build() } override fun memberShape(shape: MemberShape): Symbol { @@ -372,30 +286,20 @@ open class SymbolVisitor( fun handleRustBoxing(symbol: Symbol, shape: MemberShape): Symbol = if (shape.hasTrait()) { symbol.makeRustBoxed() - } else symbol + } else { + symbol + } -fun symbolBuilder(shape: Shape?, rustType: RustType): Symbol.Builder { - val builder = Symbol.builder().putProperty(SHAPE_KEY, shape) - return builder.rustType(rustType) +fun symbolBuilder(shape: Shape?, rustType: RustType): Symbol.Builder = + Symbol.builder().shape(shape).rustType(rustType) .name(rustType.name) // Every symbol that actually gets defined somewhere should set a definition file // If we ever generate a `thisisabug.rs`, there is a bug in our symbol generation .definitionFile("thisisabug.rs") -} fun handleOptionality(symbol: Symbol, member: MemberShape, nullableIndex: NullableIndex, nullabilityCheckMode: CheckMode): Symbol = symbol.letIf(nullableIndex.isMemberNullable(member, nullabilityCheckMode)) { symbol.makeOptional() } -private const val RUST_TYPE_KEY = "rusttype" -private const val RUST_MODULE_KEY = "rustmodule" -private const val SHAPE_KEY = "shape" -private const val SYMBOL_DEFAULT = "symboldefault" -private const val RENAMED_FROM_KEY = "renamedfrom" - -fun Symbol.Builder.rustType(rustType: RustType): Symbol.Builder = this.putProperty(RUST_TYPE_KEY, rustType) -fun Symbol.Builder.module(module: RustModule.LeafModule): Symbol.Builder = this.putProperty(RUST_MODULE_KEY, module) -fun Symbol.module(): RustModule.LeafModule = this.expectProperty(RUST_MODULE_KEY, RustModule.LeafModule::class.java) - /** * Creates a test module for this symbol. * For example if the symbol represents the name for the struct `struct MyStruct { ... }`, @@ -418,49 +322,6 @@ fun SymbolProvider.testModuleForShape(shape: Shape): RustModule.LeafModule { ) } -fun Symbol.Builder.renamedFrom(name: String): Symbol.Builder { - return this.putProperty(RENAMED_FROM_KEY, name) -} - -fun Symbol.renamedFrom(): String? = this.getProperty(RENAMED_FROM_KEY, String::class.java).orNull() - -fun Symbol.defaultValue(): Default = this.getProperty(SYMBOL_DEFAULT, Default::class.java).orElse(Default.NoDefault) -fun Symbol.Builder.setDefault(default: Default): Symbol.Builder = this.putProperty(SYMBOL_DEFAULT, default) - -/** - * Type representing the default value for a given type. (eg. for Strings, this is `""`) - */ -sealed class Default { - /** - * This symbol has no default value. If the symbol is not optional, this will be an error during builder construction - */ - object NoDefault : Default() - - /** - * This symbol should use the Rust `std::default::Default` when unset - */ - object RustDefault : Default() -} - -/** - * True when it is valid to use the default/0 value for [this] symbol during construction. - */ -fun Symbol.canUseDefault(): Boolean = this.defaultValue() != Default.NoDefault - -/** - * True when [this] is will be represented by Option in Rust - */ -fun Symbol.isOptional(): Boolean = when (this.rustType()) { - is RustType.Option -> true - else -> false -} - -fun Symbol.isRustBoxed(): Boolean = rustType().stripOuter() is RustType.Box - -// Symbols should _always_ be created with a Rust type & shape attached -fun Symbol.rustType(): RustType = this.expectProperty(RUST_TYPE_KEY, RustType::class.java) -fun Symbol.shape(): Shape = this.expectProperty(SHAPE_KEY, Shape::class.java) - /** * You should rarely need this function, rust names in general should be symbol-aware, * this is "automatic" if you use things like [software.amazon.smithy.rust.codegen.core.rustlang.rustTemplate]. diff --git a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/customizations/AllowLintsCustomization.kt b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/customizations/AllowLintsCustomization.kt index a06ff50a28..43eb40696e 100644 --- a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/customizations/AllowLintsCustomization.kt +++ b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/customizations/AllowLintsCustomization.kt @@ -21,7 +21,7 @@ private val allowedClippyLints = listOf( // Sometimes operations are named the same as our module e.g. output leading to `output::output`. "module_inception", - // Currently, we don't recase acronyms in models, e.g. `SSEVersion`. + // Currently, we don't re-case acronyms in models, e.g. `SSEVersion`. "upper_case_acronyms", // Large errors trigger this warning, we are unlikely to optimize this case currently. @@ -34,9 +34,7 @@ private val allowedClippyLints = listOf( "should_implement_trait", // Protocol tests use silly names like `baz`, don't flag that. - // TODO(msrv_upgrade): switch - "blacklisted_name", - // "disallowed_names", + "disallowed_names", // Forcing use of `vec![]` can make codegen harder in some cases. "vec_init_then_push", @@ -48,12 +46,10 @@ private val allowedClippyLints = listOf( "needless_return", // For backwards compatibility, we often don't derive Eq - // TODO(msrv_upgrade): enable - // "derive_partial_eq_without_eq", + "derive_partial_eq_without_eq", // Keeping errors small in a backwards compatible way is challenging - // TODO(msrv_upgrade): enable - // "result_large_err", + "result_large_err", ) private val allowedRustdocLints = listOf( diff --git a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/customizations/CrateVersionCustomization.kt b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/customizations/CrateVersionCustomization.kt index eca5503050..93db223c20 100644 --- a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/customizations/CrateVersionCustomization.kt +++ b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/customizations/CrateVersionCustomization.kt @@ -5,24 +5,24 @@ package software.amazon.smithy.rust.codegen.core.smithy.customizations +import software.amazon.smithy.rust.codegen.core.rustlang.RustModule 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.LibRsCustomization -import software.amazon.smithy.rust.codegen.core.smithy.generators.LibRsSection +import software.amazon.smithy.rust.codegen.core.smithy.RuntimeType +import software.amazon.smithy.rust.codegen.core.smithy.RustCrate /** * Add `PGK_VERSION` const in lib.rs to enable knowing the version of the current module */ -class CrateVersionCustomization : LibRsCustomization() { - override fun section(section: LibRsSection) = - writable { - if (section is LibRsSection.Body) { - rust( - """ - /// Crate version number. - pub static PKG_VERSION: &str = env!("CARGO_PKG_VERSION"); - """, - ) - } +object CrateVersionCustomization { + fun pkgVersion(module: RustModule): RuntimeType = RuntimeType(module.fullyQualifiedPath() + "::PKG_VERSION") + + fun extras(rustCrate: RustCrate, module: RustModule) = + rustCrate.withModule(module) { + rust( + """ + /// Crate version number. + pub static PKG_VERSION: &str = env!("CARGO_PKG_VERSION"); + """, + ) } } 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 02a8843c19..ec49a97613 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 @@ -8,17 +8,20 @@ package software.amazon.smithy.rust.codegen.core.smithy.customizations import software.amazon.smithy.model.Model import software.amazon.smithy.model.shapes.Shape import software.amazon.smithy.model.shapes.StructureShape -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.smithy.RuntimeConfig +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.RustCrate import software.amazon.smithy.rust.codegen.core.util.hasEventStreamMember import software.amazon.smithy.rust.codegen.core.util.hasStreamingMember +import software.amazon.smithy.rust.codegen.core.util.letIf private data class PubUseType( val type: RuntimeType, val shouldExport: (Model) -> Boolean, + val alias: String? = null, ) /** Returns true if the model has normal streaming operations (excluding event streams) */ @@ -46,29 +49,60 @@ private fun hasBlobs(model: Model): Boolean = structUnionMembersMatchPredicate(m private fun hasDateTimes(model: Model): Boolean = structUnionMembersMatchPredicate(model, Shape::isTimestampShape) /** Returns a list of types that should be re-exported for the given model */ -internal fun pubUseTypes(runtimeConfig: RuntimeConfig, model: Model): List { +internal fun pubUseTypes(codegenContext: CodegenContext, model: Model): List = + pubUseTypesThatShouldBeExported(codegenContext, model).map { it.type } + +private fun pubUseTypesThatShouldBeExported(codegenContext: CodegenContext, model: Model): List { + val runtimeConfig = codegenContext.runtimeConfig return ( listOf( PubUseType(RuntimeType.blob(runtimeConfig), ::hasBlobs), PubUseType(RuntimeType.dateTime(runtimeConfig), ::hasDateTimes), - ) + RuntimeType.smithyTypes(runtimeConfig).let { types -> - listOf(PubUseType(types.resolve("error::display::DisplayErrorContext")) { true }) - } + RuntimeType.smithyHttp(runtimeConfig).let { http -> + ) + RuntimeType.smithyHttp(runtimeConfig).let { http -> listOf( - PubUseType(http.resolve("result::SdkError")) { true }, PubUseType(http.resolve("byte_stream::ByteStream"), ::hasStreamingOperations), PubUseType(http.resolve("byte_stream::AggregatedBytes"), ::hasStreamingOperations), + PubUseType(http.resolve("byte_stream::error::Error"), ::hasStreamingOperations, "ByteStreamError"), + PubUseType(http.resolve("body::SdkBody"), ::hasStreamingOperations), ) } - ).filter { pubUseType -> pubUseType.shouldExport(model) }.map { it.type } + ).filter { pubUseType -> pubUseType.shouldExport(model) } } -/** Adds re-export statements in a separate file for the types module */ -fun pubUseSmithyTypes(runtimeConfig: RuntimeConfig, model: Model, rustCrate: RustCrate) { - rustCrate.withModule(RustModule.Types) { - val types = pubUseTypes(runtimeConfig, model) - if (types.isNotEmpty()) { - types.forEach { type -> rust("pub use #T;", type) } +/** Adds re-export statements for Smithy primitives */ +fun pubUseSmithyPrimitives(codegenContext: CodegenContext, model: Model): Writable = writable { + val types = pubUseTypesThatShouldBeExported(codegenContext, model) + if (types.isNotEmpty()) { + types.forEach { + val useStatement = if (it.alias == null) { + "pub use #T;" + } else { + "pub use #T as ${it.alias};" + } + rust(useStatement, it.type) } } } + +/** Adds re-export statements for error types */ +fun pubUseSmithyErrorTypes(codegenContext: CodegenContext): Writable = writable { + val runtimeConfig = codegenContext.runtimeConfig + val reexports = listOf( + listOf( + RuntimeType.smithyHttp(runtimeConfig).let { http -> + PubUseType(http.resolve("result::SdkError"), { _ -> true }) + }, + ), + RuntimeType.smithyTypes(runtimeConfig).let { types -> + listOf(PubUseType(types.resolve("error::display::DisplayErrorContext"), { _ -> true })) + // Only re-export `ProvideErrorMetadata` for clients + .letIf(codegenContext.target == CodegenTarget.CLIENT) { list -> + list + + listOf(PubUseType(types.resolve("error::metadata::ProvideErrorMetadata"), { _ -> true })) + } + }, + ).flatten() + reexports.forEach { reexport -> + rust("pub use #T;", reexport.type) + } +} 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 c886c4ed97..1df3fb7dd3 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 @@ -8,9 +8,14 @@ package software.amazon.smithy.rust.codegen.core.smithy.customize import software.amazon.smithy.build.PluginContext import software.amazon.smithy.model.Model import software.amazon.smithy.model.shapes.ServiceShape +import software.amazon.smithy.rust.codegen.core.smithy.ModuleDocProvider 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.generators.BuilderCustomization import software.amazon.smithy.rust.codegen.core.smithy.generators.LibRsCustomization import software.amazon.smithy.rust.codegen.core.smithy.generators.ManifestCustomizations +import software.amazon.smithy.rust.codegen.core.smithy.generators.StructureCustomization +import software.amazon.smithy.rust.codegen.core.smithy.generators.error.ErrorImplCustomization import software.amazon.smithy.rust.codegen.core.util.deepMergeWith import java.util.ServiceLoader import java.util.logging.Logger @@ -45,6 +50,14 @@ interface CoreCodegenDecorator { */ fun extras(codegenContext: CodegenContext, rustCrate: RustCrate) {} + /** + * Customize the documentation provider for module documentation. + */ + fun moduleDocumentationCustomization( + codegenContext: CodegenContext, + baseModuleDocProvider: ModuleDocProvider, + ): ModuleDocProvider = baseModuleDocProvider + /** * Hook to customize the generated `Cargo.toml` file. * @@ -62,10 +75,40 @@ interface CoreCodegenDecorator { baseCustomizations: List, ): List = baseCustomizations + /** + * Hook to customize structures generated by `StructureGenerator`. + */ + fun structureCustomizations( + codegenContext: CodegenContext, + baseCustomizations: List, + ): List = baseCustomizations + + // TODO(https://github.com/awslabs/smithy-rs/issues/1401): Move builder customizations into `ClientCodegenDecorator` + /** + * Hook to customize generated builders. + */ + fun builderCustomizations( + codegenContext: CodegenContext, + baseCustomizations: List, + ): List = baseCustomizations + + /** + * Hook to customize error struct `impl` blocks. + */ + fun errorImplCustomizations( + codegenContext: CodegenContext, + baseCustomizations: List, + ): List = baseCustomizations + /** * Extra sections allow one decorator to influence another. This is intended to be used by querying the `rootDecorator` */ fun extraSections(codegenContext: CodegenContext): List = listOf() + + /** + * Hook for customizing symbols by inserting an additional symbol provider. + */ + fun symbolProvider(base: RustSymbolProvider): RustSymbolProvider = base } /** @@ -76,14 +119,6 @@ abstract class CombinedCoreCodegenDecorator { private val orderedDecorators = decorators.sortedBy { it.order } - final override fun libRsCustomizations( - codegenContext: CodegenContext, - baseCustomizations: List, - ): List = - combineCustomizations(baseCustomizations) { decorator, customizations -> - decorator.libRsCustomizations(codegenContext, customizations) - } - final override fun crateManifestCustomizations(codegenContext: CodegenContext): ManifestCustomizations = combineCustomizations(emptyMap()) { decorator, customizations -> customizations.deepMergeWith(decorator.crateManifestCustomizations(codegenContext)) @@ -98,9 +133,50 @@ abstract class CombinedCoreCodegenDecorator + decorator.moduleDocumentationCustomization(codegenContext, base) + } + + final override fun libRsCustomizations( + codegenContext: CodegenContext, + baseCustomizations: List, + ): List = + combineCustomizations(baseCustomizations) { decorator, customizations -> + decorator.libRsCustomizations(codegenContext, customizations) + } + + override fun structureCustomizations( + codegenContext: CodegenContext, + baseCustomizations: List, + ): List = combineCustomizations(baseCustomizations) { decorator, customizations -> + decorator.structureCustomizations(codegenContext, customizations) + } + + override fun builderCustomizations( + codegenContext: CodegenContext, + baseCustomizations: List, + ): List = combineCustomizations(baseCustomizations) { decorator, customizations -> + decorator.builderCustomizations(codegenContext, customizations) + } + + override fun errorImplCustomizations( + codegenContext: CodegenContext, + baseCustomizations: List, + ): List = combineCustomizations(baseCustomizations) { decorator, customizations -> + decorator.errorImplCustomizations(codegenContext, customizations) + } + final override fun extraSections(codegenContext: CodegenContext): List = addCustomizations { decorator -> decorator.extraSections(codegenContext) } + final override fun symbolProvider(base: RustSymbolProvider): RustSymbolProvider = + combineCustomizations(base) { decorator, otherProvider -> + decorator.symbolProvider(otherProvider) + } + /** * Combines customizations from multiple ordered codegen decorators. * 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 index d11f98a5c7..e11936c704 100644 --- 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 @@ -52,7 +52,31 @@ sealed class OperationSection(name: String) : Section(name) { 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() { 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 ad7fe27b60..5d68d1d6d8 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 @@ -11,11 +11,8 @@ import software.amazon.smithy.model.shapes.MemberShape import software.amazon.smithy.model.shapes.StructureShape 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.RustModule -import software.amazon.smithy.rust.codegen.core.rustlang.RustReservedWords 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.Visibility 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 @@ -36,12 +33,13 @@ 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.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.customize.writeCustomizations import software.amazon.smithy.rust.codegen.core.smithy.defaultValue import software.amazon.smithy.rust.codegen.core.smithy.expectRustMetadata import software.amazon.smithy.rust.codegen.core.smithy.isOptional -import software.amazon.smithy.rust.codegen.core.smithy.locatedIn import software.amazon.smithy.rust.codegen.core.smithy.makeOptional -import software.amazon.smithy.rust.codegen.core.smithy.module import software.amazon.smithy.rust.codegen.core.smithy.rustType import software.amazon.smithy.rust.codegen.core.smithy.shape import software.amazon.smithy.rust.codegen.core.smithy.traits.SyntheticInputTrait @@ -53,22 +51,27 @@ import software.amazon.smithy.rust.codegen.core.util.toSnakeCase // TODO(https://github.com/awslabs/smithy-rs/issues/1401) This builder generator is only used by the client. // Move this entire file, and its tests, to `codegen-client`. -fun builderSymbolFn(symbolProvider: RustSymbolProvider): (StructureShape) -> Symbol = { structureShape -> - structureShape.builderSymbol(symbolProvider) -} +/** BuilderGenerator customization sections */ +sealed class BuilderSection(name: String) : Section(name) { + abstract val shape: StructureShape + + /** Hook to add additional fields to the builder */ + data class AdditionalFields(override val shape: StructureShape) : BuilderSection("AdditionalFields") + + /** Hook to add additional methods to the builder */ + data class AdditionalMethods(override val shape: StructureShape) : BuilderSection("AdditionalMethods") + + /** Hook to add additional fields to the `build()` method */ + data class AdditionalFieldsInBuild(override val shape: StructureShape) : BuilderSection("AdditionalFieldsInBuild") -fun StructureShape.builderSymbol(symbolProvider: RustSymbolProvider): Symbol { - val structureSymbol = symbolProvider.toSymbol(this) - val builderNamespace = RustReservedWords.escapeIfNeeded(structureSymbol.name.toSnakeCase()) - val module = RustModule.new(builderNamespace, visibility = Visibility.PUBLIC, parent = structureSymbol.module(), inline = true) - val rustType = RustType.Opaque("Builder", module.fullyQualifiedPath()) - return Symbol.builder() - .rustType(rustType) - .name(rustType.name) - .locatedIn(module) - .build() + /** Hook to add additional fields to the `Debug` impl */ + data class AdditionalDebugFields(override val shape: StructureShape, val formatterName: String) : + BuilderSection("AdditionalDebugFields") } +/** Customizations for BuilderGenerator */ +abstract class BuilderCustomization : NamedCustomization() + fun RuntimeConfig.operationBuildError() = RuntimeType.operationModule(this).resolve("error::BuildError") fun RuntimeConfig.serializationError() = RuntimeType.operationModule(this).resolve("error::SerializationError") @@ -93,6 +96,7 @@ class BuilderGenerator( private val model: Model, private val symbolProvider: RustSymbolProvider, private val shape: StructureShape, + private val customizations: List, ) { companion object { /** @@ -109,27 +113,34 @@ class BuilderGenerator( // generate a fallible builder. !it.isOptional() && !it.canUseDefault() } + + fun renderConvenienceMethod(implBlock: RustWriter, symbolProvider: RustSymbolProvider, shape: StructureShape) { + implBlock.docs("Creates a new builder-style object to manufacture #D.", symbolProvider.toSymbol(shape)) + symbolProvider.symbolForBuilder(shape).also { builderSymbol -> + implBlock.rustBlock("pub fn builder() -> #T", builderSymbol) { + write("#T::default()", builderSymbol) + } + } + } } - private val runtimeConfig = symbolProvider.config().runtimeConfig + private val runtimeConfig = symbolProvider.config.runtimeConfig private val members: List = shape.allMembers.values.toList() private val structureSymbol = symbolProvider.toSymbol(shape) - private val builderSymbol = shape.builderSymbol(symbolProvider) - private val baseDerives = structureSymbol.expectRustMetadata().derives + 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 - private val builderDerives = baseDerives.filter { it == RuntimeType.Debug || it == RuntimeType.PartialEq || it == RuntimeType.Clone } + RuntimeType.Default - private val builderName = "Builder" + private val builderDerives = metadata.derives.filter { + it == RuntimeType.Debug || it == RuntimeType.PartialEq || it == RuntimeType.Clone + } + RuntimeType.Default + private val builderName = symbolProvider.symbolForBuilder(shape).name fun render(writer: RustWriter) { - val symbol = symbolProvider.toSymbol(shape) - writer.docs("See #D.", symbol) - writer.withInlineModule(shape.builderSymbol(symbolProvider).module()) { - // Matching derives to the main structure + `Default` since we are a builder and everything is optional. - renderBuilder(this) - if (!structureSymbol.expectRustMetadata().hasDebugDerive()) { - renderDebugImpl(this) - } + // Matching derives to the main structure + `Default` since we are a builder and everything is optional. + renderBuilder(writer) + if (!structureSymbol.expectRustMetadata().hasDebugDerive()) { + renderDebugImpl(writer) } } @@ -154,13 +165,6 @@ class BuilderGenerator( OperationBuildError(runtimeConfig).missingField(field, detailedMessage)(this) } - fun renderConvenienceMethod(implBlock: RustWriter) { - implBlock.docs("Creates a new builder-style object to manufacture #D.", structureSymbol) - implBlock.rustBlock("pub fn builder() -> #T", builderSymbol) { - write("#T::default()", builderSymbol) - } - } - // TODO(EventStream): [DX] Consider updating builders to take EventInputStream as Into private fun renderBuilderMember(writer: RustWriter, memberName: String, memberSymbol: Symbol) { // Builder members are crate-public to enable using them directly in serializers/deserializers. @@ -210,6 +214,7 @@ class BuilderGenerator( writer.docs("This is the datatype returned when calling `Builder::build()`.") writer.rustInline("pub type OutputShape = #T;", structureSymbol) writer.docs("A builder for #D.", structureSymbol) + metadata.additionalAttributes.render(writer) Attribute(derive(builderDerives)).render(writer) RenderSerdeAttribute.forStructureShape(writer, shape, model) SensitiveWarning.addDoc(writer, shape) @@ -222,6 +227,7 @@ class BuilderGenerator( SensitiveWarning.addDoc(writer, member) renderBuilderMember(this, memberName, memberSymbol) } + writeCustomizations(customizations, BuilderSection.AdditionalFields(shape)) } writer.rustBlock("impl $builderName") { @@ -241,6 +247,7 @@ class BuilderGenerator( renderBuilderMemberSetterFn(this, outerType, member, memberName) } + writeCustomizations(customizations, BuilderSection.AdditionalMethods(shape)) renderBuildFn(this) } } @@ -257,6 +264,7 @@ class BuilderGenerator( "formatter.field(${memberName.dq()}, &$fieldValue);", ) } + writeCustomizations(customizations, BuilderSection.AdditionalDebugFields(shape, "formatter")) rust("formatter.finish()") } } @@ -335,6 +343,7 @@ class BuilderGenerator( } } } + writeCustomizations(customizations, BuilderSection.AdditionalFieldsInBuild(shape)) } } } 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 d831ca00b2..f5eb8be7ff 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 @@ -6,13 +6,16 @@ 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.MemberShape +import software.amazon.smithy.model.shapes.Shape import software.amazon.smithy.model.shapes.StringShape import software.amazon.smithy.model.traits.DocumentationTrait import software.amazon.smithy.model.traits.EnumDefinition import software.amazon.smithy.model.traits.EnumTrait import software.amazon.smithy.rust.codegen.core.rustlang.Attribute -import software.amazon.smithy.rust.codegen.core.rustlang.RustModule +import software.amazon.smithy.rust.codegen.core.rustlang.RustMetadata 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.deprecatedShape import software.amazon.smithy.rust.codegen.core.rustlang.docs import software.amazon.smithy.rust.codegen.core.rustlang.documentShape @@ -20,28 +23,92 @@ 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 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.smithy.CodegenTarget +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.RustSymbolProvider 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.util.REDACTION -import software.amazon.smithy.rust.codegen.core.util.doubleQuote 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.getTrait import software.amazon.smithy.rust.codegen.core.util.orNull import software.amazon.smithy.rust.codegen.core.util.shouldRedact +import software.amazon.smithy.rust.codegen.core.util.toPascalCase + +data class EnumGeneratorContext( + val enumName: String, + val enumMeta: RustMetadata, + val enumTrait: EnumTrait, + val sortedMembers: List, +) + +/** + * Type of enum to generate + * + * In codegen-core, there are only `Infallible` enums. Server adds additional enum types, which + * is why this class is abstract rather than sealed. + */ +abstract class EnumType { + /** Returns a writable that implements `From<&str>` and/or `TryFrom<&str>` for the enum */ + abstract fun implFromForStr(context: EnumGeneratorContext): Writable + + /** Returns a writable that implements `FromStr` for the enum */ + abstract fun implFromStr(context: EnumGeneratorContext): Writable + + /** Optionally adds additional documentation to the `enum` docs */ + open fun additionalDocs(context: EnumGeneratorContext): Writable = writable {} + + /** Optionally adds additional enum members */ + open fun additionalEnumMembers(context: EnumGeneratorContext): Writable = writable {} + + /** Optionally adds match arms to the `as_str` match implementation for named enums */ + open fun additionalAsStrMatchArms(context: EnumGeneratorContext): Writable = writable {} + + /** Optionally add more attributes to the enum */ + open fun additionalEnumAttributes(context: EnumGeneratorContext): List = emptyList() + + /** Optionally add more impls to the enum */ + open fun additionalEnumImpls(context: EnumGeneratorContext): Writable = writable {} +} /** Model that wraps [EnumDefinition] to calculate and cache values required to generate the Rust enum source. */ -class EnumMemberModel(private val definition: EnumDefinition, private val symbolProvider: RustSymbolProvider) { +class EnumMemberModel( + private val parentShape: Shape, + private val definition: EnumDefinition, + private val symbolProvider: RustSymbolProvider, +) { + companion object { + /** + * Return the name of a given `enum` variant. Note that this refers to `enum` in the Smithy context + * where enum is a trait that can be applied to [StringShape] and not in the Rust context of an algebraic data type. + * + * Ordinarily, the symbol provider would determine this name, but the enum trait doesn't allow for this. + * + * TODO(https://github.com/awslabs/smithy-rs/issues/1700): Remove this function when refactoring to EnumShape. + */ + @Deprecated("This function will go away when we handle EnumShape instead of EnumTrait") + fun toEnumVariantName( + symbolProvider: RustSymbolProvider, + parentShape: Shape, + definition: EnumDefinition, + ): MaybeRenamed? { + val name = definition.name.orNull()?.toPascalCase() ?: return null + // Create a fake member shape for symbol look up until we refactor to use EnumShape + val fakeMemberShape = + MemberShape.builder().id(parentShape.id.withMember(name)).target("smithy.api#String").build() + val symbol = symbolProvider.toSymbol(fakeMemberShape) + return MaybeRenamed(symbol.name, symbol.renamedFrom()) + } + } // Because enum variants always start with an upper case letter, they will never // conflict with reserved words (which are always lower case), therefore, we never need // to fall back to raw identifiers val value: String get() = definition.value - fun name(): MaybeRenamed? = symbolProvider.toEnumVariantName(definition) + fun name(): MaybeRenamed? = toEnumVariantName(symbolProvider, parentShape, definition) private fun renderDocumentation(writer: RustWriter) { val name = @@ -60,7 +127,7 @@ class EnumMemberModel(private val definition: EnumDefinition, private val symbol } } - fun derivedName() = checkNotNull(symbolProvider.toEnumVariantName(definition)).name + fun derivedName() = checkNotNull(toEnumVariantName(symbolProvider, parentShape, definition)).name fun render(writer: RustWriter) { renderDocumentation(writer) @@ -87,156 +154,139 @@ private fun RustWriter.docWithNote(doc: String?, note: String?) { open class EnumGenerator( private val model: Model, private val symbolProvider: RustSymbolProvider, - private val writer: RustWriter, - protected val shape: StringShape, - protected val enumTrait: EnumTrait, + private val shape: StringShape, + private val enumType: EnumType, ) { - protected val symbol: Symbol = symbolProvider.toSymbol(shape) - protected val enumName: String = symbol.name - protected val meta = symbol.expectRustMetadata() - protected val sortedMembers: List = - enumTrait.values.sortedBy { it.value }.map { EnumMemberModel(it, symbolProvider) } - protected open var target: CodegenTarget = CodegenTarget.CLIENT - companion object { - /** Name of the generated unknown enum member name for enums with named members. */ - const val UnknownVariant = "Unknown" - - /** Name of the opaque struct that is inner data for the generated [UnknownVariant]. */ - const val UnknownVariantValue = "UnknownVariantValue" - /** Name of the function on the enum impl to get a vec of value names */ const val Values = "values" } - open fun render() { + private val enumTrait: EnumTrait = shape.expectTrait() + private val symbol: Symbol = symbolProvider.toSymbol(shape) + private val context = EnumGeneratorContext( + enumName = symbol.name, + enumMeta = symbol.expectRustMetadata(), + enumTrait = enumTrait, + sortedMembers = enumTrait.values.sortedBy { it.value }.map { EnumMemberModel(shape, it, symbolProvider) }, + ) + + fun render(writer: RustWriter) { + enumType.additionalEnumAttributes(context).forEach { attribute -> + attribute.render(writer) + } if (enumTrait.hasNames()) { - // pub enum Blah { V1, V2, .. } - renderEnum() - writer.insertTrailingNewline() - // impl From for Blah { ... } - renderFromForStr() - // impl FromStr for Blah { ... } - renderFromStr() - writer.insertTrailingNewline() - // impl Blah { pub fn as_str(&self) -> &str - implBlock() - writer.rustBlock("impl AsRef for $enumName") { - rustBlock("fn as_ref(&self) -> &str") { - rust("self.as_str()") - } - } + writer.renderNamedEnum() } else { - renderUnnamedEnum() + writer.renderUnnamedEnum() } + enumType.additionalEnumImpls(context)(writer) if (shape.shouldRedact(model)) { - renderDebugImplForSensitiveEnum() + writer.renderDebugImplForSensitiveEnum() } } - private fun renderUnnamedEnum() { - writer.documentShape(shape, model) - writer.deprecatedShape(shape) + private fun RustWriter.renderNamedEnum() { + // pub enum Blah { V1, V2, .. } + renderEnum() + insertTrailingNewline() + // impl From for Blah { ... } + enumType.implFromForStr(context)(this) + // impl FromStr for Blah { ... } + enumType.implFromStr(context)(this) + insertTrailingNewline() + // impl Blah { pub fn as_str(&self) -> &str + implBlock( + asStrImpl = writable { + rustBlock("match self") { + context.sortedMembers.forEach { member -> + rust("""${context.enumName}::${member.derivedName()} => ${member.value.dq()},""") + } + enumType.additionalAsStrMatchArms(context)(this) + } + }, + ) + rust( + """ + impl AsRef for ${context.enumName} { + fn as_ref(&self) -> &str { + self.as_str() + } + } + """, + ) + } + + private fun RustWriter.renderUnnamedEnum() { + documentShape(shape, model) + deprecatedShape(shape) RenderSerdeAttribute.writeAttributes(writer) SensitiveWarning.addDoc(writer, shape) - meta.render(writer) - writer.write("struct $enumName(String);") - writer.rustBlock("impl $enumName") { - docs("Returns the `&str` value of the enum member.") - rustBlock("pub fn as_str(&self) -> &str") { + context.enumMeta.render(this) + rust("struct ${context.enumName}(String);") + implBlock( + asStrImpl = writable { rust("&self.0") - } + }, + ) - docs("Returns all the `&str` representations of the enum members.") - rustBlock("pub const fn $Values() -> &'static [&'static str]") { - withBlock("&[", "]") { - val memberList = sortedMembers.joinToString(", ") { it.value.dq() } - rust(memberList) + rustTemplate( + """ + impl #{From} for ${context.enumName} where T: #{AsRef} { + fn from(s: T) -> Self { + ${context.enumName}(s.as_ref().to_owned()) } } - } - - writer.rustBlock("impl #T for $enumName where T: #T", RuntimeType.From, RuntimeType.AsRef) { - rustBlock("fn from(s: T) -> Self") { - rust("$enumName(s.as_ref().to_owned())") - } - } + """, + "From" to RuntimeType.From, + "AsRef" to RuntimeType.AsRef, + ) } - private fun renderEnum() { - target.ifClient { - writer.renderForwardCompatibilityNote(enumName, sortedMembers, UnknownVariant, UnknownVariantValue) - } + private fun RustWriter.renderEnum() { + enumType.additionalDocs(context)(this) val renamedWarning = - sortedMembers.mapNotNull { it.name() }.filter { it.renamedFrom != null }.joinToString("\n") { + context.sortedMembers.mapNotNull { it.name() }.filter { it.renamedFrom != null }.joinToString("\n") { val previousName = it.renamedFrom!! - "`$enumName::$previousName` has been renamed to `::${it.name}`." + "`${context.enumName}::$previousName` has been renamed to `::${it.name}`." } - writer.docWithNote( + docWithNote( shape.getTrait()?.value, renamedWarning.ifBlank { null }, ) - writer.deprecatedShape(shape) + + deprecatedShape(shape) RenderSerdeAttribute.writeAttributes(writer) SensitiveWarning.addDoc(writer, shape) - meta.render(writer) - writer.rustBlock("enum $enumName") { - sortedMembers.forEach { member -> member.render(writer) } - target.ifClient { - docs("`$UnknownVariant` contains new variants that have been added since this code was generated.") - rust("$UnknownVariant(#T)", unknownVariantValue()) - } - } - } - - private fun implBlock() { - writer.rustBlock("impl $enumName") { - rust("/// Returns the `&str` value of the enum member.") - rustBlock("pub fn as_str(&self) -> &str") { - rustBlock("match self") { - sortedMembers.forEach { member -> - rust("""$enumName::${member.derivedName()} => ${member.value.dq()},""") - } - target.ifClient { - rust("$enumName::$UnknownVariant(value) => value.as_str()") - } - } - } - - rust("/// Returns all the `&str` values of the enum members.") - rustBlock("pub const fn $Values() -> &'static [&'static str]") { - withBlock("&[", "]") { - val memberList = sortedMembers.joinToString(", ") { it.value.doubleQuote() } - write(memberList) - } - } + context.enumMeta.render(this) + rustBlock("enum ${context.enumName}") { + context.sortedMembers.forEach { member -> member.render(this) } + enumType.additionalEnumMembers(context)(this) } } - private fun unknownVariantValue(): RuntimeType { - return RuntimeType.forInlineFun(UnknownVariantValue, RustModule.Types) { - docs( - """ - Opaque struct used as inner data for the `Unknown` variant defined in enums in - the crate - - While this is not intended to be used directly, it is marked as `pub` because it is - part of the enums that are public interface. - """.trimIndent(), - ) - // adding serde features here adds attribute to the end of the file for some reason - meta.render(this) - rust("struct $UnknownVariantValue(pub(crate) String);") - 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") { - rust("&self.0") + private fun RustWriter.implBlock(asStrImpl: Writable) { + rustTemplate( + """ + impl ${context.enumName} { + /// Returns the `&str` value of the enum member. + pub fn as_str(&self) -> &str { + #{asStrImpl:W} + } + /// Returns all the `&str` representations of the enum members. + pub const fn $Values() -> &'static [&'static str] { + &[#{Values:W}] } } - } + """, + "asStrImpl" to asStrImpl, + "Values" to writable { + rust(context.sortedMembers.joinToString(", ") { it.value.dq() }) + }, + ) } /** @@ -244,10 +294,10 @@ open class EnumGenerator( * * It prints the redacted text regardless of the variant it is asked to print. */ - private fun renderDebugImplForSensitiveEnum() { - writer.rustTemplate( + private fun RustWriter.renderDebugImplForSensitiveEnum() { + rustTemplate( """ - impl #{Debug} for $enumName { + impl #{Debug} for ${context.enumName} { fn fmt(&self, f: &mut #{StdFmt}::Formatter<'_>) -> #{StdFmt}::Result { write!(f, $REDACTION) } @@ -257,89 +307,4 @@ open class EnumGenerator( "StdFmt" to RuntimeType.stdFmt, ) } - - protected open fun renderFromForStr() { - writer.rustBlock("impl #T<&str> for $enumName", RuntimeType.From) { - rustBlock("fn from(s: &str) -> Self") { - rustBlock("match s") { - sortedMembers.forEach { member -> - rust("""${member.value.dq()} => $enumName::${member.derivedName()},""") - } - rust("other => $enumName::$UnknownVariant(#T(other.to_owned()))", unknownVariantValue()) - } - } - } - } - - open fun renderFromStr() { - writer.rust( - """ - impl std::str::FromStr for $enumName { - type Err = std::convert::Infallible; - - fn from_str(s: &str) -> std::result::Result { - Ok($enumName::from(s)) - } - } - """, - ) - } -} - -/** - * Generate the rustdoc describing how to write a match expression against a generated enum in a - * forward-compatible way. - */ -private fun RustWriter.renderForwardCompatibilityNote( - enumName: String, sortedMembers: List, - unknownVariant: String, unknownVariantValue: String, -) { - docs( - """ - When writing a match expression against `$enumName`, it is important to ensure - your code is forward-compatible. That is, if a match arm handles a case for a - feature that is supported by the service but has not been represented as an enum - variant in a current version of SDK, your code should continue to work when you - upgrade SDK to a future version in which the enum does include a variant for that - feature. - """.trimIndent(), - ) - docs("") - docs("Here is an example of how you can make a match expression forward-compatible:") - docs("") - docs("```text") - rust("/// ## let ${enumName.lowercase()} = unimplemented!();") - rust("/// match ${enumName.lowercase()} {") - sortedMembers.mapNotNull { it.name() }.forEach { member -> - rust("/// $enumName::${member.name} => { /* ... */ },") - } - rust("""/// other @ _ if other.as_str() == "NewFeature" => { /* handles a case for `NewFeature` */ },""") - rust("/// _ => { /* ... */ },") - rust("/// }") - docs("```") - docs( - """ - The above code demonstrates that when `${enumName.lowercase()}` represents - `NewFeature`, the execution path will lead to the second last match arm, - even though the enum does not contain a variant `$enumName::NewFeature` - in the current version of SDK. The reason is that the variable `other`, - created by the `@` operator, is bound to - `$enumName::$unknownVariant($unknownVariantValue("NewFeature".to_owned()))` - and calling `as_str` on it yields `"NewFeature"`. - This match expression is forward-compatible when executed with a newer - version of SDK where the variant `$enumName::NewFeature` is defined. - Specifically, when `${enumName.lowercase()}` represents `NewFeature`, - the execution path will hit the second last match arm as before by virtue of - calling `as_str` on `$enumName::NewFeature` also yielding `"NewFeature"`. - """.trimIndent(), - ) - docs("") - docs( - """ - Explicitly matching on the `$unknownVariant` variant should - be avoided for two reasons: - - The inner data `$unknownVariantValue` is opaque, and no further information can be extracted. - - It might inadvertently shadow other intended match arms. - """.trimIndent(), - ) } 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 a2683ab713..9f390509ed 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 @@ -55,6 +55,7 @@ import software.amazon.smithy.rust.codegen.core.util.expectMember import software.amazon.smithy.rust.codegen.core.util.hasTrait import software.amazon.smithy.rust.codegen.core.util.isTargetUnit import software.amazon.smithy.rust.codegen.core.util.letIf +import java.math.BigDecimal /** * Class describing an instantiator section that can be used in a customization. @@ -125,10 +126,16 @@ open class Instantiator( is MemberShape -> renderMember(writer, shape, data, ctx) // Wrapped Shapes - is TimestampShape -> writer.rust( - "#T::from_secs(${(data as NumberNode).value})", - RuntimeType.dateTime(runtimeConfig), - ) + is TimestampShape -> { + val node = (data as NumberNode) + val num = BigDecimal(node.toString()) + val wholePart = num.toInt() + val fractionalPart = num.remainder(BigDecimal.ONE) + writer.rust( + "#T::from_fractional_secs($wholePart, ${fractionalPart}_f64)", + RuntimeType.dateTime(runtimeConfig), + ) + } /** * ```rust diff --git a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/generators/LibRsGenerator.kt b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/generators/LibRsGenerator.kt index 359386e34d..1da954f086 100644 --- a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/generators/LibRsGenerator.kt +++ b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/generators/LibRsGenerator.kt @@ -8,23 +8,28 @@ package software.amazon.smithy.rust.codegen.core.smithy.generators import software.amazon.smithy.model.Model import software.amazon.smithy.model.traits.DocumentationTrait 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.containerDocs import software.amazon.smithy.rust.codegen.core.rustlang.escape -import software.amazon.smithy.rust.codegen.core.rustlang.isEmpty +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.smithy.CoreRustSettings 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.util.getTrait +sealed class ModuleDocSection { + data class ServiceDocs(val documentationTraitValue: String?) : ModuleDocSection() + object CrateOrganization : ModuleDocSection() + object Examples : ModuleDocSection() +} + sealed class LibRsSection(name: String) : Section(name) { object Attributes : LibRsSection("Attributes") - data class ModuleDocumentation(val subsection: String) : LibRsSection("ModuleDocumentation") + + data class ModuleDoc(val subsection: ModuleDocSection) : LibRsSection("ModuleDoc") + data class Body(val model: Model) : LibRsSection("Body") - companion object { - val Examples = "Examples" - val CrateOrganization = "CrateOrganization" - } } typealias LibRsCustomization = NamedCustomization @@ -35,6 +40,10 @@ class LibRsGenerator( private val customizations: List, private val requireDocs: Boolean, ) { + private fun docSection(section: ModuleDocSection): List = customizations + .map { customization -> customization.section(LibRsSection.ModuleDoc(section)) } + .filter { it.isNotEmpty() } + fun render(writer: RustWriter) { writer.first { customizations.forEach { it.section(LibRsSection.Attributes)(this) } @@ -42,23 +51,39 @@ class LibRsGenerator( rust("##![warn(missing_docs)]") } - val libraryDocs = settings.getService(model).getTrait()?.value ?: settings.moduleName - containerDocs(escape(libraryDocs)) - val crateLayout = customizations.map { it.section(LibRsSection.ModuleDocumentation(LibRsSection.CrateOrganization)) }.filter { !it.isEmpty() } - if (crateLayout.isNotEmpty()) { - containerDocs("\n## Crate Organization") - crateLayout.forEach { it(this) } + // Allow for overriding the default service docs via customization + val defaultServiceDocs = settings.getService(model).getTrait()?.value + val serviceDocs = docSection(ModuleDocSection.ServiceDocs(defaultServiceDocs)) + if (serviceDocs.isNotEmpty()) { + serviceDocs.forEach { writeTo -> + writeTo(this) + } + } else { + containerDocs(escape(defaultServiceDocs ?: settings.moduleName)) + } + + // Crate Organization + docSection(ModuleDocSection.CrateOrganization).also { docs -> + if (docs.isNotEmpty()) { + containerDocs("\n## Crate Organization") + docs.forEach { writeTo -> + writeTo(this) + } + } } - val examples = customizations.map { it.section(LibRsSection.ModuleDocumentation(LibRsSection.Examples)) } - .filter { section -> !section.isEmpty() } - if (examples.isNotEmpty() || settings.examplesUri != null) { - containerDocs("\n## Examples") - examples.forEach { it(this) } + // Examples + docSection(ModuleDocSection.Examples).also { docs -> + if (docs.isNotEmpty() || settings.examplesUri != null) { + containerDocs("\n## Examples") + docs.forEach { writeTo -> + writeTo(this) + } - // TODO(https://github.com/awslabs/smithy-rs/issues/69): Generate a basic example for all crates (eg. select first operation and render an example of usage) - settings.examplesUri?.also { uri -> - containerDocs("Examples can be found [here]($uri).") + // TODO(https://github.com/awslabs/smithy-rs/issues/69): Generate a basic example for all crates (eg. select first operation and render an example of usage) + settings.examplesUri?.also { uri -> + containerDocs("Examples can be found [here]($uri).") + } } } diff --git a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/generators/StructureGenerator.kt b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/generators/StructureGenerator.kt index b0c72571e3..5bd332edb5 100644 --- a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/generators/StructureGenerator.kt +++ b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/generators/StructureGenerator.kt @@ -5,16 +5,13 @@ package software.amazon.smithy.rust.codegen.core.smithy.generators import software.amazon.smithy.codegen.core.Symbol -import software.amazon.smithy.codegen.core.SymbolProvider import software.amazon.smithy.model.Model import software.amazon.smithy.model.shapes.MemberShape -import software.amazon.smithy.model.shapes.Shape import software.amazon.smithy.model.shapes.StructureShape import software.amazon.smithy.model.traits.ErrorTrait import software.amazon.smithy.model.traits.SensitiveTrait 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.asDeref import software.amazon.smithy.rust.codegen.core.rustlang.asRef import software.amazon.smithy.rust.codegen.core.rustlang.deprecatedShape @@ -24,43 +21,64 @@ import software.amazon.smithy.rust.codegen.core.rustlang.isDeref 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.smithy.CodegenTarget 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.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.smithy.expectRustMetadata -import software.amazon.smithy.rust.codegen.core.smithy.generators.error.ErrorGenerator import software.amazon.smithy.rust.codegen.core.smithy.renamedFrom import software.amazon.smithy.rust.codegen.core.smithy.rustType 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.redactIfNecessary -fun RustWriter.implBlock(structureShape: Shape, symbolProvider: SymbolProvider, block: Writable) { - rustBlock("impl ${symbolProvider.toSymbol(structureShape).name}") { - block() - } +/** StructureGenerator customization sections */ +sealed class StructureSection(name: String) : Section(name) { + abstract val shape: StructureShape + + /** Hook to add additional fields to the structure */ + data class AdditionalFields(override val shape: StructureShape) : StructureSection("AdditionalFields") + + /** Hook to add additional fields to the `Debug` impl */ + data class AdditionalDebugFields(override val shape: StructureShape, val formatterName: String) : + StructureSection("AdditionalDebugFields") + + /** Hook to add additional trait impls to the structure */ + data class AdditionalTraitImpls(override val shape: StructureShape, val structName: String) : + StructureSection("AdditionalTraitImpls") } +/** Customizations for StructureGenerator */ +abstract class StructureCustomization : NamedCustomization() + open class StructureGenerator( val model: Model, private val symbolProvider: RustSymbolProvider, private val writer: RustWriter, private val shape: StructureShape, + private val customizations: List, ) { + companion object { + /** Reserved struct member names */ + val structureMemberNameMap: Map = mapOf( + "build" to "build_value", + "builder" to "builder_value", + "default" to "default_value", + ) + } + private val errorTrait = shape.getTrait() protected val members: List = shape.allMembers.values.toList() - protected val accessorMembers: List = when (errorTrait) { + private val accessorMembers: List = when (errorTrait) { null -> members // Let the ErrorGenerator render the error message accessor if this is an error struct else -> members.filter { "message" != symbolProvider.toMemberName(it) } } - protected val name = symbolProvider.toSymbol(shape).name + protected val name: String = symbolProvider.toSymbol(shape).name - fun render(forWhom: CodegenTarget = CodegenTarget.CLIENT) { + fun render() { renderStructure() - errorTrait?.also { errorTrait -> - ErrorGenerator(model, symbolProvider, writer, shape, errorTrait).render(forWhom) - } } /** @@ -78,7 +96,9 @@ open class StructureGenerator( }.toSet().sorted() return if (lifetimes.isNotEmpty()) { "<${lifetimes.joinToString { "'$it" }}>" - } else "" + } else { + "" + } } /** @@ -97,6 +117,7 @@ open class StructureGenerator( "formatter.field(${memberName.dq()}, &$fieldValue);", ) } + writeCustomizations(customizations, StructureSection.AdditionalDebugFields(shape, "formatter")) rust("formatter.finish()") } } @@ -109,7 +130,7 @@ open class StructureGenerator( writer.rustBlock("impl $name") { // Render field accessor methods forEachMember(accessorMembers) { member, memberName, memberSymbol -> - renderMemberDoc(member, memberSymbol) + writer.renderMemberDoc(member, memberSymbol) writer.deprecatedShape(member) val memberType = memberSymbol.rustType() val returnType = when { @@ -118,7 +139,7 @@ open class StructureGenerator( memberType.isDeref() -> memberType.asDeref().asRef() else -> memberType.asRef() } - rustBlock("pub fn $memberName(&self) -> ${returnType.render()}") { + writer.rustBlock("pub fn $memberName(&self) -> ${returnType.render()}") { when { memberType.isCopy() -> rust("self.$memberName") memberType is RustType.Option && memberType.member.isDeref() -> rust("self.$memberName.as_deref()") @@ -153,12 +174,15 @@ open class StructureGenerator( SensitiveWarning.addDoc(writer, shape) renderStructureMember(writer, member, memberName, memberSymbol) } + writeCustomizations(customizations, StructureSection.AdditionalFields(shape)) } renderStructureImpl() if (!containerMeta.hasDebugDerive()) { renderDebugImpl() } + + writer.writeCustomizations(customizations, StructureSection.AdditionalTraitImpls(shape, name)) } protected fun RustWriter.forEachMember( 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 b0ba3b09ff..170729db53 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 @@ -15,6 +15,7 @@ import software.amazon.smithy.rust.codegen.core.rustlang.RustWriter 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.rustTemplate @@ -22,6 +23,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.expectRustMetadata import software.amazon.smithy.rust.codegen.core.smithy.renamedFrom +import software.amazon.smithy.rust.codegen.core.smithy.rustType import software.amazon.smithy.rust.codegen.core.util.REDACTION import software.amazon.smithy.rust.codegen.core.util.dq import software.amazon.smithy.rust.codegen.core.util.hasTrait @@ -47,7 +49,7 @@ fun CodegenTarget.renderUnknownVariant() = when (this) { * Finally, if `[renderUnknownVariant]` is true (the default), it will render an `Unknown` variant. This is used by * clients to allow response parsing to succeed, even if the server has added a new variant since the client was generated. */ -class UnionGenerator( +open class UnionGenerator( val model: Model, private val symbolProvider: SymbolProvider, private val writer: RustWriter, @@ -57,7 +59,7 @@ class UnionGenerator( private val sortedMembers: List = shape.allMembers.values.sortedBy { symbolProvider.toMemberName(it) } private val unionSymbol = symbolProvider.toSymbol(shape) - fun render() { + open fun render() { writer.documentShape(shape, model) writer.deprecatedShape(shape) RenderSerdeAttribute.writeAttributes(writer) @@ -111,7 +113,7 @@ class UnionGenerator( if (sortedMembers.size == 1) { Attribute.AllowIrrefutableLetPatterns.render(this) } - writer.renderAsVariant(member, variantName, funcNamePart, unionSymbol, memberSymbol) + writer.renderAsVariant(model, symbolProvider, member, variantName, funcNamePart, unionSymbol) rust("/// Returns true if this is a [`$variantName`](#T::$variantName).", unionSymbol) rustBlock("pub fn is_$funcNamePart(&self) -> bool") { rust("self.as_$funcNamePart().is_ok()") @@ -182,11 +184,12 @@ private fun RustWriter.renderVariant(symbolProvider: SymbolProvider, member: Mem } private fun RustWriter.renderAsVariant( + model: Model, + symbolProvider: SymbolProvider, member: MemberShape, variantName: String, funcNamePart: String, unionSymbol: Symbol, - memberSymbol: Symbol, ) { if (member.isTargetUnit()) { rust( @@ -197,13 +200,15 @@ private fun RustWriter.renderAsVariant( rust("if let ${unionSymbol.name}::$variantName = &self { Ok(()) } else { Err(self) }") } } else { + val memberSymbol = symbolProvider.toSymbol(member) + val targetSymbol = symbolProvider.toSymbol(model.expectShape(member.target)) rust( "/// Tries to convert the enum instance into [`$variantName`](#T::$variantName), extracting the inner #D.", unionSymbol, - memberSymbol, + targetSymbol, ) rust("/// Returns `Err(&Self)` if it can't be converted.") - rustBlock("pub fn as_$funcNamePart(&self) -> std::result::Result<&#T, &Self>", memberSymbol) { + 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) }") } } diff --git a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/generators/error/ErrorGenerator.kt b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/generators/error/ErrorImplGenerator.kt similarity index 83% rename from codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/generators/error/ErrorGenerator.kt rename to codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/generators/error/ErrorImplGenerator.kt index 071a5bd89a..692bf32aab 100644 --- a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/generators/error/ErrorGenerator.kt +++ b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/generators/error/ErrorImplGenerator.kt @@ -5,6 +5,7 @@ package software.amazon.smithy.rust.codegen.core.smithy.generators.error +import software.amazon.smithy.codegen.core.Symbol import software.amazon.smithy.model.Model import software.amazon.smithy.model.shapes.StructureShape import software.amazon.smithy.model.traits.ErrorTrait @@ -23,6 +24,9 @@ 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.StdError 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 +import software.amazon.smithy.rust.codegen.core.smithy.customize.writeCustomizations 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.serialize.ValueExpression @@ -34,22 +38,31 @@ import software.amazon.smithy.rust.codegen.core.util.getTrait import software.amazon.smithy.rust.codegen.core.util.letIf import software.amazon.smithy.rust.codegen.core.util.shouldRedact +/** Error customization sections */ +sealed class ErrorImplSection(name: String) : Section(name) { + /** Use this section to add additional trait implementations to the generated error structures */ + class ErrorAdditionalTraitImpls(val errorType: Symbol) : ErrorImplSection("ErrorAdditionalTraitImpls") +} + +/** Customizations for generated errors */ +abstract class ErrorImplCustomization : NamedCustomization() + sealed class ErrorKind { abstract fun writable(runtimeConfig: RuntimeConfig): Writable object Throttling : ErrorKind() { override fun writable(runtimeConfig: RuntimeConfig) = - writable { rust("#T::ThrottlingError", RuntimeType.errorKind(runtimeConfig)) } + writable { rust("#T::ThrottlingError", RuntimeType.retryErrorKind(runtimeConfig)) } } object Client : ErrorKind() { override fun writable(runtimeConfig: RuntimeConfig) = - writable { rust("#T::ClientError", RuntimeType.errorKind(runtimeConfig)) } + writable { rust("#T::ClientError", RuntimeType.retryErrorKind(runtimeConfig)) } } object Server : ErrorKind() { override fun writable(runtimeConfig: RuntimeConfig) = - writable { rust("#T::ServerError", RuntimeType.errorKind(runtimeConfig)) } + writable { rust("#T::ServerError", RuntimeType.retryErrorKind(runtimeConfig)) } } } @@ -69,19 +82,22 @@ fun StructureShape.modeledRetryKind(errorTrait: ErrorTrait): ErrorKind? { } } -class ErrorGenerator( +class ErrorImplGenerator( private val model: Model, private val symbolProvider: RustSymbolProvider, private val writer: RustWriter, private val shape: StructureShape, private val error: ErrorTrait, + private val customizations: List, ) { + private val runtimeConfig = symbolProvider.config.runtimeConfig + fun render(forWhom: CodegenTarget = CodegenTarget.CLIENT) { val symbol = symbolProvider.toSymbol(shape) val messageShape = shape.errorMessageMember() - val errorKindT = RuntimeType.errorKind(symbolProvider.config().runtimeConfig) + val errorKindT = RuntimeType.retryErrorKind(runtimeConfig) writer.rustBlock("impl ${symbol.name}") { - val retryKindWriteable = shape.modeledRetryKind(error)?.writable(symbolProvider.config().runtimeConfig) + val retryKindWriteable = shape.modeledRetryKind(error)?.writable(runtimeConfig) if (retryKindWriteable != null) { rust("/// Returns `Some(${errorKindT.name})` if the error is retryable. Otherwise, returns `None`.") rustBlock("pub fn retryable_error_kind(&self) -> #T", errorKindT) { @@ -153,6 +169,9 @@ class ErrorGenerator( write("Ok(())") } } + writer.write("impl #T for ${symbol.name} {}", StdError) + + writer.writeCustomizations(customizations, ErrorImplSection.ErrorAdditionalTraitImpls(symbol)) } } diff --git a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/generators/error/OperationErrorGenerator.kt b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/generators/error/OperationErrorGenerator.kt deleted file mode 100644 index 69f1ad8639..0000000000 --- a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/generators/error/OperationErrorGenerator.kt +++ /dev/null @@ -1,267 +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.error - -import software.amazon.smithy.codegen.core.Symbol -import software.amazon.smithy.model.Model -import software.amazon.smithy.model.shapes.OperationShape -import software.amazon.smithy.model.shapes.Shape -import software.amazon.smithy.model.shapes.StructureShape -import software.amazon.smithy.model.shapes.UnionShape -import software.amazon.smithy.model.traits.RetryableTrait -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.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.Writable -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.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.RustSymbolProvider -import software.amazon.smithy.rust.codegen.core.smithy.customize.Section -import software.amazon.smithy.rust.codegen.core.util.hasTrait -import software.amazon.smithy.rust.codegen.core.util.toSnakeCase - -/** - * For a given Operation ([this]), return the symbol referring to the operation error. This can be used - * if you, e.g. want to return an operation error from a function: - * - * ```kotlin - * rustWriter.rustBlock("fn get_error() -> #T", operation.errorSymbol(symbolProvider)) { - * write("todo!() // function body") - * } - * ``` - */ -fun OperationShape.errorSymbol(symbolProvider: RustSymbolProvider): RuntimeType { - val operationSymbol = symbolProvider.toSymbol(this) - return RustModule.Error.toType().resolve("${operationSymbol.name}Error") -} - -fun UnionShape.eventStreamErrorSymbol(symbolProvider: RustSymbolProvider): RuntimeType { - val unionSymbol = symbolProvider.toSymbol(this) - return RustModule.Error.toType().resolve("${unionSymbol.name}Error") -} - -/** - * Generates a unified error enum for [operation]. [ErrorGenerator] handles generating the individual variants, - * but we must still combine those variants into an enum covering all possible errors for a given operation. - */ -class OperationErrorGenerator( - private val model: Model, - private val symbolProvider: RustSymbolProvider, - private val operationSymbol: Symbol, - private val errors: List, -) { - private val runtimeConfig = symbolProvider.config().runtimeConfig - private val genericError = RuntimeType.genericError(symbolProvider.config().runtimeConfig) - private val createUnhandledError = - RuntimeType.smithyHttp(runtimeConfig).resolve("result::CreateUnhandledError") - - fun render(writer: RustWriter) { - val errorSymbol = RuntimeType("crate::error::${operationSymbol.name}Error") - renderErrors(writer, errorSymbol, operationSymbol) - } - - fun renderErrors( - writer: RustWriter, - errorSymbol: RuntimeType, - operationSymbol: Symbol, - ) { - val meta = RustMetadata( - derives = setOf(RuntimeType.Debug), - additionalAttributes = listOf(Attribute.NonExhaustive), - visibility = Visibility.PUBLIC, - ) - - writer.rust("/// Error type for the `${operationSymbol.name}` operation.") - meta.render(writer) - writer.rustBlock("struct ${errorSymbol.name}") { - rust( - """ - /// Kind of error that occurred. - pub kind: ${errorSymbol.name}Kind, - /// Additional metadata about the error, including error code, message, and request ID. - pub (crate) meta: #T - """, - RuntimeType.genericError(runtimeConfig), - ) - } - writer.rustBlock("impl #T for ${errorSymbol.name}", createUnhandledError) { - rustBlock("fn create_unhandled_error(source: Box) -> Self") { - rustBlock("Self") { - rust("kind: ${errorSymbol.name}Kind::Unhandled(#T::new(source)),", unhandledError()) - rust("meta: Default::default()") - } - } - } - - writer.rust("/// Types of errors that can occur for the `${operationSymbol.name}` operation.") - meta.render(writer) - writer.rustBlock("enum ${errorSymbol.name}Kind") { - errors.forEach { errorVariant -> - documentShape(errorVariant, model) - deprecatedShape(errorVariant) - val errorVariantSymbol = symbolProvider.toSymbol(errorVariant) - write("${errorVariantSymbol.name}(#T),", errorVariantSymbol) - } - docs(UNHANDLED_ERROR_DOCS) - rust( - """ - Unhandled(#T), - """, - unhandledError(), - ) - } - writer.rustBlock("impl #T for ${errorSymbol.name}", RuntimeType.Display) { - rustBlock("fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result") { - delegateToVariants(errors, errorSymbol) { - writable { rust("_inner.fmt(f)") } - } - } - } - - val errorKindT = RuntimeType.errorKind(symbolProvider.config().runtimeConfig) - writer.rustBlock( - "impl #T for ${errorSymbol.name}", - RuntimeType.provideErrorKind(symbolProvider.config().runtimeConfig), - ) { - rustBlock("fn code(&self) -> Option<&str>") { - rust("${errorSymbol.name}::code(self)") - } - - rustBlock("fn retryable_error_kind(&self) -> Option<#T>", errorKindT) { - val retryableVariants = errors.filter { it.hasTrait() } - if (retryableVariants.isEmpty()) { - rust("None") - } else { - rustBlock("match &self.kind") { - retryableVariants.forEach { - val errorVariantSymbol = symbolProvider.toSymbol(it) - rust("${errorSymbol.name}Kind::${errorVariantSymbol.name}(inner) => Some(inner.retryable_error_kind()),") - } - rust("_ => None") - } - } - } - } - - writer.rustBlock("impl ${errorSymbol.name}") { - writer.rustTemplate( - """ - /// Creates a new `${errorSymbol.name}`. - pub fn new(kind: ${errorSymbol.name}Kind, meta: #{generic_error}) -> Self { - Self { kind, meta } - } - - /// Creates the `${errorSymbol.name}::Unhandled` variant from any error type. - pub fn unhandled(err: impl Into>) -> Self { - Self { - kind: ${errorSymbol.name}Kind::Unhandled(#{Unhandled}::new(err.into())), - meta: Default::default() - } - } - - /// Creates the `${errorSymbol.name}::Unhandled` variant from a `#{generic_error}`. - pub fn generic(err: #{generic_error}) -> Self { - Self { - meta: err.clone(), - kind: ${errorSymbol.name}Kind::Unhandled(#{Unhandled}::new(err.into())), - } - } - - /// Returns the error message if one is available. - pub fn message(&self) -> Option<&str> { - self.meta.message() - } - - /// Returns error metadata, which includes the error code, message, - /// request ID, and potentially additional information. - pub fn meta(&self) -> &#{generic_error} { - &self.meta - } - - /// Returns the request ID if it's available. - pub fn request_id(&self) -> Option<&str> { - self.meta.request_id() - } - - /// Returns the error code if it's available. - pub fn code(&self) -> Option<&str> { - self.meta.code() - } - """, - "generic_error" to genericError, - "std_error" to RuntimeType.StdError, - "Unhandled" to unhandledError(), - ) - errors.forEach { error -> - val errorVariantSymbol = symbolProvider.toSymbol(error) - val fnName = errorVariantSymbol.name.toSnakeCase() - writer.rust("/// Returns `true` if the error kind is `${errorSymbol.name}Kind::${errorVariantSymbol.name}`.") - writer.rustBlock("pub fn is_$fnName(&self) -> bool") { - rust("matches!(&self.kind, ${errorSymbol.name}Kind::${errorVariantSymbol.name}(_))") - } - } - } - - writer.rustBlock("impl #T for ${errorSymbol.name}", RuntimeType.StdError) { - rustBlock("fn source(&self) -> Option<&(dyn #T + 'static)>", RuntimeType.StdError) { - delegateToVariants(errors, errorSymbol) { - writable { - rust("Some(_inner)") - } - } - } - } - } - - sealed class VariantMatch(name: String) : Section(name) { - object Unhandled : VariantMatch("Unhandled") - data class Modeled(val symbol: Symbol, val shape: Shape) : VariantMatch("Modeled") - } - - /** - * Generates code to delegate behavior to the variants, for example: - * - * ```rust - * match &self.kind { - * GreetingWithErrorsError::InvalidGreeting(_inner) => inner.fmt(f), - * GreetingWithErrorsError::ComplexError(_inner) => inner.fmt(f), - * GreetingWithErrorsError::FooError(_inner) => inner.fmt(f), - * GreetingWithErrorsError::Unhandled(_inner) => _inner.fmt(f), - * } - * ``` - * - * [handler] is passed an instance of [VariantMatch]—a [writable] should be returned containing the content to be - * written for this variant. - * - * The field will always be bound as `_inner`. - */ - private fun RustWriter.delegateToVariants( - errors: List, - symbol: RuntimeType, - handler: (VariantMatch) -> Writable, - ) { - rustBlock("match &self.kind") { - errors.forEach { - val errorSymbol = symbolProvider.toSymbol(it) - rust("""${symbol.name}Kind::${errorSymbol.name}(_inner) => """) - handler(VariantMatch.Modeled(errorSymbol, it))(this) - write(",") - } - val unhandledHandler = handler(VariantMatch.Unhandled) - rustBlock("${symbol.name}Kind::Unhandled(_inner) =>") { - unhandledHandler(this) - } - } - } -} diff --git a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/generators/error/UnhandledErrorGenerator.kt b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/generators/error/UnhandledErrorGenerator.kt deleted file mode 100644 index bd28c1a852..0000000000 --- a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/generators/error/UnhandledErrorGenerator.kt +++ /dev/null @@ -1,50 +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.error - -import software.amazon.smithy.rust.codegen.core.rustlang.RustModule -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.smithy.RuntimeType - -internal const val UNHANDLED_ERROR_DOCS = - """ - An unexpected error occurred (e.g., invalid JSON returned by the service or an unknown error code). - - When logging an error from the SDK, it is recommended that you either wrap the error in - [`DisplayErrorContext`](crate::types::DisplayErrorContext), use another - error reporter library that visits the error's cause/source chain, or call - [`Error::source`](std::error::Error::source) for more details about the underlying cause. - """ - -internal fun unhandledError(): RuntimeType = RuntimeType.forInlineFun("Unhandled", RustModule.Error) { - docs(UNHANDLED_ERROR_DOCS) - rustTemplate( - """ - ##[derive(Debug)] - pub struct Unhandled { - source: Box, - } - impl Unhandled { - ##[allow(unused)] - pub(crate) fn new(source: Box) -> Self { - Self { source } - } - } - impl std::fmt::Display for Unhandled { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> Result<(), std::fmt::Error> { - write!(f, "unhandled error") - } - } - impl #{StdError} for Unhandled { - fn source(&self) -> Option<&(dyn #{StdError} + 'static)> { - Some(self.source.as_ref() as _) - } - } - """, - "StdError" to RuntimeType.StdError, - ) -} 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 b2ca5e066f..d53e09df68 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 @@ -26,7 +26,6 @@ import software.amazon.smithy.model.shapes.UnionShape import software.amazon.smithy.model.traits.EnumTrait import software.amazon.smithy.model.traits.MediaTypeTrait import software.amazon.smithy.model.traits.TimestampFormatTrait -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.RustWriter import software.amazon.smithy.rust.codegen.core.rustlang.Writable @@ -51,6 +50,7 @@ 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.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.parse.EventStreamUnmarshallerGenerator import software.amazon.smithy.rust.codegen.core.smithy.protocols.serialize.ValueExpression import software.amazon.smithy.rust.codegen.core.smithy.rustType @@ -62,7 +62,6 @@ import software.amazon.smithy.rust.codegen.core.util.isPrimitive import software.amazon.smithy.rust.codegen.core.util.isStreaming import software.amazon.smithy.rust.codegen.core.util.outputShape import software.amazon.smithy.rust.codegen.core.util.redactIfNecessary -import software.amazon.smithy.rust.codegen.core.util.toSnakeCase /** * The type of HTTP message from which we are (de)serializing the HTTP-bound data. @@ -115,19 +114,16 @@ class HttpBindingGenerator( private val codegenContext: CodegenContext, private val symbolProvider: SymbolProvider, private val operationShape: OperationShape, - /** Function that maps a StructureShape into its builder symbol */ - private val builderSymbol: (StructureShape) -> Symbol, private val customizations: List = listOf(), ) { private val runtimeConfig = codegenContext.runtimeConfig private val codegenTarget = codegenContext.target private val model = codegenContext.model - private val service = codegenContext.serviceShape private val index = HttpBindingIndex.of(model) private val headerUtil = RuntimeType.smithyHttp(runtimeConfig).resolve("header") private val defaultTimestampFormat = TimestampFormatTrait.Format.EPOCH_SECONDS private val dateTime = RuntimeType.dateTime(runtimeConfig).toSymbol().rustType() - private val httpSerdeModule = RustModule.private("http_serde") + private val protocolFunctions = ProtocolFunctions(codegenContext) /** * Generate a function to deserialize [binding] from HTTP headers. @@ -144,8 +140,7 @@ class HttpBindingGenerator( fun generateDeserializeHeaderFn(binding: HttpBindingDescriptor): RuntimeType { check(binding.location == HttpLocation.HEADER) val outputT = symbolProvider.toSymbol(binding.member).makeOptional() - val fnName = "deser_header_${fnName(operationShape, binding)}" - return RuntimeType.forInlineFun(fnName, httpSerdeModule) { + return protocolFunctions.deserializeFn(binding.member, fnNameSuffix = "header") { fnName -> rustBlock( "pub(crate) fn $fnName(header_map: &#T::HeaderMap) -> std::result::Result<#T, #T::ParseError>", RuntimeType.Http, @@ -163,10 +158,9 @@ class HttpBindingGenerator( val outputSymbol = symbolProvider.toSymbol(binding.member) val target = model.expectShape(binding.member.target) check(target is MapShape) - val fnName = "deser_prefix_header_${fnName(operationShape, binding)}" - val inner = RuntimeType.forInlineFun("${fnName}_inner", httpSerdeModule) { + val inner = protocolFunctions.deserializeFn(binding.member, fnNameSuffix = "inner") { fnName -> rustBlock( - "pub fn ${fnName}_inner(headers: #T::header::ValueIter) -> std::result::Result, #T::ParseError>", + "pub fn $fnName(headers: #T::header::ValueIter) -> std::result::Result, #T::ParseError>", RuntimeType.Http, symbolProvider.toSymbol(model.expectShape(target.value.target)), headerUtil, @@ -175,7 +169,7 @@ class HttpBindingGenerator( } } val returnTypeSymbol = outputSymbol.mapRustType { it.asOptional() } - return RuntimeType.forInlineFun(fnName, httpSerdeModule) { + return protocolFunctions.deserializeFn(binding.member, fnNameSuffix = "prefix_header") { fnName -> rustBlock( "pub(crate) fn $fnName(header_map: &#T::HeaderMap) -> std::result::Result<#T, #T::ParseError>", RuntimeType.Http, @@ -210,21 +204,20 @@ class HttpBindingGenerator( */ fun generateDeserializePayloadFn( binding: HttpBindingDescriptor, - errorT: RuntimeType, + errorSymbol: Symbol, // Deserialize a single structure, union or document member marked as a payload payloadParser: RustWriter.(String) -> Unit, httpMessageType: HttpMessageType = HttpMessageType.RESPONSE, ): RuntimeType { check(binding.location == HttpBinding.Location.PAYLOAD) - val fnName = "deser_payload_${fnName(operationShape, binding)}" - return RuntimeType.forInlineFun(fnName, httpSerdeModule) { + return protocolFunctions.deserializeFn(binding.member, fnNameSuffix = "payload") { fnName -> if (binding.member.isStreaming(model)) { val outputT = symbolProvider.toSymbol(binding.member) rustBlock( "pub fn $fnName(body: &mut #T) -> std::result::Result<#T, #T>", RuntimeType.sdkBody(runtimeConfig), outputT, - errorT, + errorSymbol, ) { // Streaming unions are Event Streams and should be handled separately val target = model.expectShape(binding.member.target) @@ -238,10 +231,10 @@ 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, errorT) { + rustBlock("pub fn $fnName(body: &[u8]) -> std::result::Result<#T, #T>", outputT, errorSymbol) { deserializePayloadBody( binding, - errorT, + errorSymbol, structuredHandler = payloadParser, httpMessageType, ) @@ -256,7 +249,6 @@ class HttpBindingGenerator( codegenContext, operationShape, targetShape, - builderSymbol, ).render() rustTemplate( """ @@ -286,7 +278,7 @@ class HttpBindingGenerator( private fun RustWriter.deserializePayloadBody( binding: HttpBindingDescriptor, - errorSymbol: RuntimeType, + errorSymbol: Symbol, structuredHandler: RustWriter.(String) -> Unit, httpMessageType: HttpMessageType = HttpMessageType.RESPONSE, ) { @@ -367,7 +359,7 @@ class HttpBindingGenerator( HttpBinding.Location.HEADER, defaultTimestampFormat, ) - val timestampFormatType = RuntimeType.timestampFormat(runtimeConfig, timestampFormat) + val timestampFormatType = RuntimeType.parseTimestampFormat(codegenTarget, runtimeConfig, timestampFormat) rust( "let $parsedValue: Vec<${coreType.render()}> = #T::many_dates(headers, #T)?;", headerUtil, @@ -454,15 +446,6 @@ class HttpBindingGenerator( } } - /** - * Generate a unique name for the deserializer function for a given [operationShape] and HTTP binding. - */ - // Rename here technically not required, operations and members cannot be renamed. - private fun fnName(operationShape: OperationShape, binding: HttpBindingDescriptor) = - "${ - operationShape.id.getName(service).toSnakeCase() - }_${binding.member.container.name.toSnakeCase()}_${binding.memberName.toSnakeCase()}" - /** * Returns a function to set headers on an HTTP message for the given [shape]. * Returns null if no headers need to be set. @@ -488,8 +471,7 @@ class HttpBindingGenerator( return null } - val fnName = "add_headers_${shape.id.getName(service).toSnakeCase()}" - return RuntimeType.forInlineFun(fnName, httpSerdeModule) { + return protocolFunctions.serializeFn(shape, fnNameSuffix = "headers") { fnName -> // If the shape is an operation shape, the input symbol of the generated function is the input or output // shape, which is the shape holding the header-bound data. val shapeSymbol = symbolProvider.toSymbol( @@ -650,12 +632,13 @@ class HttpBindingGenerator( let $safeName = $formatted; if !$safeName.is_empty() { let header_value = $safeName; - let header_value = http::header::HeaderValue::try_from(&*header_value).map_err(|err| { + let header_value: #{HeaderValue} = header_value.parse().map_err(|err| { #{invalid_field_error:W} })?; builder = builder.header("$headerName", header_value); } """, + "HeaderValue" to RuntimeType.Http.resolve("HeaderValue"), "invalid_field_error" to renderErrorMessage("header_value"), ) } @@ -690,21 +673,22 @@ class HttpBindingGenerator( #{invalid_header_name:W} })?; let header_value = ${ - headerFmtFun( - this, - valueTargetShape, - timestampFormat, - "v", - isMultiValuedHeader = false, - ) + headerFmtFun( + this, + valueTargetShape, + timestampFormat, + "v", + isMultiValuedHeader = false, + ) }; - let header_value = http::header::HeaderValue::try_from(&*header_value).map_err(|err| { + let header_value: #{HeaderValue} = header_value.parse().map_err(|err| { #{invalid_header_value:W} })?; builder = builder.header(header_name, header_value); } """, + "HeaderValue" to RuntimeType.Http.resolve("HeaderValue"), "invalid_header_name" to OperationBuildError(runtimeConfig).invalidField(memberName) { rust("""format!("`{k}` cannot be used as a header name: {err}")""") }, @@ -746,14 +730,14 @@ class HttpBindingGenerator( target.isStringShape -> { if (target.hasTrait()) { val func = writer.format(RuntimeType.base64Encode(runtimeConfig)) - "$func(&$targetName)" + "$func($targetName)" } else { quoteValue("$targetName.as_str()") } } target.isTimestampShape -> { - val timestampFormatType = RuntimeType.timestampFormat(runtimeConfig, timestampFormat) + val timestampFormatType = RuntimeType.serializeTimestampFormat(runtimeConfig, timestampFormat) quoteValue("$targetName.fmt(${writer.format(timestampFormatType)})?") } 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 ff60da3ebe..7812f917bb 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 @@ -14,12 +14,10 @@ import software.amazon.smithy.model.shapes.ToShapeId import software.amazon.smithy.model.traits.HttpTrait import software.amazon.smithy.model.traits.TimestampFormatTrait 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.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.generators.builderSymbolFn import software.amazon.smithy.rust.codegen.core.smithy.generators.serializationError import software.amazon.smithy.rust.codegen.core.smithy.protocols.parse.JsonParserGenerator import software.amazon.smithy.rust.codegen.core.smithy.protocols.parse.StructuredDataParserGenerator @@ -102,13 +100,13 @@ class AwsJsonSerializerGenerator( "Error" to runtimeConfig.serializationError(), "SdkBody" to RuntimeType.sdkBody(runtimeConfig), ) + private val protocolFunctions = ProtocolFunctions(codegenContext) override fun operationInputSerializer(operationShape: OperationShape): RuntimeType { var serializer = jsonSerializerGenerator.operationInputSerializer(operationShape) if (serializer == null) { val inputShape = operationShape.inputShape(codegenContext.model) - val fnName = codegenContext.symbolProvider.serializeFunctionName(operationShape) - serializer = RuntimeType.forInlineFun(fnName, RustModule.private("operation_ser")) { + serializer = protocolFunctions.serializeFn(operationShape, fnNameSuffix = "input") { fnName -> rustBlockTemplate( "pub fn $fnName(_input: &#{target}) -> Result<#{SdkBody}, #{Error}>", *codegenScope, "target" to codegenContext.symbolProvider.toSymbol(inputShape), @@ -128,14 +126,12 @@ open class AwsJson( private val runtimeConfig = codegenContext.runtimeConfig private val errorScope = arrayOf( "Bytes" to RuntimeType.Bytes, - "Error" to RuntimeType.genericError(runtimeConfig), + "ErrorMetadataBuilder" to RuntimeType.errorMetadataBuilder(runtimeConfig), "HeaderMap" to RuntimeType.Http.resolve("HeaderMap"), "JsonError" to CargoDependency.smithyJson(runtimeConfig).toType() .resolve("deserialize::error::DeserializeError"), - "Response" to RuntimeType.Http.resolve("Response"), "json_errors" to RuntimeType.jsonErrors(runtimeConfig), ) - private val jsonDeserModule = RustModule.private("json_deser") val version: AwsJsonVersion get() = awsJsonVersion @@ -152,32 +148,31 @@ open class AwsJson( codegenContext, httpBindingResolver, ::awsJsonFieldName, - builderSymbolFn(codegenContext.symbolProvider), ) } override fun structuredDataSerializer(operationShape: OperationShape): StructuredDataSerializerGenerator = AwsJsonSerializerGenerator(codegenContext, httpBindingResolver) - override fun parseHttpGenericError(operationShape: OperationShape): RuntimeType = - RuntimeType.forInlineFun("parse_http_generic_error", jsonDeserModule) { + override fun parseHttpErrorMetadata(operationShape: OperationShape): RuntimeType = + ProtocolFunctions.crossOperationFn("parse_http_error_metadata") { fnName -> rustTemplate( """ - pub fn parse_http_generic_error(response: &#{Response}<#{Bytes}>) -> Result<#{Error}, #{JsonError}> { - #{json_errors}::parse_generic_error(response.body(), response.headers()) + pub fn $fnName(_response_status: u16, response_headers: &#{HeaderMap}, response_body: &[u8]) -> Result<#{ErrorMetadataBuilder}, #{JsonError}> { + #{json_errors}::parse_error_metadata(response_body, response_headers) } """, *errorScope, ) } - override fun parseEventStreamGenericError(operationShape: OperationShape): RuntimeType = - RuntimeType.forInlineFun("parse_event_stream_generic_error", jsonDeserModule) { + override fun parseEventStreamErrorMetadata(operationShape: OperationShape): RuntimeType = + ProtocolFunctions.crossOperationFn("parse_event_stream_error_metadata") { fnName -> rustTemplate( """ - pub fn parse_event_stream_generic_error(payload: &#{Bytes}) -> Result<#{Error}, #{JsonError}> { + pub fn $fnName(payload: &#{Bytes}) -> Result<#{ErrorMetadataBuilder}, #{JsonError}> { // Note: HeaderMap::new() doesn't allocate - #{json_errors}::parse_generic_error(payload, &#{HeaderMap}::new()) + #{json_errors}::parse_error_metadata(payload, &#{HeaderMap}::new()) } """, *errorScope, 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 6bb7a62a58..9d12cfbb62 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 @@ -6,20 +6,16 @@ package software.amazon.smithy.rust.codegen.core.smithy.protocols import software.amazon.smithy.aws.traits.protocols.AwsQueryErrorTrait -import software.amazon.smithy.codegen.core.Symbol import software.amazon.smithy.model.Model import software.amazon.smithy.model.pattern.UriPattern import software.amazon.smithy.model.shapes.OperationShape -import software.amazon.smithy.model.shapes.StructureShape import software.amazon.smithy.model.shapes.ToShapeId import software.amazon.smithy.model.traits.HttpTrait import software.amazon.smithy.model.traits.TimestampFormatTrait -import software.amazon.smithy.rust.codegen.core.rustlang.RustModule 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.smithy.CodegenContext import software.amazon.smithy.rust.codegen.core.smithy.RuntimeType -import software.amazon.smithy.rust.codegen.core.smithy.generators.builderSymbol import software.amazon.smithy.rust.codegen.core.smithy.protocols.parse.AwsQueryParserGenerator import software.amazon.smithy.rust.codegen.core.smithy.protocols.parse.StructuredDataParserGenerator import software.amazon.smithy.rust.codegen.core.smithy.protocols.serialize.AwsQuerySerializerGenerator @@ -45,43 +41,39 @@ class AwsQueryProtocol(private val codegenContext: CodegenContext) : Protocol { private val awsQueryErrors: RuntimeType = RuntimeType.wrappedXmlErrors(runtimeConfig) private val errorScope = arrayOf( "Bytes" to RuntimeType.Bytes, - "Error" to RuntimeType.genericError(runtimeConfig), + "ErrorMetadataBuilder" to RuntimeType.errorMetadataBuilder(runtimeConfig), "HeaderMap" to RuntimeType.HttpHeaderMap, "Response" to RuntimeType.HttpResponse, "XmlDecodeError" to RuntimeType.smithyXml(runtimeConfig).resolve("decode::XmlDecodeError"), ) - private val xmlDeserModule = RustModule.private("xml_deser") override val httpBindingResolver: HttpBindingResolver = AwsQueryBindingResolver(codegenContext.model) override val defaultTimestampFormat: TimestampFormatTrait.Format = TimestampFormatTrait.Format.DATE_TIME - override fun structuredDataParser(operationShape: OperationShape): StructuredDataParserGenerator { - fun builderSymbol(shape: StructureShape): Symbol = - shape.builderSymbol(codegenContext.symbolProvider) - return AwsQueryParserGenerator(codegenContext, awsQueryErrors, ::builderSymbol) - } + override fun structuredDataParser(operationShape: OperationShape): StructuredDataParserGenerator = + AwsQueryParserGenerator(codegenContext, awsQueryErrors) override fun structuredDataSerializer(operationShape: OperationShape): StructuredDataSerializerGenerator = AwsQuerySerializerGenerator(codegenContext) - override fun parseHttpGenericError(operationShape: OperationShape): RuntimeType = - RuntimeType.forInlineFun("parse_http_generic_error", xmlDeserModule) { + override fun parseHttpErrorMetadata(operationShape: OperationShape): RuntimeType = + ProtocolFunctions.crossOperationFn("parse_http_error_metadata") { fnName -> rustBlockTemplate( - "pub fn parse_http_generic_error(response: &#{Response}<#{Bytes}>) -> Result<#{Error}, #{XmlDecodeError}>", + "pub fn $fnName(_response_status: u16, _response_headers: &#{HeaderMap}, response_body: &[u8]) -> Result<#{ErrorMetadataBuilder}, #{XmlDecodeError}>", *errorScope, ) { - rust("#T::parse_generic_error(response.body().as_ref())", awsQueryErrors) + rust("#T::parse_error_metadata(response_body)", awsQueryErrors) } } - override fun parseEventStreamGenericError(operationShape: OperationShape): RuntimeType = - RuntimeType.forInlineFun("parse_event_stream_generic_error", xmlDeserModule) { + override fun parseEventStreamErrorMetadata(operationShape: OperationShape): RuntimeType = + ProtocolFunctions.crossOperationFn("parse_event_stream_error_metadata") { fnName -> rustBlockTemplate( - "pub fn parse_event_stream_generic_error(payload: &#{Bytes}) -> Result<#{Error}, #{XmlDecodeError}>", + "pub fn $fnName(payload: &#{Bytes}) -> Result<#{ErrorMetadataBuilder}, #{XmlDecodeError}>", *errorScope, ) { - rust("#T::parse_generic_error(payload.as_ref())", awsQueryErrors) + rust("#T::parse_error_metadata(payload.as_ref())", awsQueryErrors) } } } 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 new file mode 100644 index 0000000000..88fb69d015 --- /dev/null +++ b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/protocols/AwsQueryCompatible.kt @@ -0,0 +1,95 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +package software.amazon.smithy.rust.codegen.core.smithy.protocols + +import software.amazon.smithy.model.shapes.OperationShape +import software.amazon.smithy.model.shapes.ToShapeId +import software.amazon.smithy.model.traits.HttpTrait +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.CodegenContext +import software.amazon.smithy.rust.codegen.core.smithy.RuntimeType +import software.amazon.smithy.rust.codegen.core.smithy.protocols.parse.StructuredDataParserGenerator +import software.amazon.smithy.rust.codegen.core.smithy.protocols.serialize.StructuredDataSerializerGenerator + +class AwsQueryCompatibleHttpBindingResolver( + private val awsQueryBindingResolver: AwsQueryBindingResolver, + private val awsJsonHttpBindingResolver: AwsJsonHttpBindingResolver, +) : HttpBindingResolver { + override fun httpTrait(operationShape: OperationShape): HttpTrait = + awsJsonHttpBindingResolver.httpTrait(operationShape) + + override fun requestBindings(operationShape: OperationShape): List = + awsJsonHttpBindingResolver.requestBindings(operationShape) + + override fun responseBindings(operationShape: OperationShape): List = + awsJsonHttpBindingResolver.responseBindings(operationShape) + + override fun errorResponseBindings(errorShape: ToShapeId): List = + awsJsonHttpBindingResolver.errorResponseBindings(errorShape) + + override fun errorCode(errorShape: ToShapeId): String = + awsQueryBindingResolver.errorCode(errorShape) + + override fun requestContentType(operationShape: OperationShape): String = + awsJsonHttpBindingResolver.requestContentType(operationShape) + + override fun responseContentType(operationShape: OperationShape): String = + awsJsonHttpBindingResolver.requestContentType(operationShape) +} + +class AwsQueryCompatible( + val codegenContext: CodegenContext, + private val awsJson: AwsJson, +) : Protocol { + private val runtimeConfig = codegenContext.runtimeConfig + private val errorScope = arrayOf( + "Bytes" to RuntimeType.Bytes, + "ErrorMetadataBuilder" to RuntimeType.errorMetadataBuilder(runtimeConfig), + "HeaderMap" to RuntimeType.HttpHeaderMap, + "JsonError" to CargoDependency.smithyJson(runtimeConfig).toType() + .resolve("deserialize::error::DeserializeError"), + "aws_query_compatible_errors" to RuntimeType.awsQueryCompatibleErrors(runtimeConfig), + "json_errors" to RuntimeType.jsonErrors(runtimeConfig), + ) + + override val httpBindingResolver: HttpBindingResolver = + AwsQueryCompatibleHttpBindingResolver( + AwsQueryBindingResolver(codegenContext.model), + AwsJsonHttpBindingResolver(codegenContext.model, awsJson.version), + ) + + override val defaultTimestampFormat = awsJson.defaultTimestampFormat + + override fun structuredDataParser(operationShape: OperationShape): StructuredDataParserGenerator = + awsJson.structuredDataParser(operationShape) + + override fun structuredDataSerializer(operationShape: OperationShape): StructuredDataSerializerGenerator = + awsJson.structuredDataSerializer(operationShape) + + override fun parseHttpErrorMetadata(operationShape: OperationShape): RuntimeType = + ProtocolFunctions.crossOperationFn("parse_http_error_metadata") { fnName -> + rustTemplate( + """ + pub fn $fnName(_response_status: u16, response_headers: &#{HeaderMap}, response_body: &[u8]) -> Result<#{ErrorMetadataBuilder}, #{JsonError}> { + let mut builder = + #{json_errors}::parse_error_metadata(response_body, response_headers)?; + if let Some((error_code, error_type)) = + #{aws_query_compatible_errors}::parse_aws_query_compatible_error(response_headers) + { + builder = builder.code(error_code); + builder = builder.custom("type", error_type); + } + Ok(builder) + } + """, + *errorScope, + ) + } + + override fun parseEventStreamErrorMetadata(operationShape: OperationShape): RuntimeType = + awsJson.parseEventStreamErrorMetadata(operationShape) +} 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 c388b8e85b..01a530d46a 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 @@ -5,18 +5,14 @@ package software.amazon.smithy.rust.codegen.core.smithy.protocols -import software.amazon.smithy.codegen.core.Symbol import software.amazon.smithy.model.pattern.UriPattern import software.amazon.smithy.model.shapes.OperationShape -import software.amazon.smithy.model.shapes.StructureShape import software.amazon.smithy.model.traits.HttpTrait import software.amazon.smithy.model.traits.TimestampFormatTrait -import software.amazon.smithy.rust.codegen.core.rustlang.RustModule 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.smithy.CodegenContext import software.amazon.smithy.rust.codegen.core.smithy.RuntimeType -import software.amazon.smithy.rust.codegen.core.smithy.generators.builderSymbol import software.amazon.smithy.rust.codegen.core.smithy.protocols.parse.Ec2QueryParserGenerator import software.amazon.smithy.rust.codegen.core.smithy.protocols.parse.StructuredDataParserGenerator import software.amazon.smithy.rust.codegen.core.smithy.protocols.serialize.Ec2QuerySerializerGenerator @@ -27,12 +23,11 @@ class Ec2QueryProtocol(private val codegenContext: CodegenContext) : Protocol { private val ec2QueryErrors: RuntimeType = RuntimeType.ec2QueryErrors(runtimeConfig) private val errorScope = arrayOf( "Bytes" to RuntimeType.Bytes, - "Error" to RuntimeType.genericError(runtimeConfig), + "ErrorMetadataBuilder" to RuntimeType.errorMetadataBuilder(runtimeConfig), "HeaderMap" to RuntimeType.HttpHeaderMap, "Response" to RuntimeType.HttpResponse, "XmlDecodeError" to RuntimeType.smithyXml(runtimeConfig).resolve("decode::XmlDecodeError"), ) - private val xmlDeserModule = RustModule.private("xml_deser") override val httpBindingResolver: HttpBindingResolver = StaticHttpBindingResolver( codegenContext.model, @@ -48,31 +43,29 @@ class Ec2QueryProtocol(private val codegenContext: CodegenContext) : Protocol { override val defaultTimestampFormat: TimestampFormatTrait.Format = TimestampFormatTrait.Format.DATE_TIME override fun structuredDataParser(operationShape: OperationShape): StructuredDataParserGenerator { - fun builderSymbol(shape: StructureShape): Symbol = - shape.builderSymbol(codegenContext.symbolProvider) - return Ec2QueryParserGenerator(codegenContext, ec2QueryErrors, ::builderSymbol) + return Ec2QueryParserGenerator(codegenContext, ec2QueryErrors) } override fun structuredDataSerializer(operationShape: OperationShape): StructuredDataSerializerGenerator = Ec2QuerySerializerGenerator(codegenContext) - override fun parseHttpGenericError(operationShape: OperationShape): RuntimeType = - RuntimeType.forInlineFun("parse_http_generic_error", xmlDeserModule) { + override fun parseHttpErrorMetadata(operationShape: OperationShape): RuntimeType = + ProtocolFunctions.crossOperationFn("parse_http_error_metadata") { fnName -> rustBlockTemplate( - "pub fn parse_http_generic_error(response: &#{Response}<#{Bytes}>) -> Result<#{Error}, #{XmlDecodeError}>", + "pub fn $fnName(_response_status: u16, _response_headers: &#{HeaderMap}, response_body: &[u8]) -> Result<#{ErrorMetadataBuilder}, #{XmlDecodeError}>", *errorScope, ) { - rust("#T::parse_generic_error(response.body().as_ref())", ec2QueryErrors) + rust("#T::parse_error_metadata(response_body)", ec2QueryErrors) } } - override fun parseEventStreamGenericError(operationShape: OperationShape): RuntimeType = - RuntimeType.forInlineFun("parse_event_stream_generic_error", xmlDeserModule) { + override fun parseEventStreamErrorMetadata(operationShape: OperationShape): RuntimeType = + ProtocolFunctions.crossOperationFn("parse_event_stream_error_metadata") { fnName -> rustBlockTemplate( - "pub fn parse_event_stream_generic_error(payload: &#{Bytes}) -> Result<#{Error}, #{XmlDecodeError}>", + "pub fn parse_event_stream_error_metadata(payload: &#{Bytes}) -> Result<#{ErrorMetadataBuilder}, #{XmlDecodeError}>", *errorScope, ) { - rust("#T::parse_generic_error(payload.as_ref())", ec2QueryErrors) + rust("#T::parse_error_metadata(payload.as_ref())", ec2QueryErrors) } } } diff --git a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/protocols/HttpBindingResolver.kt b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/protocols/HttpBindingResolver.kt index cf14e07e2e..dbfe3de610 100644 --- a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/protocols/HttpBindingResolver.kt +++ b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/protocols/HttpBindingResolver.kt @@ -14,7 +14,6 @@ import software.amazon.smithy.model.shapes.ToShapeId import software.amazon.smithy.model.traits.HttpTrait import software.amazon.smithy.model.traits.TimestampFormatTrait import software.amazon.smithy.rust.codegen.core.util.expectTrait -import software.amazon.smithy.rust.codegen.core.util.getTrait import software.amazon.smithy.rust.codegen.core.util.orNull typealias HttpLocation = HttpBinding.Location @@ -74,13 +73,17 @@ interface HttpBindingResolver { /** * Determine the timestamp format based on the input parameters. + * + * By default, this uses the timestamp trait, either on the member or on the target. */ fun timestampFormat( memberShape: MemberShape, location: HttpLocation, defaultTimestampFormat: TimestampFormatTrait.Format, + model: Model, ): TimestampFormatTrait.Format = - memberShape.getTrait()?.format ?: defaultTimestampFormat + memberShape.getMemberTrait(model, TimestampFormatTrait::class.java).map { it.format } + .orElse(defaultTimestampFormat) /** * Determines the request content type for given [operationShape]. @@ -134,14 +137,23 @@ open class HttpTraitHttpBindingResolver( memberShape: MemberShape, location: HttpLocation, defaultTimestampFormat: TimestampFormatTrait.Format, + model: Model, ): TimestampFormatTrait.Format = httpIndex.determineTimestampFormat(memberShape, location, defaultTimestampFormat) override fun requestContentType(operationShape: OperationShape): String? = - httpIndex.determineRequestContentType(operationShape, contentTypes.requestDocument, contentTypes.eventStreamContentType).orNull() + httpIndex.determineRequestContentType( + operationShape, + contentTypes.requestDocument, + contentTypes.eventStreamContentType, + ).orNull() override fun responseContentType(operationShape: OperationShape): String? = - httpIndex.determineResponseContentType(operationShape, contentTypes.responseDocument, contentTypes.eventStreamContentType).orNull() + httpIndex.determineResponseContentType( + operationShape, + contentTypes.responseDocument, + contentTypes.eventStreamContentType, + ).orNull() // Sort the members after extracting them from the map to have a consistent order private fun mappedBindings(bindings: Map): List = 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 3751732a5b..bb55883a11 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 @@ -15,7 +15,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.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.rust import software.amazon.smithy.rust.codegen.core.rustlang.rustBlockTemplate @@ -42,7 +41,6 @@ import software.amazon.smithy.rust.codegen.core.util.isInputEventStream 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 -import software.amazon.smithy.rust.codegen.core.util.toSnakeCase class HttpBoundProtocolPayloadGenerator( codegenContext: CodegenContext, @@ -54,7 +52,6 @@ class HttpBoundProtocolPayloadGenerator( private val runtimeConfig = codegenContext.runtimeConfig private val target = codegenContext.target private val httpBindingResolver = protocol.httpBindingResolver - private val operationSerModule = RustModule.private("operation_ser") private val smithyEventStream = RuntimeType.smithyEventStream(runtimeConfig) private val codegenScope = arrayOf( "hyper" to CargoDependency.HyperWithStream.toType(), @@ -63,6 +60,7 @@ class HttpBoundProtocolPayloadGenerator( "SmithyHttp" to RuntimeType.smithyHttp(runtimeConfig), "NoOpSigner" to smithyEventStream.resolve("frame::NoOpSigner"), ) + private val protocolFunctions = ProtocolFunctions(codegenContext) override fun payloadMetadata(operationShape: OperationShape): ProtocolPayloadGenerator.PayloadMetadata { val (shape, payloadMemberName) = when (httpMessageType) { @@ -239,9 +237,8 @@ class HttpBoundProtocolPayloadGenerator( member: MemberShape, serializerGenerator: StructuredDataSerializerGenerator, ) { - val fnName = "serialize_payload_${member.container.name.toSnakeCase()}" val ref = if (payloadMetadata.takesOwnership) "" else "&" - val serializer = RuntimeType.forInlineFun(fnName, operationSerModule) { + val serializer = protocolFunctions.serializeFn(member, fnNameSuffix = "http_payload") { fnName -> val outputT = if (member.isStreaming(model)) symbolProvider.toSymbol(member) else RuntimeType.ByteSlab.toSymbol() rustBlockTemplate( "pub fn $fnName(payload: $ref#{Member}) -> Result<#{outputT}, #{BuildError}>", diff --git a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/protocols/InlineFunctionNamer.kt b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/protocols/InlineFunctionNamer.kt deleted file mode 100644 index 6b21fd2115..0000000000 --- a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/protocols/InlineFunctionNamer.kt +++ /dev/null @@ -1,73 +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.protocols - -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.OperationShape -import software.amazon.smithy.model.shapes.SetShape -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.shapes.UnionShape -import software.amazon.smithy.rust.codegen.core.smithy.RustSymbolProvider -import software.amazon.smithy.rust.codegen.core.util.PANIC -import software.amazon.smithy.rust.codegen.core.util.toSnakeCase - -fun RustSymbolProvider.lensName(prefix: String, root: Shape, path: List): String { - val base = shapeFunctionName("${prefix}lens", root) - val rest = path.joinToString("_") { toMemberName(it) } - return "${base}_$rest" -} - -/** - * Creates a unique name for a serialization function. - * - * The prefixes will look like the following (for grep): - * - serialize_list - * - serialize_map - * - serialize_member - * - serialize_operation - * - serialize_set - * - serialize_structure - * - serialize_union - */ -fun RustSymbolProvider.serializeFunctionName(shape: Shape): String = shapeFunctionName("serialize", shape) - -/** - * Creates a unique name for a serialization function. - * - * The prefixes will look like the following (for grep): - * - deser_list - * - deser_map - * - deser_member - * - deser_operation - * - deser_set - * - deser_structure - * - deser_union - */ -fun RustSymbolProvider.deserializeFunctionName(shape: Shape): String = shapeFunctionName("deser", shape) - -fun ShapeId.toRustIdentifier(): String { - return "${namespace.replace(".", "_").toSnakeCase()}_${name.toSnakeCase()}" -} - -private fun RustSymbolProvider.shapeFunctionName(prefix: String, shape: Shape): String { - val symbolNameSnakeCase = toSymbol(shape).fullName.replace("::", "_").toSnakeCase() - return prefix + "_" + when (shape) { - is MapShape -> "map_${shape.id.toRustIdentifier()}" - is MemberShape -> "member_${shape.container.toRustIdentifier()}_${shape.memberName.toSnakeCase()}" - is OperationShape -> "operation_$symbolNameSnakeCase" - is SetShape -> "set_${shape.id.toRustIdentifier()}" // set shape check MUST come before list, it is a subclass - is ListShape -> "list_${shape.id.toRustIdentifier()}" - is StructureShape -> "structure_$symbolNameSnakeCase" - is UnionShape -> "union_$symbolNameSnakeCase" - is DocumentShape -> "document" - else -> PANIC("SerializerFunctionNamer.name: $shape") - } -} 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 c5d93ae3b0..ec9b944e3e 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 @@ -46,21 +46,21 @@ interface Protocol { /** * Generates a function signature like the following: * ```rust - * fn parse_http_generic_error(response: &Response) -> aws_smithy_types::error::Error + * fn parse_http_error_metadata(response_status: u16, response_headers: HeaderMap, response_body: &[u8]) -> aws_smithy_types::error::Builder * ``` */ - fun parseHttpGenericError(operationShape: OperationShape): RuntimeType + fun parseHttpErrorMetadata(operationShape: OperationShape): RuntimeType /** * Generates a function signature like the following: * ```rust - * fn parse_event_stream_generic_error(payload: &Bytes) -> aws_smithy_types::error::Error + * fn parse_event_stream_error_metadata(payload: &Bytes) -> aws_smithy_types::error::Error * ``` * * Event Stream generic errors are almost identical to HTTP generic errors, except that * there are no response headers or statuses available to further inform the error parsing. */ - fun parseEventStreamGenericError(operationShape: OperationShape): RuntimeType + fun parseEventStreamErrorMetadata(operationShape: OperationShape): RuntimeType } typealias ProtocolMap = Map> diff --git a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/protocols/ProtocolFunctions.kt b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/protocols/ProtocolFunctions.kt new file mode 100644 index 0000000000..682b36a8ad --- /dev/null +++ b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/protocols/ProtocolFunctions.kt @@ -0,0 +1,162 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +package software.amazon.smithy.rust.codegen.core.smithy.protocols + +import software.amazon.smithy.model.shapes.DocumentShape +import software.amazon.smithy.model.shapes.MemberShape +import software.amazon.smithy.model.shapes.ServiceShape +import software.amazon.smithy.model.shapes.Shape +import software.amazon.smithy.rust.codegen.core.rustlang.Attribute +import software.amazon.smithy.rust.codegen.core.rustlang.EscapeFor +import software.amazon.smithy.rust.codegen.core.rustlang.RustModule +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.smithy.CodegenContext +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.contextName +import software.amazon.smithy.rust.codegen.core.util.toSnakeCase + +/** + * Similar to [Writable], but provides the function name that is being generated as an argument. + */ +typealias ProtocolFnWritable = RustWriter.(String) -> Unit + +/** + * Utilities for generating protocol serialization/deserialization functions, and common support functions. + * + * This class should be used for generating all inline functions from a protocol code generator, as it is + * responsible for correctly organizing and code splitting those functions into smaller modules. + */ +class ProtocolFunctions( + private val codegenContext: CodegenContext, +) { + companion object { + private val serDeModule = RustModule.pubCrate("protocol_serde") + + fun crossOperationFn(fnName: String, block: ProtocolFnWritable): RuntimeType = + RuntimeType.forInlineFun(fnName, serDeModule) { + block(fnName) + } + } + + private enum class FnType { + Serialize, + Deserialize, + } + + /** + * Generate a serialize function for a protocol, and return a runtime type that references it + * + * Example: + * ``` + * // Generate the function + * val serializeFn = ProtocolFunctions(codegenContext).serializeFn(myStruct) { fnName -> + * rust("fn $fnName(...) { ... }") + * } + * // Call the generated function + * rust("#T(...)", serializeFn) + * ``` + */ + fun serializeFn( + shape: Shape, + fnNameSuffix: String? = null, + block: ProtocolFnWritable, + ): RuntimeType = serDeFn(FnType.Serialize, shape, serDeModule, block, fnNameSuffix) + + /** + * Generate a deserialize function for a protocol, and return a runtime type that references it + * + * Example: + * ``` + * // Generate the function + * val deserializeFn = ProtocolFunctions(codegenContext).deserializeFn(myStruct) { fnName -> + * rust("fn $fnName(...) { ... }") + * } + * // Call the generated function + * rust("#T(...)", deserializeFn) + * ``` + */ + fun deserializeFn( + shape: Shape, + fnNameSuffix: String? = null, + block: ProtocolFnWritable, + ): RuntimeType = serDeFn(FnType.Deserialize, shape, serDeModule, block, fnNameSuffix) + + private fun serDeFn( + fnType: FnType, + shape: Shape, + parentModule: RustModule.LeafModule, + block: ProtocolFnWritable, + fnNameSuffix: String? = null, + ): RuntimeType { + val moduleName = codegenContext.symbolProvider.shapeModuleName(codegenContext.serviceShape, shape) + val fnBaseName = codegenContext.symbolProvider.shapeFunctionName(codegenContext.serviceShape, shape) + val suffix = fnNameSuffix?.let { "_$it" } ?: "" + val fnName = RustReservedWords.escapeIfNeeded( + when (fnType) { + FnType.Deserialize -> "de_$fnBaseName$suffix" + FnType.Serialize -> "ser_$fnBaseName$suffix" + }, + ) + return serDeFn(moduleName, fnName, parentModule, block) + } + + private fun serDeFn( + moduleName: String, + fnName: String, + parentModule: RustModule.LeafModule, + block: ProtocolFnWritable, + ): RuntimeType { + val additionalAttributes = when { + // Some SDK models have maps with names prefixed with `__mapOf__`, which become `__map_of__`, + // and the Rust compiler warning doesn't like multiple adjacent underscores. + moduleName.contains("__") || fnName.contains("__") -> listOf(Attribute.AllowNonSnakeCase) + else -> emptyList() + } + return RuntimeType.forInlineFun( + fnName, + RustModule.pubCrate(moduleName, parent = parentModule, additionalAttributes = additionalAttributes), + ) { + block(fnName) + } + } +} + +/** Creates a module name for a ser/de function. */ +internal fun RustSymbolProvider.shapeModuleName(serviceShape: ServiceShape?, shape: Shape): String = + RustReservedWords.escapeIfNeeded( + "shape_" + + when (shape) { + is MemberShape -> model.expectShape(shape.container) + else -> shape + }.contextName(serviceShape).toSnakeCase(), + EscapeFor.ModuleName, + ) + +/** Creates a unique name for a ser/de function. */ +internal fun RustSymbolProvider.shapeFunctionName(serviceShape: ServiceShape?, shape: Shape): String { + val containerName = when (shape) { + is MemberShape -> model.expectShape(shape.container).contextName(serviceShape).toSnakeCase() + else -> shape.contextName(serviceShape).toSnakeCase() + } + return when (shape) { + is MemberShape -> shape.memberName.toSnakeCase() + is DocumentShape -> "document" + else -> containerName + } +} + +fun RustSymbolProvider.nestedAccessorName( + serviceShape: ServiceShape?, + prefix: String, + root: Shape, + path: List, +): String { + val base = shapeFunctionName(serviceShape, root) + val rest = path.joinToString("_") { toMemberName(it) } + return "${prefix}lens_${base}_$rest" +} 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 cbcd2d511b..675a0b2126 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 @@ -5,7 +5,6 @@ package software.amazon.smithy.rust.codegen.core.smithy.protocols -import software.amazon.smithy.codegen.core.Symbol import software.amazon.smithy.model.Model import software.amazon.smithy.model.shapes.MemberShape import software.amazon.smithy.model.shapes.OperationShape @@ -16,11 +15,9 @@ import software.amazon.smithy.model.traits.MediaTypeTrait import software.amazon.smithy.model.traits.StreamingTrait import software.amazon.smithy.model.traits.TimestampFormatTrait 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.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.builderSymbol import software.amazon.smithy.rust.codegen.core.smithy.protocols.parse.JsonParserGenerator import software.amazon.smithy.rust.codegen.core.smithy.protocols.parse.StructuredDataParserGenerator import software.amazon.smithy.rust.codegen.core.smithy.protocols.serialize.JsonSerializerGenerator @@ -66,14 +63,13 @@ open class RestJson(val codegenContext: CodegenContext) : Protocol { private val runtimeConfig = codegenContext.runtimeConfig private val errorScope = arrayOf( "Bytes" to RuntimeType.Bytes, - "Error" to RuntimeType.genericError(runtimeConfig), + "ErrorMetadataBuilder" to RuntimeType.errorMetadataBuilder(runtimeConfig), "HeaderMap" to RuntimeType.Http.resolve("HeaderMap"), "JsonError" to CargoDependency.smithyJson(runtimeConfig).toType() .resolve("deserialize::error::DeserializeError"), "Response" to RuntimeType.Http.resolve("Response"), "json_errors" to RuntimeType.jsonErrors(runtimeConfig), ) - private val jsonDeserModule = RustModule.private("json_deser") override val httpBindingResolver: HttpBindingResolver = RestJsonHttpBindingResolver(codegenContext.model, ProtocolContentTypes("application/json", "application/json", "application/vnd.amazon.eventstream")) @@ -94,33 +90,31 @@ open class RestJson(val codegenContext: CodegenContext) : Protocol { listOf("x-amzn-errortype" to errorShape.id.toString()) override fun structuredDataParser(operationShape: OperationShape): StructuredDataParserGenerator { - fun builderSymbol(shape: StructureShape): Symbol = - shape.builderSymbol(codegenContext.symbolProvider) - return JsonParserGenerator(codegenContext, httpBindingResolver, ::restJsonFieldName, ::builderSymbol) + return JsonParserGenerator(codegenContext, httpBindingResolver, ::restJsonFieldName) } override fun structuredDataSerializer(operationShape: OperationShape): StructuredDataSerializerGenerator = JsonSerializerGenerator(codegenContext, httpBindingResolver, ::restJsonFieldName) - override fun parseHttpGenericError(operationShape: OperationShape): RuntimeType = - RuntimeType.forInlineFun("parse_http_generic_error", jsonDeserModule) { + override fun parseHttpErrorMetadata(operationShape: OperationShape): RuntimeType = + ProtocolFunctions.crossOperationFn("parse_http_error_metadata") { fnName -> rustTemplate( """ - pub fn parse_http_generic_error(response: &#{Response}<#{Bytes}>) -> Result<#{Error}, #{JsonError}> { - #{json_errors}::parse_generic_error(response.body(), response.headers()) + pub fn $fnName(_response_status: u16, response_headers: &#{HeaderMap}, response_body: &[u8]) -> Result<#{ErrorMetadataBuilder}, #{JsonError}> { + #{json_errors}::parse_error_metadata(response_body, response_headers) } """, *errorScope, ) } - override fun parseEventStreamGenericError(operationShape: OperationShape): RuntimeType = - RuntimeType.forInlineFun("parse_event_stream_generic_error", jsonDeserModule) { + override fun parseEventStreamErrorMetadata(operationShape: OperationShape): RuntimeType = + ProtocolFunctions.crossOperationFn("parse_event_stream_error_metadata") { fnName -> rustTemplate( """ - pub fn parse_event_stream_generic_error(payload: &#{Bytes}) -> Result<#{Error}, #{JsonError}> { + pub fn $fnName(payload: &#{Bytes}) -> Result<#{ErrorMetadataBuilder}, #{JsonError}> { // Note: HeaderMap::new() doesn't allocate - #{json_errors}::parse_generic_error(payload, &#{HeaderMap}::new()) + #{json_errors}::parse_error_metadata(payload, &#{HeaderMap}::new()) } """, *errorScope, 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 44a9631ef7..3045cf0268 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 @@ -6,16 +6,12 @@ package software.amazon.smithy.rust.codegen.core.smithy.protocols import software.amazon.smithy.aws.traits.protocols.RestXmlTrait -import software.amazon.smithy.codegen.core.Symbol import software.amazon.smithy.model.shapes.OperationShape -import software.amazon.smithy.model.shapes.StructureShape import software.amazon.smithy.model.traits.TimestampFormatTrait -import software.amazon.smithy.rust.codegen.core.rustlang.RustModule 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.smithy.CodegenContext import software.amazon.smithy.rust.codegen.core.smithy.RuntimeType -import software.amazon.smithy.rust.codegen.core.smithy.generators.builderSymbol import software.amazon.smithy.rust.codegen.core.smithy.protocols.parse.RestXmlParserGenerator import software.amazon.smithy.rust.codegen.core.smithy.protocols.parse.StructuredDataParserGenerator import software.amazon.smithy.rust.codegen.core.smithy.protocols.serialize.StructuredDataSerializerGenerator @@ -27,12 +23,11 @@ open class RestXml(val codegenContext: CodegenContext) : Protocol { private val runtimeConfig = codegenContext.runtimeConfig private val errorScope = arrayOf( "Bytes" to RuntimeType.Bytes, - "Error" to RuntimeType.genericError(runtimeConfig), + "ErrorMetadataBuilder" to RuntimeType.errorMetadataBuilder(runtimeConfig), "HeaderMap" to RuntimeType.HttpHeaderMap, "Response" to RuntimeType.HttpResponse, "XmlDecodeError" to RuntimeType.smithyXml(runtimeConfig).resolve("decode::XmlDecodeError"), ) - private val xmlDeserModule = RustModule.private("xml_deser") protected val restXmlErrors: RuntimeType = when (restXml.isNoErrorWrapping) { true -> RuntimeType.unwrappedXmlErrors(runtimeConfig) @@ -46,32 +41,30 @@ open class RestXml(val codegenContext: CodegenContext) : Protocol { TimestampFormatTrait.Format.DATE_TIME override fun structuredDataParser(operationShape: OperationShape): StructuredDataParserGenerator { - fun builderSymbol(shape: StructureShape): Symbol = - shape.builderSymbol(codegenContext.symbolProvider) - return RestXmlParserGenerator(codegenContext, restXmlErrors, ::builderSymbol) + return RestXmlParserGenerator(codegenContext, restXmlErrors) } override fun structuredDataSerializer(operationShape: OperationShape): StructuredDataSerializerGenerator { return XmlBindingTraitSerializerGenerator(codegenContext, httpBindingResolver) } - override fun parseHttpGenericError(operationShape: OperationShape): RuntimeType = - RuntimeType.forInlineFun("parse_http_generic_error", xmlDeserModule) { + override fun parseHttpErrorMetadata(operationShape: OperationShape): RuntimeType = + ProtocolFunctions.crossOperationFn("parse_http_error_metadata") { fnName -> rustBlockTemplate( - "pub fn parse_http_generic_error(response: &#{Response}<#{Bytes}>) -> Result<#{Error}, #{XmlDecodeError}>", + "pub fn $fnName(_response_status: u16, _response_headers: &#{HeaderMap}, response_body: &[u8]) -> Result<#{ErrorMetadataBuilder}, #{XmlDecodeError}>", *errorScope, ) { - rust("#T::parse_generic_error(response.body().as_ref())", restXmlErrors) + rust("#T::parse_error_metadata(response_body)", restXmlErrors) } } - override fun parseEventStreamGenericError(operationShape: OperationShape): RuntimeType = - RuntimeType.forInlineFun("parse_event_stream_generic_error", xmlDeserModule) { + override fun parseEventStreamErrorMetadata(operationShape: OperationShape): RuntimeType = + ProtocolFunctions.crossOperationFn("parse_event_stream_error_metadata") { fnName -> rustBlockTemplate( - "pub fn parse_event_stream_generic_error(payload: &#{Bytes}) -> Result<#{Error}, #{XmlDecodeError}>", + "pub fn $fnName(payload: &#{Bytes}) -> Result<#{ErrorMetadataBuilder}, #{XmlDecodeError}>", *errorScope, ) { - rust("#T::parse_generic_error(payload.as_ref())", restXmlErrors) + rust("#T::parse_error_metadata(payload.as_ref())", restXmlErrors) } } } diff --git a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/protocols/parse/AwsQueryParserGenerator.kt b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/protocols/parse/AwsQueryParserGenerator.kt index 2dbe6d72fb..2fc1f5c649 100644 --- a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/protocols/parse/AwsQueryParserGenerator.kt +++ b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/protocols/parse/AwsQueryParserGenerator.kt @@ -5,8 +5,6 @@ package software.amazon.smithy.rust.codegen.core.smithy.protocols.parse -import software.amazon.smithy.codegen.core.Symbol -import software.amazon.smithy.model.shapes.StructureShape 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 @@ -29,12 +27,10 @@ import software.amazon.smithy.rust.codegen.core.smithy.RuntimeType class AwsQueryParserGenerator( codegenContext: CodegenContext, xmlErrors: RuntimeType, - builderSymbol: (shape: StructureShape) -> Symbol, private val xmlBindingTraitParserGenerator: XmlBindingTraitParserGenerator = XmlBindingTraitParserGenerator( codegenContext, xmlErrors, - builderSymbol, ) { context, inner -> val operationName = codegenContext.symbolProvider.toSymbol(context.shape).name val responseWrapperName = operationName + "Response" diff --git a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/protocols/parse/Ec2QueryParserGenerator.kt b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/protocols/parse/Ec2QueryParserGenerator.kt index e7be46f3bd..00cdc784d1 100644 --- a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/protocols/parse/Ec2QueryParserGenerator.kt +++ b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/protocols/parse/Ec2QueryParserGenerator.kt @@ -5,8 +5,6 @@ package software.amazon.smithy.rust.codegen.core.smithy.protocols.parse -import software.amazon.smithy.codegen.core.Symbol -import software.amazon.smithy.model.shapes.StructureShape 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 @@ -27,12 +25,10 @@ import software.amazon.smithy.rust.codegen.core.smithy.RuntimeType class Ec2QueryParserGenerator( codegenContext: CodegenContext, xmlErrors: RuntimeType, - builderSymbol: (shape: StructureShape) -> Symbol, private val xmlBindingTraitParserGenerator: XmlBindingTraitParserGenerator = XmlBindingTraitParserGenerator( codegenContext, xmlErrors, - builderSymbol, ) { context, inner -> val operationName = codegenContext.symbolProvider.toSymbol(context.shape).name val responseWrapperName = operationName + "Response" 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 e6bf4812f3..4fa98fa6ee 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 @@ -33,7 +33,6 @@ 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.generators.UnionGenerator -import software.amazon.smithy.rust.codegen.core.smithy.generators.error.eventStreamErrorSymbol import software.amazon.smithy.rust.codegen.core.smithy.generators.renderUnknownVariant import software.amazon.smithy.rust.codegen.core.smithy.generators.setterName import software.amazon.smithy.rust.codegen.core.smithy.protocols.Protocol @@ -44,13 +43,14 @@ import software.amazon.smithy.rust.codegen.core.util.expectTrait import software.amazon.smithy.rust.codegen.core.util.hasTrait import software.amazon.smithy.rust.codegen.core.util.toPascalCase +fun RustModule.Companion.eventStreamSerdeModule(): RustModule.LeafModule = + private("event_stream_serde") + class EventStreamUnmarshallerGenerator( private val protocol: Protocol, codegenContext: CodegenContext, private val operationShape: OperationShape, private val unionShape: UnionShape, - /** Function that maps a StructureShape into its builder symbol */ - private val builderSymbol: (StructureShape) -> Symbol, ) { private val model = codegenContext.model private val symbolProvider = codegenContext.symbolProvider @@ -60,10 +60,10 @@ class EventStreamUnmarshallerGenerator( private val errorSymbol = if (codegenTarget == CodegenTarget.SERVER && unionShape.eventStreamErrors().isEmpty()) { RuntimeType.smithyHttp(runtimeConfig).resolve("event_stream::MessageStreamError").toSymbol() } else { - unionShape.eventStreamErrorSymbol(symbolProvider).toSymbol() + symbolProvider.symbolForEventStreamError(unionShape) } private val smithyEventStream = RuntimeType.smithyEventStream(runtimeConfig) - private val eventStreamSerdeModule = RustModule.private("event_stream_serde") + private val eventStreamSerdeModule = RustModule.eventStreamSerdeModule() private val codegenScope = arrayOf( "Blob" to RuntimeType.blob(runtimeConfig), "expect_fns" to smithyEventStream.resolve("smithy"), @@ -87,15 +87,16 @@ class EventStreamUnmarshallerGenerator( } private fun RustWriter.renderUnmarshaller(unmarshallerType: RuntimeType, unionSymbol: Symbol) { + val unmarshallerTypeName = unmarshallerType.name rust( """ ##[non_exhaustive] ##[derive(Debug)] - pub struct ${unmarshallerType.name}; + pub struct $unmarshallerTypeName; - impl ${unmarshallerType.name} { + impl $unmarshallerTypeName { pub fn new() -> Self { - ${unmarshallerType.name} + $unmarshallerTypeName } } """, @@ -157,6 +158,7 @@ class EventStreamUnmarshallerGenerator( "Output" to unionSymbol, *codegenScope, ) + false -> rustTemplate( "return Err(#{Error}::unmarshalling(format!(\"unrecognized :event-type: {}\", _unknown_variant)));", *codegenScope, @@ -182,6 +184,7 @@ class EventStreamUnmarshallerGenerator( *codegenScope, ) } + payloadOnly -> { withBlock("let parsed = ", ";") { renderParseProtocolPayload(unionMember) @@ -192,8 +195,9 @@ class EventStreamUnmarshallerGenerator( *codegenScope, ) } + else -> { - rust("let mut builder = #T::default();", builderSymbol(unionStruct)) + rust("let mut builder = #T::default();", symbolProvider.symbolForBuilder(unionStruct)) val payloadMember = unionStruct.members().firstOrNull { it.hasTrait() } if (payloadMember != null) { renderUnmarshallEventPayload(payloadMember) @@ -268,6 +272,7 @@ class EventStreamUnmarshallerGenerator( is BlobShape -> { rustTemplate("#{Blob}::new(message.payload().as_ref())", *codegenScope) } + is StringShape -> { rustTemplate( """ @@ -278,6 +283,7 @@ class EventStreamUnmarshallerGenerator( *codegenScope, ) } + is UnionShape, is StructureShape -> { renderParseProtocolPayload(member) } @@ -306,15 +312,16 @@ class EventStreamUnmarshallerGenerator( CodegenTarget.CLIENT -> { rustTemplate( """ - let generic = match #{parse_generic_error}(message.payload()) { - Ok(generic) => generic, + let generic = match #{parse_error_metadata}(message.payload()) { + Ok(builder) => builder.build(), Err(err) => return Ok(#{UnmarshalledMessage}::Error(#{OpError}::unhandled(err))), }; """, - "parse_generic_error" to protocol.parseEventStreamGenericError(operationShape), + "parse_error_metadata" to protocol.parseEventStreamErrorMetadata(operationShape), *codegenScope, ) } + CodegenTarget.SERVER -> {} } @@ -336,18 +343,16 @@ class EventStreamUnmarshallerGenerator( val target = model.expectShape(member.target, StructureShape::class.java) val parser = protocol.structuredDataParser(operationShape).errorParser(target) if (parser != null) { - rust("let mut builder = #T::default();", builderSymbol(target)) + rust("let mut builder = #T::default();", symbolProvider.symbolForBuilder(target)) rustTemplate( """ builder = #{parser}(&message.payload()[..], builder) .map_err(|err| { #{Error}::unmarshalling(format!("failed to unmarshall ${member.memberName}: {}", err)) })?; + builder.set_meta(Some(generic)); return Ok(#{UnmarshalledMessage}::Error( - #{OpError}::new( - #{OpError}Kind::${member.target.name}(builder.build()), - generic, - ) + #{OpError}::${member.target.name}(builder.build()) )) """, "parser" to parser, @@ -355,11 +360,12 @@ class EventStreamUnmarshallerGenerator( ) } } + CodegenTarget.SERVER -> { val target = model.expectShape(member.target, StructureShape::class.java) val parser = protocol.structuredDataParser(operationShape).errorParser(target) val mut = if (parser != null) { " mut" } else { "" } - rust("let$mut builder = #T::default();", builderSymbol(target)) + rust("let$mut builder = #T::default();", symbolProvider.symbolForBuilder(target)) if (parser != null) { rustTemplate( """ @@ -396,6 +402,7 @@ class EventStreamUnmarshallerGenerator( CodegenTarget.CLIENT -> { rustTemplate("Ok(#{UnmarshalledMessage}::Error(#{OpError}::generic(generic)))", *codegenScope) } + CodegenTarget.SERVER -> { rustTemplate( """ 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 1e8f8151f7..dd7255e47e 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 @@ -24,11 +24,9 @@ import software.amazon.smithy.model.traits.SparseTrait import software.amazon.smithy.model.traits.TimestampFormatTrait 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.RustWriter 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.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 @@ -49,7 +47,7 @@ import software.amazon.smithy.rust.codegen.core.smithy.isOptional import software.amazon.smithy.rust.codegen.core.smithy.isRustBoxed import software.amazon.smithy.rust.codegen.core.smithy.protocols.HttpBindingResolver import software.amazon.smithy.rust.codegen.core.smithy.protocols.HttpLocation -import software.amazon.smithy.rust.codegen.core.smithy.protocols.deserializeFunctionName +import software.amazon.smithy.rust.codegen.core.smithy.protocols.ProtocolFunctions 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.hasTrait @@ -73,12 +71,10 @@ typealias JsonParserCustomization = NamedCustomization data class ReturnSymbolToParse(val symbol: Symbol, val isUnconstrained: Boolean) class JsonParserGenerator( - codegenContext: CodegenContext, + private val codegenContext: CodegenContext, private val httpBindingResolver: HttpBindingResolver, /** Function that maps a MemberShape into a JSON field name */ private val jsonName: (MemberShape) -> String, - /** Function that maps a StructureShape into its builder symbol */ - private val builderSymbol: (StructureShape) -> Symbol, /** * Whether we should parse a value for a shape into its associated unconstrained type. For example, when the shape * is a `StructureShape`, we should construct and return a builder instead of building into the final `struct` the @@ -98,8 +94,8 @@ class JsonParserGenerator( private val runtimeConfig = codegenContext.runtimeConfig private val codegenTarget = codegenContext.target private val smithyJson = CargoDependency.smithyJson(runtimeConfig).toType() - private val jsonDeserModule = RustModule.private("json_deser") private val typeConversionGenerator = TypeConversionGenerator(model, symbolProvider, runtimeConfig) + private val protocolFunctions = ProtocolFunctions(codegenContext) private val codegenScope = arrayOf( "Error" to smithyJson.resolve("deserialize::error::DeserializeError"), "expect_blob_or_null" to smithyJson.resolve("deserialize::token::expect_blob_or_null"), @@ -125,11 +121,12 @@ class JsonParserGenerator( * generation requires parsers for all input structures. */ private fun structureParser( - fnName: String, + shape: Shape, builderSymbol: Symbol, includedMembers: List, + fnNameSuffix: String? = null, ): RuntimeType { - return RuntimeType.forInlineFun(fnName, jsonDeserModule) { + return protocolFunctions.deserializeFn(shape, fnNameSuffix) { fnName -> val unusedMut = if (includedMembers.isEmpty()) "##[allow(unused_mut)] " else "" rustBlockTemplate( "pub(crate) fn $fnName(value: &[u8], ${unusedMut}mut builder: #{Builder}) -> Result<#{Builder}, #{Error}>", @@ -153,9 +150,10 @@ class JsonParserGenerator( override fun payloadParser(member: MemberShape): RuntimeType { val shape = model.expectShape(member.target) - check(shape is UnionShape || shape is StructureShape || shape is DocumentShape) { "payload parser should only be used on structures & unions" } - val fnName = symbolProvider.deserializeFunctionName(shape) + "_payload" - return RuntimeType.forInlineFun(fnName, jsonDeserModule) { + check(shape is UnionShape || shape is StructureShape || shape is DocumentShape) { + "payload parser should only be used on structures & unions" + } + return protocolFunctions.deserializeFn(shape, fnNameSuffix = "payload") { fnName -> rustBlockTemplate( "pub fn $fnName(input: &[u8]) -> Result<#{Shape}, #{Error}>", *codegenScope, @@ -190,19 +188,22 @@ class JsonParserGenerator( return null } val outputShape = operationShape.outputShape(model) - val fnName = symbolProvider.deserializeFunctionName(operationShape) - return structureParser(fnName, builderSymbol(outputShape), httpDocumentMembers) + return structureParser(operationShape, symbolProvider.symbolForBuilder(outputShape), httpDocumentMembers) } override fun errorParser(errorShape: StructureShape): RuntimeType? { if (errorShape.members().isEmpty()) { return null } - val fnName = symbolProvider.deserializeFunctionName(errorShape) + "_json_err" - return structureParser(fnName, builderSymbol(errorShape), errorShape.members().toList()) + return structureParser( + errorShape, + symbolProvider.symbolForBuilder(errorShape), + errorShape.members().toList(), + fnNameSuffix = "json_err", + ) } - private fun orEmptyJson(): RuntimeType = RuntimeType.forInlineFun("or_empty_doc", jsonDeserModule) { + private fun orEmptyJson(): RuntimeType = ProtocolFunctions.crossOperationFn("or_empty_doc") { rust( """ pub(crate) fn or_empty_doc(data: &[u8]) -> &[u8] { @@ -222,8 +223,7 @@ class JsonParserGenerator( return null } val inputShape = operationShape.inputShape(model) - val fnName = symbolProvider.deserializeFunctionName(operationShape) - return structureParser(fnName, builderSymbol(inputShape), includedMembers) + return structureParser(operationShape, symbolProvider.symbolForBuilder(inputShape), includedMembers) } private fun RustWriter.expectEndOfTokenStream() { @@ -276,7 +276,7 @@ class JsonParserGenerator( is StringShape -> deserializeString(target) is BooleanShape -> rustTemplate("#{expect_bool_or_null}(tokens.next())?", *codegenScope) is NumberShape -> deserializeNumber(target) - is BlobShape -> deserializeBlob(target) + is BlobShape -> deserializeBlob() is TimestampShape -> deserializeTimestamp(target, memberShape) is CollectionShape -> deserializeCollection(target) is MapShape -> deserializeMap(target) @@ -294,7 +294,7 @@ class JsonParserGenerator( } } - private fun RustWriter.deserializeBlob(target: BlobShape) { + private fun RustWriter.deserializeBlob() { rustTemplate( "#{expect_blob_or_null}(tokens.next())?", *codegenScope, @@ -345,9 +345,9 @@ class JsonParserGenerator( val timestampFormat = httpBindingResolver.timestampFormat( member, HttpLocation.DOCUMENT, - TimestampFormatTrait.Format.EPOCH_SECONDS, + TimestampFormatTrait.Format.EPOCH_SECONDS, model, ) - val timestampFormatType = RuntimeType.timestampFormat(runtimeConfig, timestampFormat) + 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, @@ -355,15 +355,11 @@ class JsonParserGenerator( } private fun RustWriter.deserializeCollection(shape: CollectionShape) { - val fnName = symbolProvider.deserializeFunctionName(shape) val isSparse = shape.hasTrait() val (returnSymbol, returnUnconstrainedType) = returnSymbolToParse(shape) - val parser = RuntimeType.forInlineFun(fnName, jsonDeserModule) { - // Allow non-snake-case since some SDK models have lists with names prefixed with `__listOf__`, - // which become `__list_of__`, and the Rust compiler warning doesn't like multiple adjacent underscores. + val parser = protocolFunctions.deserializeFn(shape) { fnName -> rustBlockTemplate( """ - ##[allow(non_snake_case)] pub(crate) fn $fnName<'a, I>(tokens: &mut #{Peekable}) -> Result, #{Error}> where I: Iterator, #{Error}>> """, @@ -420,15 +416,11 @@ class JsonParserGenerator( private fun RustWriter.deserializeMap(shape: MapShape) { val keyTarget = model.expectShape(shape.key.target) as StringShape - val fnName = symbolProvider.deserializeFunctionName(shape) val isSparse = shape.hasTrait() val returnSymbolToParse = returnSymbolToParse(shape) - val parser = RuntimeType.forInlineFun(fnName, jsonDeserModule) { - // Allow non-snake-case since some SDK models have maps with names prefixed with `__mapOf__`, - // which become `__map_of__`, and the Rust compiler warning doesn't like multiple adjacent underscores. + val parser = protocolFunctions.deserializeFn(shape) { fnName -> rustBlockTemplate( """ - ##[allow(non_snake_case)] pub(crate) fn $fnName<'a, I>(tokens: &mut #{Peekable}) -> Result, #{Error}> where I: Iterator, #{Error}>> """, @@ -480,9 +472,8 @@ class JsonParserGenerator( } private fun RustWriter.deserializeStruct(shape: StructureShape) { - val fnName = symbolProvider.deserializeFunctionName(shape) val returnSymbolToParse = returnSymbolToParse(shape) - val nestedParser = RuntimeType.forInlineFun(fnName, jsonDeserModule) { + val nestedParser = protocolFunctions.deserializeFn(shape) { fnName -> rustBlockTemplate( """ pub(crate) fn $fnName<'a, I>(tokens: &mut #{Peekable}) -> Result, #{Error}> @@ -493,7 +484,11 @@ class JsonParserGenerator( ) { startObjectOrNull { Attribute.AllowUnusedMut.render(this) - rustTemplate("let mut builder = #{Builder}::default();", *codegenScope, "Builder" to builderSymbol(shape)) + rustTemplate( + "let mut builder = #{Builder}::default();", + *codegenScope, + "Builder" to symbolProvider.symbolForBuilder(shape), + ) deserializeStructInner(shape.members()) // Only call `build()` if the builder is not fallible. Otherwise, return the builder. if (returnSymbolToParse.isUnconstrained) { @@ -508,9 +503,8 @@ class JsonParserGenerator( } private fun RustWriter.deserializeUnion(shape: UnionShape) { - val fnName = symbolProvider.deserializeFunctionName(shape) val returnSymbolToParse = returnSymbolToParse(shape) - val nestedParser = RuntimeType.forInlineFun(fnName, jsonDeserModule) { + val nestedParser = protocolFunctions.deserializeFn(shape) { fnName -> rustBlockTemplate( """ pub(crate) fn $fnName<'a, I>(tokens: &mut #{Peekable}) -> Result, #{Error}> diff --git a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/protocols/parse/RestXmlParserGenerator.kt b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/protocols/parse/RestXmlParserGenerator.kt index f09598e6f1..d37413f29f 100644 --- a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/protocols/parse/RestXmlParserGenerator.kt +++ b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/protocols/parse/RestXmlParserGenerator.kt @@ -5,8 +5,6 @@ package software.amazon.smithy.rust.codegen.core.smithy.protocols.parse -import software.amazon.smithy.codegen.core.Symbol -import software.amazon.smithy.model.shapes.StructureShape 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 @@ -19,12 +17,10 @@ import software.amazon.smithy.rust.codegen.core.util.orNull class RestXmlParserGenerator( codegenContext: CodegenContext, xmlErrors: RuntimeType, - builderSymbol: (shape: StructureShape) -> Symbol, private val xmlBindingTraitParserGenerator: XmlBindingTraitParserGenerator = XmlBindingTraitParserGenerator( codegenContext, xmlErrors, - builderSymbol, ) { context, inner -> val shapeName = context.outputShapeName // Get the non-synthetic version of the outputShape and check to see if it has the `AllowInvalidXmlRoot` trait diff --git a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/protocols/parse/XmlBindingTraitParserGenerator.kt b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/protocols/parse/XmlBindingTraitParserGenerator.kt index 74b71bb7a9..d083d0e901 100644 --- a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/protocols/parse/XmlBindingTraitParserGenerator.kt +++ b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/protocols/parse/XmlBindingTraitParserGenerator.kt @@ -7,7 +7,6 @@ package software.amazon.smithy.rust.codegen.core.smithy.protocols.parse import software.amazon.smithy.aws.traits.customizations.S3UnwrappedXmlOutputTrait import software.amazon.smithy.codegen.core.CodegenException -import software.amazon.smithy.codegen.core.Symbol import software.amazon.smithy.model.Model import software.amazon.smithy.model.knowledge.HttpBinding import software.amazon.smithy.model.knowledge.HttpBindingIndex @@ -27,7 +26,6 @@ import software.amazon.smithy.model.traits.TimestampFormatTrait import software.amazon.smithy.model.traits.XmlFlattenedTrait 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.RustWriter import software.amazon.smithy.rust.codegen.core.rustlang.Writable import software.amazon.smithy.rust.codegen.core.rustlang.conditionalBlock @@ -47,9 +45,9 @@ import software.amazon.smithy.rust.codegen.core.smithy.generators.renderUnknownV 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.isRustBoxed +import software.amazon.smithy.rust.codegen.core.smithy.protocols.ProtocolFunctions import software.amazon.smithy.rust.codegen.core.smithy.protocols.XmlMemberIndex import software.amazon.smithy.rust.codegen.core.smithy.protocols.XmlNameIndex -import software.amazon.smithy.rust.codegen.core.smithy.protocols.deserializeFunctionName 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.expectMember @@ -71,7 +69,6 @@ data class OperationWrapperContext( class XmlBindingTraitParserGenerator( codegenContext: CodegenContext, private val xmlErrors: RuntimeType, - private val builderSymbol: (shape: StructureShape) -> Symbol, private val writeOperationWrapper: RustWriter.(OperationWrapperContext, OperationInnerWriteable) -> Unit, ) : StructuredDataParserGenerator { @@ -102,6 +99,8 @@ class XmlBindingTraitParserGenerator( private val scopedDecoder = smithyXml.resolve("decode::ScopedDecoder") private val runtimeConfig = codegenContext.runtimeConfig + private val protocolFunctions = ProtocolFunctions(codegenContext) + private val codegenTarget = codegenContext.target // The symbols we want all the time private val codegenScope = arrayOf( @@ -117,7 +116,6 @@ class XmlBindingTraitParserGenerator( private val index = HttpBindingIndex.of(model) private val xmlIndex = XmlNameIndex.of(model) private val target = codegenContext.target - private val xmlDeserModule = RustModule.private("xml_deser") /** * Generate a parse function for a given targeted as a payload. @@ -131,9 +129,10 @@ class XmlBindingTraitParserGenerator( */ override fun payloadParser(member: MemberShape): RuntimeType { val shape = model.expectShape(member.target) - check(shape is UnionShape || shape is StructureShape) { "payload parser should only be used on structures & unions" } - val fnName = symbolProvider.deserializeFunctionName(member) - return RuntimeType.forInlineFun(fnName, xmlDeserModule) { + check(shape is UnionShape || shape is StructureShape) { + "payload parser should only be used on structures & unions" + } + return protocolFunctions.deserializeFn(member) { fnName -> rustBlock( "pub fn $fnName(inp: &[u8]) -> Result<#1T, #2T>", symbolProvider.toSymbol(shape), @@ -178,17 +177,16 @@ class XmlBindingTraitParserGenerator( */ override fun operationParser(operationShape: OperationShape): RuntimeType? { val outputShape = operationShape.outputShape(model) - val fnName = symbolProvider.deserializeFunctionName(operationShape) val shapeName = xmlIndex.operationOutputShapeName(operationShape) val members = operationShape.operationXmlMembers() if (shapeName == null || !members.isNotEmpty()) { return null } - return RuntimeType.forInlineFun(fnName, xmlDeserModule) { + return protocolFunctions.deserializeFn(operationShape) { fnName -> Attribute.AllowUnusedMut.render(this) rustBlock( "pub fn $fnName(inp: &[u8], mut builder: #1T) -> Result<#1T, #2T>", - builderSymbol(outputShape), + symbolProvider.symbolForBuilder(outputShape), xmlDecodeError, ) { rustTemplate( @@ -216,12 +214,11 @@ class XmlBindingTraitParserGenerator( } override fun errorParser(errorShape: StructureShape): RuntimeType { - val fnName = symbolProvider.deserializeFunctionName(errorShape) + "_xml_err" - return RuntimeType.forInlineFun(fnName, xmlDeserModule) { + return protocolFunctions.deserializeFn(errorShape, fnNameSuffix = "xml_err") { fnName -> Attribute.AllowUnusedMut.render(this) rustBlock( "pub fn $fnName(inp: &[u8], mut builder: #1T) -> Result<#1T, #2T>", - builderSymbol(errorShape), + symbolProvider.symbolForBuilder(errorShape), xmlDecodeError, ) { val members = errorShape.errorXmlMembers() @@ -245,17 +242,16 @@ class XmlBindingTraitParserGenerator( override fun serverInputParser(operationShape: OperationShape): RuntimeType? { val inputShape = operationShape.inputShape(model) - val fnName = symbolProvider.deserializeFunctionName(operationShape) val shapeName = xmlIndex.operationInputShapeName(operationShape) val members = operationShape.serverInputXmlMembers() if (shapeName == null || !members.isNotEmpty()) { return null } - return RuntimeType.forInlineFun(fnName, xmlDeserModule) { + return protocolFunctions.deserializeFn(operationShape) { fnName -> Attribute.AllowUnusedMut.render(this) rustBlock( "pub fn $fnName(inp: &[u8], mut builder: #1T) -> Result<#1T, #2T>", - builderSymbol(inputShape), + symbolProvider.symbolForBuilder(inputShape), xmlDecodeError, ) { rustTemplate( @@ -408,9 +404,8 @@ class XmlBindingTraitParserGenerator( } private fun RustWriter.parseUnion(shape: UnionShape, ctx: Ctx) { - val fnName = symbolProvider.deserializeFunctionName(shape) val symbol = symbolProvider.toSymbol(shape) - val nestedParser = RuntimeType.forInlineFun(fnName, xmlDeserModule) { + val nestedParser = protocolFunctions.deserializeFn(shape) { fnName -> rustBlockTemplate( "pub fn $fnName(decoder: &mut #{ScopedDecoder}) -> Result<#{Shape}, #{XmlDecodeError}>", *codegenScope, "Shape" to symbol, @@ -456,7 +451,7 @@ class XmlBindingTraitParserGenerator( private fun RustWriter.case(member: MemberShape, inner: Writable) { rustBlock( "s if ${ - member.xmlName().matchExpression("s") + member.xmlName().matchExpression("s") } /* ${member.memberName} ${escape(member.id.toString())} */ => ", ) { inner() @@ -465,9 +460,8 @@ class XmlBindingTraitParserGenerator( } private fun RustWriter.parseStructure(shape: StructureShape, ctx: Ctx) { - val fnName = symbolProvider.deserializeFunctionName(shape) val symbol = symbolProvider.toSymbol(shape) - val nestedParser = RuntimeType.forInlineFun(fnName, xmlDeserModule) { + val nestedParser = protocolFunctions.deserializeFn(shape) { fnName -> rustBlockTemplate( "pub fn $fnName(decoder: &mut #{ScopedDecoder}) -> Result<#{Shape}, #{XmlDecodeError}>", *codegenScope, "Shape" to symbol, @@ -497,9 +491,8 @@ class XmlBindingTraitParserGenerator( } private fun RustWriter.parseList(target: CollectionShape, ctx: Ctx) { - val fnName = symbolProvider.deserializeFunctionName(target) val member = target.member - val listParser = RuntimeType.forInlineFun(fnName, xmlDeserModule) { + val listParser = protocolFunctions.deserializeFn(target) { fnName -> rustBlockTemplate( "pub fn $fnName(decoder: &mut #{ScopedDecoder}) -> Result<#{List}, #{XmlDecodeError}>", *codegenScope, @@ -532,8 +525,7 @@ class XmlBindingTraitParserGenerator( } private fun RustWriter.parseMap(target: MapShape, ctx: Ctx) { - val fnName = symbolProvider.deserializeFunctionName(target) - val mapParser = RuntimeType.forInlineFun(fnName, xmlDeserModule) { + val mapParser = protocolFunctions.deserializeFn(target) { fnName -> rustBlockTemplate( "pub fn $fnName(decoder: &mut #{ScopedDecoder}) -> Result<#{Map}, #{XmlDecodeError}>", *codegenScope, @@ -569,8 +561,7 @@ class XmlBindingTraitParserGenerator( } private fun mapEntryParser(target: MapShape, ctx: Ctx): RuntimeType { - val fnName = symbolProvider.deserializeFunctionName(target) + "_entry" - return RuntimeType.forInlineFun(fnName, xmlDeserModule) { + return protocolFunctions.deserializeFn(target, "entry") { fnName -> rustBlockTemplate( "pub fn $fnName(decoder: &mut #{ScopedDecoder}, out: &mut #{Map}) -> Result<(), #{XmlDecodeError}>", *codegenScope, @@ -638,7 +629,7 @@ class XmlBindingTraitParserGenerator( HttpBinding.Location.DOCUMENT, TimestampFormatTrait.Format.DATE_TIME, ) - val timestampFormatType = RuntimeType.timestampFormat(runtimeConfig, timestampFormat) + val timestampFormatType = RuntimeType.parseTimestampFormat(codegenTarget, runtimeConfig, timestampFormat) withBlock("#T::from_str(", ")", RuntimeType.dateTime(runtimeConfig)) { provider() rust(", #T", timestampFormatType) diff --git a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/protocols/serialize/EventStreamErrorMarshallerGenerator.kt b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/protocols/serialize/EventStreamErrorMarshallerGenerator.kt index e4ec2cec3a..3a0c5c1b30 100644 --- a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/protocols/serialize/EventStreamErrorMarshallerGenerator.kt +++ b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/protocols/serialize/EventStreamErrorMarshallerGenerator.kt @@ -23,9 +23,9 @@ import software.amazon.smithy.rust.codegen.core.smithy.CodegenTarget 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.generators.error.eventStreamErrorSymbol import software.amazon.smithy.rust.codegen.core.smithy.generators.renderUnknownVariant import software.amazon.smithy.rust.codegen.core.smithy.generators.unknownVariantError +import software.amazon.smithy.rust.codegen.core.smithy.protocols.parse.eventStreamSerdeModule import software.amazon.smithy.rust.codegen.core.smithy.rustType import software.amazon.smithy.rust.codegen.core.smithy.traits.SyntheticEventStreamUnionTrait import software.amazon.smithy.rust.codegen.core.smithy.transformers.eventStreamErrors @@ -48,9 +48,9 @@ class EventStreamErrorMarshallerGenerator( private val operationErrorSymbol = if (target == CodegenTarget.SERVER && unionShape.eventStreamErrors().isEmpty()) { RuntimeType.smithyHttp(runtimeConfig).resolve("event_stream::MessageStreamError").toSymbol() } else { - unionShape.eventStreamErrorSymbol(symbolProvider).toSymbol() + symbolProvider.symbolForEventStreamError(unionShape) } - private val eventStreamSerdeModule = RustModule.private("event_stream_serde") + private val eventStreamSerdeModule = RustModule.eventStreamSerdeModule() private val errorsShape = unionShape.expectTrait() private val codegenScope = arrayOf( "MarshallMessage" to smithyEventStream.resolve("frame::MarshallMessage"), @@ -96,25 +96,15 @@ class EventStreamErrorMarshallerGenerator( ) { rust("let mut headers = Vec::new();") addStringHeader(":message-type", """"exception".into()""") - val kind = when (target) { - CodegenTarget.CLIENT -> ".kind" - CodegenTarget.SERVER -> "" - } if (errorsShape.errorMembers.isEmpty()) { rust("let payload = Vec::new();") } else { - rustBlock("let payload = match _input$kind") { - val symbol = operationErrorSymbol - val errorName = when (target) { - CodegenTarget.CLIENT -> "${symbol}Kind" - CodegenTarget.SERVER -> "$symbol" - } - + rustBlock("let payload = match _input") { errorsShape.errorMembers.forEach { error -> - val errorSymbol = symbolProvider.toSymbol(error) val errorString = error.memberName val target = model.expectShape(error.target, StructureShape::class.java) - rustBlock("$errorName::${errorSymbol.name}(inner) => ") { + val targetSymbol = symbolProvider.toSymbol(target) + rustBlock("#T::${targetSymbol.name}(inner) => ", operationErrorSymbol) { addStringHeader(":exception-type", "${errorString.dq()}.into()") renderMarshallEvent(error, target) } @@ -122,11 +112,12 @@ class EventStreamErrorMarshallerGenerator( if (target.renderUnknownVariant()) { rustTemplate( """ - $errorName::Unhandled(_inner) => return Err( + #{OperationError}::Unhandled(_inner) => return Err( #{Error}::marshalling(${unknownVariantError(unionSymbol.rustType().name).dq()}.to_owned()) ), """, *codegenScope, + "OperationError" to operationErrorSymbol, ) } } @@ -136,7 +127,7 @@ class EventStreamErrorMarshallerGenerator( } } - fun RustWriter.renderMarshallEvent(unionMember: MemberShape, eventStruct: StructureShape) { + private fun RustWriter.renderMarshallEvent(unionMember: MemberShape, eventStruct: StructureShape) { val headerMembers = eventStruct.members().filter { it.hasTrait() } val payloadMember = eventStruct.members().firstOrNull { it.hasTrait() } for (member in headerMembers) { diff --git a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/protocols/serialize/EventStreamMarshallerGenerator.kt b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/protocols/serialize/EventStreamMarshallerGenerator.kt index cb6833aaf7..201cd82ed5 100644 --- a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/protocols/serialize/EventStreamMarshallerGenerator.kt +++ b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/protocols/serialize/EventStreamMarshallerGenerator.kt @@ -38,6 +38,7 @@ 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.unknownVariantError import software.amazon.smithy.rust.codegen.core.smithy.isOptional +import software.amazon.smithy.rust.codegen.core.smithy.protocols.parse.eventStreamSerdeModule import software.amazon.smithy.rust.codegen.core.smithy.rustType import software.amazon.smithy.rust.codegen.core.util.dq import software.amazon.smithy.rust.codegen.core.util.hasTrait @@ -53,7 +54,7 @@ open class EventStreamMarshallerGenerator( private val payloadContentType: String, ) { private val smithyEventStream = RuntimeType.smithyEventStream(runtimeConfig) - private val eventStreamSerdeModule = RustModule.private("event_stream_serde") + private val eventStreamSerdeModule = RustModule.eventStreamSerdeModule() private val codegenScope = arrayOf( "MarshallMessage" to smithyEventStream.resolve("frame::MarshallMessage"), "Message" to smithyEventStream.resolve("frame::Message"), 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 89dd945d30..33b06973b0 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,7 +26,6 @@ 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.RustModule 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 @@ -38,14 +37,13 @@ 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.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.serializationError import software.amazon.smithy.rust.codegen.core.smithy.isOptional import software.amazon.smithy.rust.codegen.core.smithy.protocols.HttpBindingResolver import software.amazon.smithy.rust.codegen.core.smithy.protocols.HttpLocation -import software.amazon.smithy.rust.codegen.core.smithy.protocols.serializeFunctionName +import software.amazon.smithy.rust.codegen.core.smithy.protocols.ProtocolFunctions import software.amazon.smithy.rust.codegen.core.smithy.traits.SyntheticOutputTrait import software.amazon.smithy.rust.codegen.core.util.dq import software.amazon.smithy.rust.codegen.core.util.expectTrait @@ -168,6 +166,7 @@ class JsonSerializerGenerator( private val symbolProvider = codegenContext.symbolProvider private val codegenTarget = codegenContext.target private val runtimeConfig = codegenContext.runtimeConfig + private val protocolFunctions = ProtocolFunctions(codegenContext) private val codegenScope = arrayOf( "String" to RuntimeType.String, "Error" to runtimeConfig.serializationError(), @@ -177,9 +176,6 @@ class JsonSerializerGenerator( "ByteSlab" to RuntimeType.ByteSlab, ) private val serializerUtil = SerializerUtil(model) - private val operationSerModule = RustModule.private("operation_ser") - private val jsonSerModule = RustModule.private("json_ser") - private val typeConversionGenerator = TypeConversionGenerator(model, symbolProvider, runtimeConfig) /** * Reusable structure serializer implementation that can be used to generate serializing code for @@ -187,12 +183,16 @@ class JsonSerializerGenerator( * This function is only used by the server, the client uses directly [serializeStructure]. */ private fun serverSerializer( - fnName: String, structureShape: StructureShape, includedMembers: List, makeSection: (StructureShape, String) -> JsonSerializerSection, + error: Boolean, ): RuntimeType { - return RuntimeType.forInlineFun(fnName, operationSerModule) { + val suffix = when (error) { + true -> "error" + else -> "output" + } + return protocolFunctions.serializeFn(structureShape, fnNameSuffix = suffix) { fnName -> rustBlockTemplate( "pub fn $fnName(value: &#{target}) -> Result", *codegenScope, @@ -209,9 +209,8 @@ class JsonSerializerGenerator( } override fun payloadSerializer(member: MemberShape): RuntimeType { - val fnName = symbolProvider.serializeFunctionName(member) val target = model.expectShape(member.target) - return RuntimeType.forInlineFun(fnName, operationSerModule) { + return protocolFunctions.serializeFn(member, fnNameSuffix = "payload") { fnName -> rustBlockTemplate( "pub fn $fnName(input: &#{target}) -> std::result::Result<#{ByteSlab}, #{Error}>", *codegenScope, @@ -230,9 +229,8 @@ class JsonSerializerGenerator( } } - override fun unsetStructure(structure: StructureShape): RuntimeType { - val fnName = "rest_json_unsetpayload" - return RuntimeType.forInlineFun(fnName, operationSerModule) { + override fun unsetStructure(structure: StructureShape): RuntimeType = + ProtocolFunctions.crossOperationFn("rest_json_unsetpayload") { fnName -> rustTemplate( """ pub fn $fnName() -> #{ByteSlab} { @@ -242,7 +240,6 @@ class JsonSerializerGenerator( *codegenScope, ) } - } override fun operationInputSerializer(operationShape: OperationShape): RuntimeType? { // Don't generate an operation JSON serializer if there is no JSON body. @@ -252,8 +249,7 @@ class JsonSerializerGenerator( } val inputShape = operationShape.inputShape(model) - val fnName = symbolProvider.serializeFunctionName(operationShape) - return RuntimeType.forInlineFun(fnName, operationSerModule) { + return protocolFunctions.serializeFn(operationShape, fnNameSuffix = "input") { fnName -> rustBlockTemplate( "pub fn $fnName(input: &#{target}) -> Result<#{SdkBody}, #{Error}>", *codegenScope, "target" to symbolProvider.toSymbol(inputShape), @@ -269,8 +265,7 @@ class JsonSerializerGenerator( } override fun documentSerializer(): RuntimeType { - val fnName = "serialize_document" - return RuntimeType.forInlineFun(fnName, operationSerModule) { + return ProtocolFunctions.crossOperationFn("serialize_document") { fnName -> rustTemplate( """ pub fn $fnName(input: &#{Document}) -> #{ByteSlab} { @@ -302,8 +297,7 @@ class JsonSerializerGenerator( val httpDocumentMembers = httpBindingResolver.responseMembers(operationShape, HttpLocation.DOCUMENT) val outputShape = operationShape.outputShape(model) - val fnName = symbolProvider.serializeFunctionName(outputShape) - return serverSerializer(fnName, outputShape, httpDocumentMembers, JsonSerializerSection::OutputStruct) + return serverSerializer(outputShape, httpDocumentMembers, JsonSerializerSection::OutputStruct, error = false) } override fun serverErrorSerializer(shape: ShapeId): RuntimeType { @@ -311,17 +305,15 @@ class JsonSerializerGenerator( val includedMembers = httpBindingResolver.errorResponseBindings(shape).filter { it.location == HttpLocation.DOCUMENT } .map { it.member } - val fnName = symbolProvider.serializeFunctionName(errorShape) - return serverSerializer(fnName, errorShape, includedMembers, JsonSerializerSection::ServerError) + return serverSerializer(errorShape, includedMembers, JsonSerializerSection::ServerError, error = true) } private fun RustWriter.serializeStructure( context: StructContext, includedMembers: List? = null, ) { - val fnName = symbolProvider.serializeFunctionName(context.shape) val structureSymbol = symbolProvider.toSymbol(context.shape) - val structureSerializer = RuntimeType.forInlineFun(fnName, jsonSerModule) { + val structureSerializer = protocolFunctions.serializeFn(context.shape) { fnName -> rustBlockTemplate( "pub fn $fnName(object: &mut #{JsonObjectWriter}, input: &#{Input}) -> Result<(), #{Error}>", "Input" to structureSymbol, @@ -405,12 +397,11 @@ class JsonSerializerGenerator( is TimestampShape -> { val timestampFormat = - httpBindingResolver.timestampFormat(context.shape, HttpLocation.DOCUMENT, EPOCH_SECONDS) - val timestampFormatType = RuntimeType.timestampFormat(runtimeConfig, timestampFormat) + httpBindingResolver.timestampFormat(context.shape, HttpLocation.DOCUMENT, EPOCH_SECONDS, model) + val timestampFormatType = RuntimeType.serializeTimestampFormat(runtimeConfig, timestampFormat) rustTemplate( - "$writer.date_time(${value.asRef()}#{ConvertInto:W}, #{FormatType})?;", + "$writer.date_time(${value.asRef()}, #{FormatType})?;", "FormatType" to timestampFormatType, - "ConvertInto" to typeConversionGenerator.convertViaInto(target), ) } @@ -490,9 +481,8 @@ class JsonSerializerGenerator( } private fun RustWriter.serializeUnion(context: Context) { - val fnName = symbolProvider.serializeFunctionName(context.shape) val unionSymbol = symbolProvider.toSymbol(context.shape) - val unionSerializer = RuntimeType.forInlineFun(fnName, jsonSerModule) { + val unionSerializer = protocolFunctions.serializeFn(context.shape) { fnName -> rustBlockTemplate( "pub fn $fnName(${context.writerExpression}: &mut #{JsonObjectWriter}, input: &#{Input}) -> Result<(), #{Error}>", "Input" to unionSymbol, diff --git a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/protocols/serialize/QuerySerializerGenerator.kt b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/protocols/serialize/QuerySerializerGenerator.kt index 2e141b2ce8..974e112cde 100644 --- a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/protocols/serialize/QuerySerializerGenerator.kt +++ b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/protocols/serialize/QuerySerializerGenerator.kt @@ -21,7 +21,6 @@ import software.amazon.smithy.model.traits.EnumTrait import software.amazon.smithy.model.traits.TimestampFormatTrait import software.amazon.smithy.model.traits.XmlNameTrait import software.amazon.smithy.rust.codegen.core.rustlang.Attribute -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.RustWriter import software.amazon.smithy.rust.codegen.core.rustlang.rust @@ -36,7 +35,7 @@ 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.serializationError import software.amazon.smithy.rust.codegen.core.smithy.isOptional -import software.amazon.smithy.rust.codegen.core.smithy.protocols.serializeFunctionName +import software.amazon.smithy.rust.codegen.core.smithy.protocols.ProtocolFunctions import software.amazon.smithy.rust.codegen.core.smithy.rustType import software.amazon.smithy.rust.codegen.core.util.dq import software.amazon.smithy.rust.codegen.core.util.getTrait @@ -95,6 +94,7 @@ abstract class QuerySerializerGenerator(codegenContext: CodegenContext) : Struct private val smithyTypes = RuntimeType.smithyTypes(runtimeConfig) private val smithyQuery = RuntimeType.smithyQuery(runtimeConfig) private val serdeUtil = SerializerUtil(model) + private val protocolFunctions = ProtocolFunctions(codegenContext) private val codegenScope = arrayOf( "String" to RuntimeType.String, "Error" to serializerError, @@ -102,8 +102,6 @@ abstract class QuerySerializerGenerator(codegenContext: CodegenContext) : Struct "QueryWriter" to smithyQuery.resolve("QueryWriter"), "QueryValueWriter" to smithyQuery.resolve("QueryValueWriter"), ) - private val operationSerModule = RustModule.private("operation_ser") - private val querySerModule = RustModule.private("query_ser") abstract val protocolName: String abstract fun MemberShape.queryKeyName(prioritizedFallback: String? = null): String @@ -124,9 +122,8 @@ abstract class QuerySerializerGenerator(codegenContext: CodegenContext) : Struct } override fun operationInputSerializer(operationShape: OperationShape): RuntimeType? { - val fnName = symbolProvider.serializeFunctionName(operationShape) val inputShape = operationShape.inputShape(model) - return RuntimeType.forInlineFun(fnName, operationSerModule) { + return protocolFunctions.serializeFn(inputShape, fnNameSuffix = "input") { fnName -> rustBlockTemplate( "pub fn $fnName(input: &#{target}) -> Result<#{SdkBody}, #{Error}>", *codegenScope, "target" to symbolProvider.toSymbol(inputShape), @@ -166,9 +163,8 @@ abstract class QuerySerializerGenerator(codegenContext: CodegenContext) : Struct if (context.shape.members().isEmpty()) { return } - val fnName = symbolProvider.serializeFunctionName(context.shape) val structureSymbol = symbolProvider.toSymbol(context.shape) - val structureSerializer = RuntimeType.forInlineFun(fnName, querySerModule) { + val structureSerializer = protocolFunctions.serializeFn(context.shape) { fnName -> Attribute.AllowUnusedMut.render(this) rustBlockTemplate( "pub fn $fnName(mut writer: #{QueryValueWriter}, input: &#{Input}) -> Result<(), #{Error}>", @@ -239,7 +235,7 @@ abstract class QuerySerializerGenerator(codegenContext: CodegenContext) : Struct ) is TimestampShape -> { val timestampFormat = determineTimestampFormat(context.shape) - val timestampFormatType = RuntimeType.timestampFormat(runtimeConfig, timestampFormat) + val timestampFormatType = RuntimeType.serializeTimestampFormat(runtimeConfig, timestampFormat) rust("$writer.date_time(${value.name}, #T)?;", timestampFormatType) } is CollectionShape -> serializeCollection(context, Context(writer, context.valueExpression, target)) @@ -312,9 +308,8 @@ abstract class QuerySerializerGenerator(codegenContext: CodegenContext) : Struct } private fun RustWriter.serializeUnion(context: Context) { - val fnName = symbolProvider.serializeFunctionName(context.shape) val unionSymbol = symbolProvider.toSymbol(context.shape) - val unionSerializer = RuntimeType.forInlineFun(fnName, querySerModule) { + val unionSerializer = protocolFunctions.serializeFn(context.shape) { fnName -> Attribute.AllowUnusedMut.render(this) rustBlockTemplate( "pub fn $fnName(mut writer: #{QueryValueWriter}, input: &#{Input}) -> Result<(), #{Error}>", diff --git a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/protocols/serialize/XmlBindingTraitSerializerGenerator.kt b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/protocols/serialize/XmlBindingTraitSerializerGenerator.kt index 3d69218e25..678768cfa7 100644 --- a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/protocols/serialize/XmlBindingTraitSerializerGenerator.kt +++ b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/protocols/serialize/XmlBindingTraitSerializerGenerator.kt @@ -23,7 +23,6 @@ import software.amazon.smithy.model.traits.TimestampFormatTrait import software.amazon.smithy.model.traits.XmlFlattenedTrait import software.amazon.smithy.model.traits.XmlNamespaceTrait import software.amazon.smithy.rust.codegen.core.rustlang.Attribute -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.RustWriter import software.amazon.smithy.rust.codegen.core.rustlang.autoDeref @@ -42,9 +41,9 @@ import software.amazon.smithy.rust.codegen.core.smithy.generators.serializationE import software.amazon.smithy.rust.codegen.core.smithy.isOptional import software.amazon.smithy.rust.codegen.core.smithy.protocols.HttpBindingResolver import software.amazon.smithy.rust.codegen.core.smithy.protocols.HttpLocation +import software.amazon.smithy.rust.codegen.core.smithy.protocols.ProtocolFunctions import software.amazon.smithy.rust.codegen.core.smithy.protocols.XmlMemberIndex import software.amazon.smithy.rust.codegen.core.smithy.protocols.XmlNameIndex -import software.amazon.smithy.rust.codegen.core.smithy.protocols.serializeFunctionName import software.amazon.smithy.rust.codegen.core.smithy.rustType import software.amazon.smithy.rust.codegen.core.util.dq import software.amazon.smithy.rust.codegen.core.util.getTrait @@ -62,6 +61,7 @@ class XmlBindingTraitSerializerGenerator( private val runtimeConfig = codegenContext.runtimeConfig private val model = codegenContext.model private val codegenTarget = codegenContext.target + private val protocolFunctions = ProtocolFunctions(codegenContext) private val codegenScope = arrayOf( "XmlWriter" to RuntimeType.smithyXml(runtimeConfig).resolve("encode::XmlWriter"), @@ -69,8 +69,6 @@ class XmlBindingTraitSerializerGenerator( "SdkBody" to RuntimeType.sdkBody(runtimeConfig), "Error" to runtimeConfig.serializationError(), ) - private val operationSerModule = RustModule.private("operation_ser") - private val xmlSerModule = RustModule.private("xml_ser") private val xmlIndex = XmlNameIndex.of(model) private val rootNamespace = codegenContext.serviceShape.getTrait() @@ -101,7 +99,6 @@ class XmlBindingTraitSerializerGenerator( this.copy(input = "$input.${symbolProvider.toMemberName(member)}") override fun operationInputSerializer(operationShape: OperationShape): RuntimeType? { - val fnName = symbolProvider.serializeFunctionName(operationShape) val inputShape = operationShape.inputShape(model) val xmlMembers = operationShape.requestBodyMembers() if (xmlMembers.isEmpty()) { @@ -109,7 +106,7 @@ class XmlBindingTraitSerializerGenerator( } val operationXmlName = xmlIndex.operationInputShapeName(operationShape) ?: throw CodegenException("operation must have a name if it has members") - return RuntimeType.forInlineFun(fnName, operationSerModule) { + return protocolFunctions.serializeFn(operationShape, fnNameSuffix = "op_input") { fnName -> rustBlockTemplate( "pub fn $fnName(input: &#{target}) -> Result<#{SdkBody}, #{Error}>", *codegenScope, "target" to symbolProvider.toSymbol(inputShape), @@ -123,11 +120,13 @@ class XmlBindingTraitSerializerGenerator( """ let mut writer = #{XmlWriter}::new(&mut out); ##[allow(unused_mut)] - let mut root = writer.start_el(${operationXmlName.dq()})${inputShape.xmlNamespace(root = true).apply()}; + let mut root = writer.start_el(${operationXmlName.dq()})${ + inputShape.xmlNamespace(root = true).apply() + }; """, *codegenScope, ) - serializeStructure(inputShape, xmlMembers, Ctx.Element("root", "input")) + serializeStructure(inputShape, xmlMembers, Ctx.Element("root", "input"), fnNameSuffix = "input") } rustTemplate("Ok(#{SdkBody}::from(out))", *codegenScope) } @@ -139,9 +138,8 @@ class XmlBindingTraitSerializerGenerator( } override fun payloadSerializer(member: MemberShape): RuntimeType { - val fnName = symbolProvider.serializeFunctionName(member) val target = model.expectShape(member.target) - return RuntimeType.forInlineFun(fnName, xmlSerModule) { + return protocolFunctions.serializeFn(member, fnNameSuffix = "payload") { fnName -> val t = symbolProvider.toSymbol(member).rustType().stripOuter().render(true) rustBlockTemplate( "pub fn $fnName(input: &$t) -> std::result::Result, #{Error}>", @@ -157,7 +155,7 @@ class XmlBindingTraitSerializerGenerator( let mut writer = #{XmlWriter}::new(&mut out); ##[allow(unused_mut)] let mut root = writer.start_el(${xmlIndex.payloadShapeName(member).dq()})${ - target.xmlNamespace(root = true).apply() + target.xmlNamespace(root = true).apply() }; """, *codegenScope, @@ -168,6 +166,7 @@ class XmlBindingTraitSerializerGenerator( XmlMemberIndex.fromMembers(target.members().toList()), Ctx.Element("root", "input"), ) + is UnionShape -> serializeUnion(target, Ctx.Element("root", "input")) else -> throw IllegalStateException("xml payloadSerializer only supports structs and unions") } @@ -178,8 +177,7 @@ class XmlBindingTraitSerializerGenerator( } override fun unsetStructure(structure: StructureShape): RuntimeType { - val fnName = "rest_xml_unset_payload" - return RuntimeType.forInlineFun(fnName, operationSerModule) { + return ProtocolFunctions.crossOperationFn("rest_xml_unset_payload") { fnName -> rustTemplate( """ pub fn $fnName() -> #{ByteSlab} { @@ -192,7 +190,6 @@ class XmlBindingTraitSerializerGenerator( } override fun operationOutputSerializer(operationShape: OperationShape): RuntimeType? { - val fnName = symbolProvider.serializeFunctionName(operationShape) val outputShape = operationShape.outputShape(model) val xmlMembers = operationShape.responseBodyMembers() if (xmlMembers.isEmpty()) { @@ -200,7 +197,7 @@ class XmlBindingTraitSerializerGenerator( } val operationXmlName = xmlIndex.operationOutputShapeName(operationShape) ?: throw CodegenException("operation must have a name if it has members") - return RuntimeType.forInlineFun(fnName, operationSerModule) { + return protocolFunctions.serializeFn(operationShape, fnNameSuffix = "output") { fnName -> rustBlockTemplate( "pub fn $fnName(output: &#{target}) -> Result", *codegenScope, "target" to symbolProvider.toSymbol(outputShape), @@ -214,7 +211,9 @@ class XmlBindingTraitSerializerGenerator( """ let mut writer = #{XmlWriter}::new(&mut out); ##[allow(unused_mut)] - let mut root = writer.start_el(${operationXmlName.dq()})${outputShape.xmlNamespace(root = true).apply()}; + let mut root = writer.start_el(${operationXmlName.dq()})${ + outputShape.xmlNamespace(root = true).apply() + }; """, *codegenScope, ) @@ -230,8 +229,7 @@ class XmlBindingTraitSerializerGenerator( val xmlMembers = httpBindingResolver.errorResponseBindings(shape) .filter { it.location == HttpLocation.DOCUMENT } .map { it.member } - val fnName = symbolProvider.serializeFunctionName(errorShape) - return RuntimeType.forInlineFun(fnName, operationSerModule) { + return protocolFunctions.serializeFn(errorShape, fnNameSuffix = "error") { fnName -> rustBlockTemplate( "pub fn $fnName(error: &#{target}) -> Result", *codegenScope, "target" to symbolProvider.toSymbol(errorShape), @@ -298,23 +296,26 @@ class XmlBindingTraitSerializerGenerator( } rust("$dereferenced.as_str()") } + is BooleanShape, is NumberShape -> { rust( "#T::from(${autoDeref(input)}).encode()", RuntimeType.smithyTypes(runtimeConfig).resolve("primitive::Encoder"), ) } + is BlobShape -> rust("#T($input.as_ref()).as_ref()", RuntimeType.base64Encode(runtimeConfig)) is TimestampShape -> { val timestampFormat = httpBindingResolver.timestampFormat( member, HttpLocation.DOCUMENT, - TimestampFormatTrait.Format.DATE_TIME, + TimestampFormatTrait.Format.DATE_TIME, model, ) - val timestampFormatType = RuntimeType.timestampFormat(runtimeConfig, timestampFormat) + val timestampFormatType = RuntimeType.parseTimestampFormat(codegenTarget, runtimeConfig, timestampFormat) rust("$input.fmt(#T)?.as_ref()", timestampFormatType) } + else -> TODO(member.toString()) } } @@ -332,18 +333,21 @@ class XmlBindingTraitSerializerGenerator( serializeRawMember(memberShape, ctx.input) } } + is CollectionShape -> if (memberShape.hasTrait()) { serializeFlatList(memberShape, target, ctx) } else { rust("let mut inner_writer = ${ctx.scopeWriter}.start_el(${xmlName.dq()})$ns.finish();") serializeList(target, Ctx.Scope("inner_writer", ctx.input)) } + is MapShape -> if (memberShape.hasTrait()) { serializeMap(target, xmlIndex.memberName(memberShape), ctx) } else { rust("let mut inner_writer = ${ctx.scopeWriter}.start_el(${xmlName.dq()})$ns.finish();") serializeMap(target, "entry", Ctx.Scope("inner_writer", ctx.input)) } + is StructureShape -> { // We call serializeStructure only when target.members() is nonempty. // If it were empty, serializeStructure would generate the following code: @@ -370,10 +374,12 @@ class XmlBindingTraitSerializerGenerator( ) } } + is UnionShape -> { rust("let inner_writer = ${ctx.scopeWriter}.start_el(${xmlName.dq()})$ns;") serializeUnion(target, Ctx.Element("inner_writer", ctx.input)) } + else -> TODO(target.toString()) } } @@ -383,10 +389,10 @@ class XmlBindingTraitSerializerGenerator( structureShape: StructureShape, members: XmlMemberIndex, ctx: Ctx.Element, + fnNameSuffix: String? = null, ) { val structureSymbol = symbolProvider.toSymbol(structureShape) - val fnName = symbolProvider.serializeFunctionName(structureShape) - val structureSerializer = RuntimeType.forInlineFun(fnName, xmlSerModule) { + val structureSerializer = protocolFunctions.serializeFn(structureShape, fnNameSuffix = fnNameSuffix) { fnName -> rustBlockTemplate( "pub fn $fnName(input: &#{Input}, writer: #{ElementWriter}) -> Result<(), #{Error}>", "Input" to structureSymbol, @@ -404,9 +410,8 @@ class XmlBindingTraitSerializerGenerator( } private fun RustWriter.serializeUnion(unionShape: UnionShape, ctx: Ctx.Element) { - val fnName = symbolProvider.serializeFunctionName(unionShape) val unionSymbol = symbolProvider.toSymbol(unionShape) - val structureSerializer = RuntimeType.forInlineFun(fnName, xmlSerModule) { + val structureSerializer = protocolFunctions.serializeFn(unionShape) { fnName -> rustBlockTemplate( "pub fn $fnName(input: &#{Input}, writer: #{ElementWriter}) -> Result<(), #{Error}>", "Input" to unionSymbol, diff --git a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/traits/RustBoxTrait.kt b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/traits/RustBoxTrait.kt index 2d5a313401..41144d5945 100644 --- a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/traits/RustBoxTrait.kt +++ b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/traits/RustBoxTrait.kt @@ -12,7 +12,8 @@ import software.amazon.smithy.model.traits.Trait /** * Trait indicating that this shape should be represented with `Box` when converted into Rust * - * This is used to handle recursive shapes. See RecursiveShapeBoxer. + * This is used to handle recursive shapes. + * See [software.amazon.smithy.rust.codegen.core.smithy.transformers.RecursiveShapeBoxer]. * * This trait is synthetic, applied during code generation, and never used in actual models. */ diff --git a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/transformers/OperationNormalizer.kt b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/transformers/OperationNormalizer.kt index 1a00d431ae..78a6852fd6 100644 --- a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/transformers/OperationNormalizer.kt +++ b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/transformers/OperationNormalizer.kt @@ -59,7 +59,9 @@ object OperationNormalizer { val shapeConflict = newShapes.firstOrNull { shape -> model.getShape(shape.id).isPresent } check( shapeConflict == null, - ) { "shape $shapeConflict conflicted with an existing shape in the model (${model.getShape(shapeConflict!!.id)}. This is a bug." } + ) { + "shape $shapeConflict conflicted with an existing shape in the model (${model.getShape(shapeConflict!!.id)}. This is a bug." + } val modelWithOperationInputs = model.toBuilder().addShapes(newShapes).build() return transformer.mapShapes(modelWithOperationInputs) { // Update all operations to point to their new input/output shapes diff --git a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/transformers/RecursiveShapeBoxer.kt b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/transformers/RecursiveShapeBoxer.kt index 4b47801a8d..d53751829f 100644 --- a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/transformers/RecursiveShapeBoxer.kt +++ b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/transformers/RecursiveShapeBoxer.kt @@ -7,25 +7,50 @@ package software.amazon.smithy.rust.codegen.core.smithy.transformers import software.amazon.smithy.codegen.core.TopologicalIndex import software.amazon.smithy.model.Model -import software.amazon.smithy.model.shapes.ListShape +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.SetShape import software.amazon.smithy.model.shapes.Shape import software.amazon.smithy.model.transform.ModelTransformer import software.amazon.smithy.rust.codegen.core.smithy.traits.RustBoxTrait import software.amazon.smithy.rust.codegen.core.util.hasTrait -object RecursiveShapeBoxer { +class RecursiveShapeBoxer( /** - * Transform a model which may contain recursive shapes into a model annotated with [RustBoxTrait] + * A predicate that determines when a cycle in the shape graph contains "indirection". If a cycle contains + * indirection, no shape needs to be tagged. What constitutes indirection is up to the caller to decide. + */ + private val containsIndirectionPredicate: (Collection) -> Boolean = ::containsIndirection, + /** + * A closure that gets called on one member shape of a cycle that does not contain indirection for "fixing". For + * example, the [RustBoxTrait] trait can be used to tag the member shape. + */ + private val boxShapeFn: (MemberShape) -> MemberShape = ::addRustBoxTrait, +) { + /** + * Transform a model which may contain recursive shapes. * - * When recursive shapes do NOT go through a List, Map, or Set, they must be boxed in Rust. This function will - * iteratively find loops & add the `RustBox` trait in a deterministic way until it reaches a fixed point. + * For example, when recursive shapes do NOT go through a `CollectionShape` or a `MapShape` shape, they must be + * boxed in Rust. This function will iteratively find cycles and call [boxShapeFn] on a member shape in the + * cycle to act on it. This is done in a deterministic way until it reaches a fixed point. * - * This function MUST be deterministic (always choose the same shapes to `Box`). If it is not, that is a bug. Even so + * This function MUST be deterministic (always choose the same shapes to fix). If it is not, that is a bug. Even so * this function may cause backward compatibility issues in certain pathological cases where a changes to recursive * structures cause different members to be boxed. We may need to address these via customizations. + * + * For example, given the following model, + * + * ```smithy + * namespace com.example + * + * structure Recursive { + * recursiveStruct: Recursive + * anotherField: Boolean + * } + * ``` + * + * The `com.example#Recursive$recursiveStruct` member shape is part of a cycle, but the + * `com.example#Recursive$anotherField` member shape is not. */ fun transform(model: Model): Model { val next = transformInner(model) @@ -37,16 +62,17 @@ object RecursiveShapeBoxer { } /** - * If [model] contains a recursive loop that must be boxed, apply one instance of [RustBoxTrait] return the new model. - * If [model] contains no loops, return null. + * If [model] contains a recursive loop that must be boxed, return the transformed model resulting form a call to + * [boxShapeFn]. + * If [model] contains no loops, return `null`. */ private fun transformInner(model: Model): Model? { - // Execute 1-step of the boxing algorithm in the path to reaching a fixed point - // 1. Find all the shapes that are part of a cycle - // 2. Find all the loops that those shapes are part of - // 3. Filter out the loops that go through a layer of indirection - // 3. Pick _just one_ of the remaining loops to fix - // 4. Select the member shape in that loop with the earliest shape id + // Execute 1 step of the boxing algorithm in the path to reaching a fixed point: + // 1. Find all the shapes that are part of a cycle. + // 2. Find all the loops that those shapes are part of. + // 3. Filter out the loops that go through a layer of indirection. + // 3. Pick _just one_ of the remaining loops to fix. + // 4. Select the member shape in that loop with the earliest shape id. // 5. Box it. // (External to this function) Go back to 1. val index = TopologicalIndex.of(model) @@ -58,34 +84,38 @@ object RecursiveShapeBoxer { // Flatten the connections into shapes. loops.map { it.shapes } } - val loopToFix = loops.firstOrNull { !containsIndirection(it) } + val loopToFix = loops.firstOrNull { !containsIndirectionPredicate(it) } return loopToFix?.let { loop: List -> check(loop.isNotEmpty()) - // pick the shape to box in a deterministic way + // Pick the shape to box in a deterministic way. val shapeToBox = loop.filterIsInstance().minByOrNull { it.id }!! ModelTransformer.create().mapShapes(model) { shape -> if (shape == shapeToBox) { - shape.asMemberShape().get().toBuilder().addTrait(RustBoxTrait()).build() + boxShapeFn(shape.asMemberShape().get()) } else { shape } } } } +} - /** - * Check if a List contains a shape which will use a pointer when represented in Rust, avoiding the - * need to add more Boxes - */ - private fun containsIndirection(loop: List): Boolean { - return loop.find { - when (it) { - is ListShape, - is MapShape, - is SetShape, -> true - else -> it.hasTrait() - } - } != null +/** + * Check if a `List` contains a shape which will use a pointer when represented in Rust, avoiding the + * need to add more `Box`es. + * + * Why `CollectionShape`s and `MapShape`s? Note that `CollectionShape`s get rendered in Rust as `Vec`, and + * `MapShape`s as `HashMap`; they're the only Smithy shapes that "organically" introduce indirection + * (via a pointer to the heap) in the recursive path. For other recursive paths, we thus have to introduce the + * indirection artificially ourselves using `Box`. + * + */ +private fun containsIndirection(loop: Collection): Boolean = loop.find { + when (it) { + is CollectionShape, is MapShape -> true + else -> it.hasTrait() } -} +} != null + +private fun addRustBoxTrait(shape: MemberShape): MemberShape = shape.toBuilder().addTrait(RustBoxTrait()).build() 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 new file mode 100644 index 0000000000..d1f208c393 --- /dev/null +++ b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/testutil/CodegenIntegrationTest.kt @@ -0,0 +1,45 @@ +/* + * 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.build.PluginContext +import software.amazon.smithy.model.Model +import software.amazon.smithy.model.node.ObjectNode +import software.amazon.smithy.rust.codegen.core.smithy.RuntimeConfig +import software.amazon.smithy.rust.codegen.core.util.runCommand +import java.io.File +import java.nio.file.Path + +/** + * A helper class holding common data with defaults that is threaded through several functions, to make their + * signatures shorter. + */ +data class IntegrationTestParams( + val addModuleToEventStreamAllowList: Boolean = false, + val service: String? = null, + val runtimeConfig: RuntimeConfig? = null, + val additionalSettings: ObjectNode = ObjectNode.builder().build(), + val overrideTestDir: File? = null, + val command: ((Path) -> Unit)? = null, +) + +/** + * Run cargo test on a true, end-to-end, codegen product of a given model. + */ +fun codegenIntegrationTest(model: Model, params: IntegrationTestParams, invokePlugin: (PluginContext) -> Unit): Path { + val (ctx, testDir) = generatePluginContext( + model, + params.additionalSettings, + params.addModuleToEventStreamAllowList, + params.service, + params.runtimeConfig, + params.overrideTestDir, + ) + invokePlugin(ctx) + ctx.fileManifest.printGeneratedFiles() + params.command?.invoke(testDir) ?: "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/EventStreamMarshallTestCases.kt b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/testutil/EventStreamMarshallTestCases.kt index 6e82fc1b2c..896e3c0102 100644 --- a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/testutil/EventStreamMarshallTestCases.kt +++ b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/testutil/EventStreamMarshallTestCases.kt @@ -5,27 +5,40 @@ package software.amazon.smithy.rust.codegen.core.testutil +import org.intellij.lang.annotations.Language 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.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.RuntimeType +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.lookup -internal object EventStreamMarshallTestCases { - internal fun RustWriter.writeMarshallTestCases( +object EventStreamMarshallTestCases { + fun RustWriter.writeMarshallTestCases( + codegenContext: CodegenContext, testCase: EventStreamTestModels.TestCase, - generator: RuntimeType, + optionalBuilderInputs: Boolean, ) { + val generator = "crate::event_stream_serde::TestStreamMarshaller" + val protocolTestHelpers = CargoDependency.smithyProtocolTestHelpers(TestRuntimeConfig) .copy(scope = DependencyScope.Compile) + + fun builderInput( + @Language("Rust", prefix = "macro_rules! foo { () => {{\n", suffix = "\n}}}") + input: String, + vararg ctx: Pair, + ): Writable = conditionalBuilderInput(input, conditional = optionalBuilderInputs, ctx = ctx) + + val typesModule = codegenContext.symbolProvider.moduleForShape(codegenContext.model.lookup("test#TestStruct")) rustTemplate( """ use aws_smithy_eventstream::frame::{Message, Header, HeaderValue, MarshallMessage}; use std::collections::HashMap; use aws_smithy_types::{Blob, DateTime}; - use crate::error::*; - use crate::model::*; + use ${typesModule.fullyQualifiedPath()}::*; use #{validate_body}; use #{MediaType}; @@ -46,163 +59,192 @@ internal object EventStreamMarshallTestCases { "MediaType" to protocolTestHelpers.toType().resolve("MediaType"), ) - unitTest( - "message_with_blob", - """ - let event = TestStream::MessageWithBlob( - MessageWithBlob::builder().data(Blob::new(&b"hello, world!"[..])).build() - ); - let result = ${format(generator)}().marshall(event); - assert!(result.is_ok(), "expected ok, got: {:?}", result); - let message = result.unwrap(); - let headers = headers_to_map(message.headers()); - assert_eq!(&str_header("event"), *headers.get(":message-type").unwrap()); - assert_eq!(&str_header("MessageWithBlob"), *headers.get(":event-type").unwrap()); - assert_eq!(&str_header("application/octet-stream"), *headers.get(":content-type").unwrap()); - assert_eq!(&b"hello, world!"[..], message.payload()); - """, - ) - - unitTest( - "message_with_string", - """ - let event = TestStream::MessageWithString( - MessageWithString::builder().data("hello, world!").build() - ); - let result = ${format(generator)}().marshall(event); - assert!(result.is_ok(), "expected ok, got: {:?}", result); - let message = result.unwrap(); - let headers = headers_to_map(message.headers()); - assert_eq!(&str_header("event"), *headers.get(":message-type").unwrap()); - assert_eq!(&str_header("MessageWithString"), *headers.get(":event-type").unwrap()); - assert_eq!(&str_header("text/plain"), *headers.get(":content-type").unwrap()); - assert_eq!(&b"hello, world!"[..], message.payload()); - """, - ) - - unitTest( - "message_with_struct", - """ - let event = TestStream::MessageWithStruct( - MessageWithStruct::builder().some_struct( - TestStruct::builder() - .some_string("hello") - .some_int(5) - .build() - ).build() - ); - let result = ${format(generator)}().marshall(event); - assert!(result.is_ok(), "expected ok, got: {:?}", result); - let message = result.unwrap(); - let headers = headers_to_map(message.headers()); - assert_eq!(&str_header("event"), *headers.get(":message-type").unwrap()); - assert_eq!(&str_header("MessageWithStruct"), *headers.get(":event-type").unwrap()); - assert_eq!(&str_header(${testCase.requestContentType.dq()}), *headers.get(":content-type").unwrap()); - - validate_body( - message.payload(), - ${testCase.validTestStruct.dq()}, - MediaType::from(${testCase.requestContentType.dq()}) - ).unwrap(); - """, - ) - - unitTest( - "message_with_union", - """ - let event = TestStream::MessageWithUnion(MessageWithUnion::builder().some_union( - TestUnion::Foo("hello".into()) - ).build()); - let result = ${format(generator)}().marshall(event); - assert!(result.is_ok(), "expected ok, got: {:?}", result); - let message = result.unwrap(); - let headers = headers_to_map(message.headers()); - assert_eq!(&str_header("event"), *headers.get(":message-type").unwrap()); - assert_eq!(&str_header("MessageWithUnion"), *headers.get(":event-type").unwrap()); - assert_eq!(&str_header(${testCase.requestContentType.dq()}), *headers.get(":content-type").unwrap()); - - validate_body( - message.payload(), - ${testCase.validTestUnion.dq()}, - MediaType::from(${testCase.requestContentType.dq()}) - ).unwrap(); - """, - ) - - unitTest( - "message_with_headers", - """ - let event = TestStream::MessageWithHeaders(MessageWithHeaders::builder() - .blob(Blob::new(&b"test"[..])) - .boolean(true) - .byte(55i8) - .int(100_000i32) - .long(9_000_000_000i64) - .short(16_000i16) - .string("test") - .timestamp(DateTime::from_secs(5)) - .build() - ); - let result = ${format(generator)}().marshall(event); - assert!(result.is_ok(), "expected ok, got: {:?}", result); - let actual_message = result.unwrap(); - let expected_message = Message::new(&b""[..]) - .add_header(Header::new(":message-type", HeaderValue::String("event".into()))) - .add_header(Header::new(":event-type", HeaderValue::String("MessageWithHeaders".into()))) - .add_header(Header::new("blob", HeaderValue::ByteArray((&b"test"[..]).into()))) - .add_header(Header::new("boolean", HeaderValue::Bool(true))) - .add_header(Header::new("byte", HeaderValue::Byte(55i8))) - .add_header(Header::new("int", HeaderValue::Int32(100_000i32))) - .add_header(Header::new("long", HeaderValue::Int64(9_000_000_000i64))) - .add_header(Header::new("short", HeaderValue::Int16(16_000i16))) - .add_header(Header::new("string", HeaderValue::String("test".into()))) - .add_header(Header::new("timestamp", HeaderValue::Timestamp(DateTime::from_secs(5)))); - assert_eq!(expected_message, actual_message); - """, - ) - - unitTest( - "message_with_header_and_payload", - """ - let event = TestStream::MessageWithHeaderAndPayload(MessageWithHeaderAndPayload::builder() - .header("header") - .payload(Blob::new(&b"payload"[..])) - .build() - ); - let result = ${format(generator)}().marshall(event); - assert!(result.is_ok(), "expected ok, got: {:?}", result); - let actual_message = result.unwrap(); - let expected_message = Message::new(&b"payload"[..]) - .add_header(Header::new(":message-type", HeaderValue::String("event".into()))) - .add_header(Header::new(":event-type", HeaderValue::String("MessageWithHeaderAndPayload".into()))) - .add_header(Header::new("header", HeaderValue::String("header".into()))) - .add_header(Header::new(":content-type", HeaderValue::String("application/octet-stream".into()))); - assert_eq!(expected_message, actual_message); - """, - ) - - unitTest( - "message_with_no_header_payload_traits", - """ - let event = TestStream::MessageWithNoHeaderPayloadTraits(MessageWithNoHeaderPayloadTraits::builder() - .some_int(5) - .some_string("hello") - .build() - ); - let result = ${format(generator)}().marshall(event); - assert!(result.is_ok(), "expected ok, got: {:?}", result); - let message = result.unwrap(); - let headers = headers_to_map(message.headers()); - assert_eq!(&str_header("event"), *headers.get(":message-type").unwrap()); - assert_eq!(&str_header("MessageWithNoHeaderPayloadTraits"), *headers.get(":event-type").unwrap()); - assert_eq!(&str_header(${testCase.requestContentType.dq()}), *headers.get(":content-type").unwrap()); - - validate_body( - message.payload(), - ${testCase.validMessageWithNoHeaderPayloadTraits.dq()}, - MediaType::from(${testCase.requestContentType.dq()}) - ).unwrap(); - """, - ) + unitTest("message_with_blob") { + rustTemplate( + """ + let event = TestStream::MessageWithBlob( + MessageWithBlob::builder().data(#{BlobInput:W}).build() + ); + let result = $generator::new().marshall(event); + assert!(result.is_ok(), "expected ok, got: {:?}", result); + let message = result.unwrap(); + let headers = headers_to_map(message.headers()); + assert_eq!(&str_header("event"), *headers.get(":message-type").unwrap()); + assert_eq!(&str_header("MessageWithBlob"), *headers.get(":event-type").unwrap()); + assert_eq!(&str_header("application/octet-stream"), *headers.get(":content-type").unwrap()); + assert_eq!(&b"hello, world!"[..], message.payload()); + """, + "BlobInput" to builderInput("Blob::new(&b\"hello, world!\"[..])"), + ) + } + + unitTest("message_with_string") { + rustTemplate( + """ + let event = TestStream::MessageWithString( + MessageWithString::builder().data(#{StringInput}).build() + ); + let result = $generator::new().marshall(event); + assert!(result.is_ok(), "expected ok, got: {:?}", result); + let message = result.unwrap(); + let headers = headers_to_map(message.headers()); + assert_eq!(&str_header("event"), *headers.get(":message-type").unwrap()); + assert_eq!(&str_header("MessageWithString"), *headers.get(":event-type").unwrap()); + assert_eq!(&str_header("text/plain"), *headers.get(":content-type").unwrap()); + assert_eq!(&b"hello, world!"[..], message.payload()); + """, + "StringInput" to builderInput("\"hello, world!\""), + ) + } + + unitTest("message_with_struct") { + rustTemplate( + """ + let event = TestStream::MessageWithStruct( + MessageWithStruct::builder().some_struct(#{StructInput}).build() + ); + let result = $generator::new().marshall(event); + assert!(result.is_ok(), "expected ok, got: {:?}", result); + let message = result.unwrap(); + let headers = headers_to_map(message.headers()); + assert_eq!(&str_header("event"), *headers.get(":message-type").unwrap()); + assert_eq!(&str_header("MessageWithStruct"), *headers.get(":event-type").unwrap()); + assert_eq!(&str_header(${testCase.requestContentType.dq()}), *headers.get(":content-type").unwrap()); + + validate_body( + message.payload(), + ${testCase.validTestStruct.dq()}, + MediaType::from(${testCase.mediaType.dq()}) + ).unwrap(); + """, + "StructInput" to + builderInput( + """ + TestStruct::builder() + .some_string(#{StringInput}) + .some_int(#{IntInput}) + .build() + """, + "IntInput" to builderInput("5"), + "StringInput" to builderInput("\"hello\""), + ), + ) + } + + unitTest("message_with_union") { + rustTemplate( + """ + let event = TestStream::MessageWithUnion(MessageWithUnion::builder() + .some_union(#{UnionInput}) + .build() + ); + let result = $generator::new().marshall(event); + assert!(result.is_ok(), "expected ok, got: {:?}", result); + let message = result.unwrap(); + let headers = headers_to_map(message.headers()); + assert_eq!(&str_header("event"), *headers.get(":message-type").unwrap()); + assert_eq!(&str_header("MessageWithUnion"), *headers.get(":event-type").unwrap()); + assert_eq!(&str_header(${testCase.requestContentType.dq()}), *headers.get(":content-type").unwrap()); + + validate_body( + message.payload(), + ${testCase.validTestUnion.dq()}, + MediaType::from(${testCase.mediaType.dq()}) + ).unwrap(); + """, + "UnionInput" to builderInput("TestUnion::Foo(\"hello\".into())"), + ) + } + + unitTest("message_with_headers") { + rustTemplate( + """ + let event = TestStream::MessageWithHeaders(MessageWithHeaders::builder() + .blob(#{BlobInput}) + .boolean(#{BooleanInput}) + .byte(#{ByteInput}) + .int(#{IntInput}) + .long(#{LongInput}) + .short(#{ShortInput}) + .string(#{StringInput}) + .timestamp(#{TimestampInput}) + .build() + ); + let result = $generator::new().marshall(event); + assert!(result.is_ok(), "expected ok, got: {:?}", result); + let actual_message = result.unwrap(); + let expected_message = Message::new(&b""[..]) + .add_header(Header::new(":message-type", HeaderValue::String("event".into()))) + .add_header(Header::new(":event-type", HeaderValue::String("MessageWithHeaders".into()))) + .add_header(Header::new("blob", HeaderValue::ByteArray((&b"test"[..]).into()))) + .add_header(Header::new("boolean", HeaderValue::Bool(true))) + .add_header(Header::new("byte", HeaderValue::Byte(55i8))) + .add_header(Header::new("int", HeaderValue::Int32(100_000i32))) + .add_header(Header::new("long", HeaderValue::Int64(9_000_000_000i64))) + .add_header(Header::new("short", HeaderValue::Int16(16_000i16))) + .add_header(Header::new("string", HeaderValue::String("test".into()))) + .add_header(Header::new("timestamp", HeaderValue::Timestamp(DateTime::from_secs(5)))); + assert_eq!(expected_message, actual_message); + """, + "BlobInput" to builderInput("Blob::new(&b\"test\"[..])"), + "BooleanInput" to builderInput("true"), + "ByteInput" to builderInput("55i8"), + "IntInput" to builderInput("100_000i32"), + "LongInput" to builderInput("9_000_000_000i64"), + "ShortInput" to builderInput("16_000i16"), + "StringInput" to builderInput("\"test\""), + "TimestampInput" to builderInput("DateTime::from_secs(5)"), + ) + } + + unitTest("message_with_header_and_payload") { + rustTemplate( + """ + let event = TestStream::MessageWithHeaderAndPayload(MessageWithHeaderAndPayload::builder() + .header(#{HeaderInput}) + .payload(#{PayloadInput}) + .build() + ); + let result = $generator::new().marshall(event); + assert!(result.is_ok(), "expected ok, got: {:?}", result); + let actual_message = result.unwrap(); + let expected_message = Message::new(&b"payload"[..]) + .add_header(Header::new(":message-type", HeaderValue::String("event".into()))) + .add_header(Header::new(":event-type", HeaderValue::String("MessageWithHeaderAndPayload".into()))) + .add_header(Header::new("header", HeaderValue::String("header".into()))) + .add_header(Header::new(":content-type", HeaderValue::String("application/octet-stream".into()))); + assert_eq!(expected_message, actual_message); + """, + "HeaderInput" to builderInput("\"header\""), + "PayloadInput" to builderInput("Blob::new(&b\"payload\"[..])"), + ) + } + + unitTest("message_with_no_header_payload_traits") { + rustTemplate( + """ + let event = TestStream::MessageWithNoHeaderPayloadTraits(MessageWithNoHeaderPayloadTraits::builder() + .some_int(#{IntInput}) + .some_string(#{StringInput}) + .build() + ); + let result = $generator::new().marshall(event); + assert!(result.is_ok(), "expected ok, got: {:?}", result); + let message = result.unwrap(); + let headers = headers_to_map(message.headers()); + assert_eq!(&str_header("event"), *headers.get(":message-type").unwrap()); + assert_eq!(&str_header("MessageWithNoHeaderPayloadTraits"), *headers.get(":event-type").unwrap()); + assert_eq!(&str_header(${testCase.requestContentType.dq()}), *headers.get(":content-type").unwrap()); + + validate_body( + message.payload(), + ${testCase.validMessageWithNoHeaderPayloadTraits.dq()}, + MediaType::from(${testCase.mediaType.dq()}) + ).unwrap(); + """, + "IntInput" to builderInput("5"), + "StringInput" to builderInput("\"hello\""), + ) + } } } diff --git a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/testutil/EventStreamTestModels.kt b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/testutil/EventStreamTestModels.kt index 58ab85eec6..e944a552a0 100644 --- a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/testutil/EventStreamTestModels.kt +++ b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/testutil/EventStreamTestModels.kt @@ -19,6 +19,7 @@ private fun fillInBaseModel( ): String = """ namespace test + use smithy.framework#ValidationException use aws.protocols#$protocolName union TestUnion { @@ -69,12 +70,20 @@ private fun fillInBaseModel( MessageWithNoHeaderPayloadTraits: MessageWithNoHeaderPayloadTraits, SomeError: SomeError, } - structure TestStreamInputOutput { @httpPayload @required value: TestStream } + + structure TestStreamInputOutput { + @required + @httpPayload + value: TestStream + } + + @http(method: "POST", uri: "/test") operation TestStreamOp { input: TestStreamInputOutput, output: TestStreamInputOutput, - errors: [SomeError], + errors: [SomeError, ValidationException], } + $extraServiceAnnotations @$protocolName service TestService { version: "123", operations: [TestStreamOp] } @@ -92,6 +101,7 @@ object EventStreamTestModels { data class TestCase( val protocolShapeId: String, val model: Model, + val mediaType: String, val requestContentType: String, val responseContentType: String, val validTestStruct: String, @@ -111,7 +121,8 @@ object EventStreamTestModels { TestCase( protocolShapeId = "aws.protocols#restJson1", model = restJson1(), - requestContentType = "application/json", + mediaType = "application/json", + requestContentType = "application/vnd.amazon.eventstream", responseContentType = "application/json", validTestStruct = """{"someString":"hello","someInt":5}""", validMessageWithNoHeaderPayloadTraits = """{"someString":"hello","someInt":5}""", @@ -126,6 +137,7 @@ object EventStreamTestModels { TestCase( protocolShapeId = "aws.protocols#awsJson1_1", model = awsJson11(), + mediaType = "application/x-amz-json-1.1", requestContentType = "application/x-amz-json-1.1", responseContentType = "application/x-amz-json-1.1", validTestStruct = """{"someString":"hello","someInt":5}""", @@ -141,7 +153,8 @@ object EventStreamTestModels { TestCase( protocolShapeId = "aws.protocols#restXml", model = restXml(), - requestContentType = "application/xml", + mediaType = "application/xml", + requestContentType = "application/vnd.amazon.eventstream", responseContentType = "application/xml", validTestStruct = """ diff --git a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/testutil/EventStreamTestTools.kt b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/testutil/EventStreamTestTools.kt deleted file mode 100644 index 05151b7ce7..0000000000 --- a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/testutil/EventStreamTestTools.kt +++ /dev/null @@ -1,176 +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.testutil - -import software.amazon.smithy.codegen.core.Symbol -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.shapes.Shape -import software.amazon.smithy.model.shapes.ShapeId -import software.amazon.smithy.model.shapes.StructureShape -import software.amazon.smithy.model.shapes.UnionShape -import software.amazon.smithy.model.traits.ErrorTrait -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.smithy.CodegenContext -import software.amazon.smithy.rust.codegen.core.smithy.CodegenTarget -import software.amazon.smithy.rust.codegen.core.smithy.ErrorsModule -import software.amazon.smithy.rust.codegen.core.smithy.ModelsModule -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.generators.StructureGenerator -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.protocols.Protocol -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.testutil.EventStreamMarshallTestCases.writeMarshallTestCases -import software.amazon.smithy.rust.codegen.core.testutil.EventStreamUnmarshallTestCases.writeUnmarshallTestCases -import software.amazon.smithy.rust.codegen.core.util.hasTrait -import software.amazon.smithy.rust.codegen.core.util.lookup -import software.amazon.smithy.rust.codegen.core.util.outputShape - -data class TestEventStreamProject( - val model: Model, - val serviceShape: ServiceShape, - val operationShape: OperationShape, - val streamShape: UnionShape, - val symbolProvider: RustSymbolProvider, - val project: TestWriterDelegator, -) - -enum class EventStreamTestVariety { - Marshall, - Unmarshall -} - -interface EventStreamTestRequirements { - /** Create a codegen context for the tests */ - fun createCodegenContext( - model: Model, - serviceShape: ServiceShape, - protocolShapeId: ShapeId, - codegenTarget: CodegenTarget, - ): C - - /** Render the event stream marshall/unmarshall code generator */ - fun renderGenerator( - codegenContext: C, - project: TestEventStreamProject, - protocol: Protocol, - ): RuntimeType - - /** Render a builder for the given shape */ - fun renderBuilderForShape( - writer: RustWriter, - codegenContext: C, - shape: StructureShape, - ) - - /** Render an operation error for the given operation and error shapes */ - fun renderOperationError( - writer: RustWriter, - model: Model, - symbolProvider: RustSymbolProvider, - operationSymbol: Symbol, - errors: List, - ) -} - -object EventStreamTestTools { - fun runTestCase( - testCase: EventStreamTestModels.TestCase, - requirements: EventStreamTestRequirements, - codegenTarget: CodegenTarget, - variety: EventStreamTestVariety, - ) { - val model = EventStreamNormalizer.transform(OperationNormalizer.transform(testCase.model)) - val serviceShape = model.expectShape(ShapeId.from("test#TestService")) as ServiceShape - val codegenContext = requirements.createCodegenContext( - model, - serviceShape, - ShapeId.from(testCase.protocolShapeId), - codegenTarget, - ) - val test = generateTestProject(requirements, codegenContext, codegenTarget) - val protocol = testCase.protocolBuilder(codegenContext) - val generator = requirements.renderGenerator(codegenContext, test, protocol) - - test.project.lib { - when (variety) { - EventStreamTestVariety.Marshall -> writeMarshallTestCases(testCase, generator) - EventStreamTestVariety.Unmarshall -> writeUnmarshallTestCases(testCase, codegenTarget, generator) - } - } - test.project.compileAndTest() - } - - private fun generateTestProject( - requirements: EventStreamTestRequirements, - codegenContext: C, - codegenTarget: CodegenTarget, - ): TestEventStreamProject { - val model = codegenContext.model - val symbolProvider = codegenContext.symbolProvider - val operationShape = model.expectShape(ShapeId.from("test#TestStreamOp")) as OperationShape - val unionShape = model.expectShape(ShapeId.from("test#TestStream")) as UnionShape - - val project = TestWorkspace.testProject(symbolProvider) - val operationSymbol = symbolProvider.toSymbol(operationShape) - project.withModule(ErrorsModule) { - val errors = model.structureShapes.filter { shape -> shape.hasTrait() } - requirements.renderOperationError(this, model, symbolProvider, operationSymbol, errors) - requirements.renderOperationError(this, model, symbolProvider, symbolProvider.toSymbol(unionShape), errors) - for (shape in errors) { - StructureGenerator(model, symbolProvider, this, shape).render(codegenTarget) - requirements.renderBuilderForShape(this, codegenContext, shape) - } - } - project.withModule(ModelsModule) { - val inputOutput = model.lookup("test#TestStreamInputOutput") - recursivelyGenerateModels(model, symbolProvider, inputOutput, this, codegenTarget) - } - project.withModule(RustModule.Output) { - operationShape.outputShape(model).renderWithModelBuilder(model, symbolProvider, this) - } - return TestEventStreamProject( - model, - codegenContext.serviceShape, - operationShape, - unionShape, - symbolProvider, - project, - ) - } - - private fun recursivelyGenerateModels( - model: Model, - symbolProvider: RustSymbolProvider, - shape: Shape, - writer: RustWriter, - mode: CodegenTarget, - ) { - for (member in shape.members()) { - if (member.target.namespace == "smithy.api") { - continue - } - val target = model.expectShape(member.target) - when (target) { - is StructureShape -> target.renderWithModelBuilder(model, symbolProvider, writer) - is UnionShape -> UnionGenerator( - model, - symbolProvider, - writer, - target, - renderUnknownVariant = mode.renderUnknownVariant(), - ).render() - else -> TODO("EventStreamTestTools doesn't support rendering $target") - } - recursivelyGenerateModels(model, symbolProvider, target, writer, mode) - } - } -} diff --git a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/testutil/EventStreamUnmarshallTestCases.kt b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/testutil/EventStreamUnmarshallTestCases.kt index bb27c724e0..215e7ad9de 100644 --- a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/testutil/EventStreamUnmarshallTestCases.kt +++ b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/testutil/EventStreamUnmarshallTestCases.kt @@ -5,23 +5,33 @@ package software.amazon.smithy.rust.codegen.core.testutil +import org.intellij.lang.annotations.Language +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.conditionalBlock import software.amazon.smithy.rust.codegen.core.rustlang.rust -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.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.lookup -internal object EventStreamUnmarshallTestCases { - internal fun RustWriter.writeUnmarshallTestCases( +object EventStreamUnmarshallTestCases { + fun RustWriter.writeUnmarshallTestCases( + codegenContext: CodegenContext, testCase: EventStreamTestModels.TestCase, - codegenTarget: CodegenTarget, - generator: RuntimeType, + optionalBuilderInputs: Boolean = false, ) { + val generator = "crate::event_stream_serde::TestStreamUnmarshaller" + + val testStreamError = codegenContext.symbolProvider.symbolForEventStreamError(codegenContext.model.lookup("test#TestStream")) + val typesModule = codegenContext.symbolProvider.moduleForShape(codegenContext.model.lookup("test#TestStruct")) rust( """ use aws_smithy_eventstream::frame::{Header, HeaderValue, Message, UnmarshallMessage, UnmarshalledMessage}; use aws_smithy_types::{Blob, DateTime}; - use crate::error::*; - use crate::model::*; + use $testStreamError; + use ${typesModule.fullyQualifiedPath()}::*; fn msg( message_type: &'static str, @@ -53,206 +63,199 @@ internal object EventStreamUnmarshallTestCases { """, ) - unitTest( - name = "message_with_blob", - test = """ + unitTest("message_with_blob") { + rustTemplate( + """ let message = msg("event", "MessageWithBlob", "application/octet-stream", b"hello, world!"); - let result = ${format(generator)}().unmarshall(&message); + let result = $generator::new().unmarshall(&message); assert!(result.is_ok(), "expected ok, got: {:?}", result); assert_eq!( TestStream::MessageWithBlob( - MessageWithBlob::builder().data(Blob::new(&b"hello, world!"[..])).build() + MessageWithBlob::builder().data(#{DataInput:W}).build() ), expect_event(result.unwrap()) ); - """, - ) + """, + "DataInput" to conditionalBuilderInput( + """ + Blob::new(&b"hello, world!"[..]) + """, + conditional = optionalBuilderInputs, + ), - if (codegenTarget == CodegenTarget.CLIENT) { - unitTest( - "unknown_message", + ) + } + + unitTest("message_with_string") { + rustTemplate( """ - let message = msg("event", "NewUnmodeledMessageType", "application/octet-stream", b"hello, world!"); - let result = ${format(generator)}().unmarshall(&message); + let message = msg("event", "MessageWithString", "text/plain", b"hello, world!"); + let result = $generator::new().unmarshall(&message); assert!(result.is_ok(), "expected ok, got: {:?}", result); assert_eq!( - TestStream::Unknown, + TestStream::MessageWithString(MessageWithString::builder().data(#{DataInput}).build()), expect_event(result.unwrap()) ); """, + "DataInput" to conditionalBuilderInput("\"hello, world!\"", conditional = optionalBuilderInputs), ) } - unitTest( - "message_with_string", - """ - let message = msg("event", "MessageWithString", "text/plain", b"hello, world!"); - let result = ${format(generator)}().unmarshall(&message); - assert!(result.is_ok(), "expected ok, got: {:?}", result); - assert_eq!( - TestStream::MessageWithString(MessageWithString::builder().data("hello, world!").build()), - expect_event(result.unwrap()) - ); - """, - ) - - unitTest( - "message_with_struct", - """ - let message = msg( - "event", - "MessageWithStruct", - "${testCase.responseContentType}", - br#"${testCase.validTestStruct}"# - ); - let result = ${format(generator)}().unmarshall(&message); - assert!(result.is_ok(), "expected ok, got: {:?}", result); - assert_eq!( - TestStream::MessageWithStruct(MessageWithStruct::builder().some_struct( + unitTest("message_with_struct") { + rustTemplate( + """ + let message = msg( + "event", + "MessageWithStruct", + "${testCase.responseContentType}", + br##"${testCase.validTestStruct}"## + ); + let result = $generator::new().unmarshall(&message); + assert!(result.is_ok(), "expected ok, got: {:?}", result); + assert_eq!( + TestStream::MessageWithStruct(MessageWithStruct::builder().some_struct(#{StructInput}).build()), + expect_event(result.unwrap()) + ); + """, + "StructInput" to conditionalBuilderInput( + """ TestStruct::builder() - .some_string("hello") - .some_int(5) + .some_string(#{StringInput}) + .some_int(#{IntInput}) .build() - ).build()), - expect_event(result.unwrap()) - ); - """, - ) + """, + conditional = optionalBuilderInputs, + "StringInput" to conditionalBuilderInput("\"hello\"", conditional = optionalBuilderInputs), + "IntInput" to conditionalBuilderInput("5", conditional = optionalBuilderInputs), + ), - unitTest( - "message_with_union", - """ - let message = msg( - "event", - "MessageWithUnion", - "${testCase.responseContentType}", - br#"${testCase.validTestUnion}"# - ); - let result = ${format(generator)}().unmarshall(&message); - assert!(result.is_ok(), "expected ok, got: {:?}", result); - assert_eq!( - TestStream::MessageWithUnion(MessageWithUnion::builder().some_union( - TestUnion::Foo("hello".into()) - ).build()), - expect_event(result.unwrap()) - ); - """, - ) + ) + } - unitTest( - "message_with_headers", - """ - let message = msg("event", "MessageWithHeaders", "application/octet-stream", b"") - .add_header(Header::new("blob", HeaderValue::ByteArray((&b"test"[..]).into()))) - .add_header(Header::new("boolean", HeaderValue::Bool(true))) - .add_header(Header::new("byte", HeaderValue::Byte(55i8))) - .add_header(Header::new("int", HeaderValue::Int32(100_000i32))) - .add_header(Header::new("long", HeaderValue::Int64(9_000_000_000i64))) - .add_header(Header::new("short", HeaderValue::Int16(16_000i16))) - .add_header(Header::new("string", HeaderValue::String("test".into()))) - .add_header(Header::new("timestamp", HeaderValue::Timestamp(DateTime::from_secs(5)))); - let result = ${format(generator)}().unmarshall(&message); - assert!(result.is_ok(), "expected ok, got: {:?}", result); - assert_eq!( - TestStream::MessageWithHeaders(MessageWithHeaders::builder() - .blob(Blob::new(&b"test"[..])) - .boolean(true) - .byte(55i8) - .int(100_000i32) - .long(9_000_000_000i64) - .short(16_000i16) - .string("test") - .timestamp(DateTime::from_secs(5)) - .build() - ), - expect_event(result.unwrap()) - ); - """, - ) + unitTest("message_with_union") { + rustTemplate( + """ + let message = msg( + "event", + "MessageWithUnion", + "${testCase.responseContentType}", + br##"${testCase.validTestUnion}"## + ); + let result = $generator::new().unmarshall(&message); + assert!(result.is_ok(), "expected ok, got: {:?}", result); + assert_eq!( + TestStream::MessageWithUnion(MessageWithUnion::builder().some_union(#{UnionInput}).build()), + expect_event(result.unwrap()) + ); + """, + "UnionInput" to conditionalBuilderInput("TestUnion::Foo(\"hello\".into())", conditional = optionalBuilderInputs), + ) + } - unitTest( - "message_with_header_and_payload", - """ - let message = msg("event", "MessageWithHeaderAndPayload", "application/octet-stream", b"payload") - .add_header(Header::new("header", HeaderValue::String("header".into()))); - let result = ${format(generator)}().unmarshall(&message); - assert!(result.is_ok(), "expected ok, got: {:?}", result); - assert_eq!( - TestStream::MessageWithHeaderAndPayload(MessageWithHeaderAndPayload::builder() - .header("header") - .payload(Blob::new(&b"payload"[..])) - .build() - ), - expect_event(result.unwrap()) - ); - """, - ) + unitTest("message_with_headers") { + rustTemplate( + """ + let message = msg("event", "MessageWithHeaders", "application/octet-stream", b"") + .add_header(Header::new("blob", HeaderValue::ByteArray((&b"test"[..]).into()))) + .add_header(Header::new("boolean", HeaderValue::Bool(true))) + .add_header(Header::new("byte", HeaderValue::Byte(55i8))) + .add_header(Header::new("int", HeaderValue::Int32(100_000i32))) + .add_header(Header::new("long", HeaderValue::Int64(9_000_000_000i64))) + .add_header(Header::new("short", HeaderValue::Int16(16_000i16))) + .add_header(Header::new("string", HeaderValue::String("test".into()))) + .add_header(Header::new("timestamp", HeaderValue::Timestamp(DateTime::from_secs(5)))); + let result = $generator::new().unmarshall(&message); + assert!(result.is_ok(), "expected ok, got: {:?}", result); + assert_eq!( + TestStream::MessageWithHeaders(MessageWithHeaders::builder() + .blob(#{BlobInput}) + .boolean(#{BoolInput}) + .byte(#{ByteInput}) + .int(#{IntInput}) + .long(#{LongInput}) + .short(#{ShortInput}) + .string(#{StringInput}) + .timestamp(#{TimestampInput}) + .build() + ), + expect_event(result.unwrap()) + ); + """, + "BlobInput" to conditionalBuilderInput("Blob::new(&b\"test\"[..])", conditional = optionalBuilderInputs), + "BoolInput" to conditionalBuilderInput("true", conditional = optionalBuilderInputs), + "ByteInput" to conditionalBuilderInput("55i8", conditional = optionalBuilderInputs), + "IntInput" to conditionalBuilderInput("100_000i32", conditional = optionalBuilderInputs), + "LongInput" to conditionalBuilderInput("9_000_000_000i64", conditional = optionalBuilderInputs), + "ShortInput" to conditionalBuilderInput("16_000i16", conditional = optionalBuilderInputs), + "StringInput" to conditionalBuilderInput("\"test\"", conditional = optionalBuilderInputs), + "TimestampInput" to conditionalBuilderInput("DateTime::from_secs(5)", conditional = optionalBuilderInputs), + ) + } - unitTest( - "message_with_no_header_payload_traits", - """ - let message = msg( - "event", - "MessageWithNoHeaderPayloadTraits", - "${testCase.responseContentType}", - br#"${testCase.validMessageWithNoHeaderPayloadTraits}"# - ); - let result = ${format(generator)}().unmarshall(&message); - assert!(result.is_ok(), "expected ok, got: {:?}", result); - assert_eq!( - TestStream::MessageWithNoHeaderPayloadTraits(MessageWithNoHeaderPayloadTraits::builder() - .some_int(5) - .some_string("hello") - .build() - ), - expect_event(result.unwrap()) - ); - """, - ) + unitTest("message_with_header_and_payload") { + rustTemplate( + """ + let message = msg("event", "MessageWithHeaderAndPayload", "application/octet-stream", b"payload") + .add_header(Header::new("header", HeaderValue::String("header".into()))); + let result = $generator::new().unmarshall(&message); + assert!(result.is_ok(), "expected ok, got: {:?}", result); + assert_eq!( + TestStream::MessageWithHeaderAndPayload(MessageWithHeaderAndPayload::builder() + .header(#{HeaderInput}) + .payload(#{PayloadInput}) + .build() + ), + expect_event(result.unwrap()) + ); + """, + "HeaderInput" to conditionalBuilderInput("\"header\"", conditional = optionalBuilderInputs), + "PayloadInput" to conditionalBuilderInput("Blob::new(&b\"payload\"[..])", conditional = optionalBuilderInputs), + ) + } - val (someError, kindSuffix) = when (codegenTarget) { - CodegenTarget.CLIENT -> "TestStreamErrorKind::SomeError" to ".kind" - CodegenTarget.SERVER -> "TestStreamError::SomeError" to "" + unitTest("message_with_no_header_payload_traits") { + rustTemplate( + """ + let message = msg( + "event", + "MessageWithNoHeaderPayloadTraits", + "${testCase.responseContentType}", + br##"${testCase.validMessageWithNoHeaderPayloadTraits}"## + ); + let result = $generator::new().unmarshall(&message); + assert!(result.is_ok(), "expected ok, got: {:?}", result); + assert_eq!( + TestStream::MessageWithNoHeaderPayloadTraits(MessageWithNoHeaderPayloadTraits::builder() + .some_int(#{IntInput}) + .some_string(#{StringInput}) + .build() + ), + expect_event(result.unwrap()) + ); + """, + "IntInput" to conditionalBuilderInput("5", conditional = optionalBuilderInputs), + "StringInput" to conditionalBuilderInput("\"hello\"", conditional = optionalBuilderInputs), + ) } - unitTest( - "some_error", - """ - let message = msg( - "exception", - "SomeError", - "${testCase.responseContentType}", - br#"${testCase.validSomeError}"# - ); - let result = ${format(generator)}().unmarshall(&message); - assert!(result.is_ok(), "expected ok, got: {:?}", result); - match expect_error(result.unwrap())$kindSuffix { - $someError(err) => assert_eq!(Some("some error"), err.message()), - kind => panic!("expected SomeError, but got {:?}", kind), - } - """, - ) - if (codegenTarget == CodegenTarget.CLIENT) { - unitTest( - "generic_error", + unitTest("some_error") { + rustTemplate( """ let message = msg( "exception", - "UnmodeledError", + "SomeError", "${testCase.responseContentType}", - br#"${testCase.validUnmodeledError}"# + br##"${testCase.validSomeError}"## ); - let result = ${format(generator)}().unmarshall(&message); + let result = $generator::new().unmarshall(&message); assert!(result.is_ok(), "expected ok, got: {:?}", result); - match expect_error(result.unwrap())$kindSuffix { - TestStreamErrorKind::Unhandled(err) => { - let message = format!("{}", aws_smithy_types::error::display::DisplayErrorContext(&err)); - let expected = "message: \"unmodeled error\""; - assert!(message.contains(expected), "Expected '{message}' to contain '{expected}'"); - } - kind => panic!("expected generic error, but got {:?}", kind), + match expect_error(result.unwrap()) { + TestStreamError::SomeError(err) => assert_eq!(Some("some error"), err.message()), + #{AllowUnreachablePatterns:W} + kind => panic!("expected SomeError, but got {:?}", kind), } """, + "AllowUnreachablePatterns" to writable { Attribute.AllowUnreachablePatterns.render(this) }, ) } @@ -265,10 +268,21 @@ internal object EventStreamUnmarshallTestCases { "wrong-content-type", br#"${testCase.validTestStruct}"# ); - let result = ${format(generator)}().unmarshall(&message); + let result = $generator::new().unmarshall(&message); assert!(result.is_err(), "expected error, got: {:?}", result); assert!(format!("{}", result.err().unwrap()).contains("expected :content-type to be")); """, ) } } + +internal fun conditionalBuilderInput( + @Language("Rust", prefix = "macro_rules! foo { () => {{\n", suffix = "\n}}}") contents: String, + conditional: Boolean, + vararg ctx: Pair, +): Writable = + writable { + conditionalBlock("Some(", ".into())", conditional = conditional) { + rustTemplate(contents, *ctx) + } + } 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 a8567c4db4..5f9e612ee7 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 @@ -9,37 +9,45 @@ import com.moandjiezana.toml.TomlWriter import org.intellij.lang.annotations.Language import software.amazon.smithy.build.FileManifest import software.amazon.smithy.build.PluginContext -import software.amazon.smithy.codegen.core.Symbol +import software.amazon.smithy.codegen.core.CodegenException import software.amazon.smithy.model.Model +import software.amazon.smithy.model.loader.ModelAssembler import software.amazon.smithy.model.node.Node import software.amazon.smithy.model.node.ObjectNode -import software.amazon.smithy.model.shapes.Shape import software.amazon.smithy.model.shapes.ShapeId -import software.amazon.smithy.model.traits.EnumDefinition 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.DependencyScope import software.amazon.smithy.rust.codegen.core.rustlang.RustDependency +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 +import software.amazon.smithy.rust.codegen.core.rustlang.docs 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.writable import software.amazon.smithy.rust.codegen.core.smithy.CoreCodegenConfig -import software.amazon.smithy.rust.codegen.core.smithy.MaybeRenamed +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.smithy.SymbolVisitorConfig import software.amazon.smithy.rust.codegen.core.util.CommandFailed -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.letIf +import software.amazon.smithy.rust.codegen.core.util.orNullIfEmpty import software.amazon.smithy.rust.codegen.core.util.runCommand import java.io.File import java.nio.file.Files.createTempDirectory import java.nio.file.Path import kotlin.io.path.absolutePathString +val TestModuleDocProvider = object : ModuleDocProvider { + override fun docsWriter(module: RustModule.LeafModule): Writable = writable { + docs("Some test documentation\n\nSome more details...") + } +} + /** * Waiting for Kotlin to stabilize their temp directory functionality */ @@ -101,7 +109,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.62.1\"\n", + "[toolchain]\nchannel = \"1.66.1\"\n", ) // ensure there at least an empty lib.rs file to avoid broken crates newProject.resolve("src").mkdirs() @@ -112,26 +120,20 @@ object TestWorkspace { } } - @Suppress("NAME_SHADOWING") - fun testProject(symbolProvider: RustSymbolProvider? = null, debugMode: Boolean = false): TestWriterDelegator { - val subprojectDir = subproject() - val symbolProvider = symbolProvider ?: object : RustSymbolProvider { - override fun config(): SymbolVisitorConfig { - PANIC("") - } - - override fun toEnumVariantName(definition: EnumDefinition): MaybeRenamed? { - PANIC("") - } + fun testProject( + model: Model = ModelAssembler().assemble().unwrap(), + codegenConfig: CoreCodegenConfig = CoreCodegenConfig(), + ): TestWriterDelegator = testProject(testSymbolProvider(model), codegenConfig) - override fun toSymbol(shape: Shape?): Symbol { - PANIC("") - } - } + fun testProject( + symbolProvider: RustSymbolProvider, + codegenConfig: CoreCodegenConfig = CoreCodegenConfig(), + ): TestWriterDelegator { + val subprojectDir = subproject() return TestWriterDelegator( FileManifest.create(subprojectDir.toPath()), symbolProvider, - CoreCodegenConfig(debugMode = debugMode), + codegenConfig, ).apply { lib { // If the test fails before the crate is finalized, we'll end up with a broken crate. @@ -190,8 +192,7 @@ fun generatePluginContext( ) } - val settings = settingsBuilder.merge(additionalSettings) - .build() + val settings = settingsBuilder.merge(additionalSettings).build() val pluginContext = PluginContext.builder().model(model).fileManifest(manifest).settings(settings).build() return pluginContext to testPath } @@ -221,7 +222,47 @@ fun RustWriter.unitTest( if (async) { rust("async") } - return rustBlock("fn $name()", *args, block = block) + return testDependenciesOnly { rustBlock("fn $name()", *args, block = block) } +} + +fun RustWriter.cargoDependencies() = dependencies.map { RustDependency.fromSymbolDependency(it) } + .filterIsInstance().distinct() + +fun RustWriter.assertNoNewDependencies(block: Writable, dependencyFilter: (CargoDependency) -> String?): RustWriter { + val startingDependencies = cargoDependencies().toSet() + block(this) + val endingDependencies = cargoDependencies().toSet() + val newDeps = (endingDependencies - startingDependencies) + val invalidDeps = + newDeps.mapNotNull { dep -> dependencyFilter(dep)?.let { message -> message to dep } }.orNullIfEmpty() + if (invalidDeps != null) { + val badDeps = invalidDeps.map { it.second.rustName } + val writtenOut = this.toString() + val badLines = writtenOut.lines().filter { line -> badDeps.any { line.contains(it) } } + throw CodegenException( + "found invalid dependencies. ${invalidDeps.map { + it.first + }}\nHint: the following lines may be the problem.\n${ + badLines.joinToString( + separator = "\n", + prefix = " ", + ) + }", + ) + } + return this +} + +fun RustWriter.testDependenciesOnly(block: Writable) = assertNoNewDependencies(block) { dep -> + if (dep.scope != DependencyScope.Dev) { + "Cannot add $dep — this writer should only add test dependencies." + } else { + null + } +} + +fun testDependenciesOnly(block: Writable): Writable = { + testDependenciesOnly(block) } fun RustWriter.tokioTest(name: String, vararg args: Any, block: Writable) { @@ -237,12 +278,8 @@ class TestWriterDelegator( private val fileManifest: FileManifest, symbolProvider: RustSymbolProvider, val codegenConfig: CoreCodegenConfig, -) : - RustCrate( - fileManifest, - symbolProvider, - codegenConfig, - ) { + moduleDocProvider: ModuleDocProvider = TestModuleDocProvider, +) : RustCrate(fileManifest, symbolProvider, codegenConfig, moduleDocProvider) { val baseDir: Path = fileManifest.baseDir fun printGeneratedFiles() { @@ -252,6 +289,19 @@ class TestWriterDelegator( fun generatedFiles() = fileManifest.files.map { baseDir.relativize(it) } } +/** + * Generate a newtest module + * + * This should only be used in test code—the generated module name will be something like `tests_123` + */ +fun RustCrate.testModule(block: Writable) = lib { + withInlineModule( + RustModule.inlineTests(safeName("tests")), + TestModuleDocProvider, + block, + ) +} + fun FileManifest.printGeneratedFiles() { this.files.forEach { path -> println("file:///$path") @@ -263,7 +313,10 @@ fun FileManifest.printGeneratedFiles() { * should generally be set to `false` to avoid invalidating the Cargo cache between * every unit test run. */ -fun TestWriterDelegator.compileAndTest(runClippy: Boolean = false) { +fun TestWriterDelegator.compileAndTest( + runClippy: Boolean = false, + expectFailure: Boolean = false, +): String { val stubModel = """ namespace fake service Fake { @@ -284,10 +337,11 @@ fun TestWriterDelegator.compileAndTest(runClippy: Boolean = false) { // cargo fmt errors are useless, ignore } val env = mapOf("RUSTFLAGS" to "-A dead_code") - "cargo test".runCommand(baseDir, env) + val testOutput = "cargo test".runCommand(baseDir, env) if (runClippy) { "cargo clippy".runCommand(baseDir, env) } + return testOutput } fun TestWriterDelegator.rustSettings() = @@ -424,8 +478,11 @@ fun RustCrate.integrationTest(name: String, writable: Writable) = this.withFile( fun TestWriterDelegator.unitTest(test: Writable): TestWriterDelegator { lib { - unitTest(safeName("test")) { - test(this) + val name = safeName("test") + withInlineModule(RustModule.inlineTests(name), TestModuleDocProvider) { + unitTest(name) { + test(this) + } } } return this diff --git a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/testutil/TestHelpers.kt b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/testutil/TestHelpers.kt index 6c9310a309..b80f211c76 100644 --- a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/testutil/TestHelpers.kt +++ b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/testutil/TestHelpers.kt @@ -5,37 +5,106 @@ package software.amazon.smithy.rust.codegen.core.testutil +import software.amazon.smithy.codegen.core.Symbol 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.Shape import software.amazon.smithy.model.shapes.ShapeId import software.amazon.smithy.model.shapes.StructureShape +import software.amazon.smithy.model.shapes.UnionShape +import software.amazon.smithy.model.traits.ErrorTrait import software.amazon.smithy.rust.codegen.core.rustlang.Attribute +import software.amazon.smithy.rust.codegen.core.rustlang.RustModule +import software.amazon.smithy.rust.codegen.core.rustlang.RustReservedWordConfig import software.amazon.smithy.rust.codegen.core.rustlang.RustReservedWordSymbolProvider -import software.amazon.smithy.rust.codegen.core.rustlang.RustWriter +import software.amazon.smithy.rust.codegen.core.rustlang.RustReservedWords +import software.amazon.smithy.rust.codegen.core.rustlang.Visibility +import software.amazon.smithy.rust.codegen.core.rustlang.implBlock import software.amazon.smithy.rust.codegen.core.smithy.BaseSymbolMetadataProvider 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.CoreCodegenConfig import software.amazon.smithy.rust.codegen.core.smithy.CoreRustSettings +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.RuntimeConfig import software.amazon.smithy.rust.codegen.core.smithy.RuntimeCrateLocation +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.RustSymbolProviderConfig import software.amazon.smithy.rust.codegen.core.smithy.SymbolVisitor -import software.amazon.smithy.rust.codegen.core.smithy.SymbolVisitorConfig import software.amazon.smithy.rust.codegen.core.smithy.generators.BuilderGenerator import software.amazon.smithy.rust.codegen.core.smithy.generators.StructureGenerator -import software.amazon.smithy.rust.codegen.core.smithy.generators.implBlock +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.dq +import software.amazon.smithy.rust.codegen.core.util.hasTrait import software.amazon.smithy.rust.codegen.core.util.letIf +import software.amazon.smithy.rust.codegen.core.util.toSnakeCase import java.io.File val TestRuntimeConfig = RuntimeConfig(runtimeCrateLocation = RuntimeCrateLocation.Path(File("../rust-runtime/").absolutePath)) -val TestSymbolVisitorConfig = SymbolVisitorConfig( + +/** + * IMPORTANT: You shouldn't need to refer to these directly in code or tests. They are private for a reason. + * + * In general, the RustSymbolProvider's `config()` has a `moduleFor` function that should be used + * to find the destination module for a given shape. + */ +private object CodegenCoreTestModules { + // Use module paths that don't align with either server or client to make sure + // the codegen is resilient to differences in module path. + val ModelsTestModule = RustModule.public("test_model") + val ErrorsTestModule = RustModule.public("test_error") + val InputsTestModule = RustModule.public("test_input") + val OutputsTestModule = RustModule.public("test_output") + val OperationsTestModule = RustModule.public("test_operation") + + object TestModuleProvider : ModuleProvider { + override fun moduleForShape(context: ModuleProviderContext, shape: Shape): RustModule.LeafModule = + when (shape) { + is OperationShape -> OperationsTestModule + is StructureShape -> when { + shape.hasTrait() -> ErrorsTestModule + shape.hasTrait() -> InputsTestModule + shape.hasTrait() -> OutputsTestModule + else -> ModelsTestModule + } + + else -> ModelsTestModule + } + + override fun moduleForOperationError( + context: ModuleProviderContext, + operation: OperationShape, + ): RustModule.LeafModule = ErrorsTestModule + + override fun moduleForEventStreamError( + context: ModuleProviderContext, + eventStream: UnionShape, + ): RustModule.LeafModule = ErrorsTestModule + + override fun moduleForBuilder(context: ModuleProviderContext, shape: Shape, symbol: Symbol): RustModule.LeafModule { + val builderNamespace = RustReservedWords.escapeIfNeeded("test_" + symbol.name.toSnakeCase()) + return RustModule.new( + builderNamespace, + visibility = Visibility.PUBLIC, + parent = symbol.module(), + inline = true, + ) + } + } +} + +val TestRustSymbolProviderConfig = RustSymbolProviderConfig( runtimeConfig = TestRuntimeConfig, renameExceptions = true, nullabilityCheckMode = NullableIndex.CheckMode.CLIENT_ZERO_VALUE_V1, + moduleProvider = CodegenCoreTestModules.TestModuleProvider, ) fun testRustSettings( @@ -70,12 +139,21 @@ fun String.asSmithyModel(sourceLocation: String? = null, smithyVersion: String = } // Intentionally only visible to codegen-core since the other modules have their own symbol providers -internal fun testSymbolProvider(model: Model): RustSymbolProvider = SymbolVisitor( +internal fun testSymbolProvider( + model: Model, + rustReservedWordConfig: RustReservedWordConfig? = null, +): RustSymbolProvider = SymbolVisitor( + testRustSettings(), model, ServiceShape.builder().version("test").id("test#Service").build(), - TestSymbolVisitorConfig, -).let { BaseSymbolMetadataProvider(it, model, additionalAttributes = listOf(Attribute.NonExhaustive)) } - .let { RustReservedWordSymbolProvider(it, model) } + TestRustSymbolProviderConfig, +).let { BaseSymbolMetadataProvider(it, additionalAttributes = listOf(Attribute.NonExhaustive)) } + .let { + RustReservedWordSymbolProvider( + it, + rustReservedWordConfig ?: RustReservedWordConfig(emptyMap(), emptyMap(), emptyMap()), + ) + } // Intentionally only visible to codegen-core since the other modules have their own contexts internal fun testCodegenContext( @@ -86,6 +164,7 @@ internal fun testCodegenContext( ): CodegenContext = CodegenContext( model, testSymbolProvider(model), + TestModuleDocProvider, serviceShape ?: model.serviceShapes.firstOrNull() ?: ServiceShape.builder().version("test").id("test#Service").build(), @@ -100,13 +179,16 @@ internal fun testCodegenContext( fun StructureShape.renderWithModelBuilder( model: Model, symbolProvider: RustSymbolProvider, - writer: RustWriter, - forWhom: CodegenTarget = CodegenTarget.CLIENT, + rustCrate: RustCrate, ) { - StructureGenerator(model, symbolProvider, writer, this).render(forWhom) - val modelBuilder = BuilderGenerator(model, symbolProvider, this) - modelBuilder.render(writer) - writer.implBlock(this, symbolProvider) { - modelBuilder.renderConvenienceMethod(this) + val struct = this + rustCrate.withModule(symbolProvider.moduleForShape(struct)) { + StructureGenerator(model, symbolProvider, this, struct, emptyList()).render() + implBlock(symbolProvider.toSymbol(struct)) { + BuilderGenerator.renderConvenienceMethod(this, symbolProvider, struct) + } + } + rustCrate.withModule(symbolProvider.moduleForBuilder(struct)) { + BuilderGenerator(model, symbolProvider, struct, emptyList()).render(this) } } diff --git a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/util/LetIf.kt b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/util/LetIf.kt index 2ac4da683d..89868f7a00 100644 --- a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/util/LetIf.kt +++ b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/util/LetIf.kt @@ -10,7 +10,9 @@ package software.amazon.smithy.rust.codegen.core.util fun T.letIf(cond: Boolean, f: (T) -> T): T { return if (cond) { f(this) - } else this + } else { + this + } } fun List.extendIf(condition: Boolean, f: () -> T) = if (condition) { diff --git a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/util/Panic.kt b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/util/Panic.kt index b7dc3e681c..7d24a17988 100644 --- a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/util/Panic.kt +++ b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/util/Panic.kt @@ -6,7 +6,7 @@ package software.amazon.smithy.rust.codegen.core.util /** Something has gone horribly wrong due to a coding error */ -fun PANIC(reason: String): Nothing = throw RuntimeException(reason) +fun PANIC(reason: String = ""): Nothing = throw RuntimeException(reason) /** This code should never be executed (but Kotlin cannot prove that) */ fun UNREACHABLE(reason: String): Nothing = throw IllegalStateException("This should be unreachable: $reason") 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 267327c35e..299cdff24d 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 @@ -18,6 +18,7 @@ import software.amazon.smithy.model.shapes.StructureShape import software.amazon.smithy.model.shapes.UnionShape import software.amazon.smithy.model.traits.SensitiveTrait import software.amazon.smithy.model.traits.StreamingTrait +import software.amazon.smithy.model.traits.TitleTrait import software.amazon.smithy.model.traits.Trait import software.amazon.smithy.rust.codegen.core.smithy.traits.SyntheticInputTrait import software.amazon.smithy.rust.codegen.core.smithy.traits.SyntheticOutputTrait @@ -42,7 +43,9 @@ fun StructureShape.expectMember(member: String): MemberShape = fun UnionShape.expectMember(member: String): MemberShape = this.getMember(member).orElseThrow { CodegenException("$member did not exist on $this") } -fun StructureShape.errorMessageMember(): MemberShape? = this.getMember("message").or { this.getMember("Message") }.orNull() +fun StructureShape.errorMessageMember(): MemberShape? = this.getMember("message").or { + this.getMember("Message") +}.orNull() fun StructureShape.hasStreamingMember(model: Model) = this.findStreamingMember(model) != null fun UnionShape.hasStreamingMember(model: Model) = this.findMemberWithTrait(model) != null @@ -137,3 +140,9 @@ fun Shape.isPrimitive(): Boolean { else -> false } } + +/** Convert a string to a ShapeId */ +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 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 2f1d170e5e..9d613f558f 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 @@ -34,12 +34,15 @@ internal class InlineDependencyTest { fun `locate dependencies from the inlineable module`() { val dep = InlineDependency.idempotencyToken() val testProject = TestWorkspace.testProject() - testProject.unitTest { + testProject.lib { rustTemplate( """ - use #{idempotency}::uuid_v4; - let res = uuid_v4(0); - assert_eq!(res, "00000000-0000-4000-8000-000000000000"); + ##[test] + fn idempotency_works() { + use #{idempotency}::uuid_v4; + let res = uuid_v4(0); + assert_eq!(res, "00000000-0000-4000-8000-000000000000"); + } """, "idempotency" to dep.toType(), @@ -77,8 +80,8 @@ internal class InlineDependencyTest { fun `prevent the creation of duplicate modules`() { val root = RustModule.private("parent") // create a child module with no docs - val child1 = RustModule.private("child", parent = root) - val child2 = RustModule.public("child", parent = root) + val child1 = RustModule.public("child", parent = root) + val child2 = RustModule.private("child", parent = root) val crate = TestWorkspace.testProject() crate.withModule(child1) { } shouldThrow { @@ -87,11 +90,11 @@ internal class InlineDependencyTest { shouldThrow { // can't make one with docs when the old one had no docs - crate.withModule(RustModule.private("child", documentation = "docs", parent = root)) {} + crate.withModule(child1.copy(documentationOverride = "docs")) {} } // but making an identical module is fine - val identicalChild = RustModule.private("child", parent = root) + val identicalChild = RustModule.public("child", parent = root) crate.withModule(identicalChild) {} identicalChild.fullyQualifiedPath() shouldBe "crate::parent::child" } diff --git a/codegen-core/src/test/kotlin/software/amazon/smithy/rust/codegen/core/rustlang/RustReservedWordsTest.kt b/codegen-core/src/test/kotlin/software/amazon/smithy/rust/codegen/core/rustlang/RustReservedWordsTest.kt index 5f72e33765..25e47e0963 100644 --- a/codegen-core/src/test/kotlin/software/amazon/smithy/rust/codegen/core/rustlang/RustReservedWordsTest.kt +++ b/codegen-core/src/test/kotlin/software/amazon/smithy/rust/codegen/core/rustlang/RustReservedWordsTest.kt @@ -7,30 +7,120 @@ package software.amazon.smithy.rust.codegen.core.rustlang import io.kotest.matchers.shouldBe import org.junit.jupiter.api.Test -import software.amazon.smithy.codegen.core.Symbol +import software.amazon.smithy.model.Model import software.amazon.smithy.model.shapes.MemberShape -import software.amazon.smithy.model.shapes.Shape -import software.amazon.smithy.model.traits.EnumDefinition +import software.amazon.smithy.model.shapes.ShapeId import software.amazon.smithy.rust.codegen.core.smithy.MaybeRenamed -import software.amazon.smithy.rust.codegen.core.smithy.RustSymbolProvider -import software.amazon.smithy.rust.codegen.core.smithy.SymbolVisitorConfig +import software.amazon.smithy.rust.codegen.core.smithy.SymbolVisitor +import software.amazon.smithy.rust.codegen.core.smithy.WrappingSymbolProvider +import software.amazon.smithy.rust.codegen.core.smithy.renamedFrom +import software.amazon.smithy.rust.codegen.core.testutil.TestRustSymbolProviderConfig import software.amazon.smithy.rust.codegen.core.testutil.asSmithyModel -import software.amazon.smithy.rust.codegen.core.util.PANIC -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.testutil.testRustSettings +import software.amazon.smithy.rust.codegen.core.util.lookup internal class RustReservedWordSymbolProviderTest { - class Stub : RustSymbolProvider { - override fun config(): SymbolVisitorConfig { - PANIC("") + private class TestSymbolProvider(model: Model) : + WrappingSymbolProvider(SymbolVisitor(testRustSettings(), model, null, TestRustSymbolProviderConfig)) + private val emptyConfig = RustReservedWordConfig(emptyMap(), emptyMap(), emptyMap()) + + @Test + fun `structs are escaped`() { + val model = """ + namespace test + structure Self {} + """.asSmithyModel() + val provider = RustReservedWordSymbolProvider(TestSymbolProvider(model), emptyConfig) + val symbol = provider.toSymbol(model.lookup("test#Self")) + symbol.name shouldBe "SelfValue" + } + + private fun mappingTest(config: RustReservedWordConfig, model: Model, id: String, test: (String) -> Unit) { + val provider = RustReservedWordSymbolProvider(TestSymbolProvider(model), config) + val symbol = provider.toMemberName(model.lookup("test#Container\$$id")) + test(symbol) + } + + @Test + fun `structs member names are mapped via config`() { + val config = emptyConfig.copy( + structureMemberMap = mapOf( + "name_to_map" to "mapped_name", + "NameToMap" to "MappedName", + ), + ) + var model = """ + namespace test + structure Container { + name_to_map: String + } + """.asSmithyModel() + mappingTest(config, model, "name_to_map") { memberName -> + memberName shouldBe "mapped_name" } - override fun toEnumVariantName(definition: EnumDefinition): MaybeRenamed? { - return definition.name.orNull()?.let { MaybeRenamed(it.toPascalCase(), null) } + model = """ + namespace test + enum Container { + NameToMap = "NameToMap" + } + """.asSmithyModel(smithyVersion = "2.0") + mappingTest(config, model, "NameToMap") { memberName -> + // Container was not a struct, so the field keeps its old name + memberName shouldBe "NameToMap" } - override fun toSymbol(shape: Shape): Symbol { - return Symbol.builder().name(shape.id.name).build() + model = """ + namespace test + union Container { + NameToMap: String + } + """.asSmithyModel() + mappingTest(config, model, "NameToMap") { memberName -> + // Container was not a struct, so the field keeps its old name + memberName shouldBe "NameToMap" + } + } + + @Test + fun `union member names are mapped via config`() { + val config = emptyConfig.copy( + unionMemberMap = mapOf( + "name_to_map" to "mapped_name", + "NameToMap" to "MappedName", + ), + ) + + var model = """ + namespace test + union Container { + NameToMap: String + } + """.asSmithyModel() + mappingTest(config, model, "NameToMap") { memberName -> + memberName shouldBe "MappedName" + } + + model = """ + namespace test + structure Container { + name_to_map: String + } + """.asSmithyModel() + mappingTest(config, model, "name_to_map") { memberName -> + // Container was not a union, so the field keeps its old name + memberName shouldBe "name_to_map" + } + + model = """ + namespace test + enum Container { + NameToMap = "NameToMap" + } + """.asSmithyModel(smithyVersion = "2.0") + mappingTest(config, model, "NameToMap") { memberName -> + // Container was not a union, so the field keeps its old name + memberName shouldBe "NameToMap" } } @@ -41,8 +131,8 @@ internal class RustReservedWordSymbolProviderTest { structure container { async: String } - """.trimMargin().asSmithyModel() - val provider = RustReservedWordSymbolProvider(Stub(), model) + """.asSmithyModel() + val provider = RustReservedWordSymbolProvider(TestSymbolProvider(model), emptyConfig) provider.toMemberName( MemberShape.builder().id("namespace#container\$async").target("namespace#Integer").build(), ) shouldBe "r##async" @@ -54,6 +144,31 @@ internal class RustReservedWordSymbolProviderTest { @Test fun `enum variant names are updated to avoid conflicts`() { + val model = """ + namespace foo + @enum([{ name: "dontcare", value: "dontcare" }]) string Container + """.asSmithyModel() + val provider = RustReservedWordSymbolProvider( + TestSymbolProvider(model), + reservedWordConfig = emptyConfig.copy( + enumMemberMap = mapOf( + "Unknown" to "UnknownValue", + "UnknownValue" to "UnknownValue_", + ), + ), + ) + + fun expectEnumRename(original: String, expected: MaybeRenamed) { + val symbol = provider.toSymbol( + MemberShape.builder() + .id(ShapeId.fromParts("foo", "Container").withMember(original)) + .target("smithy.api#String") + .build(), + ) + symbol.name shouldBe expected.name + symbol.renamedFrom() shouldBe expected.renamedFrom + } + expectEnumRename("Unknown", MaybeRenamed("UnknownValue", "Unknown")) expectEnumRename("UnknownValue", MaybeRenamed("UnknownValue_", "UnknownValue")) expectEnumRename("UnknownOther", MaybeRenamed("UnknownOther", null)) @@ -63,10 +178,4 @@ internal class RustReservedWordSymbolProviderTest { expectEnumRename("SelfOther", MaybeRenamed("SelfOther", null)) expectEnumRename("SELF", MaybeRenamed("SelfValue", "Self")) } - - private fun expectEnumRename(original: String, expected: MaybeRenamed) { - val model = "namespace foo".asSmithyModel() - val provider = RustReservedWordSymbolProvider(Stub(), model) - provider.toEnumVariantName(EnumDefinition.builder().name(original).value("foo").build()) shouldBe expected - } } 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 41e0c10066..7fa364dfaf 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 @@ -163,7 +163,12 @@ internal class RustTypesTest { ), ), ) - forInputExpectOutput(writable { attributeMacro.render(this) }, "#[cfg(all(feature = \"unstable\", any(feature = \"serialize\", feature = \"deserialize\")))]\n") + forInputExpectOutput( + writable { + attributeMacro.render(this) + }, + "#[cfg(all(feature = \"unstable\", any(feature = \"serialize\", feature = \"deserialize\")))]\n", + ) } @Test @@ -178,7 +183,12 @@ internal class RustTypesTest { ), ), ) - forInputExpectOutput(writable { attributeMacro.render(this) }, "#[cfg(all(feature = \"unstable\", feature = \"serialize\", feature = \"deserialize\"))]\n") + forInputExpectOutput( + writable { + attributeMacro.render(this) + }, + "#[cfg(all(feature = \"unstable\", feature = \"serialize\", feature = \"deserialize\"))]\n", + ) } @Test @@ -197,7 +207,12 @@ internal class RustTypesTest { RuntimeType.StdError, ), ) - forInputExpectOutput(writable { attributeMacro.render(this) }, "#[derive(std::clone::Clone, std::error::Error, std::fmt::Debug)]\n") + forInputExpectOutput( + writable { + attributeMacro.render(this) + }, + "#[derive(std::clone::Clone, std::error::Error, std::fmt::Debug)]\n", + ) } @Test 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 86d48a2602..3b0ef7c557 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 @@ -10,17 +10,20 @@ import io.kotest.matchers.shouldBe import io.kotest.matchers.string.shouldContain import io.kotest.matchers.string.shouldContainOnlyOnce import org.junit.jupiter.api.Test +import org.junit.jupiter.api.assertThrows +import software.amazon.smithy.codegen.core.CodegenException import software.amazon.smithy.model.Model import software.amazon.smithy.model.shapes.SetShape import software.amazon.smithy.model.shapes.StringShape import software.amazon.smithy.model.shapes.StructureShape import software.amazon.smithy.rust.codegen.core.rustlang.Attribute.Companion.deprecated import software.amazon.smithy.rust.codegen.core.smithy.RuntimeType +import software.amazon.smithy.rust.codegen.core.testutil.TestModuleDocProvider +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.compileAndRun import software.amazon.smithy.rust.codegen.core.testutil.compileAndTest -import software.amazon.smithy.rust.codegen.core.testutil.shouldCompile import software.amazon.smithy.rust.codegen.core.testutil.testSymbolProvider +import software.amazon.smithy.rust.codegen.core.testutil.unitTest import software.amazon.smithy.rust.codegen.core.util.lookup class RustWriterTest { @@ -28,7 +31,10 @@ class RustWriterTest { fun `inner modules correctly handle dependencies`() { val sut = RustWriter.forModule("parent") val requestBuilder = RuntimeType.HttpRequestBuilder - sut.withInlineModule(RustModule.new("inner", visibility = Visibility.PUBLIC, inline = true)) { + sut.withInlineModule( + RustModule.new("inner", visibility = Visibility.PUBLIC, inline = true), + TestModuleDocProvider, + ) { rustBlock("fn build(builder: #T)", requestBuilder) { } } @@ -39,7 +45,6 @@ class RustWriterTest { @Test fun `manually created struct`() { - val sut = RustWriter.forModule("lib") val stringShape = StringShape.builder().id("test#Hello").build() val set = SetShape.builder() .id("foo.bar#Records") @@ -53,37 +58,43 @@ class RustWriterTest { val provider = testSymbolProvider(model) val setSymbol = provider.toSymbol(set) val stringSymbol = provider.toSymbol(stringShape) - sut.rustBlock("struct Test") { - write("member: #T,", setSymbol) - write("otherMember: #T,", stringSymbol) - } - val output = sut.toString() - output.shouldCompile() - output shouldContain RustType.HashSet.Type - output shouldContain "struct Test" - output.compileAndRun( - """ - let test = Test { member: ${RustType.HashSet.Namespace}::${RustType.HashSet.Type}::default(), otherMember: "hello".to_string() }; - assert_eq!(test.otherMember, "hello"); - assert_eq!(test.member.is_empty(), true); - """, - ) + + TestWorkspace.testProject(provider) + .unitTest { + rustBlock("struct Test") { + write("member: #T,", setSymbol) + write("other_member: #T,", stringSymbol) + } + + unitTest("test_manually_created_struct") { + rust( + """ + let test = Test { member: ${RustType.HashSet.Namespace}::${RustType.HashSet.Type}::default(), other_member: "hello".to_string() }; + assert_eq!(test.other_member, "hello"); + assert_eq!(test.member.is_empty(), true); + + // If this compiles, then we know the symbol provider resolved the correct type for a set + let _isVec: Vec<_> = test.member; + """, + ) + } + }.compileAndTest(runClippy = true) } @Test fun `generate docs`() { - val sut = RustWriter.forModule("lib") - sut.docs( + val writer = RustWriter.root() + writer.docs( """Top level module documentation |More docs |/* handle weird characters */ |`a backtick` - |[a link](asdf) - """.trimMargin(), + |[a link](some_url) + """, ) - sut.rustBlock("pub fn foo()") { } - sut.compileAndTest() - sut.toString() shouldContain "Top level module" + writer.rustBlock("pub fn foo()") { } + val output = writer.toString() + output shouldContain "/// Top level module" } @Test @@ -94,9 +105,10 @@ class RustWriterTest { """.asSmithyModel() val shape = model.lookup("test#Foo") val symbol = testSymbolProvider(model).toSymbol(shape) - val sut = RustWriter.forModule("lib") - sut.docs("A link! #D", symbol) - sut.toString() shouldContain "/// A link! [`Foo`](crate::model::Foo)" + val writer = RustWriter.root() + writer.docs("A link! #D", symbol) + val output = writer.toString() + output shouldContain "/// A link! [`Foo`](crate::test_model::Foo)" } @Test @@ -107,7 +119,7 @@ class RustWriterTest { @Test fun `attributes with comments when using rust`() { - val sut = RustWriter.forModule("lib") + val sut = RustWriter.root() Attribute("foo").render(sut) sut.rust(" // here's an attribute") sut.toString().shouldContain("#[foo]\n// here's an attribute") @@ -115,7 +127,7 @@ class RustWriterTest { @Test fun `attributes with comments when using docs`() { - val sut = RustWriter.forModule("lib") + val sut = RustWriter.root() Attribute("foo").render(sut) sut.docs("here's an attribute") sut.toString().shouldContain("#[foo]\n/// here's an attribute") @@ -123,28 +135,28 @@ class RustWriterTest { @Test fun `deprecated attribute without any field`() { - val sut = RustWriter.forModule("lib") + val sut = RustWriter.root() Attribute.Deprecated.render(sut) sut.toString() shouldContain "#[deprecated]" } @Test fun `deprecated attribute with a note`() { - val sut = RustWriter.forModule("lib") + val sut = RustWriter.root() Attribute(deprecated(note = "custom")).render(sut) sut.toString() shouldContain "#[deprecated(note = \"custom\")]" } @Test fun `deprecated attribute with a since`() { - val sut = RustWriter.forModule("lib") + val sut = RustWriter.root() Attribute(deprecated(since = "1.2.3")).render(sut) sut.toString() shouldContain "#[deprecated(since = \"1.2.3\")]" } @Test fun `deprecated attribute with a note and a since`() { - val sut = RustWriter.forModule("lib") + val sut = RustWriter.root() Attribute(deprecated("1.2.3", "custom")).render(sut) sut.toString() shouldContain "#[deprecated(note = \"custom\", since = \"1.2.3\")]" } @@ -152,7 +164,7 @@ class RustWriterTest { @Test fun `template writables with upper case names`() { val inner = writable { rust("hello") } - val sut = RustWriter.forModule("lib") + val sut = RustWriter.root() sut.rustTemplate( "inner: #{Inner:W}, regular: #{http}", "Inner" to inner, @@ -161,6 +173,23 @@ class RustWriterTest { sut.toString().shouldContain("inner: hello, regular: http::foo") } + @Test + fun `missing template parameters are enclosed in backticks in the exception message`() { + val sut = RustWriter.root() + val exception = assertThrows { + sut.rustTemplate( + "#{Foo} #{Bar}", + "Foo Bar" to CargoDependency.Http.toType().resolve("foo"), + "Baz" to CargoDependency.Http.toType().resolve("foo"), + ) + } + exception.message shouldBe + """ + Rust block template expected `Foo` but was not present in template. + Hint: Template contains: [`Foo Bar`, `Baz`] + """.trimIndent() + } + @Test fun `can handle file paths properly when determining module`() { val sut = RustWriter.forModule("src/module_name") 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 5be3d0a898..c9407372d7 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 @@ -10,11 +10,12 @@ import org.junit.jupiter.api.Test 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.Compile +import software.amazon.smithy.rust.codegen.core.rustlang.DependencyScope.Dev class CodegenDelegatorTest { @Test fun testMergeDependencyFeatures() { - val merged = mergeDependencyFeatures( + val merged = listOf( CargoDependency("A", CratesIo("1"), Compile, optional = false, features = setOf()), CargoDependency("A", CratesIo("1"), Compile, optional = false, features = setOf("f1")), @@ -26,8 +27,7 @@ class CodegenDelegatorTest { CargoDependency("C", CratesIo("3"), Compile, optional = true, features = setOf()), CargoDependency("C", CratesIo("3"), Compile, optional = true, features = setOf()), - ).shuffled(), - ) + ).shuffled().mergeDependencyFeatures() merged shouldBe setOf( CargoDependency("A", CratesIo("1"), Compile, optional = false, features = setOf("f1", "f2")), @@ -35,4 +35,19 @@ class CodegenDelegatorTest { CargoDependency("C", CratesIo("3"), Compile, optional = true, features = setOf()), ) } + + @Test + fun testMergeIdenticalFeatures() { + val merged = listOf( + CargoDependency("A", CratesIo("1"), Compile), + CargoDependency("A", CratesIo("1"), Dev), + CargoDependency("B", CratesIo("1"), Compile), + CargoDependency("B", CratesIo("1"), Dev, features = setOf("a", "b")), + ).mergeIdenticalTestDependencies() + merged shouldBe setOf( + CargoDependency("A", CratesIo("1"), Compile), + CargoDependency("B", CratesIo("1"), Compile), + CargoDependency("B", CratesIo("1"), Dev, features = setOf("a", "b")), + ) + } } diff --git a/codegen-client/src/test/kotlin/software/amazon/smithy/rust/codegen/client/smithy/SymbolVisitorTest.kt b/codegen-core/src/test/kotlin/software/amazon/smithy/rust/codegen/core/smithy/SymbolVisitorTest.kt similarity index 91% rename from codegen-client/src/test/kotlin/software/amazon/smithy/rust/codegen/client/smithy/SymbolVisitorTest.kt rename to codegen-core/src/test/kotlin/software/amazon/smithy/rust/codegen/core/smithy/SymbolVisitorTest.kt index bc26847bef..3c47ca4f67 100644 --- a/codegen-client/src/test/kotlin/software/amazon/smithy/rust/codegen/client/smithy/SymbolVisitorTest.kt +++ b/codegen-core/src/test/kotlin/software/amazon/smithy/rust/codegen/core/smithy/SymbolVisitorTest.kt @@ -3,12 +3,11 @@ * SPDX-License-Identifier: Apache-2.0 */ -package software.amazon.smithy.rust.codegen.client.smithy +package software.amazon.smithy.rust.codegen.core.smithy import io.kotest.matchers.collections.shouldContain import io.kotest.matchers.collections.shouldNotBeEmpty import io.kotest.matchers.shouldBe -import io.kotest.matchers.string.shouldContain import org.junit.jupiter.api.Assertions import org.junit.jupiter.api.DisplayName import org.junit.jupiter.api.Test @@ -26,15 +25,10 @@ import software.amazon.smithy.model.shapes.StringShape import software.amazon.smithy.model.shapes.StructureShape import software.amazon.smithy.model.traits.ErrorTrait import software.amazon.smithy.model.traits.SparseTrait -import software.amazon.smithy.rust.codegen.client.testutil.testSymbolProvider 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.smithy.ErrorsModule -import software.amazon.smithy.rust.codegen.core.smithy.ModelsModule -import software.amazon.smithy.rust.codegen.core.smithy.OperationsModule -import software.amazon.smithy.rust.codegen.core.smithy.isOptional -import software.amazon.smithy.rust.codegen.core.smithy.rustType import software.amazon.smithy.rust.codegen.core.testutil.asSmithyModel +import software.amazon.smithy.rust.codegen.core.testutil.testSymbolProvider class SymbolVisitorTest { private fun Symbol.referenceClosure(): List { @@ -57,8 +51,8 @@ class SymbolVisitorTest { val provider: SymbolProvider = testSymbolProvider(model) val sym = provider.toSymbol(struct) sym.rustType().render(false) shouldBe "MyStruct" - sym.definitionFile shouldContain ModelsModule.definitionFile() - sym.namespace shouldBe "crate::model" + sym.definitionFile shouldBe "src/test_model.rs" + sym.namespace shouldBe "crate::test_model" } @Test @@ -77,7 +71,7 @@ class SymbolVisitorTest { val provider: SymbolProvider = testSymbolProvider(model) val sym = provider.toSymbol(struct) sym.rustType().render(false) shouldBe "TerribleError" - sym.definitionFile shouldContain ErrorsModule.definitionFile() + sym.definitionFile shouldBe "src/test_error.rs" } @Test @@ -101,8 +95,8 @@ class SymbolVisitorTest { val provider: SymbolProvider = testSymbolProvider(model) val sym = provider.toSymbol(shape) sym.rustType().render(false) shouldBe "StandardUnit" - sym.definitionFile shouldContain ModelsModule.definitionFile() - sym.namespace shouldBe "crate::model" + sym.definitionFile shouldBe "src/test_model.rs" + sym.namespace shouldBe "crate::test_model" } @DisplayName("Creates primitives") @@ -260,7 +254,7 @@ class SymbolVisitorTest { } """.asSmithyModel() val symbol = testSymbolProvider(model).toSymbol(model.expectShape(ShapeId.from("smithy.example#PutObject"))) - symbol.definitionFile shouldBe(OperationsModule.definitionFile()) + symbol.definitionFile shouldBe "src/test_operation.rs" symbol.name shouldBe "PutObject" } } diff --git a/codegen-core/src/test/kotlin/software/amazon/smithy/rust/codegen/core/smithy/customizations/SmithyTypesPubUseGeneratorTest.kt b/codegen-core/src/test/kotlin/software/amazon/smithy/rust/codegen/core/smithy/customizations/SmithyTypesPubUseExtraTest.kt similarity index 92% rename from codegen-core/src/test/kotlin/software/amazon/smithy/rust/codegen/core/smithy/customizations/SmithyTypesPubUseGeneratorTest.kt rename to codegen-core/src/test/kotlin/software/amazon/smithy/rust/codegen/core/smithy/customizations/SmithyTypesPubUseExtraTest.kt index c147567d71..120fc5cb20 100644 --- a/codegen-core/src/test/kotlin/software/amazon/smithy/rust/codegen/core/smithy/customizations/SmithyTypesPubUseGeneratorTest.kt +++ b/codegen-core/src/test/kotlin/software/amazon/smithy/rust/codegen/core/smithy/customizations/SmithyTypesPubUseExtraTest.kt @@ -8,10 +8,11 @@ package software.amazon.smithy.rust.codegen.core.smithy.customizations import org.junit.jupiter.api.Test import software.amazon.smithy.model.Model 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.smithy.generators.StructureGeneratorTest.Companion.model import software.amazon.smithy.rust.codegen.core.testutil.asSmithyModel +import software.amazon.smithy.rust.codegen.core.testutil.testCodegenContext -class SmithyTypesPubUseGeneratorTest { +class SmithyTypesPubUseExtraTest { private fun modelWithMember( inputMember: String = "", outputMember: String = "", @@ -48,7 +49,7 @@ class SmithyTypesPubUseGeneratorTest { outputMember: String = "", unionMember: String = "", additionalShape: String = "", - ) = pubUseTypes(TestRuntimeConfig, modelWithMember(inputMember, outputMember, unionMember, additionalShape)) + ) = pubUseTypes(testCodegenContext(model), modelWithMember(inputMember, outputMember, unionMember, additionalShape)) private fun assertDoesntHaveTypes(types: List, expectedTypes: List) = expectedTypes.forEach { assertDoesntHaveType(types, it) } @@ -71,11 +72,6 @@ class SmithyTypesPubUseGeneratorTest { } } - @Test - fun `it always re-exports SdkError`() { - assertHasType(typesWithEmptyModel(), "aws_smithy_http::result::SdkError") - } - @Test fun `it re-exports Blob when a model uses blobs`() { assertDoesntHaveType(typesWithEmptyModel(), "aws_smithy_types::Blob") 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 3d5c75384c..ec107f3fcb 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 @@ -7,18 +7,17 @@ package software.amazon.smithy.rust.codegen.core.smithy.generators import org.junit.jupiter.api.Test import software.amazon.smithy.codegen.core.Symbol -import software.amazon.smithy.model.shapes.MemberShape import software.amazon.smithy.model.shapes.Shape -import software.amazon.smithy.model.traits.EnumDefinition -import software.amazon.smithy.rust.codegen.core.rustlang.RustWriter +import software.amazon.smithy.rust.codegen.core.rustlang.Attribute.Companion.AllowDeprecated +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.smithy.Default -import software.amazon.smithy.rust.codegen.core.smithy.MaybeRenamed -import software.amazon.smithy.rust.codegen.core.smithy.RustSymbolProvider -import software.amazon.smithy.rust.codegen.core.smithy.SymbolVisitorConfig +import software.amazon.smithy.rust.codegen.core.smithy.WrappingSymbolProvider import software.amazon.smithy.rust.codegen.core.smithy.setDefault +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.testSymbolProvider +import software.amazon.smithy.rust.codegen.core.testutil.unitTest internal class BuilderGeneratorTest { private val model = StructureGeneratorTest.model @@ -30,114 +29,112 @@ internal class BuilderGeneratorTest { @Test fun `generate builders`() { val provider = testSymbolProvider(model) - val writer = RustWriter.forModule("model") - writer.rust("##![allow(deprecated)]") - val innerGenerator = StructureGenerator(model, provider, writer, inner) - val generator = StructureGenerator(model, provider, writer, struct) - val builderGenerator = BuilderGenerator(model, provider, struct) - generator.render() - innerGenerator.render() - builderGenerator.render(writer) - writer.implBlock(struct, provider) { - builderGenerator.renderConvenienceMethod(this) + val project = TestWorkspace.testProject(provider) + project.moduleFor(inner) { + rust("##![allow(deprecated)]") + StructureGenerator(model, provider, this, inner, emptyList()).render() + StructureGenerator(model, provider, this, struct, emptyList()).render() + implBlock(provider.toSymbol(struct)) { + BuilderGenerator.renderConvenienceMethod(this, provider, struct) + } + unitTest("generate_builders") { + rust( + """ + let my_struct = MyStruct::builder().byte_value(4).foo("hello!").build(); + assert_eq!(my_struct.foo.unwrap(), "hello!"); + assert_eq!(my_struct.bar, 0); + """, + ) + } } - writer.compileAndTest( - """ - let my_struct = MyStruct::builder().byte_value(4).foo("hello!").build(); - assert_eq!(my_struct.foo.unwrap(), "hello!"); - assert_eq!(my_struct.bar, 0); - """, - ) + project.withModule(provider.moduleForBuilder(struct)) { + BuilderGenerator(model, provider, struct, emptyList()).render(this) + } + project.compileAndTest() } @Test fun `generate fallible builders`() { - val baseProvider: RustSymbolProvider = testSymbolProvider(StructureGeneratorTest.model) - val provider = - object : RustSymbolProvider { - override fun config(): SymbolVisitorConfig { - return baseProvider.config() - } - - override fun toEnumVariantName(definition: EnumDefinition): MaybeRenamed? { - return baseProvider.toEnumVariantName(definition) - } - - override fun toSymbol(shape: Shape?): Symbol { - return baseProvider.toSymbol(shape).toBuilder().setDefault(Default.NoDefault).build() - } + val baseProvider = testSymbolProvider(StructureGeneratorTest.model) + val provider = object : WrappingSymbolProvider(baseProvider) { + override fun toSymbol(shape: Shape): Symbol { + return baseProvider.toSymbol(shape).toBuilder().setDefault(Default.NoDefault).build() + } + } + val project = TestWorkspace.testProject(provider) - override fun toMemberName(shape: MemberShape?): String { - return baseProvider.toMemberName(shape) - } + project.moduleFor(StructureGeneratorTest.struct) { + AllowDeprecated.render(this) + StructureGenerator(model, provider, this, inner, emptyList()).render() + StructureGenerator(model, provider, this, struct, emptyList()).render() + implBlock(provider.toSymbol(struct)) { + BuilderGenerator.renderConvenienceMethod(this, provider, struct) + } + unitTest("generate_fallible_builders") { + rust( + """ + let my_struct = MyStruct::builder().byte_value(4).foo("hello!").bar(0).build().expect("required field was not provided"); + assert_eq!(my_struct.foo.unwrap(), "hello!"); + assert_eq!(my_struct.bar, 0); + """, + ) } - val writer = RustWriter.forModule("model") - writer.rust("##![allow(deprecated)]") - val innerGenerator = StructureGenerator( - StructureGeneratorTest.model, provider, writer, - StructureGeneratorTest.inner, - ) - val generator = StructureGenerator( - StructureGeneratorTest.model, provider, writer, - StructureGeneratorTest.struct, - ) - generator.render() - innerGenerator.render() - val builderGenerator = BuilderGenerator(model, provider, struct) - builderGenerator.render(writer) - writer.implBlock(struct, provider) { - builderGenerator.renderConvenienceMethod(this) } - writer.compileAndTest( - """ - let my_struct = MyStruct::builder().byte_value(4).foo("hello!").bar(0).build().expect("required field was not provided"); - assert_eq!(my_struct.foo.unwrap(), "hello!"); - assert_eq!(my_struct.bar, 0); - """, - ) + project.withModule(provider.moduleForBuilder(struct)) { + BuilderGenerator(model, provider, struct, emptyList()).render(this) + } + project.compileAndTest() } @Test fun `builder for a struct with sensitive fields should implement the debug trait as such`() { val provider = testSymbolProvider(model) - val writer = RustWriter.forModule("model") - val credsGenerator = StructureGenerator(model, provider, writer, credentials) - val builderGenerator = BuilderGenerator(model, provider, credentials) - credsGenerator.render() - builderGenerator.render(writer) - writer.implBlock(credentials, provider) { - builderGenerator.renderConvenienceMethod(this) + val project = TestWorkspace.testProject(provider) + project.moduleFor(credentials) { + StructureGenerator(model, provider, this, credentials, emptyList()).render() + implBlock(provider.toSymbol(credentials)) { + BuilderGenerator.renderConvenienceMethod(this, provider, credentials) + } + unitTest("sensitive_fields") { + rust( + """ + let builder = Credentials::builder() + .username("admin") + .password("pswd") + .secret_key("12345"); + assert_eq!(format!("{:?}", builder), "Builder { username: Some(\"admin\"), password: \"*** Sensitive Data Redacted ***\", secret_key: \"*** Sensitive Data Redacted ***\" }"); + """, + ) + } + } + project.withModule(provider.moduleForBuilder(credentials)) { + BuilderGenerator(model, provider, credentials, emptyList()).render(this) } - writer.compileAndTest( - """ - use super::*; - let builder = Credentials::builder() - .username("admin") - .password("pswd") - .secret_key("12345"); - assert_eq!(format!("{:?}", builder), "Builder { username: Some(\"admin\"), password: \"*** Sensitive Data Redacted ***\", secret_key: \"*** Sensitive Data Redacted ***\" }"); - """, - ) + project.compileAndTest() } @Test fun `builder for a sensitive struct should implement the debug trait as such`() { val provider = testSymbolProvider(model) - val writer = RustWriter.forModule("model") - val structGenerator = StructureGenerator(model, provider, writer, secretStructure) - val builderGenerator = BuilderGenerator(model, provider, secretStructure) - structGenerator.render() - builderGenerator.render(writer) - writer.implBlock(secretStructure, provider) { - builderGenerator.renderConvenienceMethod(this) + val project = TestWorkspace.testProject(provider) + project.moduleFor(secretStructure) { + StructureGenerator(model, provider, this, secretStructure, emptyList()).render() + implBlock(provider.toSymbol(secretStructure)) { + BuilderGenerator.renderConvenienceMethod(this, provider, secretStructure) + } + unitTest("sensitive_struct") { + rust( + """ + let builder = SecretStructure::builder() + .secret_field("secret"); + assert_eq!(format!("{:?}", builder), "Builder { secret_field: \"*** Sensitive Data Redacted ***\" }"); + """, + ) + } + } + project.withModule(provider.moduleForBuilder(secretStructure)) { + BuilderGenerator(model, provider, secretStructure, emptyList()).render(this) } - writer.compileAndTest( - """ - use super::*; - let builder = SecretStructure::builder() - .secret_field("secret"); - assert_eq!(format!("{:?}", builder), "Builder { secret_field: \"*** Sensitive Data Redacted ***\" }"); - """, - ) + project.compileAndTest() } } diff --git a/codegen-core/src/test/kotlin/software/amazon/smithy/rust/codegen/core/smithy/generators/EnumGeneratorTest.kt b/codegen-core/src/test/kotlin/software/amazon/smithy/rust/codegen/core/smithy/generators/EnumGeneratorTest.kt index a36fbf0b08..b233a763bd 100644 --- a/codegen-core/src/test/kotlin/software/amazon/smithy/rust/codegen/core/smithy/generators/EnumGeneratorTest.kt +++ b/codegen-core/src/test/kotlin/software/amazon/smithy/rust/codegen/core/smithy/generators/EnumGeneratorTest.kt @@ -7,14 +7,19 @@ package software.amazon.smithy.rust.codegen.core.smithy.generators import io.kotest.matchers.shouldBe import io.kotest.matchers.string.shouldContain +import io.kotest.matchers.string.shouldNotContain import org.junit.jupiter.api.Nested import org.junit.jupiter.api.Test import software.amazon.smithy.model.Model import software.amazon.smithy.model.shapes.StringShape import software.amazon.smithy.model.traits.EnumTrait -import software.amazon.smithy.rust.codegen.core.rustlang.RustModule +import software.amazon.smithy.rust.codegen.core.rustlang.Attribute.Companion.AllowDeprecated +import software.amazon.smithy.rust.codegen.core.rustlang.RustReservedWordConfig 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 +import software.amazon.smithy.rust.codegen.core.smithy.RustSymbolProvider 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 @@ -26,6 +31,12 @@ import software.amazon.smithy.rust.codegen.core.util.lookup import software.amazon.smithy.rust.codegen.core.util.orNull class EnumGeneratorTest { + private val rustReservedWordConfig = RustReservedWordConfig( + enumMemberMap = mapOf("Unknown" to "UnknownValue"), + structureMemberMap = emptyMap(), + unionMemberMap = emptyMap(), + ) + @Nested inner class EnumMemberModelTests { private val testModel = """ @@ -43,12 +54,15 @@ class EnumGeneratorTest { ]) string EnumWithUnknown """.asSmithyModel() - private val symbolProvider = testSymbolProvider(testModel) + private val symbolProvider = testSymbolProvider(testModel, rustReservedWordConfig = rustReservedWordConfig) private val enumTrait = testModel.lookup("test#EnumWithUnknown").expectTrait() - private fun model(name: String): EnumMemberModel = - EnumMemberModel(enumTrait.values.first { it.name.orNull() == name }, symbolProvider) + private fun model(name: String): EnumMemberModel = EnumMemberModel( + testModel.lookup("test#EnumWithUnknown"), + enumTrait.values.first { it.name.orNull() == name }, + symbolProvider, + ) @Test fun `it converts enum names to PascalCase and renames any named Unknown to UnknownValue`() { @@ -89,6 +103,15 @@ class EnumGeneratorTest { @Nested inner class EnumGeneratorTests { + fun RustWriter.renderEnum( + model: Model, + provider: RustSymbolProvider, + shape: StringShape, + enumType: EnumType = TestEnumType, + ) { + EnumGenerator(model, provider, shape, enumType).render(this) + } + @Test fun `it generates named enums`() { val model = """ @@ -113,30 +136,26 @@ class EnumGeneratorTest { """.asSmithyModel() val shape = model.lookup("test#InstanceType") - val trait = shape.expectTrait() val provider = testSymbolProvider(model) val project = TestWorkspace.testProject(provider) - project.withModule(RustModule.Model) { + project.moduleFor(shape) { rust("##![allow(deprecated)]") - val generator = EnumGenerator(model, provider, this, shape, trait) - generator.render() + renderEnum(model, provider, shape) unitTest( "it_generates_named_enums", """ let instance = InstanceType::T2Micro; assert_eq!(instance.as_str(), "t2.micro"); assert_eq!(InstanceType::from("t2.nano"), InstanceType::T2Nano); - assert_eq!(InstanceType::from("other"), InstanceType::Unknown(crate::types::UnknownVariantValue("other".to_owned()))); - // round trip unknown variants: - assert_eq!(InstanceType::from("other").as_str(), "other"); """, ) - val output = toString() - output.shouldContain("#[non_exhaustive]") - // on enum variant `T2Micro` - output.shouldContain("#[deprecated]") - // on enum itself - output.shouldContain("#[deprecated(since = \"1.2.3\")]") + toString().also { output -> + output.shouldContain("#[non_exhaustive]") + // on enum variant `T2Micro` + output.shouldContain("#[deprecated]") + // on enum itself + output.shouldContain("#[deprecated(since = \"1.2.3\")]") + } } project.compileAndTest() } @@ -158,12 +177,10 @@ class EnumGeneratorTest { """.asSmithyModel() val shape = model.lookup("test#FooEnum") - val trait = shape.expectTrait() val provider = testSymbolProvider(model) val project = TestWorkspace.testProject(provider) - project.withModule(RustModule.Model) { - val generator = EnumGenerator(model, provider, this, shape, trait) - generator.render() + project.moduleFor(shape) { + renderEnum(model, provider, shape) unitTest( "named_enums_implement_eq_and_hash", """ @@ -193,13 +210,11 @@ class EnumGeneratorTest { """.asSmithyModel() val shape = model.lookup("test#FooEnum") - val trait = shape.expectTrait() val provider = testSymbolProvider(model) val project = TestWorkspace.testProject(provider) - project.withModule(RustModule.Model) { - rust("##![allow(deprecated)]") - val generator = EnumGenerator(model, provider, this, shape, trait) - generator.render() + project.moduleFor(shape) { + AllowDeprecated.render(this) + renderEnum(model, provider, shape) unitTest( "unnamed_enums_implement_eq_and_hash", """ @@ -238,13 +253,11 @@ class EnumGeneratorTest { """.asSmithyModel() val shape = model.lookup("test#FooEnum") - val trait = shape.expectTrait() val provider = testSymbolProvider(model) val project = TestWorkspace.testProject(provider) - project.withModule(RustModule.Model) { - rust("##![allow(deprecated)]") - val generator = EnumGenerator(model, provider, this, shape, trait) - generator.render() + project.moduleFor(shape) { + AllowDeprecated.render(this) + renderEnum(model, provider, shape) unitTest( "it_generates_unnamed_enums", """ @@ -257,304 +270,256 @@ class EnumGeneratorTest { } @Test - fun `it escapes the Unknown variant if the enum has an unknown value in the model`() { + fun `it should generate documentation for enums`() { val model = """ namespace test + + /// Some top-level documentation. @enum([ { name: "Known", value: "Known" }, { name: "Unknown", value: "Unknown" }, - { name: "UnknownValue", value: "UnknownValue" }, ]) string SomeEnum """.asSmithyModel() val shape = model.lookup("test#SomeEnum") - val trait = shape.expectTrait() - val provider = testSymbolProvider(model) + val provider = testSymbolProvider(model, rustReservedWordConfig = rustReservedWordConfig) val project = TestWorkspace.testProject(provider) - project.withModule(RustModule.Model) { - val generator = EnumGenerator(model, provider, this, shape, trait) - generator.render() - unitTest( - "it_escapes_the_unknown_variant_if_the_enum_has_an_unknown_value_in_the_model", + project.moduleFor(shape) { + renderEnum(model, provider, shape) + val rendered = toString() + rendered shouldContain """ - assert_eq!(SomeEnum::from("Unknown"), SomeEnum::UnknownValue); - assert_eq!(SomeEnum::from("UnknownValue"), SomeEnum::UnknownValue_); - assert_eq!(SomeEnum::from("SomethingNew"), SomeEnum::Unknown(crate::types::UnknownVariantValue("SomethingNew".to_owned()))); - """.trimIndent(), - ) + /// Some top-level documentation. + /// + /// _Note: `SomeEnum::Unknown` has been renamed to `::UnknownValue`._ + """.trimIndent() } project.compileAndTest() } @Test - fun `it should generate documentation for enums`() { + fun `it should generate documentation for unnamed enums`() { val model = """ namespace test /// Some top-level documentation. @enum([ - { name: "Known", value: "Known" }, - { name: "Unknown", value: "Unknown" }, + { value: "One" }, + { value: "Two" }, ]) string SomeEnum """.asSmithyModel() val shape = model.lookup("test#SomeEnum") - val trait = shape.expectTrait() val provider = testSymbolProvider(model) val project = TestWorkspace.testProject(provider) - project.withModule(RustModule.Model) { - val generator = EnumGenerator(model, provider, this, shape, trait) - generator.render() + project.moduleFor(shape) { + renderEnum(model, provider, shape) val rendered = toString() rendered shouldContain """ /// Some top-level documentation. - /// - /// _Note: `SomeEnum::Unknown` has been renamed to `::UnknownValue`._ """.trimIndent() } project.compileAndTest() } @Test - fun `it should generate documentation for unnamed enums`() { + fun `it handles variants that clash with Rust reserved words`() { val model = """ namespace test + @enum([ + { name: "Known", value: "Known" }, + { name: "Self", value: "other" }, + ]) + string SomeEnum + """.asSmithyModel() - /// Some top-level documentation. + val shape = model.lookup("test#SomeEnum") + val provider = testSymbolProvider(model) + val project = TestWorkspace.testProject(provider) + project.moduleFor(shape) { + renderEnum(model, provider, shape) + unitTest( + "it_handles_variants_that_clash_with_rust_reserved_words", + """assert_eq!(SomeEnum::from("other"), SomeEnum::SelfValue);""", + ) + } + project.compileAndTest() + } + + @Test + fun `impl debug for non-sensitive enum should implement the derived debug trait`() { + val model = """ + namespace test @enum([ - { value: "One" }, - { value: "Two" }, + { name: "Foo", value: "Foo" }, + { name: "Bar", value: "Bar" }, ]) string SomeEnum """.asSmithyModel() val shape = model.lookup("test#SomeEnum") - val trait = shape.expectTrait() val provider = testSymbolProvider(model) val project = TestWorkspace.testProject(provider) - project.withModule(RustModule.Model) { - val generator = EnumGenerator(model, provider, this, shape, trait) - generator.render() - val rendered = toString() - rendered shouldContain + project.moduleFor(shape) { + renderEnum(model, provider, shape) + unitTest( + "impl_debug_for_non_sensitive_enum_should_implement_the_derived_debug_trait", """ - /// Some top-level documentation. - """.trimIndent() + assert_eq!(format!("{:?}", SomeEnum::Foo), "Foo"); + assert_eq!(format!("{:?}", SomeEnum::Bar), "Bar"); + """, + ) } project.compileAndTest() } - } - @Test - fun `it handles variants that clash with Rust reserved words`() { - val model = """ - namespace test - @enum([ - { name: "Known", value: "Known" }, - { name: "Self", value: "other" }, - ]) - string SomeEnum - """.asSmithyModel() + @Test + fun `impl debug for sensitive enum should redact text`() { + val model = """ + namespace test + @sensitive + @enum([ + { name: "Foo", value: "Foo" }, + { name: "Bar", value: "Bar" }, + ]) + string SomeEnum + """.asSmithyModel() - val shape = model.lookup("test#SomeEnum") - val trait = shape.expectTrait() - val provider = testSymbolProvider(model) - val project = TestWorkspace.testProject(provider) - project.withModule(RustModule.Model) { - val generator = EnumGenerator(model, provider, this, shape, trait) - generator.render() - unitTest( - "it_handles_variants_that_clash_with_rust_reserved_words", - """ - assert_eq!(SomeEnum::from("other"), SomeEnum::SelfValue); - assert_eq!(SomeEnum::from("SomethingNew"), SomeEnum::Unknown(crate::types::UnknownVariantValue("SomethingNew".to_owned()))); - """.trimIndent(), - ) + val shape = model.lookup("test#SomeEnum") + val provider = testSymbolProvider(model) + val project = TestWorkspace.testProject(provider) + project.moduleFor(shape) { + renderEnum(model, provider, shape) + unitTest( + "impl_debug_for_sensitive_enum_should_redact_text", + """ + assert_eq!(format!("{:?}", SomeEnum::Foo), $REDACTION); + assert_eq!(format!("{:?}", SomeEnum::Bar), $REDACTION); + """, + ) + } + project.compileAndTest() } - project.compileAndTest() - } - @Test - fun `matching on enum should be forward-compatible`() { - fun expectMatchExpressionCompiles(model: Model, shapeId: String, enumToMatchOn: String) { - val shape = model.lookup(shapeId) - val trait = shape.expectTrait() + @Test + fun `impl debug for non-sensitive unnamed enum should implement the derived debug trait`() { + val model = """ + namespace test + @enum([ + { value: "Foo" }, + { value: "Bar" }, + ]) + string SomeEnum + """.asSmithyModel() + + val shape = model.lookup("test#SomeEnum") val provider = testSymbolProvider(model) val project = TestWorkspace.testProject(provider) - project.withModule(RustModule.Model) { - val generator = EnumGenerator(model, provider, this, shape, trait) - generator.render() + project.moduleFor(shape) { + renderEnum(model, provider, shape) unitTest( - "matching_on_enum_should_be_forward_compatible", + "impl_debug_for_non_sensitive_unnamed_enum_should_implement_the_derived_debug_trait", """ - match $enumToMatchOn { - SomeEnum::Variant1 => assert!(false, "expected `Variant3` but got `Variant1`"), - SomeEnum::Variant2 => assert!(false, "expected `Variant3` but got `Variant2`"), - other @ _ if other.as_str() == "Variant3" => assert!(true), - _ => assert!(false, "expected `Variant3` but got `_`"), + for variant in SomeEnum::values() { + assert_eq!( + format!("{:?}", SomeEnum(variant.to_string())), + format!("SomeEnum(\"{}\")", variant.to_owned()) + ); } - """.trimIndent(), + """, ) } project.compileAndTest() } - val modelV1 = """ - namespace test - - @enum([ - { name: "Variant1", value: "Variant1" }, - { name: "Variant2", value: "Variant2" }, - ]) - string SomeEnum - """.asSmithyModel() - val variant3AsUnknown = """SomeEnum::from("Variant3")""" - expectMatchExpressionCompiles(modelV1, "test#SomeEnum", variant3AsUnknown) - - val modelV2 = """ - namespace test - - @enum([ - { name: "Variant1", value: "Variant1" }, - { name: "Variant2", value: "Variant2" }, - { name: "Variant3", value: "Variant3" }, - ]) - string SomeEnum - """.asSmithyModel() - val variant3AsVariant3 = "SomeEnum::Variant3" - expectMatchExpressionCompiles(modelV2, "test#SomeEnum", variant3AsVariant3) - } - - @Test - fun `impl debug for non-sensitive enum should implement the derived debug trait`() { - val model = """ - namespace test - @enum([ - { name: "Foo", value: "Foo" }, - { name: "Bar", value: "Bar" }, - ]) - string SomeEnum - """.asSmithyModel() + @Test + fun `impl debug for sensitive unnamed enum should redact text`() { + val model = """ + namespace test + @sensitive + @enum([ + { value: "Foo" }, + { value: "Bar" }, + ]) + string SomeEnum + """.asSmithyModel() - val shape = model.lookup("test#SomeEnum") - val trait = shape.expectTrait() - val provider = testSymbolProvider(model) - val project = TestWorkspace.testProject(provider) - project.withModule(RustModule.Model) { - val generator = EnumGenerator(model, provider, this, shape, trait) - generator.render() - unitTest( - "impl_debug_for_non_sensitive_enum_should_implement_the_derived_debug_trait", - """ - assert_eq!(format!("{:?}", SomeEnum::Foo), "Foo"); - assert_eq!(format!("{:?}", SomeEnum::Bar), "Bar"); - assert_eq!( - format!("{:?}", SomeEnum::from("Baz")), - "Unknown(UnknownVariantValue(\"Baz\"))" - ); - """, - ) + val shape = model.lookup("test#SomeEnum") + val provider = testSymbolProvider(model) + val project = TestWorkspace.testProject(provider) + project.moduleFor(shape) { + renderEnum(model, provider, shape) + unitTest( + "impl_debug_for_sensitive_unnamed_enum_should_redact_text", + """ + for variant in SomeEnum::values() { + assert_eq!( + format!("{:?}", SomeEnum(variant.to_string())), + $REDACTION + ); + } + """, + ) + } + project.compileAndTest() } - project.compileAndTest() - } - - @Test - fun `impl debug for sensitive enum should redact text`() { - val model = """ - namespace test - @sensitive - @enum([ - { name: "Foo", value: "Foo" }, - { name: "Bar", value: "Bar" }, - ]) - string SomeEnum - """.asSmithyModel() - val shape = model.lookup("test#SomeEnum") - val trait = shape.expectTrait() - val provider = testSymbolProvider(model) - val project = TestWorkspace.testProject(provider) - project.withModule(RustModule.Model) { - val generator = EnumGenerator(model, provider, this, shape, trait) - generator.render() - unitTest( - "impl_debug_for_sensitive_enum_should_redact_text", - """ - assert_eq!(format!("{:?}", SomeEnum::Foo), $REDACTION); - assert_eq!(format!("{:?}", SomeEnum::Bar), $REDACTION); - """, - ) - } - project.compileAndTest() - } + @Test + fun `it supports other enum types`() { + class CustomizingEnumType : EnumType() { + override fun implFromForStr(context: EnumGeneratorContext): Writable = writable { + // intentional no-op + } - @Test - fun `impl debug for non-sensitive unnamed enum should implement the derived debug trait`() { - val model = """ - namespace test - @enum([ - { value: "Foo" }, - { value: "Bar" }, - ]) - string SomeEnum - """.asSmithyModel() + override fun implFromStr(context: EnumGeneratorContext): Writable = writable { + // intentional no-op + } - val shape = model.lookup("test#SomeEnum") - val trait = shape.expectTrait() - val provider = testSymbolProvider(model) - val project = TestWorkspace.testProject(provider) - project.withModule(RustModule.Model) { - val generator = EnumGenerator(model, provider, this, shape, trait) - generator.render() - unitTest( - "impl_debug_for_non_sensitive_unnamed_enum_should_implement_the_derived_debug_trait", - """ - for variant in SomeEnum::values() { - assert_eq!( - format!("{:?}", SomeEnum(variant.to_string())), - format!("SomeEnum(\"{}\")", variant.to_owned()) - ); + override fun additionalEnumMembers(context: EnumGeneratorContext): Writable = writable { + rust("// additional enum members") } - """, - ) - } - project.compileAndTest() - } - @Test - fun `impl debug for sensitive unnamed enum should redact text`() { - val model = """ - namespace test - @sensitive - @enum([ - { value: "Foo" }, - { value: "Bar" }, - ]) - string SomeEnum - """.asSmithyModel() + override fun additionalAsStrMatchArms(context: EnumGeneratorContext): Writable = writable { + rust("// additional as_str match arm") + } - val shape = model.lookup("test#SomeEnum") - val trait = shape.expectTrait() - val provider = testSymbolProvider(model) - val project = TestWorkspace.testProject(provider) - project.withModule(RustModule.Model) { - val generator = EnumGenerator(model, provider, this, shape, trait) - generator.render() - unitTest( - "impl_debug_for_sensitive_unnamed_enum_should_redact_text", - """ - for variant in SomeEnum::values() { - assert_eq!( - format!("{:?}", SomeEnum(variant.to_string())), - $REDACTION - ); + override fun additionalDocs(context: EnumGeneratorContext): Writable = writable { + rust("// additional docs") } - """, - ) + } + + val model = """ + namespace test + @enum([ + { name: "Known", value: "Known" }, + { name: "Self", value: "other" }, + ]) + string SomeEnum + """.asSmithyModel() + val shape = model.lookup("test#SomeEnum") + + val provider = testSymbolProvider(model) + val output = RustWriter.root().apply { + renderEnum(model, provider, shape, CustomizingEnumType()) + }.toString() + + // Since we didn't use the Infallible EnumType, there should be no Unknown variant + output shouldNotContain "Unknown" + output shouldNotContain "unknown" + output shouldNotContain "impl From" + output shouldNotContain "impl FromStr" + output shouldContain "// additional enum members" + output shouldContain "// additional as_str match arm" + output shouldContain "// additional docs" + + val project = TestWorkspace.testProject(provider) + project.moduleFor(shape) { + renderEnum(model, provider, shape, CustomizingEnumType()) + } + project.compileAndTest() } - project.compileAndTest() } } diff --git a/codegen-core/src/test/kotlin/software/amazon/smithy/rust/codegen/core/smithy/generators/InstantiatorTest.kt b/codegen-core/src/test/kotlin/software/amazon/smithy/rust/codegen/core/smithy/generators/InstantiatorTest.kt index a0f8200c50..5232fb1df2 100644 --- a/codegen-core/src/test/kotlin/software/amazon/smithy/rust/codegen/core/smithy/generators/InstantiatorTest.kt +++ b/codegen-core/src/test/kotlin/software/amazon/smithy/rust/codegen/core/smithy/generators/InstantiatorTest.kt @@ -8,23 +8,27 @@ package software.amazon.smithy.rust.codegen.core.smithy.generators import org.junit.jupiter.api.Test import software.amazon.smithy.codegen.core.Symbol import software.amazon.smithy.model.node.Node +import software.amazon.smithy.model.node.NumberNode import software.amazon.smithy.model.node.StringNode import software.amazon.smithy.model.shapes.BlobShape import software.amazon.smithy.model.shapes.MemberShape import software.amazon.smithy.model.shapes.ShapeId 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.RustModule 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.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.transformers.RecursiveShapeBoxer 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.renderWithModelBuilder import software.amazon.smithy.rust.codegen.core.testutil.testCodegenContext +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.dq import software.amazon.smithy.rust.codegen.core.util.lookup @@ -82,7 +86,7 @@ class InstantiatorTest { @required num: Integer } - """.asSmithyModel().let { RecursiveShapeBoxer.transform(it) } + """.asSmithyModel().let { RecursiveShapeBoxer().transform(it) } private val codegenContext = testCodegenContext(model) private val symbolProvider = codegenContext.symbolProvider @@ -108,8 +112,8 @@ class InstantiatorTest { Instantiator(symbolProvider, model, runtimeConfig, BuilderKindBehavior(codegenContext), ::enumFromStringFn) val data = Node.parse("""{ "stringVariant": "ok!" }""") - val project = TestWorkspace.testProject() - project.withModule(RustModule.Model) { + val project = TestWorkspace.testProject(model) + project.moduleFor(union) { UnionGenerator(model, symbolProvider, this, union).render() unitTest("generate_unions") { withBlock("let result = ", ";") { @@ -128,9 +132,9 @@ class InstantiatorTest { Instantiator(symbolProvider, model, runtimeConfig, BuilderKindBehavior(codegenContext), ::enumFromStringFn) val data = Node.parse("""{ "bar": 10, "foo": "hello" }""") - val project = TestWorkspace.testProject() - project.withModule(RustModule.Model) { - structure.renderWithModelBuilder(model, symbolProvider, this) + val project = TestWorkspace.testProject(model) + structure.renderWithModelBuilder(model, symbolProvider, project) + project.moduleFor(structure) { unitTest("generate_struct_builders") { withBlock("let result = ", ";") { sut.render(this, structure, data) @@ -162,9 +166,9 @@ class InstantiatorTest { """, ) - val project = TestWorkspace.testProject() - project.withModule(RustModule.Model) { - structure.renderWithModelBuilder(model, symbolProvider, this) + val project = TestWorkspace.testProject(model) + structure.renderWithModelBuilder(model, symbolProvider, project) + project.moduleFor(structure) { unitTest("generate_builders_for_boxed_structs") { withBlock("let result = ", ";") { sut.render(this, structure, data) @@ -192,7 +196,7 @@ class InstantiatorTest { Instantiator(symbolProvider, model, runtimeConfig, BuilderKindBehavior(codegenContext), ::enumFromStringFn) val project = TestWorkspace.testProject() - project.withModule(RustModule.Model) { + project.lib { unitTest("generate_lists") { withBlock("let result = ", ";") { sut.render(this, model.lookup("com.test#MyList"), data) @@ -213,8 +217,8 @@ class InstantiatorTest { ::enumFromStringFn, ) - val project = TestWorkspace.testProject() - project.withModule(RustModule.Model) { + val project = TestWorkspace.testProject(model) + project.lib { unitTest("generate_sparse_lists") { withBlock("let result = ", ";") { sut.render(this, model.lookup("com.test#MySparseList"), data) @@ -245,9 +249,9 @@ class InstantiatorTest { ) val inner = model.lookup("com.test#Inner") - val project = TestWorkspace.testProject() - project.withModule(RustModule.Model) { - inner.renderWithModelBuilder(model, symbolProvider, this) + val project = TestWorkspace.testProject(model) + inner.renderWithModelBuilder(model, symbolProvider, project) + project.moduleFor(inner) { unitTest("generate_maps_of_maps") { withBlock("let result = ", ";") { sut.render(this, model.lookup("com.test#NestedMap"), data) @@ -277,8 +281,8 @@ class InstantiatorTest { ::enumFromStringFn, ) - val project = TestWorkspace.testProject() - project.withModule(RustModule.Model) { + val project = TestWorkspace.testProject(model) + project.testModule { unitTest("blob_inputs_are_binary_data") { withBlock("let blob = ", ";") { sut.render( @@ -292,4 +296,45 @@ class InstantiatorTest { } project.compileAndTest() } + + @Test + fun `integer and fractional timestamps`() { + // "Parameter values that contain binary data MUST be defined using values + // that can be represented in plain text (for example, use "foo" and not "Zm9vCg==")." + val sut = Instantiator( + symbolProvider, + model, + runtimeConfig, + BuilderKindBehavior(codegenContext), + ::enumFromStringFn, + ) + val project = TestWorkspace.testProject(model) + project.testModule { + unitTest("timestamps") { + withBlock("let ts_frac = ", ";") { + sut.render( + this, + TimestampShape.builder().id(ShapeId.from("com.example#Timestamp")).build(), + NumberNode.from(946845296.123), + ) + } + withBlock("let ts_int = ", ";") { + sut.render( + this, + TimestampShape.builder().id(ShapeId.from("com.example#Timestamp")).build(), + NumberNode.from(123), + ) + } + rustTemplate( + "assert_eq!(ts_frac, #{Timestamp}::from_millis(946845296*1000 + 123));", + "Timestamp" to RuntimeType.dateTime(runtimeConfig), + ) + rustTemplate( + "assert_eq!(ts_int, #{Timestamp}::from_secs(123));", + "Timestamp" to RuntimeType.dateTime(runtimeConfig), + ) + } + } + project.compileAndTest() + } } 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 37d73291ef..f765587300 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 @@ -12,10 +12,10 @@ import org.junit.jupiter.api.Test import software.amazon.smithy.model.shapes.StructureShape import software.amazon.smithy.rust.codegen.core.rustlang.Attribute import software.amazon.smithy.rust.codegen.core.rustlang.RustModule +import software.amazon.smithy.rust.codegen.core.rustlang.RustReservedWordConfig 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.smithy.ModelsModule import software.amazon.smithy.rust.codegen.core.smithy.transformers.RecursiveShapeBoxer import software.amazon.smithy.rust.codegen.core.testutil.TestWorkspace import software.amazon.smithy.rust.codegen.core.testutil.asSmithyModel @@ -86,15 +86,21 @@ class StructureGeneratorTest { val secretStructure = model.lookup("com.test#SecretStructure") val structWithInnerSecretStructure = model.lookup("com.test#StructWithInnerSecretStructure") val error = model.lookup("com.test#MyError") + + val rustReservedWordConfig: RustReservedWordConfig = RustReservedWordConfig( + structureMemberMap = StructureGenerator.structureMemberNameMap, + enumMemberMap = emptyMap(), + unionMemberMap = emptyMap(), + ) } @Test fun `generate basic structures`() { - val provider = testSymbolProvider(model) + val provider = testSymbolProvider(model, rustReservedWordConfig = rustReservedWordConfig) val project = TestWorkspace.testProject(provider) project.useShapeWriter(inner) { - StructureGenerator(model, provider, this, inner).render() - StructureGenerator(model, provider, this, struct).render() + StructureGenerator(model, provider, this, inner, emptyList()).render() + StructureGenerator(model, provider, this, struct, emptyList()).render() unitTest( "struct_fields_optional", """ @@ -111,16 +117,16 @@ class StructureGeneratorTest { @Test fun `generate structures with public fields`() { - val project = TestWorkspace.testProject() - val provider = testSymbolProvider(model) + val provider = testSymbolProvider(model, rustReservedWordConfig = rustReservedWordConfig) + val project = TestWorkspace.testProject(provider) project.lib { Attribute.AllowDeprecated.render(this) } - project.withModule(ModelsModule) { - val innerGenerator = StructureGenerator(model, provider, this, inner) + project.moduleFor(inner) { + val innerGenerator = StructureGenerator(model, provider, this, inner, emptyList()) innerGenerator.render() } project.withModule(RustModule.public("structs")) { - val generator = StructureGenerator(model, provider, this, struct) + val generator = StructureGenerator(model, provider, this, struct, emptyList()) generator.render() } // By putting the test in another module, it can't access the struct @@ -139,65 +145,51 @@ class StructureGeneratorTest { project.compileAndTest() } - @Test - fun `generate error structures`() { - val provider = testSymbolProvider(model) - val writer = RustWriter.forModule("error") - val generator = StructureGenerator(model, provider, writer, error) - generator.render() - writer.compileAndTest( - """ - let err = MyError { message: None }; - assert_eq!(err.retryable_error_kind(), aws_smithy_types::retry::ErrorKind::ServerError); - """, - ) - } - @Test fun `generate a custom debug implementation when the sensitive trait is applied to some members`() { - val provider = testSymbolProvider(model) - val writer = RustWriter.forModule("lib") - val generator = StructureGenerator(model, provider, writer, credentials) - generator.render() - writer.unitTest( - "sensitive_fields_redacted", - """ - let creds = Credentials { - username: Some("not_redacted".to_owned()), - password: Some("don't leak me".to_owned()), - secret_key: Some("don't leak me".to_owned()) - }; - assert_eq!(format!("{:?}", creds), "Credentials { username: Some(\"not_redacted\"), password: \"*** Sensitive Data Redacted ***\", secret_key: \"*** Sensitive Data Redacted ***\" }"); - """, - ) - writer.compileAndTest() + val provider = testSymbolProvider(model, rustReservedWordConfig = rustReservedWordConfig) + TestWorkspace.testProject().unitTest { + StructureGenerator(model, provider, this, credentials, emptyList()).render() + + this.unitTest( + "sensitive_fields_redacted", + """ + let creds = Credentials { + username: Some("not_redacted".to_owned()), + password: Some("don't leak me".to_owned()), + secret_key: Some("don't leak me".to_owned()) + }; + assert_eq!(format!("{:?}", creds), "Credentials { username: Some(\"not_redacted\"), password: \"*** Sensitive Data Redacted ***\", secret_key: \"*** Sensitive Data Redacted ***\" }"); + """, + ) + }.compileAndTest() } @Test fun `generate a custom debug implementation when the sensitive trait is applied to the struct`() { - val provider = testSymbolProvider(model) - val writer = RustWriter.forModule("lib") - val generator = StructureGenerator(model, provider, writer, secretStructure) - generator.render() - writer.unitTest( - "sensitive_structure_redacted", - """ - let secret_structure = SecretStructure { - secret_field: Some("secret".to_owned()), - }; - assert_eq!(format!("{:?}", secret_structure), "SecretStructure { secret_field: \"*** Sensitive Data Redacted ***\" }"); - """, - ) - writer.compileAndTest() + val provider = testSymbolProvider(model, rustReservedWordConfig = rustReservedWordConfig) + TestWorkspace.testProject().unitTest { + StructureGenerator(model, provider, this, secretStructure, emptyList()).render() + + this.unitTest( + "sensitive_structure_redacted", + """ + let secret_structure = SecretStructure { + secret_field: Some("secret".to_owned()), + }; + assert_eq!(format!("{:?}", secret_structure), "SecretStructure { secret_field: \"*** Sensitive Data Redacted ***\" }"); + """, + ) + }.compileAndTest() } @Test fun `generate a custom debug implementation when the sensitive trait is applied to an inner struct`() { - val provider = testSymbolProvider(model) + val provider = testSymbolProvider(model, rustReservedWordConfig = rustReservedWordConfig) val project = TestWorkspace.testProject(provider) project.useShapeWriter(inner) { - val secretGenerator = StructureGenerator(model, provider, this, secretStructure) - val generator = StructureGenerator(model, provider, this, structWithInnerSecretStructure) + val secretGenerator = StructureGenerator(model, provider, this, secretStructure, emptyList()) + val generator = StructureGenerator(model, provider, this, structWithInnerSecretStructure, emptyList()) secretGenerator.render() generator.render() unitTest( @@ -234,14 +226,14 @@ class StructureGeneratorTest { nested2: Inner }""".asSmithyModel() - val provider = testSymbolProvider(model) + val provider = testSymbolProvider(model, rustReservedWordConfig = rustReservedWordConfig) val project = TestWorkspace.testProject(provider) project.lib { Attribute.DenyMissingDocs.render(this) } - project.withModule(ModelsModule) { - StructureGenerator(model, provider, this, model.lookup("com.test#Inner")).render() - StructureGenerator(model, provider, this, model.lookup("com.test#MyStruct")).render() + project.moduleFor(model.lookup("com.test#Inner")) { + StructureGenerator(model, provider, this, model.lookup("com.test#Inner"), emptyList()).render() + StructureGenerator(model, provider, this, model.lookup("com.test#MyStruct"), emptyList()).render() } project.compileAndTest() @@ -249,18 +241,18 @@ class StructureGeneratorTest { @Test fun `documents are optional in structs`() { - val provider = testSymbolProvider(model) - val writer = RustWriter.forModule("lib") - StructureGenerator(model, provider, writer, structWithDoc).render() - - writer.compileAndTest( - """ - let _struct = StructWithDoc { - // This will only compile if the document is optional - doc: None - }; - """, - ) + val provider = testSymbolProvider(model, rustReservedWordConfig = rustReservedWordConfig) + TestWorkspace.testProject().unitTest { + StructureGenerator(model, provider, this, structWithDoc, emptyList()).render() + rust( + """ + let _struct = StructWithDoc { + // This will only compile if the document is optional + doc: None + }; + """.trimIndent(), + ) + }.compileAndTest() } @Test @@ -280,14 +272,14 @@ class StructureGeneratorTest { @deprecated(message: "Fly, you fools!", since: "1.2.3") structure Qux {} """.asSmithyModel() - val provider = testSymbolProvider(model) + val provider = testSymbolProvider(model, rustReservedWordConfig = rustReservedWordConfig) val project = TestWorkspace.testProject(provider) project.lib { rust("##![allow(deprecated)]") } - project.withModule(ModelsModule) { - StructureGenerator(model, provider, this, model.lookup("test#Foo")).render() - StructureGenerator(model, provider, this, model.lookup("test#Bar")).render() - StructureGenerator(model, provider, this, model.lookup("test#Baz")).render() - StructureGenerator(model, provider, this, model.lookup("test#Qux")).render() + project.moduleFor(model.lookup("test#Foo")) { + StructureGenerator(model, provider, this, model.lookup("test#Foo"), emptyList()).render() + StructureGenerator(model, provider, this, model.lookup("test#Bar"), emptyList()).render() + StructureGenerator(model, provider, this, model.lookup("test#Baz"), emptyList()).render() + StructureGenerator(model, provider, this, model.lookup("test#Qux"), emptyList()).render() } // turn on clippy to check the semver-compliant version of `since`. @@ -313,13 +305,13 @@ class StructureGeneratorTest { @deprecated structure Bar {} """.asSmithyModel() - val provider = testSymbolProvider(model) + val provider = testSymbolProvider(model, rustReservedWordConfig = rustReservedWordConfig) val project = TestWorkspace.testProject(provider) project.lib { rust("##![allow(deprecated)]") } - project.withModule(ModelsModule) { - StructureGenerator(model, provider, this, model.lookup("test#Nested")).render() - StructureGenerator(model, provider, this, model.lookup("test#Foo")).render() - StructureGenerator(model, provider, this, model.lookup("test#Bar")).render() + project.moduleFor(model.lookup("test#Nested")) { + StructureGenerator(model, provider, this, model.lookup("test#Nested"), emptyList()).render() + StructureGenerator(model, provider, this, model.lookup("test#Foo"), emptyList()).render() + StructureGenerator(model, provider, this, model.lookup("test#Bar"), emptyList()).render() } project.compileAndTest() @@ -328,7 +320,7 @@ class StructureGeneratorTest { @Test fun `it generates accessor methods`() { val testModel = - RecursiveShapeBoxer.transform( + RecursiveShapeBoxer().transform( """ namespace test @@ -362,14 +354,14 @@ class StructureGeneratorTest { } """.asSmithyModel(), ) - val provider = testSymbolProvider(testModel) + val provider = testSymbolProvider(testModel, rustReservedWordConfig = rustReservedWordConfig) val project = TestWorkspace.testProject(provider) project.useShapeWriter(inner) { - StructureGenerator(testModel, provider, this, testModel.lookup("test#One")).render() - StructureGenerator(testModel, provider, this, testModel.lookup("test#Two")).render() + StructureGenerator(testModel, provider, this, testModel.lookup("test#One"), emptyList()).render() + StructureGenerator(testModel, provider, this, testModel.lookup("test#Two"), emptyList()).render() - rustBlock("fn compile_test_one(one: &crate::model::One)") { + rustBlock("fn compile_test_one(one: &crate::test_model::One)") { rust( """ let _: Option<&str> = one.field_string(); @@ -390,17 +382,17 @@ class StructureGeneratorTest { let _: f32 = one.field_primitive_float(); let _: Option = one.field_double(); let _: f64 = one.field_primitive_double(); - let _: Option<&crate::model::Two> = one.two(); + let _: Option<&crate::test_model::Two> = one.two(); let _: Option = one.build_value(); let _: Option = one.builder_value(); let _: Option = one.default_value(); """, ) } - rustBlock("fn compile_test_two(two: &crate::model::Two)") { + rustBlock("fn compile_test_two(two: &crate::test_model::Two)") { rust( """ - let _: Option<&crate::model::One> = two.one(); + let _: Option<&crate::test_model::One> = two.one(); """, ) } @@ -422,9 +414,9 @@ class StructureGeneratorTest { """.asSmithyModel() val struct = model.lookup("com.test#MyStruct") - val provider = testSymbolProvider(model) + val provider = testSymbolProvider(model, rustReservedWordConfig = rustReservedWordConfig) RustWriter.forModule("test").let { - StructureGenerator(model, provider, it, struct).render() + StructureGenerator(model, provider, it, struct, emptyList()).render() assertEquals(6, it.toString().split("#[doc(hidden)]").size, "there should be 5 doc-hiddens") } } @@ -438,9 +430,9 @@ class StructureGeneratorTest { """.asSmithyModel() val struct = model.lookup("com.test#MyStruct") - val provider = testSymbolProvider(model) + val provider = testSymbolProvider(model, rustReservedWordConfig = rustReservedWordConfig) RustWriter.forModule("test").let { writer -> - StructureGenerator(model, provider, writer, struct).render() + 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/generators/TestEnumType.kt b/codegen-core/src/test/kotlin/software/amazon/smithy/rust/codegen/core/smithy/generators/TestEnumType.kt new file mode 100644 index 0000000000..e8ea12c0db --- /dev/null +++ b/codegen-core/src/test/kotlin/software/amazon/smithy/rust/codegen/core/smithy/generators/TestEnumType.kt @@ -0,0 +1,49 @@ +/* + * 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.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.dq + +object TestEnumType : EnumType() { + override fun implFromForStr(context: EnumGeneratorContext): Writable = writable { + rustTemplate( + """ + impl #{From}<&str> for ${context.enumName} { + fn from(s: &str) -> Self { + match s { + #{matchArms} + } + } + } + """, + "From" to RuntimeType.From, + "matchArms" to writable { + context.sortedMembers.forEach { member -> + rust("${member.value.dq()} => ${context.enumName}::${member.derivedName()},") + } + rust("_ => panic!()") + }, + ) + } + + override fun implFromStr(context: EnumGeneratorContext): Writable = writable { + rust( + """ + 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)) + } + } + """, + ) + } +} diff --git a/codegen-core/src/test/kotlin/software/amazon/smithy/rust/codegen/core/smithy/generators/UnionGeneratorTest.kt b/codegen-core/src/test/kotlin/software/amazon/smithy/rust/codegen/core/smithy/generators/UnionGeneratorTest.kt index 8a66ad112e..8b6778890f 100644 --- a/codegen-core/src/test/kotlin/software/amazon/smithy/rust/codegen/core/smithy/generators/UnionGeneratorTest.kt +++ b/codegen-core/src/test/kotlin/software/amazon/smithy/rust/codegen/core/smithy/generators/UnionGeneratorTest.kt @@ -10,7 +10,6 @@ import org.junit.jupiter.api.Test import software.amazon.smithy.codegen.core.SymbolProvider 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.smithy.ModelsModule 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 @@ -117,7 +116,7 @@ class UnionGeneratorTest { val provider = testSymbolProvider(model) val project = TestWorkspace.testProject(provider) project.lib { rust("##![allow(deprecated)]") } - project.withModule(ModelsModule) { + project.moduleFor(model.lookup("test#Nested")) { UnionGenerator(model, provider, this, model.lookup("test#Nested")).render() UnionGenerator(model, provider, this, model.lookup("test#Foo")).render() UnionGenerator(model, provider, this, model.lookup("test#Bar")).render() diff --git a/codegen-core/src/test/kotlin/software/amazon/smithy/rust/codegen/core/smithy/generators/error/ErrorImplGeneratorTest.kt b/codegen-core/src/test/kotlin/software/amazon/smithy/rust/codegen/core/smithy/generators/error/ErrorImplGeneratorTest.kt new file mode 100644 index 0000000000..678cc808b5 --- /dev/null +++ b/codegen-core/src/test/kotlin/software/amazon/smithy/rust/codegen/core/smithy/generators/error/ErrorImplGeneratorTest.kt @@ -0,0 +1,49 @@ +/* + * 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.error + +import org.junit.jupiter.api.Test +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.rust.codegen.core.smithy.CodegenTarget +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.renderWithModelBuilder +import software.amazon.smithy.rust.codegen.core.testutil.testSymbolProvider +import software.amazon.smithy.rust.codegen.core.util.getTrait + +class ErrorImplGeneratorTest { + val model = + """ + namespace com.test + + @error("server") + @retryable + structure MyError { + message: String + } + """.asSmithyModel() + + @Test + fun `generate error structures`() { + val provider = testSymbolProvider(model) + val project = TestWorkspace.testProject(provider) + val errorShape = model.expectShape(ShapeId.from("com.test#MyError")) as StructureShape + errorShape.renderWithModelBuilder(model, provider, project) + project.moduleFor(errorShape) { + val errorTrait = errorShape.getTrait()!! + ErrorImplGenerator(model, provider, this, errorShape, errorTrait, emptyList()).render(CodegenTarget.CLIENT) + compileAndTest( + """ + let err = MyError::builder().build(); + assert_eq!(err.retryable_error_kind(), aws_smithy_types::retry::ErrorKind::ServerError); + """, + ) + } + } +} diff --git a/codegen-core/src/test/kotlin/software/amazon/smithy/rust/codegen/core/smithy/generators/error/OperationErrorGeneratorTest.kt b/codegen-core/src/test/kotlin/software/amazon/smithy/rust/codegen/core/smithy/generators/error/OperationErrorGeneratorTest.kt deleted file mode 100644 index 38b27ecf4f..0000000000 --- a/codegen-core/src/test/kotlin/software/amazon/smithy/rust/codegen/core/smithy/generators/error/OperationErrorGeneratorTest.kt +++ /dev/null @@ -1,96 +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.error - -import org.junit.jupiter.api.Test -import software.amazon.smithy.model.shapes.StructureShape -import software.amazon.smithy.rust.codegen.core.smithy.ErrorsModule -import software.amazon.smithy.rust.codegen.core.smithy.transformers.OperationNormalizer -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.renderWithModelBuilder -import software.amazon.smithy.rust.codegen.core.testutil.testSymbolProvider -import software.amazon.smithy.rust.codegen.core.testutil.unitTest -import software.amazon.smithy.rust.codegen.core.util.lookup - -class OperationErrorGeneratorTest { - private val baseModel = """ - namespace error - - operation Greeting { - errors: [InvalidGreeting, ComplexError, FooException, Deprecated] - } - - @error("client") - @retryable - structure InvalidGreeting { - message: String, - } - - @error("server") - structure FooException { } - - @error("server") - structure ComplexError { - abc: String, - other: Integer - } - - @error("server") - @deprecated - structure Deprecated { } - """.asSmithyModel() - private val model = OperationNormalizer.transform(baseModel) - private val symbolProvider = testSymbolProvider(model) - - @Test - fun `generates combined error enums`() { - val project = TestWorkspace.testProject(symbolProvider) - project.withModule(ErrorsModule) { - listOf("FooException", "ComplexError", "InvalidGreeting", "Deprecated").forEach { - model.lookup("error#$it").renderWithModelBuilder(model, symbolProvider, this) - } - val errors = listOf("FooException", "ComplexError", "InvalidGreeting").map { model.lookup("error#$it") } - val generator = OperationErrorGenerator(model, symbolProvider, symbolProvider.toSymbol(model.lookup("error#Greeting")), errors) - generator.render(this) - - unitTest( - name = "generates_combined_error_enums", - test = """ - let kind = GreetingErrorKind::InvalidGreeting(InvalidGreeting::builder().message("an error").build()); - let error = GreetingError::new(kind, aws_smithy_types::Error::builder().code("InvalidGreeting").message("an error").build()); - assert_eq!(format!("{}", error), "InvalidGreeting: an error"); - assert_eq!(error.message(), Some("an error")); - assert_eq!(error.code(), Some("InvalidGreeting")); - use aws_smithy_types::retry::ProvideErrorKind; - assert_eq!(error.retryable_error_kind(), Some(aws_smithy_types::retry::ErrorKind::ClientError)); - - // Generate is_xyz methods for errors. - assert_eq!(error.is_invalid_greeting(), true); - assert_eq!(error.is_complex_error(), false); - - // Unhandled variants properly delegate message. - let error = GreetingError::generic(aws_smithy_types::Error::builder().message("hello").build()); - assert_eq!(error.message(), Some("hello")); - - let error = GreetingError::unhandled("some other error"); - assert_eq!(error.message(), None); - assert_eq!(error.code(), None); - - // Indicate the original name in the display output. - let error = FooError::builder().build(); - assert_eq!(format!("{}", error), "FooError [FooException]"); - - let error = Deprecated::builder().build(); - assert_eq!(error.to_string(), "Deprecated"); - """, - ) - - project.compileAndTest() - } - } -} diff --git a/codegen-core/src/test/kotlin/software/amazon/smithy/rust/codegen/core/smithy/generators/error/ServiceErrorGeneratorTest.kt b/codegen-core/src/test/kotlin/software/amazon/smithy/rust/codegen/core/smithy/generators/error/ServiceErrorGeneratorTest.kt deleted file mode 100644 index 746927d134..0000000000 --- a/codegen-core/src/test/kotlin/software/amazon/smithy/rust/codegen/core/smithy/generators/error/ServiceErrorGeneratorTest.kt +++ /dev/null @@ -1,119 +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.error - -import org.junit.jupiter.api.Test -import software.amazon.smithy.model.shapes.ServiceShape -import software.amazon.smithy.model.shapes.ShapeId -import software.amazon.smithy.model.shapes.StructureShape -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.RustModule -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.CoreRustSettings -import software.amazon.smithy.rust.codegen.core.smithy.RustCrate -import software.amazon.smithy.rust.codegen.core.smithy.generators.StructureGenerator -import software.amazon.smithy.rust.codegen.core.smithy.transformers.operationErrors -import software.amazon.smithy.rust.codegen.core.testutil.asSmithyModel -import software.amazon.smithy.rust.codegen.core.testutil.generatePluginContext -import software.amazon.smithy.rust.codegen.core.testutil.testSymbolProvider -import software.amazon.smithy.rust.codegen.core.util.runCommand -import kotlin.io.path.ExperimentalPathApi -import kotlin.io.path.createDirectory -import kotlin.io.path.writeText - -internal class ServiceErrorGeneratorTest { - @ExperimentalPathApi - @Test - fun `top level errors are send + sync`() { - val model = """ - namespace com.example - - use aws.protocols#restJson1 - - @restJson1 - service HelloService { - operations: [SayHello], - version: "1" - } - - @http(uri: "/", method: "POST") - operation SayHello { - input: EmptyStruct, - output: EmptyStruct, - errors: [SorryBusy, CanYouRepeatThat, MeDeprecated] - } - - structure EmptyStruct { } - - @error("server") - structure SorryBusy { } - - @error("client") - structure CanYouRepeatThat { } - - @error("client") - @deprecated - structure MeDeprecated { } - """.asSmithyModel() - - val (pluginContext, testDir) = generatePluginContext(model) - val moduleName = pluginContext.settings.expectStringMember("module").value.replace('-', '_') - val symbolProvider = testSymbolProvider(model) - val settings = CoreRustSettings.from(model, pluginContext.settings) - val codegenContext = CodegenContext( - model, - symbolProvider, - model.expectShape(ShapeId.from("com.example#HelloService")) as ServiceShape, - ShapeId.from("aws.protocols#restJson1"), - settings, - CodegenTarget.CLIENT, - ) - - val rustCrate = RustCrate( - pluginContext.fileManifest, - symbolProvider, - codegenContext.settings.codegenConfig, - ) - - rustCrate.lib { - Attribute.AllowDeprecated.render(this, AttributeKind.Inner) - } - rustCrate.withModule(RustModule.Error) { - for (operation in model.operationShapes) { - if (operation.id.namespace == "com.example") { - OperationErrorGenerator( - model, - symbolProvider, - symbolProvider.toSymbol(operation), - operation.operationErrors(model).map { it as StructureShape }, - ).render(this) - } - } - for (shape in model.structureShapes) { - if (shape.id.namespace == "com.example") { - StructureGenerator(model, symbolProvider, this, shape).render(CodegenTarget.CLIENT) - } - } - } - ServiceErrorGenerator(codegenContext, model.operationShapes.toList()).render(rustCrate) - - testDir.resolve("tests").createDirectory() - testDir.resolve("tests/validate_errors.rs").writeText( - """ - fn check_send_sync() {} - #[test] - fn tl_errors_are_send_sync() { - check_send_sync::<$moduleName::Error>() - } - """, - ) - rustCrate.finalize(settings, model, emptyMap(), emptyList(), false) - - "cargo test".runCommand(testDir) - } -} diff --git a/codegen-core/src/test/kotlin/software/amazon/smithy/rust/codegen/core/smithy/protocols/InlineFunctionNamerTest.kt b/codegen-core/src/test/kotlin/software/amazon/smithy/rust/codegen/core/smithy/protocols/ProtocolFunctionsTest.kt similarity index 55% rename from codegen-core/src/test/kotlin/software/amazon/smithy/rust/codegen/core/smithy/protocols/InlineFunctionNamerTest.kt rename to codegen-core/src/test/kotlin/software/amazon/smithy/rust/codegen/core/smithy/protocols/ProtocolFunctionsTest.kt index a988872b8b..cbc08421f5 100644 --- a/codegen-core/src/test/kotlin/software/amazon/smithy/rust/codegen/core/smithy/protocols/InlineFunctionNamerTest.kt +++ b/codegen-core/src/test/kotlin/software/amazon/smithy/rust/codegen/core/smithy/protocols/ProtocolFunctionsTest.kt @@ -5,14 +5,13 @@ package software.amazon.smithy.rust.codegen.core.smithy.protocols -import io.kotest.assertions.withClue import io.kotest.matchers.shouldBe import org.junit.jupiter.api.Test import software.amazon.smithy.rust.codegen.core.testutil.asSmithyModel import software.amazon.smithy.rust.codegen.core.testutil.testSymbolProvider import software.amazon.smithy.rust.codegen.core.util.lookup -class InlineFunctionNamerTest { +class ProtocolFunctionsTest { private val testModel = """ namespace test @@ -82,45 +81,20 @@ class InlineFunctionNamerTest { } """.asSmithyModel() - class UniqueChecker { - private val names = HashSet() - - fun checkName(value: String) { - withClue("Name '$value' should be unique") { - names.contains(value) shouldBe false - } - names.add(value) - } - } - @Test fun `generates function names for shapes`() { val symbolProvider = testSymbolProvider(testModel) - fun test(shapeId: String, suffix: String) { - symbolProvider.serializeFunctionName(testModel.lookup(shapeId)) shouldBe "serialize_$suffix" - symbolProvider.deserializeFunctionName(testModel.lookup(shapeId)) shouldBe "deser_$suffix" + fun test(shapeId: String, expected: String) { + symbolProvider.shapeFunctionName(null, testModel.lookup(shapeId)) shouldBe expected } - test("test#Op1", "operation_crate_operation_op1") - test("test#SomeList1", "list_test_some_list1") - test("test#SomeMap1", "map_test_some_map1") - test("test#SomeSet1", "set_test_some_set1") - test("test#SomeStruct1", "structure_crate_model_some_struct1") - test("test#SomeUnion1", "union_crate_model_some_union1") - test("test#SomeStruct1\$some_string", "member_test_some_struct1_some_string") - } - - @Test - fun `generates unique function names for member shapes`() { - val symbolProvider = testSymbolProvider(testModel) - UniqueChecker().also { checker -> - for (shape in testModel.shapes().filter { it.id.namespace == "test" }) { - for (member in shape.members()) { - checker.checkName(symbolProvider.serializeFunctionName(member)) - checker.checkName(symbolProvider.deserializeFunctionName(member)) - } - } - } + test("test#Op1", "op1") + test("test#SomeList1", "some_list1") + test("test#SomeMap1", "some_map1") + test("test#SomeSet1", "some_set1") + test("test#SomeStruct1", "some_struct1") + test("test#SomeUnion1", "some_union1") + test("test#SomeStruct1\$some_string", "some_string") } } diff --git a/codegen-core/src/test/kotlin/software/amazon/smithy/rust/codegen/core/smithy/protocols/parse/AwsQueryParserGeneratorTest.kt b/codegen-core/src/test/kotlin/software/amazon/smithy/rust/codegen/core/smithy/protocols/parse/AwsQueryParserGeneratorTest.kt index b90543bea3..38beea1e1b 100644 --- a/codegen-core/src/test/kotlin/software/amazon/smithy/rust/codegen/core/smithy/protocols/parse/AwsQueryParserGeneratorTest.kt +++ b/codegen-core/src/test/kotlin/software/amazon/smithy/rust/codegen/core/smithy/protocols/parse/AwsQueryParserGeneratorTest.kt @@ -8,9 +8,7 @@ package software.amazon.smithy.rust.codegen.core.smithy.protocols.parse import org.junit.jupiter.api.Test import software.amazon.smithy.model.shapes.OperationShape import software.amazon.smithy.model.shapes.StructureShape -import software.amazon.smithy.rust.codegen.core.rustlang.RustModule import software.amazon.smithy.rust.codegen.core.smithy.RuntimeType -import software.amazon.smithy.rust.codegen.core.smithy.generators.builderSymbolFn 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.testutil.TestRuntimeConfig @@ -43,13 +41,12 @@ class AwsQueryParserGeneratorTest { @Test fun `it modifies operation parsing to include Response and Result tags`() { - val model = RecursiveShapeBoxer.transform(OperationNormalizer.transform(baseModel)) + val model = RecursiveShapeBoxer().transform(OperationNormalizer.transform(baseModel)) val codegenContext = testCodegenContext(model) val symbolProvider = codegenContext.symbolProvider val parserGenerator = AwsQueryParserGenerator( codegenContext, RuntimeType.wrappedXmlErrors(TestRuntimeConfig), - builderSymbolFn(symbolProvider), ) val operationParser = parserGenerator.operationParser(model.lookup("test#SomeOperation"))!! val project = TestWorkspace.testProject(testSymbolProvider(model)) @@ -65,20 +62,17 @@ class AwsQueryParserGeneratorTest { "#; - let output = ${format(operationParser)}(xml, output::some_operation_output::Builder::default()).unwrap().build(); + let output = ${format(operationParser)}(xml, test_output::SomeOperationOutput::builder()).unwrap().build(); assert_eq!(output.some_attribute, Some(5)); assert_eq!(output.some_val, Some("Some value".to_string())); """, ) } - - project.withModule(RustModule.public("model")) { - model.lookup("test#SomeOutput").renderWithModelBuilder(model, symbolProvider, this) + model.lookup("test#SomeOutput").also { struct -> + struct.renderWithModelBuilder(model, symbolProvider, project) } - - project.withModule(RustModule.public("output")) { - model.lookup("test#SomeOperation").outputShape(model) - .renderWithModelBuilder(model, symbolProvider, this) + model.lookup("test#SomeOperation").outputShape(model).also { output -> + output.renderWithModelBuilder(model, symbolProvider, project) } project.compileAndTest() } diff --git a/codegen-core/src/test/kotlin/software/amazon/smithy/rust/codegen/core/smithy/protocols/parse/Ec2QueryParserGeneratorTest.kt b/codegen-core/src/test/kotlin/software/amazon/smithy/rust/codegen/core/smithy/protocols/parse/Ec2QueryParserGeneratorTest.kt index 7b835d8223..9a51b07253 100644 --- a/codegen-core/src/test/kotlin/software/amazon/smithy/rust/codegen/core/smithy/protocols/parse/Ec2QueryParserGeneratorTest.kt +++ b/codegen-core/src/test/kotlin/software/amazon/smithy/rust/codegen/core/smithy/protocols/parse/Ec2QueryParserGeneratorTest.kt @@ -8,9 +8,7 @@ package software.amazon.smithy.rust.codegen.core.smithy.protocols.parse import org.junit.jupiter.api.Test import software.amazon.smithy.model.shapes.OperationShape import software.amazon.smithy.model.shapes.StructureShape -import software.amazon.smithy.rust.codegen.core.rustlang.RustModule import software.amazon.smithy.rust.codegen.core.smithy.RuntimeType -import software.amazon.smithy.rust.codegen.core.smithy.generators.builderSymbolFn 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.testutil.TestRuntimeConfig @@ -43,13 +41,12 @@ class Ec2QueryParserGeneratorTest { @Test fun `it modifies operation parsing to include Response and Result tags`() { - val model = RecursiveShapeBoxer.transform(OperationNormalizer.transform(baseModel)) + val model = RecursiveShapeBoxer().transform(OperationNormalizer.transform(baseModel)) val codegenContext = testCodegenContext(model) val symbolProvider = codegenContext.symbolProvider val parserGenerator = Ec2QueryParserGenerator( codegenContext, RuntimeType.wrappedXmlErrors(TestRuntimeConfig), - builderSymbolFn(symbolProvider), ) val operationParser = parserGenerator.operationParser(model.lookup("test#SomeOperation"))!! val project = TestWorkspace.testProject(testSymbolProvider(model)) @@ -63,20 +60,19 @@ class Ec2QueryParserGeneratorTest { Some value "#; - let output = ${format(operationParser)}(xml, output::some_operation_output::Builder::default()).unwrap().build(); + let output = ${format(operationParser)}(xml, test_output::SomeOperationOutput::builder()).unwrap().build(); assert_eq!(output.some_attribute, Some(5)); assert_eq!(output.some_val, Some("Some value".to_string())); """, ) } - project.withModule(RustModule.public("model")) { - model.lookup("test#SomeOutput").renderWithModelBuilder(model, symbolProvider, this) + model.lookup("test#SomeOutput").also { struct -> + struct.renderWithModelBuilder(model, symbolProvider, project) } - project.withModule(RustModule.public("output")) { - model.lookup("test#SomeOperation").outputShape(model) - .renderWithModelBuilder(model, symbolProvider, this) + model.lookup("test#SomeOperation").outputShape(model).also { output -> + output.renderWithModelBuilder(model, symbolProvider, project) } project.compileAndTest() } diff --git a/codegen-core/src/test/kotlin/software/amazon/smithy/rust/codegen/core/smithy/protocols/parse/JsonParserGeneratorTest.kt b/codegen-core/src/test/kotlin/software/amazon/smithy/rust/codegen/core/smithy/protocols/parse/JsonParserGeneratorTest.kt index 9bd71a7a4e..79435b5e9b 100644 --- a/codegen-core/src/test/kotlin/software/amazon/smithy/rust/codegen/core/smithy/protocols/parse/JsonParserGeneratorTest.kt +++ b/codegen-core/src/test/kotlin/software/amazon/smithy/rust/codegen/core/smithy/protocols/parse/JsonParserGeneratorTest.kt @@ -6,14 +6,12 @@ package software.amazon.smithy.rust.codegen.core.smithy.protocols.parse import org.junit.jupiter.api.Test -import software.amazon.smithy.codegen.core.Symbol import software.amazon.smithy.model.shapes.OperationShape import software.amazon.smithy.model.shapes.StringShape import software.amazon.smithy.model.shapes.StructureShape -import software.amazon.smithy.rust.codegen.core.rustlang.RustModule import software.amazon.smithy.rust.codegen.core.smithy.generators.EnumGenerator +import software.amazon.smithy.rust.codegen.core.smithy.generators.TestEnumType import software.amazon.smithy.rust.codegen.core.smithy.generators.UnionGenerator -import software.amazon.smithy.rust.codegen.core.smithy.generators.builderSymbol import software.amazon.smithy.rust.codegen.core.smithy.protocols.HttpTraitHttpBindingResolver import software.amazon.smithy.rust.codegen.core.smithy.protocols.ProtocolContentTypes import software.amazon.smithy.rust.codegen.core.smithy.protocols.restJsonFieldName @@ -26,7 +24,6 @@ import software.amazon.smithy.rust.codegen.core.testutil.renderWithModelBuilder import software.amazon.smithy.rust.codegen.core.testutil.testCodegenContext import software.amazon.smithy.rust.codegen.core.testutil.testSymbolProvider import software.amazon.smithy.rust.codegen.core.testutil.unitTest -import software.amazon.smithy.rust.codegen.core.util.expectTrait import software.amazon.smithy.rust.codegen.core.util.lookup import software.amazon.smithy.rust.codegen.core.util.outputShape @@ -115,17 +112,14 @@ class JsonParserGeneratorTest { @Test fun `generates valid deserializers`() { - val model = RecursiveShapeBoxer.transform(OperationNormalizer.transform(baseModel)) + val model = RecursiveShapeBoxer().transform(OperationNormalizer.transform(baseModel)) val codegenContext = testCodegenContext(model) val symbolProvider = codegenContext.symbolProvider - fun builderSymbol(shape: StructureShape): Symbol = - shape.builderSymbol(symbolProvider) val parserGenerator = JsonParserGenerator( codegenContext, HttpTraitHttpBindingResolver(model, ProtocolContentTypes.consistent("application/json")), ::restJsonFieldName, - ::builderSymbol, ) val operationGenerator = parserGenerator.operationParser(model.lookup("test#Op")) val payloadGenerator = parserGenerator.payloadParser(model.lookup("test#OpOutput\$top")) @@ -136,7 +130,7 @@ class JsonParserGeneratorTest { unitTest( "json_parser", """ - use model::Choice; + use test_model::Choice; // Generate the document serializer even though it's not tested directly // ${format(payloadGenerator)} @@ -151,7 +145,7 @@ class JsonParserGeneratorTest { } "#; - let output = ${format(operationGenerator!!)}(json, output::op_output::Builder::default()).unwrap().build(); + let output = ${format(operationGenerator!!)}(json, test_output::OpOutput::builder()).unwrap().build(); let top = output.top.expect("top"); assert_eq!(Some(45), top.extra); assert_eq!(Some("something".to_string()), top.field); @@ -162,7 +156,7 @@ class JsonParserGeneratorTest { "empty_body", """ // empty body - let output = ${format(operationGenerator)}(b"", output::op_output::Builder::default()).unwrap().build(); + let output = ${format(operationGenerator)}(b"", test_output::OpOutput::builder()).unwrap().build(); assert_eq!(output.top, None); """, ) @@ -171,7 +165,7 @@ class JsonParserGeneratorTest { """ // unknown variant let input = br#"{ "top": { "choice": { "somenewvariant": "data" } } }"#; - let output = ${format(operationGenerator)}(input, output::op_output::Builder::default()).unwrap().build(); + let output = ${format(operationGenerator)}(input, test_output::OpOutput::builder()).unwrap().build(); assert!(output.top.unwrap().choice.unwrap().is_unknown()); """, ) @@ -180,7 +174,7 @@ class JsonParserGeneratorTest { "empty_error", """ // empty error - let error_output = ${format(errorParser!!)}(b"", error::error::Builder::default()).unwrap().build(); + let error_output = ${format(errorParser!!)}(b"", test_error::Error::builder()).unwrap().build(); assert_eq!(error_output.message, None); """, ) @@ -189,24 +183,25 @@ class JsonParserGeneratorTest { "error_with_message", """ // error with message - let error_output = ${format(errorParser)}(br#"{"message": "hello"}"#, error::error::Builder::default()).unwrap().build(); + let error_output = ${format(errorParser)}(br#"{"message": "hello"}"#, test_error::Error::builder()).unwrap().build(); assert_eq!(error_output.message.expect("message should be set"), "hello"); """, ) } - project.withModule(RustModule.public("model")) { - model.lookup("test#Top").renderWithModelBuilder(model, symbolProvider, this) - model.lookup("test#EmptyStruct").renderWithModelBuilder(model, symbolProvider, this) - UnionGenerator(model, symbolProvider, this, model.lookup("test#Choice")).render() - val enum = model.lookup("test#FooEnum") - EnumGenerator(model, symbolProvider, this, enum, enum.expectTrait()).render() + model.lookup("test#Top").also { top -> + top.renderWithModelBuilder(model, symbolProvider, project) + model.lookup("test#EmptyStruct").renderWithModelBuilder(model, symbolProvider, project) + project.moduleFor(top) { + UnionGenerator(model, symbolProvider, this, model.lookup("test#Choice")).render() + val enum = model.lookup("test#FooEnum") + EnumGenerator(model, symbolProvider, enum, TestEnumType).render(this) + } } - - project.withModule(RustModule.public("output")) { - model.lookup("test#Op").outputShape(model).renderWithModelBuilder(model, symbolProvider, this) + model.lookup("test#Op").outputShape(model).also { output -> + output.renderWithModelBuilder(model, symbolProvider, project) } - project.withModule(RustModule.public("error")) { - model.lookup("test#Error").renderWithModelBuilder(model, symbolProvider, this) + model.lookup("test#Error").also { error -> + error.renderWithModelBuilder(model, symbolProvider, project) } project.compileAndTest() } diff --git a/codegen-core/src/test/kotlin/software/amazon/smithy/rust/codegen/core/smithy/protocols/parse/XmlBindingTraitParserGeneratorTest.kt b/codegen-core/src/test/kotlin/software/amazon/smithy/rust/codegen/core/smithy/protocols/parse/XmlBindingTraitParserGeneratorTest.kt index 50fb343d6d..47f310e83d 100644 --- a/codegen-core/src/test/kotlin/software/amazon/smithy/rust/codegen/core/smithy/protocols/parse/XmlBindingTraitParserGeneratorTest.kt +++ b/codegen-core/src/test/kotlin/software/amazon/smithy/rust/codegen/core/smithy/protocols/parse/XmlBindingTraitParserGeneratorTest.kt @@ -9,11 +9,12 @@ import org.junit.jupiter.api.Test import software.amazon.smithy.model.shapes.OperationShape import software.amazon.smithy.model.shapes.StringShape import software.amazon.smithy.model.shapes.StructureShape -import software.amazon.smithy.rust.codegen.core.rustlang.RustModule +import software.amazon.smithy.model.shapes.UnionShape +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.EnumGenerator +import software.amazon.smithy.rust.codegen.core.smithy.generators.TestEnumType import software.amazon.smithy.rust.codegen.core.smithy.generators.UnionGenerator -import software.amazon.smithy.rust.codegen.core.smithy.generators.builderSymbolFn 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.testutil.TestRuntimeConfig @@ -24,7 +25,6 @@ import software.amazon.smithy.rust.codegen.core.testutil.renderWithModelBuilder import software.amazon.smithy.rust.codegen.core.testutil.testCodegenContext import software.amazon.smithy.rust.codegen.core.testutil.testSymbolProvider import software.amazon.smithy.rust.codegen.core.testutil.unitTest -import software.amazon.smithy.rust.codegen.core.util.expectTrait import software.amazon.smithy.rust.codegen.core.util.lookup import software.amazon.smithy.rust.codegen.core.util.outputShape @@ -93,21 +93,22 @@ internal class XmlBindingTraitParserGeneratorTest { @Test fun `generates valid parsers`() { - val model = RecursiveShapeBoxer.transform(OperationNormalizer.transform(baseModel)) + val model = RecursiveShapeBoxer().transform(OperationNormalizer.transform(baseModel)) val codegenContext = testCodegenContext(model) val symbolProvider = codegenContext.symbolProvider val parserGenerator = XmlBindingTraitParserGenerator( codegenContext, RuntimeType.wrappedXmlErrors(TestRuntimeConfig), - builderSymbolFn(symbolProvider), ) { _, inner -> inner("decoder") } val operationParser = parserGenerator.operationParser(model.lookup("test#Op"))!! + + val choiceShape = model.lookup("test#Choice") val project = TestWorkspace.testProject(testSymbolProvider(model)) project.lib { - unitTest( - name = "valid_input", - test = """ - let xml = br#" + unitTest(name = "valid_input") { + rustTemplate( + """ + let xml = br##" some key @@ -118,19 +119,21 @@ internal class XmlBindingTraitParserGeneratorTest { hey - "#; - let output = ${format(operationParser)}(xml, output::op_output::Builder::default()).unwrap().build(); + "##; + let output = ${format(operationParser)}(xml, test_output::OpOutput::builder()).unwrap().build(); let mut map = std::collections::HashMap::new(); - map.insert("some key".to_string(), model::Choice::S("hello".to_string())); - assert_eq!(output.choice, Some(model::Choice::FlatMap(map))); + map.insert("some key".to_string(), #{Choice}::S("hello".to_string())); + assert_eq!(output.choice, Some(#{Choice}::FlatMap(map))); assert_eq!(output.renamed_with_prefix.as_deref(), Some("hey")); - """, - ) - - unitTest( - name = "ignore_extras", - test = """ - let xml = br#" + """, + "Choice" to symbolProvider.toSymbol(choiceShape), + ) + } + + unitTest(name = "ignore_extras") { + rustTemplate( + """ + let xml = br##" @@ -146,13 +149,15 @@ internal class XmlBindingTraitParserGeneratorTest { - "#; - let output = ${format(operationParser)}(xml, output::op_output::Builder::default()).unwrap().build(); + "##; + let output = ${format(operationParser)}(xml, test_output::OpOutput::builder()).unwrap().build(); let mut map = std::collections::HashMap::new(); - map.insert("some key".to_string(), model::Choice::S("hello".to_string())); - assert_eq!(output.choice, Some(model::Choice::FlatMap(map))); - """, - ) + map.insert("some key".to_string(), #{Choice}::S("hello".to_string())); + assert_eq!(output.choice, Some(#{Choice}::FlatMap(map))); + """, + "Choice" to symbolProvider.toSymbol(choiceShape), + ) + } unitTest( name = "nopanics_on_invalid", @@ -174,7 +179,7 @@ internal class XmlBindingTraitParserGeneratorTest { "#; - ${format(operationParser)}(xml, output::op_output::Builder::default()).expect("unknown union variant does not cause failure"); + ${format(operationParser)}(xml, test_output::OpOutput::builder()).expect("unknown union variant does not cause failure"); """, ) unitTest( @@ -191,20 +196,23 @@ internal class XmlBindingTraitParserGeneratorTest { "#; - let output = ${format(operationParser)}(xml, output::op_output::Builder::default()).unwrap().build(); + let output = ${format(operationParser)}(xml, test_output::OpOutput::builder()).unwrap().build(); assert!(output.choice.unwrap().is_unknown()); """, ) } - project.withModule(RustModule.public("model")) { - model.lookup("test#Top").renderWithModelBuilder(model, symbolProvider, this) - UnionGenerator(model, symbolProvider, this, model.lookup("test#Choice")).render() - val enum = model.lookup("test#FooEnum") - EnumGenerator(model, symbolProvider, this, enum, enum.expectTrait()).render() + model.lookup("test#Top").also { top -> + top.renderWithModelBuilder(model, symbolProvider, project) + project.moduleFor(top) { + UnionGenerator(model, symbolProvider, this, choiceShape).render() + model.lookup("test#FooEnum").also { enum -> + EnumGenerator(model, symbolProvider, enum, TestEnumType).render(this) + } + } } - project.withModule(RustModule.public("output")) { - model.lookup("test#Op").outputShape(model).renderWithModelBuilder(model, symbolProvider, this) + model.lookup("test#Op").outputShape(model).also { out -> + out.renderWithModelBuilder(model, symbolProvider, project) } project.compileAndTest() } diff --git a/codegen-core/src/test/kotlin/software/amazon/smithy/rust/codegen/core/smithy/protocols/serialize/AwsQuerySerializerGeneratorTest.kt b/codegen-core/src/test/kotlin/software/amazon/smithy/rust/codegen/core/smithy/protocols/serialize/AwsQuerySerializerGeneratorTest.kt index f8ef938bea..ad44554e36 100644 --- a/codegen-core/src/test/kotlin/software/amazon/smithy/rust/codegen/core/smithy/protocols/serialize/AwsQuerySerializerGeneratorTest.kt +++ b/codegen-core/src/test/kotlin/software/amazon/smithy/rust/codegen/core/smithy/protocols/serialize/AwsQuerySerializerGeneratorTest.kt @@ -10,9 +10,9 @@ import org.junit.jupiter.params.provider.CsvSource import software.amazon.smithy.model.shapes.OperationShape import software.amazon.smithy.model.shapes.StringShape import software.amazon.smithy.model.shapes.StructureShape -import software.amazon.smithy.rust.codegen.core.rustlang.RustModule import software.amazon.smithy.rust.codegen.core.smithy.CodegenTarget import software.amazon.smithy.rust.codegen.core.smithy.generators.EnumGenerator +import software.amazon.smithy.rust.codegen.core.smithy.generators.TestEnumType import software.amazon.smithy.rust.codegen.core.smithy.generators.UnionGenerator import software.amazon.smithy.rust.codegen.core.smithy.transformers.OperationNormalizer import software.amazon.smithy.rust.codegen.core.smithy.transformers.RecursiveShapeBoxer @@ -22,7 +22,6 @@ import software.amazon.smithy.rust.codegen.core.testutil.compileAndTest import software.amazon.smithy.rust.codegen.core.testutil.renderWithModelBuilder import software.amazon.smithy.rust.codegen.core.testutil.testCodegenContext import software.amazon.smithy.rust.codegen.core.testutil.unitTest -import software.amazon.smithy.rust.codegen.core.util.expectTrait import software.amazon.smithy.rust.codegen.core.util.inputShape import software.amazon.smithy.rust.codegen.core.util.lookup @@ -93,7 +92,7 @@ class AwsQuerySerializerGeneratorTest { true -> CodegenTarget.CLIENT false -> CodegenTarget.SERVER } - val model = RecursiveShapeBoxer.transform(OperationNormalizer.transform(baseModel)) + val model = RecursiveShapeBoxer().transform(OperationNormalizer.transform(baseModel)) val codegenContext = testCodegenContext(model, codegenTarget = codegenTarget) val symbolProvider = codegenContext.symbolProvider val parserGenerator = AwsQuerySerializerGenerator(testCodegenContext(model, codegenTarget = codegenTarget)) @@ -104,9 +103,9 @@ class AwsQuerySerializerGeneratorTest { unitTest( "query_serializer", """ - use model::Top; + use test_model::Top; - let input = crate::input::OpInput::builder() + let input = crate::test_input::OpInput::builder() .top( Top::builder() .field("hello!") @@ -133,15 +132,23 @@ class AwsQuerySerializerGeneratorTest { """, ) } - project.withModule(RustModule.public("model")) { - model.lookup("test#Top").renderWithModelBuilder(model, symbolProvider, this) - UnionGenerator(model, symbolProvider, this, model.lookup("test#Choice"), renderUnknownVariant = generateUnknownVariant).render() - val enum = model.lookup("test#FooEnum") - EnumGenerator(model, symbolProvider, this, enum, enum.expectTrait()).render() + model.lookup("test#Top").also { top -> + top.renderWithModelBuilder(model, symbolProvider, project) + project.moduleFor(top) { + UnionGenerator( + model, + symbolProvider, + this, + model.lookup("test#Choice"), + renderUnknownVariant = generateUnknownVariant, + ).render() + val enum = model.lookup("test#FooEnum") + EnumGenerator(model, symbolProvider, enum, TestEnumType).render(this) + } } - project.withModule(RustModule.public("input")) { - model.lookup("test#Op").inputShape(model).renderWithModelBuilder(model, symbolProvider, this) + model.lookup("test#Op").inputShape(model).also { input -> + input.renderWithModelBuilder(model, symbolProvider, project) } project.compileAndTest() } diff --git a/codegen-core/src/test/kotlin/software/amazon/smithy/rust/codegen/core/smithy/protocols/serialize/Ec2QuerySerializerGeneratorTest.kt b/codegen-core/src/test/kotlin/software/amazon/smithy/rust/codegen/core/smithy/protocols/serialize/Ec2QuerySerializerGeneratorTest.kt index b3a21898ee..2436aff706 100644 --- a/codegen-core/src/test/kotlin/software/amazon/smithy/rust/codegen/core/smithy/protocols/serialize/Ec2QuerySerializerGeneratorTest.kt +++ b/codegen-core/src/test/kotlin/software/amazon/smithy/rust/codegen/core/smithy/protocols/serialize/Ec2QuerySerializerGeneratorTest.kt @@ -9,8 +9,8 @@ import org.junit.jupiter.api.Test import software.amazon.smithy.model.shapes.OperationShape import software.amazon.smithy.model.shapes.StringShape import software.amazon.smithy.model.shapes.StructureShape -import software.amazon.smithy.rust.codegen.core.rustlang.RustModule import software.amazon.smithy.rust.codegen.core.smithy.generators.EnumGenerator +import software.amazon.smithy.rust.codegen.core.smithy.generators.TestEnumType import software.amazon.smithy.rust.codegen.core.smithy.generators.UnionGenerator import software.amazon.smithy.rust.codegen.core.smithy.transformers.OperationNormalizer import software.amazon.smithy.rust.codegen.core.smithy.transformers.RecursiveShapeBoxer @@ -21,7 +21,6 @@ import software.amazon.smithy.rust.codegen.core.testutil.renderWithModelBuilder import software.amazon.smithy.rust.codegen.core.testutil.testCodegenContext import software.amazon.smithy.rust.codegen.core.testutil.testSymbolProvider import software.amazon.smithy.rust.codegen.core.testutil.unitTest -import software.amazon.smithy.rust.codegen.core.util.expectTrait import software.amazon.smithy.rust.codegen.core.util.inputShape import software.amazon.smithy.rust.codegen.core.util.lookup @@ -86,7 +85,7 @@ class Ec2QuerySerializerGeneratorTest { @Test fun `generates valid serializers`() { - val model = RecursiveShapeBoxer.transform(OperationNormalizer.transform(baseModel)) + val model = RecursiveShapeBoxer().transform(OperationNormalizer.transform(baseModel)) val codegenContext = testCodegenContext(model) val symbolProvider = codegenContext.symbolProvider val parserGenerator = Ec2QuerySerializerGenerator(codegenContext) @@ -97,9 +96,9 @@ class Ec2QuerySerializerGeneratorTest { unitTest( "ec2query_serializer", """ - use model::Top; + use test_model::Top; - let input = crate::input::OpInput::builder() + let input = crate::test_input::OpInput::builder() .top( Top::builder() .field("hello!") @@ -126,15 +125,17 @@ class Ec2QuerySerializerGeneratorTest { """, ) } - project.withModule(RustModule.public("model")) { - model.lookup("test#Top").renderWithModelBuilder(model, symbolProvider, this) - UnionGenerator(model, symbolProvider, this, model.lookup("test#Choice")).render() - val enum = model.lookup("test#FooEnum") - EnumGenerator(model, symbolProvider, this, enum, enum.expectTrait()).render() + model.lookup("test#Top").also { top -> + top.renderWithModelBuilder(model, symbolProvider, project) + project.moduleFor(top) { + UnionGenerator(model, symbolProvider, this, model.lookup("test#Choice")).render() + val enum = model.lookup("test#FooEnum") + EnumGenerator(model, symbolProvider, enum, TestEnumType).render(this) + } } - project.withModule(RustModule.public("input")) { - model.lookup("test#Op").inputShape(model).renderWithModelBuilder(model, symbolProvider, this) + model.lookup("test#Op").inputShape(model).also { input -> + input.renderWithModelBuilder(model, symbolProvider, project) } project.compileAndTest() } diff --git a/codegen-core/src/test/kotlin/software/amazon/smithy/rust/codegen/core/smithy/protocols/serialize/JsonSerializerGeneratorTest.kt b/codegen-core/src/test/kotlin/software/amazon/smithy/rust/codegen/core/smithy/protocols/serialize/JsonSerializerGeneratorTest.kt index bf3fb604da..23c27f331b 100644 --- a/codegen-core/src/test/kotlin/software/amazon/smithy/rust/codegen/core/smithy/protocols/serialize/JsonSerializerGeneratorTest.kt +++ b/codegen-core/src/test/kotlin/software/amazon/smithy/rust/codegen/core/smithy/protocols/serialize/JsonSerializerGeneratorTest.kt @@ -9,8 +9,8 @@ import org.junit.jupiter.api.Test import software.amazon.smithy.model.shapes.OperationShape import software.amazon.smithy.model.shapes.StringShape import software.amazon.smithy.model.shapes.StructureShape -import software.amazon.smithy.rust.codegen.core.rustlang.RustModule import software.amazon.smithy.rust.codegen.core.smithy.generators.EnumGenerator +import software.amazon.smithy.rust.codegen.core.smithy.generators.TestEnumType import software.amazon.smithy.rust.codegen.core.smithy.generators.UnionGenerator import software.amazon.smithy.rust.codegen.core.smithy.protocols.HttpTraitHttpBindingResolver import software.amazon.smithy.rust.codegen.core.smithy.protocols.ProtocolContentTypes @@ -24,7 +24,6 @@ import software.amazon.smithy.rust.codegen.core.testutil.renderWithModelBuilder import software.amazon.smithy.rust.codegen.core.testutil.testCodegenContext import software.amazon.smithy.rust.codegen.core.testutil.testSymbolProvider import software.amazon.smithy.rust.codegen.core.testutil.unitTest -import software.amazon.smithy.rust.codegen.core.util.expectTrait import software.amazon.smithy.rust.codegen.core.util.inputShape import software.amazon.smithy.rust.codegen.core.util.lookup @@ -101,7 +100,7 @@ class JsonSerializerGeneratorTest { @Test fun `generates valid serializers`() { - val model = RecursiveShapeBoxer.transform(OperationNormalizer.transform(baseModel)) + val model = RecursiveShapeBoxer().transform(OperationNormalizer.transform(baseModel)) val codegenContext = testCodegenContext(model) val symbolProvider = codegenContext.symbolProvider val parserSerializer = JsonSerializerGenerator( @@ -117,12 +116,12 @@ class JsonSerializerGeneratorTest { unitTest( "json_serializers", """ - use model::{Top, Choice}; + use test_model::{Top, Choice}; // Generate the document serializer even though it's not tested directly // ${format(documentGenerator)} - let input = crate::input::OpInput::builder().top( + let input = crate::test_input::OpInput::builder().top( Top::builder() .field("hello!") .extra(45) @@ -133,7 +132,7 @@ class JsonSerializerGeneratorTest { let output = std::str::from_utf8(serialized.bytes().unwrap()).unwrap(); assert_eq!(output, r#"{"top":{"field":"hello!","extra":45,"rec":[{"extra":55}]}}"#); - let input = crate::input::OpInput::builder().top( + let input = crate::test_input::OpInput::builder().top( Top::builder() .choice(Choice::Unknown) .build() @@ -142,15 +141,17 @@ class JsonSerializerGeneratorTest { """, ) } - project.withModule(RustModule.public("model")) { - model.lookup("test#Top").renderWithModelBuilder(model, symbolProvider, this) - UnionGenerator(model, symbolProvider, this, model.lookup("test#Choice")).render() - val enum = model.lookup("test#FooEnum") - EnumGenerator(model, symbolProvider, this, enum, enum.expectTrait()).render() + model.lookup("test#Top").also { top -> + top.renderWithModelBuilder(model, symbolProvider, project) + project.moduleFor(top) { + UnionGenerator(model, symbolProvider, this, model.lookup("test#Choice")).render() + val enum = model.lookup("test#FooEnum") + EnumGenerator(model, symbolProvider, enum, TestEnumType).render(this) + } } - project.withModule(RustModule.public("input")) { - model.lookup("test#Op").inputShape(model).renderWithModelBuilder(model, symbolProvider, this) + model.lookup("test#Op").inputShape(model).also { input -> + input.renderWithModelBuilder(model, symbolProvider, project) } project.compileAndTest() } diff --git a/codegen-core/src/test/kotlin/software/amazon/smithy/rust/codegen/core/smithy/protocols/serialize/XmlBindingTraitSerializerGeneratorTest.kt b/codegen-core/src/test/kotlin/software/amazon/smithy/rust/codegen/core/smithy/protocols/serialize/XmlBindingTraitSerializerGeneratorTest.kt index f8f9aafa7b..a695d2a401 100644 --- a/codegen-core/src/test/kotlin/software/amazon/smithy/rust/codegen/core/smithy/protocols/serialize/XmlBindingTraitSerializerGeneratorTest.kt +++ b/codegen-core/src/test/kotlin/software/amazon/smithy/rust/codegen/core/smithy/protocols/serialize/XmlBindingTraitSerializerGeneratorTest.kt @@ -9,8 +9,8 @@ import org.junit.jupiter.api.Test import software.amazon.smithy.model.shapes.OperationShape import software.amazon.smithy.model.shapes.StringShape import software.amazon.smithy.model.shapes.StructureShape -import software.amazon.smithy.rust.codegen.core.rustlang.RustModule import software.amazon.smithy.rust.codegen.core.smithy.generators.EnumGenerator +import software.amazon.smithy.rust.codegen.core.smithy.generators.TestEnumType import software.amazon.smithy.rust.codegen.core.smithy.generators.UnionGenerator import software.amazon.smithy.rust.codegen.core.smithy.protocols.HttpTraitHttpBindingResolver import software.amazon.smithy.rust.codegen.core.smithy.protocols.ProtocolContentTypes @@ -23,7 +23,6 @@ import software.amazon.smithy.rust.codegen.core.testutil.renderWithModelBuilder import software.amazon.smithy.rust.codegen.core.testutil.testCodegenContext import software.amazon.smithy.rust.codegen.core.testutil.testSymbolProvider import software.amazon.smithy.rust.codegen.core.testutil.unitTest -import software.amazon.smithy.rust.codegen.core.util.expectTrait import software.amazon.smithy.rust.codegen.core.util.inputShape import software.amazon.smithy.rust.codegen.core.util.lookup @@ -106,7 +105,7 @@ internal class XmlBindingTraitSerializerGeneratorTest { @Test fun `generates valid serializers`() { - val model = RecursiveShapeBoxer.transform(OperationNormalizer.transform(baseModel)) + val model = RecursiveShapeBoxer().transform(OperationNormalizer.transform(baseModel)) val codegenContext = testCodegenContext(model) val symbolProvider = codegenContext.symbolProvider val parserGenerator = XmlBindingTraitSerializerGenerator( @@ -120,8 +119,8 @@ internal class XmlBindingTraitSerializerGeneratorTest { unitTest( "serialize_xml", """ - use model::Top; - let inp = crate::input::OpInput::builder().payload( + use test_model::Top; + let inp = crate::test_input::OpInput::builder().payload( Top::builder() .field("hello!") .extra(45) @@ -136,8 +135,8 @@ internal class XmlBindingTraitSerializerGeneratorTest { unitTest( "unknown_variants", """ - use model::{Top, Choice}; - let input = crate::input::OpInput::builder().payload( + use test_model::{Top, Choice}; + let input = crate::test_input::OpInput::builder().payload( Top::builder() .choice(Choice::Unknown) .build() @@ -146,15 +145,16 @@ internal class XmlBindingTraitSerializerGeneratorTest { """, ) } - project.withModule(RustModule.public("model")) { - model.lookup("test#Top").renderWithModelBuilder(model, symbolProvider, this) - UnionGenerator(model, symbolProvider, this, model.lookup("test#Choice")).render() - val enum = model.lookup("test#FooEnum") - EnumGenerator(model, symbolProvider, this, enum, enum.expectTrait()).render() + model.lookup("test#Top").also { top -> + top.renderWithModelBuilder(model, symbolProvider, project) + project.moduleFor(top) { + UnionGenerator(model, symbolProvider, this, model.lookup("test#Choice")).render() + val enum = model.lookup("test#FooEnum") + EnumGenerator(model, symbolProvider, enum, TestEnumType).render(this) + } } - - project.withModule(RustModule.public("input")) { - model.lookup("test#Op").inputShape(model).renderWithModelBuilder(model, symbolProvider, this) + model.lookup("test#Op").inputShape(model).also { input -> + input.renderWithModelBuilder(model, symbolProvider, project) } project.compileAndTest() } diff --git a/codegen-core/src/test/kotlin/software/amazon/smithy/rust/codegen/core/smithy/transformers/RecursiveShapeBoxerTest.kt b/codegen-core/src/test/kotlin/software/amazon/smithy/rust/codegen/core/smithy/transformers/RecursiveShapeBoxerTest.kt index 061814a73a..293e221713 100644 --- a/codegen-core/src/test/kotlin/software/amazon/smithy/rust/codegen/core/smithy/transformers/RecursiveShapeBoxerTest.kt +++ b/codegen-core/src/test/kotlin/software/amazon/smithy/rust/codegen/core/smithy/transformers/RecursiveShapeBoxerTest.kt @@ -31,7 +31,7 @@ internal class RecursiveShapeBoxerTest { hello: Hello } """.asSmithyModel() - RecursiveShapeBoxer.transform(model) shouldBe model + RecursiveShapeBoxer().transform(model) shouldBe model } @Test @@ -43,7 +43,7 @@ internal class RecursiveShapeBoxerTest { anotherField: Boolean } """.asSmithyModel() - val transformed = RecursiveShapeBoxer.transform(model) + val transformed = RecursiveShapeBoxer().transform(model) val member: MemberShape = transformed.lookup("com.example#Recursive\$RecursiveStruct") member.expectTrait() } @@ -70,7 +70,7 @@ internal class RecursiveShapeBoxerTest { third: SecondTree } """.asSmithyModel() - val transformed = RecursiveShapeBoxer.transform(model) + val transformed = RecursiveShapeBoxer().transform(model) val boxed = transformed.shapes().filter { it.hasTrait() }.toList() boxed.map { it.id.toString().removePrefix("com.example#") }.toSet() shouldBe setOf( "Atom\$add", 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 bea21fd083..1dfc41795d 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 @@ -10,9 +10,10 @@ import org.junit.jupiter.api.Test import org.junit.jupiter.api.assertThrows import software.amazon.smithy.model.Model import software.amazon.smithy.model.shapes.StructureShape -import software.amazon.smithy.rust.codegen.core.rustlang.RustWriter +import software.amazon.smithy.model.shapes.UnionShape import software.amazon.smithy.rust.codegen.core.smithy.generators.StructureGenerator import software.amazon.smithy.rust.codegen.core.smithy.generators.UnionGenerator +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 @@ -42,23 +43,31 @@ class RecursiveShapesIntegrationTest { third: SecondTree } """.asSmithyModel() + val check = { input: Model -> + val symbolProvider = testSymbolProvider(model) + val project = TestWorkspace.testProject(symbolProvider) val structures = listOf("Expr", "SecondTree").map { input.lookup("com.example#$it") } - val writer = RustWriter.forModule("model") - val symbolProvider = testSymbolProvider(input) - structures.forEach { - StructureGenerator(input, symbolProvider, writer, it).render() + structures.forEach { struct -> + project.moduleFor(struct) { + StructureGenerator(input, symbolProvider, this, struct, emptyList()).render() + } + } + input.lookup("com.example#Atom").also { atom -> + project.moduleFor(atom) { + UnionGenerator(input, symbolProvider, this, atom).render() + } } - UnionGenerator(input, symbolProvider, writer, input.lookup("com.example#Atom")).render() - writer + project } - val unmodifiedWriter = check(model) + val unmodifiedProject = check(model) val output = assertThrows { - unmodifiedWriter.compileAndTest(expectFailure = true) + unmodifiedProject.compileAndTest(expectFailure = true) } - output.message shouldContain "has infinite size" + // THIS IS A LOAD-BEARING shouldContain! If the compiler error changes then this will break! + output.message shouldContain "have infinite size" - val fixedWriter = check(RecursiveShapeBoxer.transform(model)) - fixedWriter.compileAndTest() + val fixedProject = check(RecursiveShapeBoxer().transform(model)) + fixedProject.compileAndTest() } } diff --git a/codegen-server-test/build.gradle.kts b/codegen-server-test/build.gradle.kts index b2841452c1..693ed13c82 100644 --- a/codegen-server-test/build.gradle.kts +++ b/codegen-server-test/build.gradle.kts @@ -38,6 +38,7 @@ dependencies { val allCodegenTests = "../codegen-core/common-test-models".let { commonModels -> listOf( CodegenTest("crate#Config", "naming_test_ops", 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", @@ -76,12 +77,6 @@ val allCodegenTests = "../codegen-core/common-test-models".let { commonModels -> "rest_json_validation", extraConfig = """, "codegen": { "ignoreUnsupportedConstraints": true } """, ), - CodegenTest( - "aws.protocoltests.extras.restjson.validation#MalformedRangeValidation", - "malformed_range_extras", - extraConfig = """, "codegen": { "ignoreUnsupportedConstraints": true } """, - imports = listOf("$commonModels/malformed-range-extras.smithy"), - ), CodegenTest("aws.protocoltests.json10#JsonRpc10", "json_rpc10"), CodegenTest( "aws.protocoltests.json#JsonProtocol", diff --git a/codegen-server-test/python/build.gradle.kts b/codegen-server-test/python/build.gradle.kts index fede26a024..35144f42f0 100644 --- a/codegen-server-test/python/build.gradle.kts +++ b/codegen-server-test/python/build.gradle.kts @@ -42,6 +42,26 @@ 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", + 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", + // extraConfig = """, "codegen": { "ignoreUnsupportedConstraints": true } """, + // ), + // TODO(https://github.com/awslabs/smithy-rs/issues/2479) + // CodegenTest("aws.protocoltests.json10#JsonRpc10", "json_rpc10"), ) } diff --git a/codegen-server-test/python/model/pokemon.smithy b/codegen-server-test/python/model/pokemon.smithy index 84ce9fff86..4ad42a0b69 100644 --- a/codegen-server-test/python/model/pokemon.smithy +++ b/codegen-server-test/python/model/pokemon.smithy @@ -11,17 +11,19 @@ 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], + version: "2021-12-01" + resources: [PokemonSpecies] operations: [ - GetServerStatistics, - DoNothing, - CheckHealth, + GetServerStatistics + DoNothing + CheckHealth StreamPokemonRadio ], } @@ -30,13 +32,13 @@ service PokemonService { @readonly @http(uri: "/radio", method: "GET") operation StreamPokemonRadio { - output: StreamPokemonRadioOutput, + output: StreamPokemonRadioOutput } @output structure StreamPokemonRadioOutput { @httpPayload - data: StreamingBlob, + data: StreamingBlob } @streaming diff --git a/codegen-server/build.gradle.kts b/codegen-server/build.gradle.kts index 8dd6dc5d18..f5b2f5bc29 100644 --- a/codegen-server/build.gradle.kts +++ b/codegen-server/build.gradle.kts @@ -26,6 +26,10 @@ dependencies { implementation(project(":codegen-core")) implementation("software.amazon.smithy:smithy-aws-traits:$smithyVersion") implementation("software.amazon.smithy:smithy-protocol-test-traits:$smithyVersion") + + // `smithy.framework#ValidationException` is defined here, which is used in `constraints.smithy`, which is used + // in `CustomValidationExceptionWithReasonDecoratorTest`. + testImplementation("software.amazon.smithy:smithy-validation-model:$smithyVersion") } tasks.compileKotlin { kotlinOptions.jvmTarget = "1.8" } diff --git a/codegen-server/python/build.gradle.kts b/codegen-server/python/build.gradle.kts index 5a23bd5d7d..ac792966a9 100644 --- a/codegen-server/python/build.gradle.kts +++ b/codegen-server/python/build.gradle.kts @@ -24,10 +24,12 @@ val smithyVersion: String by project dependencies { implementation(project(":codegen-core")) - implementation(project(":codegen-client")) implementation(project(":codegen-server")) implementation("software.amazon.smithy:smithy-aws-traits:$smithyVersion") implementation("software.amazon.smithy:smithy-protocol-test-traits:$smithyVersion") + + // `smithy.framework#ValidationException` is defined here, which is used in `PythonServerTypesTest`. + testImplementation("software.amazon.smithy:smithy-validation-model:$smithyVersion") } tasks.compileKotlin { kotlinOptions.jvmTarget = "1.8" } 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 8fd2a07f58..216f21a4f5 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 @@ -1,4 +1,3 @@ - /* * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. * SPDX-License-Identifier: Apache-2.0 @@ -7,7 +6,6 @@ package software.amazon.smithy.rust.codegen.server.python.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 @@ -16,21 +14,34 @@ 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.rust.codegen.core.rustlang.RustModule -import software.amazon.smithy.rust.codegen.core.rustlang.RustWriter +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.SymbolVisitorConfig +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.PythonApplicationGenerator import software.amazon.smithy.rust.codegen.server.python.smithy.generators.PythonServerEnumGenerator +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.PythonServerServiceGenerator 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.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.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.traits.isReachableFromOperationInput /** * Entrypoint for Python server-side code generation. This class will walk the in-memory model and @@ -41,15 +52,16 @@ import software.amazon.smithy.rust.codegen.server.smithy.protocols.ServerProtoco */ class PythonServerCodegenVisitor( context: PluginContext, - codegenDecorator: ServerCodegenDecorator, + private val codegenDecorator: ServerCodegenDecorator, ) : ServerCodegenVisitor(context, codegenDecorator) { init { - val symbolVisitorConfig = - SymbolVisitorConfig( + val rustSymbolProviderConfig = + RustSymbolProviderConfig( runtimeConfig = settings.runtimeConfig, renameExceptions = false, nullabilityCheckMode = NullableIndex.CheckMode.SERVER, + moduleProvider = ServerModuleProvider, ) val baseModel = baselineTransform(context.model) val service = settings.getService(baseModel) @@ -70,25 +82,35 @@ class PythonServerCodegenVisitor( settings = settings.copy(codegenConfig = settings.codegenConfig.copy(publicConstrainedTypes = false)) fun baseSymbolProviderFactory( + settings: ServerRustSettings, model: Model, serviceShape: ServiceShape, - symbolVisitorConfig: SymbolVisitorConfig, + rustSymbolProviderConfig: RustSymbolProviderConfig, publicConstrainedTypes: Boolean, - ) = PythonCodegenServerPlugin.baseSymbolProvider(model, serviceShape, symbolVisitorConfig, publicConstrainedTypes) + includeConstraintShapeProvider: Boolean, + codegenDecorator: ServerCodegenDecorator, + ) = RustServerCodegenPythonPlugin.baseSymbolProvider(settings, model, serviceShape, rustSymbolProviderConfig, publicConstrainedTypes, includeConstraintShapeProvider, codegenDecorator) val serverSymbolProviders = ServerSymbolProviders.from( + settings, model, service, - symbolVisitorConfig, + rustSymbolProviderConfig, settings.codegenConfig.publicConstrainedTypes, + codegenDecorator, ::baseSymbolProviderFactory, ) // Override `codegenContext` which carries the various symbol providers. + val moduleDocProvider = codegenDecorator.moduleDocumentationCustomization( + codegenContext, + PythonServerModuleDocProvider(ServerModuleDocProvider(codegenContext)), + ) codegenContext = ServerCodegenContext( model, serverSymbolProviders.symbolProvider, + moduleDocProvider, service, protocol, settings, @@ -99,7 +121,12 @@ class PythonServerCodegenVisitor( ) // Override `rustCrate` which carries the symbolProvider. - rustCrate = RustCrate(context.fileManifest, codegenContext.symbolProvider, settings.codegenConfig) + rustCrate = RustCrate( + context.fileManifest, + codegenContext.symbolProvider, + settings.codegenConfig, + codegenContext.expectModuleDocProvider(), + ) // Override `protocolGenerator` which carries the symbolProvider. protocolGenerator = protocolGeneratorFactory.buildProtocolGenerator(codegenContext) } @@ -119,7 +146,18 @@ 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(CodegenTarget.SERVER) + PythonServerStructureGenerator(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) } @@ -131,8 +169,8 @@ class PythonServerCodegenVisitor( * Although raw strings require no code generation, enums are actually [EnumTrait] applied to string shapes. */ override fun stringShape(shape: StringShape) { - fun pythonServerEnumGeneratorFactory(codegenContext: ServerCodegenContext, writer: RustWriter, shape: StringShape) = - PythonServerEnumGenerator(codegenContext, writer, shape) + fun pythonServerEnumGeneratorFactory(codegenContext: ServerCodegenContext, shape: StringShape) = + PythonServerEnumGenerator(codegenContext, shape, validationExceptionConversionGenerator) stringShape(shape, ::pythonServerEnumGeneratorFactory) } @@ -144,7 +182,36 @@ class PythonServerCodegenVisitor( * Note: this does not generate serializers */ override fun unionShape(shape: UnionShape) { - throw CodegenException("Union shapes are not supported in Python yet") + logger.info("[python-server-codegen] Generating an union shape $shape") + rustCrate.useShapeWriter(shape) { + PythonServerUnionGenerator(model, codegenContext.symbolProvider, this, shape, renderUnknownVariant = false).render() + } + + if (shape.isReachableFromOperationInput() && shape.canReachConstrainedShape( + model, + codegenContext.symbolProvider, + ) + ) { + logger.info("[python-server-codegen] Generating an unconstrained type for union shape $shape") + rustCrate.withModule(ServerRustModule.UnconstrainedModule) modelsModuleWriter@{ + UnconstrainedUnionGenerator( + codegenContext, + rustCrate.createInlineModuleCreator(), + this@modelsModuleWriter, + shape, + ).render() + } + } + + if (shape.isEventStream()) { + rustCrate.withModule(ServerRustModule.Error) { + ServerOperationErrorGenerator(model, codegenContext.symbolProvider, shape).render(this) + } + } + } + + override fun protocolTests() { + logger.warning("[python-server-codegen] Protocol tests are disabled for this language") } /** @@ -157,21 +224,26 @@ class PythonServerCodegenVisitor( * - Python operation handlers */ override fun serviceShape(shape: ServiceShape) { + super.serviceShape(shape) + logger.info("[python-server-codegen] Generating a service $shape") - PythonServerServiceGenerator( - rustCrate, - protocolGenerator, - protocolGeneratorFactory.support(), - protocolGeneratorFactory.protocol(codegenContext) as ServerProtocol, - codegenContext, - ) - .render() + + val serverProtocol = protocolGeneratorFactory.protocol(codegenContext) as ServerProtocol + rustCrate.withModule(PythonServerRustModule.PythonServerApplication) { + PythonApplicationGenerator(codegenContext, serverProtocol) + .render(this) + } } override fun operationShape(shape: OperationShape) { super.operationShape(shape) - rustCrate.withModule(RustModule.public("python_operation_adaptor")) { + + rustCrate.withModule(PythonServerRustModule.PythonOperationAdapter) { PythonServerOperationHandlerGenerator(codegenContext, shape).render(this) } + + rustCrate.withModule(ServerRustModule.Error) { + PythonServerOperationErrorGenerator(codegenContext.model, codegenContext.symbolProvider, shape).render(this) + } } } 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 new file mode 100644 index 0000000000..c5438b4a9f --- /dev/null +++ b/codegen-server/python/src/main/kotlin/software/amazon/smithy/rust/codegen/server/python/smithy/PythonServerRustModule.kt @@ -0,0 +1,31 @@ +/* + * 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.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 PythonServerRustModule { + val PythonModuleExport = RustModule.public("python_module_export") + val PythonOperationAdapter = RustModule.public("python_operation_adaptor") + val PythonServerApplication = RustModule.public("python_server_application") +} + +class PythonServerModuleDocProvider(private val base: ModuleDocProvider) : ModuleDocProvider { + override fun docsWriter(module: RustModule.LeafModule): Writable? { + val strDoc: (String) -> Writable = { str -> writable { docs(str) } } + 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 + else -> base.docsWriter(module) + } + } +} diff --git a/codegen-server/python/src/main/kotlin/software/amazon/smithy/rust/codegen/server/python/smithy/PythonServerSymbolProvider.kt b/codegen-server/python/src/main/kotlin/software/amazon/smithy/rust/codegen/server/python/smithy/PythonServerSymbolProvider.kt index ca750bbb5c..f01e9a41c4 100644 --- a/codegen-server/python/src/main/kotlin/software/amazon/smithy/rust/codegen/server/python/smithy/PythonServerSymbolProvider.kt +++ b/codegen-server/python/src/main/kotlin/software/amazon/smithy/rust/codegen/server/python/smithy/PythonServerSymbolProvider.kt @@ -22,18 +22,44 @@ import software.amazon.smithy.model.shapes.UnionShape import software.amazon.smithy.rust.codegen.core.rustlang.RustMetadata 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.SymbolVisitorConfig 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.ConstrainedShapeSymbolProvider +import software.amazon.smithy.rust.codegen.server.smithy.ServerRustSettings +import java.util.logging.Logger + +/* Returns the Python implementation of the ByteStream shape or the original symbol that is provided in input. */ +private fun toPythonByteStreamSymbolOrOriginal(model: Model, config: RustSymbolProviderConfig, initial: Symbol, shape: Shape): Symbol { + if (shape !is MemberShape) { + return initial + } + + val target = model.expectShape(shape.target) + val container = model.expectShape(shape.container) + + 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)) { + PythonServerRuntimeType.byteStream(config.runtimeConfig).toSymbol() + } else { + initial + } +} /** - * Symbol visitor allowing that recursively replace symbols in nested shapes. + * Symbol provider 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`). @@ -44,34 +70,18 @@ import software.amazon.smithy.rust.codegen.core.util.isStreaming * `aws_smithy_http_server_python::types`. */ class PythonServerSymbolVisitor( - private val model: Model, + settings: ServerRustSettings, + model: Model, serviceShape: ServiceShape?, - config: SymbolVisitorConfig, -) : SymbolVisitor(model, serviceShape, config) { - private val runtimeConfig = config().runtimeConfig + 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)) { - PythonServerRuntimeType.byteStream(config().runtimeConfig).toSymbol() - } else { - initial - } + return toPythonByteStreamSymbolOrOriginal(model, config, initial, shape) } override fun timestampShape(shape: TimestampShape?): Symbol { @@ -87,6 +97,27 @@ class PythonServerSymbolVisitor( } } +/** + * Constrained symbol provider that recursively replace symbols in nested shapes. + * + * This symbol provider extends the `ConstrainedShapeSymbolProvider` to ensure constraints are + * applied properly and swaps out shapes that do not implement `pyo3::PyClass` with their + * wrappers. + * + * See `PythonServerSymbolVisitor` documentation for more info. + */ +class PythonConstrainedShapeSymbolProvider( + base: RustSymbolProvider, + serviceShape: ServiceShape, + publicConstrainedTypes: Boolean, +) : ConstrainedShapeSymbolProvider(base, serviceShape, publicConstrainedTypes) { + + override fun toSymbol(shape: Shape): Symbol { + val initial = super.toSymbol(shape) + return toPythonByteStreamSymbolOrOriginal(model, config, initial, shape) + } +} + /** * SymbolProvider to drop the PartialEq bounds in streaming shapes * @@ -95,24 +126,28 @@ class PythonServerSymbolVisitor( * * Note that since streaming members can only be used on the root shape, this can only impact input and output shapes. */ -class PythonStreamingShapeMetadataProvider(private val base: RustSymbolProvider, private val model: Model) : SymbolMetadataProvider(base) { +class PythonStreamingShapeMetadataProvider(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 + } 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 + } else { + baseMetadata + } } override fun memberMeta(memberShape: MemberShape) = base.toSymbol(memberShape).expectRustMetadata() override fun enumMeta(stringShape: StringShape) = base.toSymbol(stringShape).expectRustMetadata() - 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() 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 new file mode 100644 index 0000000000..c5606b5d38 --- /dev/null +++ b/codegen-server/python/src/main/kotlin/software/amazon/smithy/rust/codegen/server/python/smithy/PythonType.kt @@ -0,0 +1,176 @@ +/* + * 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.rust.codegen.core.rustlang.RustType + +/** + * A hierarchy of Python types handled by Smithy codegen. + * + * Mostly copied from [RustType] and modified for Python accordingly. + */ +sealed class PythonType { + /** + * A Python type that contains [member], another [PythonType]. + * Used to generically operate over shapes that contain other shape. + */ + sealed interface Container { + val member: PythonType + val namespace: String? + val name: String + } + + /** + * Name refers to the top-level type for import purposes. + */ + abstract val name: String + + open val namespace: String? = null + + object None : PythonType() { + override val name: String = "None" + } + + object Bool : PythonType() { + override val name: String = "bool" + } + + object Int : PythonType() { + override val name: String = "int" + } + + object Float : PythonType() { + override val name: String = "float" + } + + object Str : PythonType() { + override val name: String = "str" + } + + object Any : PythonType() { + override val name: String = "Any" + override val namespace: String = "typing" + } + + data class List(override val member: PythonType) : PythonType(), Container { + override val name: String = "List" + override val namespace: String = "typing" + } + + data class Dict(val key: PythonType, override val member: PythonType) : PythonType(), Container { + override val name: String = "Dict" + override val namespace: String = "typing" + } + + data class Set(override val member: PythonType) : PythonType(), Container { + override val name: String = "Set" + override val namespace: String = "typing" + } + + data class Optional(override val member: PythonType) : PythonType(), Container { + override val name: String = "Optional" + override val namespace: String = "typing" + } + + data class Awaitable(override val member: PythonType) : PythonType(), Container { + override val name: String = "Awaitable" + override val namespace: String = "typing" + } + + data class Callable(val args: kotlin.collections.List, val rtype: PythonType) : PythonType() { + override val name: String = "Callable" + override val namespace: String = "typing" + } + + data class Union(val args: kotlin.collections.List) : PythonType() { + override val name: String = "Union" + 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__" + + override val namespace: String? = rustNamespace?.split("::")?.joinToString(".") { + when (it) { + "crate" -> pythonRootModulePlaceholder + // 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 + else -> it + } + } + } +} + +/** + * Return corresponding [PythonType] for a [RustType]. + */ +fun RustType.pythonType(): 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() + } + +/** + * Render this type, including references and generic parameters. + * It generates something like `typing.Dict[String, String]`. + */ +fun PythonType.render(fullyQualified: Boolean = true): String { + val namespace = if (fullyQualified) { + this.namespace?.let { "$it." } ?: "" + } else { + "" + } + val base = when (this) { + is PythonType.None -> this.name + is PythonType.Bool -> this.name + is PythonType.Float -> this.name + is PythonType.Int -> this.name + is PythonType.Str -> this.name + is PythonType.Any -> this.name + is PythonType.Opaque -> this.name + is PythonType.List -> "${this.name}[${this.member.render(fullyQualified)}]" + is PythonType.Dict -> "${this.name}[${this.key.render(fullyQualified)}, ${this.member.render(fullyQualified)}]" + 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.Callable -> { + val args = this.args.joinToString(", ") { it.render(fullyQualified) } + val rtype = this.rtype.render(fullyQualified) + "${this.name}[[$args], $rtype]" + } + is PythonType.Union -> { + val args = this.args.joinToString(", ") { it.render(fullyQualified) } + "${this.name}[$args]" + } + } + return "$namespace$base" +} + +/** + * Renders [PythonType] with proper escaping for Docstrings. + */ +fun PythonType.renderAsDocstring(): String = + this.render() + .replace("[", "\\[") + .replace("]", "\\]") diff --git a/codegen-server/python/src/main/kotlin/software/amazon/smithy/rust/codegen/server/python/smithy/PythonCodegenServerPlugin.kt b/codegen-server/python/src/main/kotlin/software/amazon/smithy/rust/codegen/server/python/smithy/RustServerCodegenPythonPlugin.kt similarity index 66% rename from codegen-server/python/src/main/kotlin/software/amazon/smithy/rust/codegen/server/python/smithy/PythonCodegenServerPlugin.kt rename to codegen-server/python/src/main/kotlin/software/amazon/smithy/rust/codegen/server/python/smithy/RustServerCodegenPythonPlugin.kt index 7e277185f7..faf01b1fcf 100644 --- a/codegen-server/python/src/main/kotlin/software/amazon/smithy/rust/codegen/server/python/smithy/PythonCodegenServerPlugin.kt +++ b/codegen-server/python/src/main/kotlin/software/amazon/smithy/rust/codegen/server/python/smithy/RustServerCodegenPythonPlugin.kt @@ -14,28 +14,35 @@ import software.amazon.smithy.rust.codegen.core.rustlang.RustReservedWordSymbolP 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.SymbolVisitor -import software.amazon.smithy.rust.codegen.core.smithy.SymbolVisitorConfig +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 -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 java.util.logging.Level import java.util.logging.Logger /** - * Rust with Python bindings Codegen Plugin. + * Rust Server with Python 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 PythonCodegenServerPlugin : SmithyBuildPlugin { +class RustServerCodegenPythonPlugin : SmithyBuildPlugin { private val logger = Logger.getLogger(javaClass.name) override fun getName(): String = "rust-server-codegen-python" + /** + * See [software.amazon.smithy.rust.codegen.server.smithy.RustServerCodegenPlugin]. + */ override fun execute(context: PluginContext) { // Suppress extremely noisy logs about reserved words Logger.getLogger(ReservedWordSymbolProvider::class.java.name).level = Level.OFF @@ -47,7 +54,10 @@ class PythonCodegenServerPlugin : SmithyBuildPlugin { val codegenDecorator: CombinedServerCodegenDecorator = CombinedServerCodegenDecorator.fromClasspath( context, - CombinedServerCodegenDecorator(DECORATORS + ServerRequiredCustomizations()), + ServerRequiredCustomizations(), + SmithyValidationExceptionDecorator(), + CustomValidationExceptionWithReasonDecorator(), + *DECORATORS, ) // PythonServerCodegenVisitor is the main driver of code generation that traverses the model and generates code @@ -57,36 +67,40 @@ class PythonCodegenServerPlugin : SmithyBuildPlugin { 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. + * See [software.amazon.smithy.rust.codegen.server.smithy.RustServerCodegenPlugin]. */ fun baseSymbolProvider( + settings: ServerRustSettings, model: Model, serviceShape: ServiceShape, - symbolVisitorConfig: SymbolVisitorConfig, + rustSymbolProviderConfig: RustSymbolProviderConfig, constrainedTypes: Boolean = true, + includeConstrainedShapeProvider: Boolean = true, + codegenDecorator: ServerCodegenDecorator, ) = // Rename a set of symbols that do not implement `PyClass` and have been wrapped in // `aws_smithy_http_server_python::types`. - PythonServerSymbolVisitor(model, serviceShape = serviceShape, config = symbolVisitorConfig) + PythonServerSymbolVisitor(settings, model, serviceShape = serviceShape, config = rustSymbolProviderConfig) // Generate public constrained types for directly constrained shapes. // In the Python 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 (constrainedTypes) ConstrainedShapeSymbolProvider(it, model, serviceShape) else it } + .let { + if (includeConstrainedShapeProvider) PythonConstrainedShapeSymbolProvider(it, serviceShape, constrainedTypes) else it + } // Generate different types for EventStream shapes (e.g. transcribe streaming) - .let { EventStreamSymbolProvider(symbolVisitorConfig.runtimeConfig, it, model, CodegenTarget.SERVER) } + .let { EventStreamSymbolProvider(rustSymbolProviderConfig.runtimeConfig, it, CodegenTarget.SERVER) } // Add Rust attributes (like `#[derive(PartialEq)]`) to generated shapes - .let { BaseSymbolMetadataProvider(it, model, additionalAttributes = listOf()) } + .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, model, constrainedTypes) } + .let { ConstrainedShapeSymbolMetadataProvider(it, constrainedTypes) } // Streaming shapes need different derives (e.g. they cannot derive Eq) - .let { PythonStreamingShapeMetadataProvider(it, model) } + .let { PythonStreamingShapeMetadataProvider(it) } // Derive `Eq` and `Hash` if possible. - .let { DeriveEqAndHashSymbolMetadataProvider(it, model) } + .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, model) } + .let { RustReservedWordSymbolProvider(it, ServerReservedWords) } + // Allows decorators to inject a custom symbol provider + .let { codegenDecorator.symbolProvider(it) } } } 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 ff07ba1207..9e353db2cd 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 @@ -98,6 +98,7 @@ class PubUsePythonTypesDecorator : ServerCodegenDecorator { /** * Generates `pyproject.toml` for the crate. * - Configures Maturin as the build system + * - Configures Python source directory */ class PyProjectTomlDecorator : ServerCodegenDecorator { override val name: String = "PyProjectTomlDecorator" @@ -110,6 +111,11 @@ class PyProjectTomlDecorator : ServerCodegenDecorator { "requires" to listOfNotNull("maturin>=0.14,<0.15"), "build-backend" to "maturin", ).toMap(), + "tool" to listOfNotNull( + "maturin" to listOfNotNull( + "python-source" to "python", + ).toMap(), + ).toMap(), ) writeWithNoFormatting(TomlWriter().write(config)) } @@ -134,7 +140,61 @@ class PyO3ExtensionModuleDecorator : ServerCodegenDecorator { } } -val DECORATORS = listOf( +/** + * Generates `__init__.py` for the Python source. + * + * This file allows Python module to be imported like: + * ``` + * import pokemon_service_server_sdk + * pokemon_service_server_sdk.App() + * ``` + * instead of: + * ``` + * from pokemon_service_server_sdk import pokemon_service_server_sdk + * ``` + */ +class InitPyDecorator : ServerCodegenDecorator { + override val name: String = "InitPyDecorator" + override val order: Byte = 0 + + override fun extras(codegenContext: ServerCodegenContext, rustCrate: RustCrate) { + val libName = codegenContext.settings.moduleName.toSnakeCase() + + rustCrate.withFile("python/$libName/__init__.py") { + writeWithNoFormatting( + """ + from .$libName import * + + __doc__ = $libName.__doc__ + if hasattr($libName, "__all__"): + __all__ = $libName.__all__ + """.trimIndent(), + ) + } + } +} + +/** + * Generates `py.typed` for the Python source. + * + * This marker file is required to be PEP 561 compliant stub package. + * Type definitions will be ignored by `mypy` if the package is not PEP 561 compliant: + * https://mypy.readthedocs.io/en/stable/running_mypy.html#missing-library-stubs-or-py-typed-marker + */ +class PyTypedMarkerDecorator : ServerCodegenDecorator { + override val name: String = "PyTypedMarkerDecorator" + override val order: Byte = 0 + + override fun extras(codegenContext: ServerCodegenContext, rustCrate: RustCrate) { + val libName = codegenContext.settings.moduleName.toSnakeCase() + + rustCrate.withFile("python/$libName/py.typed") { + writeWithNoFormatting("") + } + } +} + +val DECORATORS = arrayOf( /** * Add the [InternalServerError] error to all operations. * This is done because the Python interpreter can raise exceptions during execution. @@ -150,4 +210,8 @@ val DECORATORS = listOf( PyProjectTomlDecorator(), // Add PyO3 extension module feature. PyO3ExtensionModuleDecorator(), + // Generate `__init__.py` for the Python source. + InitPyDecorator(), + // Generate `py.typed` for the Python source. + PyTypedMarkerDecorator(), ) 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 793118b441..cc4804bb21 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 @@ -5,6 +5,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.RustWriter @@ -12,9 +13,6 @@ 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.CodegenContext -import software.amazon.smithy.rust.codegen.core.smithy.ErrorsModule -import software.amazon.smithy.rust.codegen.core.smithy.InputsModule -import software.amazon.smithy.rust.codegen.core.smithy.OutputsModule import software.amazon.smithy.rust.codegen.core.smithy.RuntimeType import software.amazon.smithy.rust.codegen.core.util.getTrait import software.amazon.smithy.rust.codegen.core.util.inputShape @@ -22,8 +20,13 @@ import software.amazon.smithy.rust.codegen.core.util.outputShape 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.PythonType +import software.amazon.smithy.rust.codegen.server.python.smithy.renderAsDocstring 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.smithy.ServerRustModule.Error as ErrorModule +import software.amazon.smithy.rust.codegen.server.smithy.ServerRustModule.Input as InputModule +import software.amazon.smithy.rust.codegen.server.smithy.ServerRustModule.Output as OutputModule /** * Generates a Python compatible application and server that can be configured from Python. @@ -64,8 +67,13 @@ import software.amazon.smithy.rust.codegen.server.smithy.generators.protocol.Ser class PythonApplicationGenerator( codegenContext: CodegenContext, private val protocol: ServerProtocol, - private val operations: List, ) { + 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 @@ -103,6 +111,9 @@ class PythonApplicationGenerator( """ ##[#{pyo3}::pyclass] ##[derive(Debug)] + /// :generic Ctx: + /// :extends typing.Generic\[Ctx\]: + /// :rtype None: pub struct App { handlers: #{HashMap}, middlewares: Vec<#{SmithyPython}::PyMiddlewareHandler>, @@ -239,6 +250,12 @@ class PythonApplicationGenerator( """, *codegenScope, ) { + val middlewareRequest = PythonType.Opaque("Request", "crate::middleware") + val middlewareResponse = PythonType.Opaque("Response", "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") + rustTemplate( """ /// Create a new [App]. @@ -246,12 +263,20 @@ class PythonApplicationGenerator( pub fn new() -> Self { Self::default() } + /// Register a context object that will be shared between handlers. + /// + /// :param context Ctx: + /// :rtype ${PythonType.None.renderAsDocstring()}: ##[pyo3(text_signature = "(${'$'}self, context)")] pub fn context(&mut self, context: #{pyo3}::PyObject) { self.context = Some(context); } + /// Register a Python function to be executed inside a Tower middleware layer. + /// + /// :param func ${middlewareFunc.renderAsDocstring()}: + /// :rtype ${PythonType.None.renderAsDocstring()}: ##[pyo3(text_signature = "(${'$'}self, func)")] pub fn middleware(&mut self, py: #{pyo3}::Python, func: #{pyo3}::PyObject) -> #{pyo3}::PyResult<()> { let handler = #{SmithyPython}::PyMiddlewareHandler::new(py, func)?; @@ -263,8 +288,16 @@ class PythonApplicationGenerator( self.middlewares.push(handler); Ok(()) } + /// Main entrypoint: start the server on multiple workers. - ##[pyo3(text_signature = "(${'$'}self, address, port, backlog, workers, tls)")] + /// + /// :param address ${PythonType.Optional(PythonType.Str).renderAsDocstring()}: + /// :param port ${PythonType.Optional(PythonType.Int).renderAsDocstring()}: + /// :param backlog ${PythonType.Optional(PythonType.Int).renderAsDocstring()}: + /// :param workers ${PythonType.Optional(PythonType.Int).renderAsDocstring()}: + /// :param tls ${PythonType.Optional(tlsConfig).renderAsDocstring()}: + /// :rtype ${PythonType.None.renderAsDocstring()}: + ##[pyo3(text_signature = "(${'$'}self, address=None, port=None, backlog=None, workers=None, tls=None)")] pub fn run( &mut self, py: #{pyo3}::Python, @@ -277,7 +310,10 @@ class PythonApplicationGenerator( use #{SmithyPython}::PyApp; self.run_server(py, address, port, backlog, workers, tls) } + /// Lambda entrypoint: start the server on Lambda. + /// + /// :rtype ${PythonType.None.renderAsDocstring()}: ##[pyo3(text_signature = "(${'$'}self)")] pub fn run_lambda( &mut self, @@ -286,8 +322,9 @@ class PythonApplicationGenerator( use #{SmithyPython}::PyApp; self.run_lambda_handler(py) } + /// Build the service and start a single worker. - ##[pyo3(text_signature = "(${'$'}self, socket, worker_number, tls)")] + ##[pyo3(text_signature = "(${'$'}self, socket, worker_number, tls=None)")] pub fn start_worker( &mut self, py: pyo3::Python, @@ -306,10 +343,31 @@ class PythonApplicationGenerator( operations.map { operation -> val operationName = symbolProvider.toSymbol(operation).name val name = operationName.toSnakeCase() + + val input = PythonType.Opaque("${operationName}Input", "crate::input") + val output = PythonType.Opaque("${operationName}Output", "crate::output") + val context = PythonType.Opaque("Ctx") + val returnType = PythonType.Union(listOf(output, PythonType.Awaitable(output))) + val handler = PythonType.Union( + listOf( + PythonType.Callable( + listOf(input, context), + returnType, + ), + PythonType.Callable( + listOf(input), + returnType, + ), + ), + ) + rustTemplate( """ /// Method to register `$name` 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<()> { use #{SmithyPython}::PyApp; @@ -338,12 +396,12 @@ class PythonApplicationGenerator( ) writer.rust( """ - /// from $libName import ${InputsModule.name} - /// from $libName import ${OutputsModule.name} + /// from $libName import ${InputModule.name} + /// from $libName import ${OutputModule.name} """.trimIndent(), ) if (operations.any { it.errors.isNotEmpty() }) { - writer.rust("""/// from $libName import ${ErrorsModule.name}""".trimIndent()) + writer.rust("""/// from $libName import ${ErrorModule.name}""".trimIndent()) } writer.rust( """ @@ -382,7 +440,9 @@ class PythonApplicationGenerator( val operationDocumentation = it.getTrait()?.value val ret = if (!operationDocumentation.isNullOrBlank()) { operationDocumentation.replace("#", "##").prependIndent("/// ## ") + "\n" - } else "" + } else { + "" + } ret + """ /// ${it.signature()}: @@ -397,8 +457,8 @@ class PythonApplicationGenerator( private fun OperationShape.signature(): String { val inputSymbol = symbolProvider.toSymbol(inputShape(model)) val outputSymbol = symbolProvider.toSymbol(outputShape(model)) - val inputT = "${InputsModule.name}::${inputSymbol.name}" - val outputT = "${OutputsModule.name}::${outputSymbol.name}" + val inputT = "${InputModule.name}::${inputSymbol.name}" + val outputT = "${OutputModule.name}::${outputSymbol.name}" val operationName = symbolProvider.toSymbol(this).name.toSnakeCase() return "@app.$operationName\n/// def $operationName(input: $inputT, ctx: Context) -> $outputT" } diff --git a/codegen-server/python/src/main/kotlin/software/amazon/smithy/rust/codegen/server/python/smithy/generators/PythonServerEnumGenerator.kt b/codegen-server/python/src/main/kotlin/software/amazon/smithy/rust/codegen/server/python/smithy/generators/PythonServerEnumGenerator.kt index 90bdcc6e44..ac12cc0df3 100644 --- a/codegen-server/python/src/main/kotlin/software/amazon/smithy/rust/codegen/server/python/smithy/generators/PythonServerEnumGenerator.kt +++ b/codegen-server/python/src/main/kotlin/software/amazon/smithy/rust/codegen/server/python/smithy/generators/PythonServerEnumGenerator.kt @@ -7,45 +7,39 @@ package software.amazon.smithy.rust.codegen.server.python.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.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.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.generators.EnumGenerator +import software.amazon.smithy.rust.codegen.core.smithy.generators.EnumGeneratorContext import software.amazon.smithy.rust.codegen.core.util.dq import software.amazon.smithy.rust.codegen.server.python.smithy.PythonServerCargoDependency import software.amazon.smithy.rust.codegen.server.smithy.ServerCodegenContext -import software.amazon.smithy.rust.codegen.server.smithy.generators.ServerEnumGenerator +import software.amazon.smithy.rust.codegen.server.smithy.generators.ConstrainedEnum +import software.amazon.smithy.rust.codegen.server.smithy.generators.ValidationExceptionConversionGenerator /** * To share enums defined in Rust with Python, `pyo3` provides the `PyClass` trait. * This class generates enums definitions, implements the `PyClass` trait and adds * some utility functions like `__str__()` and `__repr__()`. */ -class PythonServerEnumGenerator( +class PythonConstrainedEnum( codegenContext: ServerCodegenContext, - private val writer: RustWriter, shape: StringShape, -) : ServerEnumGenerator(codegenContext, writer, shape) { - + validationExceptionConversionGenerator: ValidationExceptionConversionGenerator, +) : ConstrainedEnum(codegenContext, shape, validationExceptionConversionGenerator) { private val pyO3 = PythonServerCargoDependency.PyO3.toType() - override fun render() { - renderPyClass() - super.render() - renderPyO3Methods() - } - - private fun renderPyClass() { - Attribute(pyO3.resolve("pyclass")).render(writer) - } + override fun additionalEnumAttributes(context: EnumGeneratorContext): List = + listOf(Attribute(pyO3.resolve("pyclass"))) - private fun renderPyO3Methods() { - Attribute(pyO3.resolve("pymethods")).render(writer) - writer.rustTemplate( + override fun additionalEnumImpls(context: EnumGeneratorContext): Writable = writable { + Attribute(pyO3.resolve("pymethods")).render(this) + rustTemplate( """ - impl $enumName { + impl ${context.enumName} { #{name_method:W} ##[getter] pub fn value(&self) -> &str { @@ -59,11 +53,11 @@ class PythonServerEnumGenerator( } } """, - "name_method" to renderPyEnumName(), + "name_method" to pyEnumName(context), ) } - private fun renderPyEnumName(): Writable = + private fun pyEnumName(context: EnumGeneratorContext): Writable = writable { rustBlock( """ @@ -72,11 +66,22 @@ class PythonServerEnumGenerator( """, ) { rustBlock("match self") { - sortedMembers.forEach { member -> + context.sortedMembers.forEach { member -> val memberName = member.name()?.name - rust("""$enumName::$memberName => ${memberName?.dq()},""") + rust("""${context.enumName}::$memberName => ${memberName?.dq()},""") } } } } } + +class PythonServerEnumGenerator( + codegenContext: ServerCodegenContext, + shape: StringShape, + validationExceptionConversionGenerator: ValidationExceptionConversionGenerator, +) : EnumGenerator( + codegenContext.model, + codegenContext.symbolProvider, + shape, + PythonConstrainedEnum(codegenContext, shape, validationExceptionConversionGenerator), +) 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 9d9d4d2617..8a622c51ef 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 @@ -10,13 +10,14 @@ import software.amazon.smithy.model.shapes.OperationShape 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.rust.codegen.core.rustlang.RustModule +import software.amazon.smithy.model.shapes.UnionShape import software.amazon.smithy.rust.codegen.core.rustlang.RustWriter 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.toSnakeCase import software.amazon.smithy.rust.codegen.server.python.smithy.PythonServerCargoDependency +import software.amazon.smithy.rust.codegen.server.python.smithy.PythonServerRustModule import software.amazon.smithy.rust.codegen.server.smithy.ServerCodegenContext class PythonServerModuleGenerator( @@ -32,9 +33,7 @@ class PythonServerModuleGenerator( private val libName = codegenContext.settings.moduleName.toSnakeCase() fun render() { - rustCrate.withModule( - RustModule.public("python_module_export", "Export PyO3 symbols in the shared library"), - ) { + rustCrate.withModule(PythonServerRustModule.PythonModuleExport) { rustBlockTemplate( """ ##[#{pyo3}::pymodule] @@ -69,12 +68,20 @@ class PythonServerModuleGenerator( serviceShapes.forEach { shape -> val moduleType = moduleType(shape) if (moduleType != null) { - rustTemplate( - """ - $moduleType.add_class::()?; - """, - *codegenScope, - ) + when (shape) { + is UnionShape -> rustTemplate( + """ + $moduleType.add_class::()?; + """, + *codegenScope, + ) + else -> rustTemplate( + """ + $moduleType.add_class::()?; + """, + *codegenScope, + ) + } } } rustTemplate( @@ -99,6 +106,7 @@ class PythonServerModuleGenerator( let types = #{pyo3}::types::PyModule::new(py, "types")?; types.add_class::<#{SmithyPython}::types::Blob>()?; types.add_class::<#{SmithyPython}::types::DateTime>()?; + types.add_class::<#{SmithyPython}::types::Format>()?; types.add_class::<#{SmithyPython}::types::ByteStream>()?; #{pyo3}::py_run!( py, @@ -185,6 +193,10 @@ class PythonServerModuleGenerator( """ let aws_lambda = #{pyo3}::types::PyModule::new(py, "aws_lambda")?; aws_lambda.add_class::<#{SmithyPython}::lambda::PyLambdaContext>()?; + aws_lambda.add_class::<#{SmithyPython}::lambda::PyClientApplication>()?; + aws_lambda.add_class::<#{SmithyPython}::lambda::PyClientContext>()?; + aws_lambda.add_class::<#{SmithyPython}::lambda::PyCognitoIdentity>()?; + aws_lambda.add_class::<#{SmithyPython}::lambda::PyConfig>()?; pyo3::py_run!( py, aws_lambda, diff --git a/codegen-server/python/src/main/kotlin/software/amazon/smithy/rust/codegen/server/python/smithy/generators/PythonServerOperationErrorGenerator.kt b/codegen-server/python/src/main/kotlin/software/amazon/smithy/rust/codegen/server/python/smithy/generators/PythonServerOperationErrorGenerator.kt index 65e53adb6c..e2f66a8368 100644 --- a/codegen-server/python/src/main/kotlin/software/amazon/smithy/rust/codegen/server/python/smithy/generators/PythonServerOperationErrorGenerator.kt +++ b/codegen-server/python/src/main/kotlin/software/amazon/smithy/rust/codegen/server/python/smithy/generators/PythonServerOperationErrorGenerator.kt @@ -15,7 +15,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.RustSymbolProvider -import software.amazon.smithy.rust.codegen.core.smithy.generators.error.errorSymbol import software.amazon.smithy.rust.codegen.server.python.smithy.PythonServerCargoDependency import software.amazon.smithy.rust.codegen.server.smithy.generators.ServerOperationErrorGenerator @@ -27,13 +26,11 @@ class PythonServerOperationErrorGenerator( private val model: Model, private val symbolProvider: RustSymbolProvider, private val operation: OperationShape, -) : ServerOperationErrorGenerator(model, symbolProvider, symbolProvider.toSymbol(operation), listOf()) { - +) { private val operationIndex = OperationIndex.of(model) private val errors = operationIndex.getErrors(operation) - override fun render(writer: RustWriter) { - super.render(writer) + fun render(writer: RustWriter) { renderFromPyErr(writer) } @@ -52,7 +49,7 @@ class PythonServerOperationErrorGenerator( """, "pyo3" to PythonServerCargoDependency.PyO3.toType(), - "Error" to operation.errorSymbol(symbolProvider), + "Error" to symbolProvider.symbolForOperationError(operation), "From" to RuntimeType.From, "CastPyErrToRustError" to castPyErrToRustError(), ) diff --git a/codegen-server/python/src/main/kotlin/software/amazon/smithy/rust/codegen/server/python/smithy/generators/PythonServerServiceGenerator.kt b/codegen-server/python/src/main/kotlin/software/amazon/smithy/rust/codegen/server/python/smithy/generators/PythonServerServiceGenerator.kt deleted file mode 100644 index 58e87bc388..0000000000 --- a/codegen-server/python/src/main/kotlin/software/amazon/smithy/rust/codegen/server/python/smithy/generators/PythonServerServiceGenerator.kt +++ /dev/null @@ -1,42 +0,0 @@ -/* - * 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.OperationShape -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.smithy.RustCrate -import software.amazon.smithy.rust.codegen.core.smithy.generators.protocol.ProtocolSupport -import software.amazon.smithy.rust.codegen.server.smithy.ServerCodegenContext -import software.amazon.smithy.rust.codegen.server.smithy.generators.ServerServiceGenerator -import software.amazon.smithy.rust.codegen.server.smithy.generators.protocol.ServerProtocol -import software.amazon.smithy.rust.codegen.server.smithy.generators.protocol.ServerProtocolGenerator - -/** - * PythonServerServiceGenerator - * - * Service generator is the main code generation entry point for Smithy services. Individual structures and unions are - * generated in codegen visitor, but this class handles all protocol-specific code generation (i.e. operations). - */ -class PythonServerServiceGenerator( - private val rustCrate: RustCrate, - protocolGenerator: ServerProtocolGenerator, - protocolSupport: ProtocolSupport, - protocol: ServerProtocol, - private val context: ServerCodegenContext, -) : ServerServiceGenerator(rustCrate, protocolGenerator, protocolSupport, protocol, context) { - - override fun renderCombinedErrors(writer: RustWriter, operation: OperationShape) { - PythonServerOperationErrorGenerator(context.model, context.symbolProvider, operation).render(writer) - } - - override fun renderExtras(operations: List) { - rustCrate.withModule(RustModule.public("python_server_application", "Python server and application implementation.")) { - PythonApplicationGenerator(context, protocol, operations) - .render(this) - } - } -} 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 436d956fa2..5f226267f6 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 @@ -23,6 +23,9 @@ import software.amazon.smithy.rust.codegen.core.smithy.generators.StructureGener import software.amazon.smithy.rust.codegen.core.smithy.rustType import software.amazon.smithy.rust.codegen.core.util.hasTrait 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 /** * To share structures defined in Rust with Python, `pyo3` provides the `PyClass` trait. @@ -34,7 +37,7 @@ class PythonServerStructureGenerator( private val symbolProvider: RustSymbolProvider, private val writer: RustWriter, private val shape: StructureShape, -) : StructureGenerator(model, symbolProvider, writer, shape) { +) : StructureGenerator(model, symbolProvider, writer, shape, emptyList()) { private val pyO3 = PythonServerCargoDependency.PyO3.toType() @@ -52,6 +55,7 @@ class PythonServerStructureGenerator( } else { Attribute(pyO3.resolve("pyclass")).render(writer) } + writer.rustTemplate("#{ConstructorSignature:W}", "ConstructorSignature" to renderConstructorSignature()) super.renderStructure() renderPyO3Methods() } @@ -65,11 +69,13 @@ 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)) super.renderStructureMember(writer, member, memberName, memberSymbol) } private fun renderPyO3Methods() { Attribute.AllowClippyNewWithoutDefault.render(writer) + Attribute.AllowClippyTooManyArguments.render(writer) Attribute(pyO3.resolve("pymethods")).render(writer) writer.rustTemplate( """ @@ -107,4 +113,20 @@ class PythonServerStructureGenerator( rust("$memberName,") } } + + private fun renderConstructorSignature(): Writable = + writable { + forEachMember(members) { _, memberName, memberSymbol -> + val memberType = memberSymbol.rustType().pythonType() + rust("/// :param $memberName ${memberType.renderAsDocstring()}:") + } + + rust("/// :rtype ${PythonType.None.renderAsDocstring()}:") + } + + private fun renderSymbolSignature(symbol: Symbol): Writable = + writable { + val pythonType = symbol.rustType().pythonType() + rust("/// :type ${pythonType.renderAsDocstring()}:") + } } 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 new file mode 100644 index 0000000000..df083751ae --- /dev/null +++ b/codegen-server/python/src/main/kotlin/software/amazon/smithy/rust/codegen/server/python/smithy/generators/PythonServerUnionGenerator.kt @@ -0,0 +1,198 @@ +/* + * 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.codegen.core.SymbolProvider +import software.amazon.smithy.model.Model +import software.amazon.smithy.model.shapes.MemberShape +import software.amazon.smithy.model.shapes.UnionShape +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.isCopy +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.expectRustMetadata +import software.amazon.smithy.rust.codegen.core.smithy.generators.UnionGenerator +import software.amazon.smithy.rust.codegen.core.smithy.rustType +import software.amazon.smithy.rust.codegen.core.util.isTargetUnit +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 + +/* + * Generate unions that are compatible with Python by wrapping the Rust implementation into + * a new structure and implementing `IntoPy` and `FromPyObject` to ensure the ability to extract + * the union inside the Python context. + */ +class PythonServerUnionGenerator( + model: Model, + private val symbolProvider: SymbolProvider, + private val writer: RustWriter, + shape: UnionShape, + private val renderUnknownVariant: Boolean = true, +) : UnionGenerator(model, symbolProvider, writer, shape, renderUnknownVariant) { + private val sortedMembers: List = shape.allMembers.values.sortedBy { symbolProvider.toMemberName(it) } + private val unionSymbol = symbolProvider.toSymbol(shape) + + private val pyo3 = PythonServerCargoDependency.PyO3.toType() + + override fun render() { + super.render() + renderPyUnionStruct() + renderPyUnionImpl() + renderPyObjectConverters() + } + + private fun renderPyUnionStruct() { + writer.rust("""##[pyo3::pyclass(name = "${unionSymbol.name}")]""") + val containerMeta = unionSymbol.expectRustMetadata() + containerMeta.render(writer) + writer.rust("struct PyUnionMarker${unionSymbol.name}(pub ${unionSymbol.name});") + } + + private fun renderPyUnionImpl() { + Attribute(pyo3.resolve("pymethods")).render(writer) + writer.rustBlock("impl PyUnionMarker${unionSymbol.name}") { + sortedMembers.forEach { member -> + val funcNamePart = member.memberName.toSnakeCase() + val variantName = symbolProvider.toMemberName(member) + + if (sortedMembers.size == 1) { + Attribute.AllowIrrefutableLetPatterns.render(this) + } + renderNewVariant(writer, model, symbolProvider, member, variantName, funcNamePart, unionSymbol) + renderAsVariant(writer, model, symbolProvider, member, variantName, funcNamePart, unionSymbol) + rust("/// Returns true if this is a [`$variantName`](#T::$variantName).", unionSymbol) + rust("/// :rtype bool:") + rustBlock("pub fn is_$funcNamePart(&self) -> bool") { + rust("self.0.is_$funcNamePart()") + } + } + if (renderUnknownVariant) { + rust("/// Returns true if the union instance is the `Unknown` variant.") + rust("/// :rtype bool:") + rustBlock("pub fn is_unknown(&self) -> bool") { + rust("self.0.is_unknown()") + } + } + } + } + + private fun renderPyObjectConverters() { + writer.rustBlockTemplate("impl #{pyo3}::IntoPy<#{pyo3}::PyObject> for ${unionSymbol.name}", "pyo3" to pyo3) { + rustBlockTemplate("fn into_py(self, py: #{pyo3}::Python<'_>) -> #{pyo3}::PyObject", "pyo3" to pyo3) { + rust("PyUnionMarker${unionSymbol.name}(self).into_py(py)") + } + } + writer.rustBlockTemplate("impl<'source> #{pyo3}::FromPyObject<'source> for ${unionSymbol.name}", "pyo3" to pyo3) { + rustBlockTemplate("fn extract(obj: &'source #{pyo3}::PyAny) -> #{pyo3}::PyResult", "pyo3" to pyo3) { + rust( + """ + let data: PyUnionMarker${unionSymbol.name} = obj.extract()?; + Ok(data.0) + """, + ) + } + } + } + + private fun renderNewVariant( + writer: RustWriter, + model: Model, + symbolProvider: SymbolProvider, + member: MemberShape, + variantName: String, + funcNamePart: String, + unionSymbol: Symbol, + ) { + if (member.isTargetUnit()) { + Attribute("staticmethod").render(writer) + writer.rust( + "/// Creates a new union instance of [`$variantName`](#T::$variantName)", + unionSymbol, + ) + writer.rust("/// :rtype ${unionSymbol.name}:") + writer.rustBlock("pub fn $funcNamePart() -> Self") { + rust("Self(${unionSymbol.name}::$variantName") + } + } else { + val memberSymbol = symbolProvider.toSymbol(member) + val pythonType = memberSymbol.rustType().pythonType() + val targetType = memberSymbol.rustType() + Attribute("staticmethod").render(writer) + writer.rust( + "/// Creates a new union instance of [`$variantName`](#T::$variantName)", + unionSymbol, + ) + writer.rust("/// :param data ${pythonType.renderAsDocstring()}:") + writer.rust("/// :rtype ${unionSymbol.name}:") + writer.rustBlock("pub fn $funcNamePart(data: ${targetType.render()}) -> Self") { + rust("Self(${unionSymbol.name}::$variantName(data))") + } + } + } + + private fun renderAsVariant( + writer: RustWriter, + model: Model, + symbolProvider: SymbolProvider, + member: MemberShape, + variantName: String, + funcNamePart: String, + unionSymbol: Symbol, + ) { + if (member.isTargetUnit()) { + writer.rust( + "/// Tries to convert the union instance into [`$variantName`].", + ) + writer.rust("/// :rtype None:") + writer.rustBlockTemplate("pub fn as_$funcNamePart(&self) -> #{pyo3}::PyResult<()>", "pyo3" to pyo3) { + rustTemplate( + """ + self.0.as_$funcNamePart().map_err(#{pyo3}::exceptions::PyValueError::new_err( + "${unionSymbol.name} variant is not None" + )) + """, + "pyo3" to pyo3, + ) + } + } else { + val memberSymbol = symbolProvider.toSymbol(member) + val pythonType = memberSymbol.rustType().pythonType() + val targetSymbol = symbolProvider.toSymbol(model.expectShape(member.target)) + val rustType = memberSymbol.rustType() + writer.rust( + "/// Tries to convert the enum instance into [`$variantName`](#T::$variantName), extracting the inner #D.", + unionSymbol, + targetSymbol, + ) + writer.rust("/// :rtype ${pythonType.renderAsDocstring()}:") + writer.rustBlockTemplate("pub fn as_$funcNamePart(&self) -> #{pyo3}::PyResult<${rustType.render()}>", "pyo3" to pyo3) { + val variantType = if (rustType.isCopy()) { + "*variant" + } else { + "variant.clone()" + } + 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()}" + )), + } + """, + "pyo3" to pyo3, + ) + } + } + } +} 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 e38e1ae67c..669279c8d5 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 @@ -13,7 +13,9 @@ import software.amazon.smithy.rust.codegen.core.testutil.generatePluginContext import software.amazon.smithy.rust.codegen.core.util.runCommand import software.amazon.smithy.rust.codegen.server.python.smithy.PythonServerCodegenVisitor import software.amazon.smithy.rust.codegen.server.python.smithy.customizations.DECORATORS +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 java.io.File import java.nio.file.Path @@ -25,10 +27,13 @@ fun generatePythonServerPluginContext(model: Model) = generatePluginContext(model, runtimeConfig = TestRuntimeConfig) fun executePythonServerCodegenVisitor(pluginCtx: PluginContext) { - val codegenDecorator: CombinedServerCodegenDecorator = + val codegenDecorator = CombinedServerCodegenDecorator.fromClasspath( pluginCtx, - CombinedServerCodegenDecorator(DECORATORS + ServerRequiredCustomizations()), + *DECORATORS, + ServerRequiredCustomizations(), + SmithyValidationExceptionDecorator(), + CustomValidationExceptionWithReasonDecorator(), ) PythonServerCodegenVisitor(pluginCtx, codegenDecorator).execute() } diff --git a/codegen-server/python/src/main/resources/META-INF/services/software.amazon.smithy.build.SmithyBuildPlugin b/codegen-server/python/src/main/resources/META-INF/services/software.amazon.smithy.build.SmithyBuildPlugin index 6dc5b76c78..000cc4b7ae 100644 --- a/codegen-server/python/src/main/resources/META-INF/services/software.amazon.smithy.build.SmithyBuildPlugin +++ b/codegen-server/python/src/main/resources/META-INF/services/software.amazon.smithy.build.SmithyBuildPlugin @@ -2,4 +2,4 @@ # Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. # SPDX-License-Identifier: Apache-2.0 # -software.amazon.smithy.rust.codegen.server.python.smithy.PythonCodegenServerPlugin +software.amazon.smithy.rust.codegen.server.python.smithy.RustServerCodegenPythonPlugin 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 96439ac787..c7467b58ed 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 @@ -12,7 +12,8 @@ import software.amazon.smithy.rust.codegen.core.rustlang.RustType import software.amazon.smithy.rust.codegen.core.smithy.rustType import software.amazon.smithy.rust.codegen.core.testutil.asSmithyModel import software.amazon.smithy.rust.codegen.server.python.smithy.PythonServerSymbolVisitor -import software.amazon.smithy.rust.codegen.server.smithy.testutil.ServerTestSymbolVisitorConfig +import software.amazon.smithy.rust.codegen.server.smithy.testutil.ServerTestRustSymbolProviderConfig +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") @@ -45,7 +46,8 @@ internal class PythonServerSymbolProviderTest { value: Timestamp } """.asSmithyModel() - val provider = PythonServerSymbolVisitor(model, null, ServerTestSymbolVisitorConfig) + val provider = + PythonServerSymbolVisitor(serverTestRustSettings(), model, null, ServerTestRustSymbolProviderConfig) // Struct test val timestamp = provider.toSymbol(model.expectShape(ShapeId.from("test#TimestampStruct\$inner"))).rustType() @@ -95,7 +97,8 @@ internal class PythonServerSymbolProviderTest { value: Blob } """.asSmithyModel() - val provider = PythonServerSymbolVisitor(model, null, ServerTestSymbolVisitorConfig) + val provider = + PythonServerSymbolVisitor(serverTestRustSettings(), model, null, ServerTestRustSymbolProviderConfig) // Struct test val blob = provider.toSymbol(model.expectShape(ShapeId.from("test#BlobStruct\$inner"))).rustType() diff --git a/codegen-server/python/src/test/kotlin/software/amazon/smithy/rust/codegen/server/python/smithy/generators/PythonServerTypesTest.kt b/codegen-server/python/src/test/kotlin/software/amazon/smithy/rust/codegen/server/python/smithy/generators/PythonServerTypesTest.kt index c15b399744..9c38d34395 100644 --- a/codegen-server/python/src/test/kotlin/software/amazon/smithy/rust/codegen/server/python/smithy/generators/PythonServerTypesTest.kt +++ b/codegen-server/python/src/test/kotlin/software/amazon/smithy/rust/codegen/server/python/smithy/generators/PythonServerTypesTest.kt @@ -21,7 +21,7 @@ internal class PythonServerTypesTest { fun `document type`() { val model = """ namespace test - + use aws.protocols#restJson1 @restJson1 @@ -30,7 +30,7 @@ internal class PythonServerTypesTest { Echo, ], } - + @http(method: "POST", uri: "/echo") operation Echo { input: EchoInput, @@ -40,7 +40,7 @@ internal class PythonServerTypesTest { structure EchoInput { value: Document, } - + structure EchoOutput { value: Document, } @@ -53,38 +53,38 @@ internal class PythonServerTypesTest { Pair( """ { "value": 42 } """, """ - assert input.value == 42 - output = EchoOutput(value=input.value) + assert input.value == 42 + output = EchoOutput(value=input.value) """, ), Pair( """ { "value": "foobar" } """, """ - assert input.value == "foobar" - output = EchoOutput(value=input.value) + assert input.value == "foobar" + output = EchoOutput(value=input.value) """, ), Pair( """ - { - "value": [ - true, - false, - 42, - 42.0, - -42, - { - "nested": "value" - }, - { - "nested": [1, 2, 3] - } - ] - } + { + "value": [ + true, + false, + 42, + 42.0, + -42, + { + "nested": "value" + }, + { + "nested": [1, 2, 3] + } + ] + } """, """ - assert input.value == [True, False, 42, 42.0, -42, {"nested": "value"}, {"nested": [1, 2, 3]}] - output = EchoOutput(value=input.value) + assert input.value == [True, False, 42, 42.0, -42, {"nested": "value"}, {"nested": [1, 2, 3]}] + output = EchoOutput(value=input.value) """, ), ) @@ -97,7 +97,7 @@ internal class PythonServerTypesTest { use pyo3::{types::IntoPyDict, IntoPy, Python}; use hyper::{Body, Request, body}; use crate::{input, output}; - + pyo3::prepare_freethreaded_python(); """.trimIndent(), ) @@ -112,9 +112,9 @@ internal class PythonServerTypesTest { Ok(Python::with_gil(|py| { let globals = [("EchoOutput", py.get_type::())].into_py_dict(py); let locals = [("input", input.into_py(py))].into_py_dict(py); - + py.run(${pythonHandler.dq()}, Some(globals), Some(locals)).unwrap(); - + locals .get_item("output") .unwrap() @@ -124,13 +124,13 @@ internal class PythonServerTypesTest { }) .build() .unwrap(); - + let req = Request::builder() .method("POST") .uri("/echo") .body(Body::from(${payload.dq()})) .unwrap(); - + let res = service.call(req).await.unwrap(); assert!(res.status().is_success()); let body = body::to_bytes(res.into_body()).await.unwrap(); @@ -144,4 +144,95 @@ internal class PythonServerTypesTest { cargoTest(testDir) } + + @Test + fun `timestamp type`() { + val model = """ + namespace test + + use aws.protocols#restJson1 + use smithy.framework#ValidationException + + @restJson1 + service Service { + operations: [ + Echo, + ], + } + + @http(method: "POST", uri: "/echo") + operation Echo { + input: EchoInput, + output: EchoOutput, + errors: [ValidationException], + } + + structure EchoInput { + @required + value: Timestamp, + opt_value: Timestamp, + } + + structure EchoOutput { + @required + value: Timestamp, + opt_value: Timestamp, + } + """.asSmithyModel() + + val (pluginCtx, testDir) = generatePythonServerPluginContext(model) + executePythonServerCodegenVisitor(pluginCtx) + + val writer = RustWriter.forModule("service") + writer.tokioTest("timestamp_type") { + rust( + """ + use tower::Service as _; + use pyo3::{types::IntoPyDict, IntoPy, Python}; + use hyper::{Body, Request, body}; + use crate::{input, output, python_types}; + + pyo3::prepare_freethreaded_python(); + + let mut service = Service::builder_without_plugins() + .echo(|input: input::EchoInput| async { + Ok(Python::with_gil(|py| { + let globals = [ + ("EchoOutput", py.get_type::()), + ("DateTime", py.get_type::()), + ].into_py_dict(py); + let locals = [("input", input.into_py(py))].into_py_dict(py); + + py.run("assert input.value.secs() == 1676298520", Some(globals), Some(locals)).unwrap(); + py.run("output = EchoOutput(value=input.value, opt_value=DateTime.from_secs(1677771678))", Some(globals), Some(locals)).unwrap(); + + locals + .get_item("output") + .unwrap() + .extract::() + .unwrap() + })) + }) + .build() + .unwrap(); + + let req = Request::builder() + .method("POST") + .uri("/echo") + .body(Body::from("{\"value\":1676298520}")) + .unwrap(); + let res = service.call(req).await.unwrap(); + assert!(res.status().is_success()); + let body = body::to_bytes(res.into_body()).await.unwrap(); + let body = std::str::from_utf8(&body).unwrap(); + assert!(body.contains("\"value\":1676298520")); + assert!(body.contains("\"opt_value\":1677771678")); + """.trimIndent(), + ) + } + + testDir.resolve("src/service.rs").appendText(writer.toString()) + + cargoTest(testDir) + } } 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 new file mode 100644 index 0000000000..1473edcffe --- /dev/null +++ b/codegen-server/python/src/test/kotlin/software/amazon/smithy/rust/codegen/server/python/smithy/generators/PythonTypeInformationGenerationTest.kt @@ -0,0 +1,45 @@ +/* + * 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 io.kotest.matchers.string.shouldContain +import org.junit.jupiter.api.Test +import software.amazon.smithy.model.shapes.StructureShape +import software.amazon.smithy.rust.codegen.core.rustlang.RustWriter +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.serverTestCodegenContext + +internal class PythonTypeInformationGenerationTest { + @Test + fun `generates python type information`() { + val model = """ + namespace test + + structure Foo { + @required + bar: String, + baz: Integer + } + """.asSmithyModel() + 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() + + val result = writer.toString() + + // Constructor signature + result.shouldContain("/// :param bar str:") + result.shouldContain("/// :param baz typing.Optional\\[int\\]:") + + // Field types + result.shouldContain("/// :type str:") + result.shouldContain("/// :type typing.Optional\\[int\\]:") + } +} 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 01e8255ccc..0c83795f2f 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 @@ -5,7 +5,6 @@ package software.amazon.smithy.rust.codegen.server.smithy -import software.amazon.smithy.model.Model import software.amazon.smithy.model.shapes.BlobShape import software.amazon.smithy.model.shapes.ListShape import software.amazon.smithy.model.shapes.MapShape @@ -29,7 +28,6 @@ import software.amazon.smithy.rust.codegen.core.smithy.expectRustMetadata */ class ConstrainedShapeSymbolMetadataProvider( private val base: RustSymbolProvider, - private val model: Model, private val constrainedTypes: Boolean, ) : SymbolMetadataProvider(base) { diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/ConstrainedShapeSymbolProvider.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/ConstrainedShapeSymbolProvider.kt index 5d37c465a0..fb1a1acac7 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/ConstrainedShapeSymbolProvider.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/ConstrainedShapeSymbolProvider.kt @@ -6,7 +6,6 @@ package software.amazon.smithy.rust.codegen.server.smithy import software.amazon.smithy.codegen.core.Symbol -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.ByteShape @@ -19,9 +18,12 @@ import software.amazon.smithy.model.shapes.ServiceShape import software.amazon.smithy.model.shapes.Shape import software.amazon.smithy.model.shapes.ShortShape import software.amazon.smithy.model.shapes.StringShape +import software.amazon.smithy.model.shapes.StructureShape import software.amazon.smithy.model.traits.LengthTrait +import software.amazon.smithy.rust.codegen.core.rustlang.RustModule +import software.amazon.smithy.rust.codegen.core.rustlang.RustReservedWords import software.amazon.smithy.rust.codegen.core.rustlang.RustType -import software.amazon.smithy.rust.codegen.core.smithy.ModelsModule +import software.amazon.smithy.rust.codegen.core.rustlang.Visibility 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.contextName @@ -30,9 +32,13 @@ import software.amazon.smithy.rust.codegen.core.smithy.handleRustBoxing import software.amazon.smithy.rust.codegen.core.smithy.locatedIn import software.amazon.smithy.rust.codegen.core.smithy.rustType import software.amazon.smithy.rust.codegen.core.smithy.symbolBuilder +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.orNull 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.serverBuilderModule +import software.amazon.smithy.rust.codegen.server.smithy.traits.SyntheticStructureFromConstrainedMemberTrait /** * The [ConstrainedShapeSymbolProvider] returns, for a given _directly_ @@ -53,18 +59,19 @@ import software.amazon.smithy.rust.codegen.core.util.toPascalCase * whose associated types are `pub(crate)` and thus not exposed to the end * user. */ -class ConstrainedShapeSymbolProvider( +open class ConstrainedShapeSymbolProvider( private val base: RustSymbolProvider, - private val model: Model, private val serviceShape: ServiceShape, + private val publicConstrainedTypes: Boolean, ) : WrappingSymbolProvider(base) { private val nullableIndex = NullableIndex.of(model) private fun publicConstrainedSymbolForMapOrCollectionShape(shape: Shape): Symbol { check(shape is MapShape || shape is CollectionShape) - val rustType = RustType.Opaque(shape.contextName(serviceShape).toPascalCase()) - return symbolBuilder(shape, rustType).locatedIn(ModelsModule).build() + val (name, module) = getMemberNameAndModule(shape, serviceShape, ServerRustModule.Model, !publicConstrainedTypes) + val rustType = RustType.Opaque(name) + return symbolBuilder(shape, rustType).locatedIn(module).build() } override fun toSymbol(shape: Shape): Symbol { @@ -75,8 +82,14 @@ class ConstrainedShapeSymbolProvider( val target = model.expectShape(shape.target) val targetSymbol = this.toSymbol(target) // Handle boxing first, so we end up with `Option>`, not `Box>`. - handleOptionality(handleRustBoxing(targetSymbol, shape), shape, nullableIndex, base.config().nullabilityCheckMode) + handleOptionality( + handleRustBoxing(targetSymbol, shape), + shape, + nullableIndex, + base.config.nullabilityCheckMode, + ) } + is MapShape -> { if (shape.isDirectlyConstrained(base)) { check(shape.hasTrait()) { @@ -92,6 +105,7 @@ class ConstrainedShapeSymbolProvider( .build() } } + is CollectionShape -> { if (shape.isDirectlyConstrained(base)) { check(constrainedCollectionCheck(shape)) { @@ -106,8 +120,11 @@ class ConstrainedShapeSymbolProvider( is StringShape, is IntegerShape, is ShortShape, is LongShape, is ByteShape, is BlobShape -> { if (shape.isDirectlyConstrained(base)) { - val rustType = RustType.Opaque(shape.contextName(serviceShape).toPascalCase()) - symbolBuilder(shape, rustType).locatedIn(ModelsModule).build() + // A standalone constrained shape goes into `ModelsModule`, but one + // arising from a constrained member shape goes into a module for the container. + val (name, module) = getMemberNameAndModule(shape, serviceShape, ServerRustModule.Model, !publicConstrainedTypes) + val rustType = RustType.Opaque(name) + symbolBuilder(shape, rustType).locatedIn(module).build() } else { base.toSymbol(shape) } @@ -123,9 +140,52 @@ class ConstrainedShapeSymbolProvider( * - That it has no unsupported constraints applied. */ private fun constrainedCollectionCheck(shape: CollectionShape): Boolean { - val supportedConstraintTraits = supportedCollectionConstraintTraits.mapNotNull { shape.getTrait(it).orNull() }.toSet() + val supportedConstraintTraits = + supportedCollectionConstraintTraits.mapNotNull { shape.getTrait(it).orNull() }.toSet() val allConstraintTraits = allConstraintTraits.mapNotNull { shape.getTrait(it).orNull() }.toSet() - return supportedConstraintTraits.isNotEmpty() && allConstraintTraits.subtract(supportedConstraintTraits).isEmpty() + return supportedConstraintTraits.isNotEmpty() && allConstraintTraits.subtract(supportedConstraintTraits) + .isEmpty() + } + + /** + * Returns the pair (Rust Symbol Name, Inline Module) for the shape. At the time of model transformation all + * constrained member shapes are extracted and are given a model-wide unique name. However, the generated code + * for the new shapes is in a module that is named after the containing shape (structure, list, map or union). + * The new shape's Rust Symbol is renamed from `{structureName}{memberName}` to `{structure_name}::{member_name}` + */ + private fun getMemberNameAndModule( + shape: Shape, + serviceShape: ServiceShape, + defaultModule: RustModule.LeafModule, + pubCrateServerBuilder: Boolean, + ): Pair { + val syntheticMemberTrait = shape.getTrait() + ?: return Pair(shape.contextName(serviceShape), defaultModule) + + return if (syntheticMemberTrait.container is StructureShape) { + val builderModule = syntheticMemberTrait.container.serverBuilderModule(base, pubCrateServerBuilder) + val renameTo = syntheticMemberTrait.member.memberName ?: syntheticMemberTrait.member.id.name + Pair(renameTo.toPascalCase(), builderModule) + } else { + // For non-structure shapes, the new shape defined for a constrained member shape + // needs to be placed in an inline module named `pub {container_name_in_snake_case}`. + val moduleName = RustReservedWords.escapeIfNeeded(syntheticMemberTrait.container.id.name.toSnakeCase()) + val innerModuleName = moduleName + if (pubCrateServerBuilder) { + "_internal" + } else { + "" + } + + val innerModule = RustModule.new( + innerModuleName, + visibility = Visibility.publicIf(!pubCrateServerBuilder, Visibility.PUBCRATE), + parent = defaultModule, + inline = true, + documentationOverride = "", + ) + val renameTo = syntheticMemberTrait.member.memberName ?: syntheticMemberTrait.member.id.name + Pair(renameTo.toPascalCase(), innerModule) + } } } diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/ConstraintViolationSymbolProvider.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/ConstraintViolationSymbolProvider.kt index 845150c5cc..c42ed10198 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/ConstraintViolationSymbolProvider.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/ConstraintViolationSymbolProvider.kt @@ -6,7 +6,6 @@ package software.amazon.smithy.rust.codegen.server.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.ByteShape import software.amazon.smithy.model.shapes.CollectionShape @@ -23,15 +22,16 @@ import software.amazon.smithy.rust.codegen.core.rustlang.RustModule import software.amazon.smithy.rust.codegen.core.rustlang.RustReservedWords import software.amazon.smithy.rust.codegen.core.rustlang.RustType import software.amazon.smithy.rust.codegen.core.rustlang.Visibility -import software.amazon.smithy.rust.codegen.core.smithy.ModelsModule 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.contextName import software.amazon.smithy.rust.codegen.core.smithy.locatedIn import software.amazon.smithy.rust.codegen.core.smithy.module 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.toSnakeCase import software.amazon.smithy.rust.codegen.server.smithy.generators.serverBuilderSymbol +import software.amazon.smithy.rust.codegen.server.smithy.traits.SyntheticStructureFromConstrainedMemberTrait /** * The [ConstraintViolationSymbolProvider] returns, for a given constrained @@ -68,7 +68,6 @@ import software.amazon.smithy.rust.codegen.server.smithy.generators.serverBuilde */ class ConstraintViolationSymbolProvider( private val base: RustSymbolProvider, - private val model: Model, private val publicConstrainedTypes: Boolean, private val serviceShape: ServiceShape, ) : WrappingSymbolProvider(base) { @@ -80,17 +79,31 @@ class ConstraintViolationSymbolProvider( private fun Shape.shapeModule(): RustModule.LeafModule { val documentation = if (publicConstrainedTypes && this.isDirectlyConstrained(base)) { - "See [`${this.contextName(serviceShape)}`]." + val symbol = base.toSymbol(this) + "See [`${this.contextName(serviceShape)}`]($symbol)." } else { - null + "" } - return RustModule.new( + + val syntheticTrait = getTrait() + + val (module, name) = if (syntheticTrait != null) { + // For constrained member shapes, the ConstraintViolation code needs to go in an inline rust module + // that is a descendant of the module that contains the extracted shape itself. + val overriddenMemberModule = this.getParentAndInlineModuleForConstrainedMember(base, publicConstrainedTypes)!! + val name = syntheticTrait.member.memberName + Pair(overriddenMemberModule.second, RustReservedWords.escapeIfNeeded(name).toSnakeCase()) + } else { // Need to use the context name so we get the correct name for maps. - name = RustReservedWords.escapeIfNeeded(this.contextName(serviceShape)).toSnakeCase(), + Pair(ServerRustModule.Model, RustReservedWords.escapeIfNeeded(this.contextName(serviceShape)).toSnakeCase()) + } + + return RustModule.new( + name = name, visibility = visibility, - parent = ModelsModule, + parent = module, inline = true, - documentation = documentation, + documentationOverride = documentation, ) } diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/Constraints.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/Constraints.kt index f6cc943a9c..8152272a1c 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/Constraints.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/Constraints.kt @@ -26,11 +26,20 @@ import software.amazon.smithy.model.traits.PatternTrait import software.amazon.smithy.model.traits.RangeTrait import software.amazon.smithy.model.traits.RequiredTrait import software.amazon.smithy.model.traits.UniqueItemsTrait +import software.amazon.smithy.rust.codegen.core.rustlang.RustModule +import software.amazon.smithy.rust.codegen.core.rustlang.RustReservedWords +import software.amazon.smithy.rust.codegen.core.rustlang.Visibility import software.amazon.smithy.rust.codegen.core.smithy.CodegenContext import software.amazon.smithy.rust.codegen.core.smithy.DirectedWalker +import software.amazon.smithy.rust.codegen.core.smithy.RustSymbolProvider import software.amazon.smithy.rust.codegen.core.smithy.isOptional +import software.amazon.smithy.rust.codegen.core.smithy.module import software.amazon.smithy.rust.codegen.core.util.UNREACHABLE +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.toSnakeCase +import software.amazon.smithy.rust.codegen.server.smithy.generators.serverBuilderModule +import software.amazon.smithy.rust.codegen.server.smithy.traits.SyntheticStructureFromConstrainedMemberTrait /** * This file contains utilities to work with constrained shapes. @@ -83,7 +92,7 @@ fun Shape.isDirectlyConstrained(symbolProvider: SymbolProvider): Boolean = when // The only reason why the functions in this file have // to take in a `SymbolProvider` is because non-`required` blob streaming members are interpreted as // `required`, so we can't use `member.isOptional` here. - this.members().map { symbolProvider.toSymbol(it) }.any { !it.isOptional() } + this.members().any { !symbolProvider.toSymbol(it).isOptional() && !it.hasNonNullDefault() } } is MapShape -> this.hasTrait() @@ -160,3 +169,47 @@ fun Shape.typeNameContainsNonPublicType( is StructureShape, is UnionShape -> false else -> UNREACHABLE("the above arms should be exhaustive, but we received shape: $this") } + +/** + * For synthetic shapes that are added to the model because of member constrained shapes, it returns + * the "container" and "the member shape" that originally had the constraint trait. For all other + * shapes, it returns null. + */ +fun Shape.overriddenConstrainedMemberInfo(): Pair? { + val trait = getTrait() ?: return null + return Pair(trait.container, trait.member) +} + +/** + * Returns the parent and the inline module that this particular shape should go in. + */ +fun Shape.getParentAndInlineModuleForConstrainedMember(symbolProvider: RustSymbolProvider, publicConstrainedTypes: Boolean): Pair? { + val overriddenTrait = getTrait() ?: return null + return if (overriddenTrait.container is StructureShape) { + val structureModule = symbolProvider.toSymbol(overriddenTrait.container).module() + val builderModule = overriddenTrait.container.serverBuilderModule(symbolProvider, !publicConstrainedTypes) + Pair(structureModule, builderModule) + } else { + // For constrained member shapes, the ConstraintViolation code needs to go in an inline rust module + // that is a descendant of the module that contains the extracted shape itself. + return if (publicConstrainedTypes) { + // Non-structured shape types need to go into their own module. + val shapeSymbol = symbolProvider.toSymbol(this) + val shapeModule = shapeSymbol.module() + check(!shapeModule.parent.isInline()) { + "Parent module of $id should not be an inline module." + } + Pair(shapeModule.parent as RustModule.LeafModule, shapeModule) + } else { + val name = RustReservedWords.escapeIfNeeded(overriddenTrait.container.id.name).toSnakeCase() + "_internal" + val innerModule = RustModule.new( + name = name, + visibility = Visibility.PUBCRATE, + parent = ServerRustModule.Model, + inline = true, + ) + + Pair(ServerRustModule.Model, innerModule) + } + } +} diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/DeriveEqAndHashSymbolMetadataProvider.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/DeriveEqAndHashSymbolMetadataProvider.kt index 5438447ed5..d3f7059271 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/DeriveEqAndHashSymbolMetadataProvider.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/DeriveEqAndHashSymbolMetadataProvider.kt @@ -5,7 +5,6 @@ package software.amazon.smithy.rust.codegen.server.smithy -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.DoubleShape @@ -49,7 +48,6 @@ import software.amazon.smithy.rust.codegen.core.util.hasTrait */ class DeriveEqAndHashSymbolMetadataProvider( private val base: RustSymbolProvider, - val model: Model, ) : SymbolMetadataProvider(base) { private val walker = DirectedWalker(model) @@ -58,7 +56,7 @@ class DeriveEqAndHashSymbolMetadataProvider( val baseMetadata = base.toSymbol(shape).expectRustMetadata() // See class-level documentation for why we filter these out. return if (walker.walkShapes(shape) - .any { it is FloatShape || it is DoubleShape || it is DocumentShape || it.hasTrait() } + .any { it is FloatShape || it is DoubleShape || it is DocumentShape || it.hasTrait() } ) { baseMetadata } else { diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/LengthTraitValidationErrorMessage.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/LengthTraitValidationErrorMessage.kt index b15b2dc8f0..cb08de3c73 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/LengthTraitValidationErrorMessage.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/LengthTraitValidationErrorMessage.kt @@ -11,9 +11,11 @@ fun LengthTrait.validationErrorMessage(): String { val beginning = "Value with length {} at '{}' failed to satisfy constraint: Member must have length " val ending = if (this.min.isPresent && this.max.isPresent) { "between ${this.min.get()} and ${this.max.get()}, inclusive" - } else if (this.min.isPresent) ( - "greater than or equal to ${this.min.get()}" - ) else { + } else if (this.min.isPresent) { + ( + "greater than or equal to ${this.min.get()}" + ) + } else { check(this.max.isPresent) "less than or equal to ${this.max.get()}" } diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/PatternTraitValidationErrorMessage.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/PatternTraitValidationErrorMessage.kt deleted file mode 100644 index 8bb3cb648e..0000000000 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/PatternTraitValidationErrorMessage.kt +++ /dev/null @@ -1,12 +0,0 @@ -/* - * 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.traits.PatternTrait - -@Suppress("UnusedReceiverParameter") -fun PatternTrait.validationErrorMessage(): String = - "Value {} at '{}' failed to satisfy constraint: Member must satisfy regular expression pattern: {}" diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/PubCrateConstrainedShapeSymbolProvider.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/PubCrateConstrainedShapeSymbolProvider.kt index 800dc6c730..c64182f152 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/PubCrateConstrainedShapeSymbolProvider.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/PubCrateConstrainedShapeSymbolProvider.kt @@ -6,7 +6,6 @@ package software.amazon.smithy.rust.codegen.server.smithy import software.amazon.smithy.codegen.core.Symbol -import software.amazon.smithy.model.Model import software.amazon.smithy.model.knowledge.NullableIndex import software.amazon.smithy.model.shapes.CollectionShape import software.amazon.smithy.model.shapes.MapShape @@ -20,7 +19,6 @@ import software.amazon.smithy.rust.codegen.core.rustlang.RustModule import software.amazon.smithy.rust.codegen.core.rustlang.RustReservedWords import software.amazon.smithy.rust.codegen.core.rustlang.RustType import software.amazon.smithy.rust.codegen.core.rustlang.Visibility -import software.amazon.smithy.rust.codegen.core.smithy.ConstrainedModule 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.handleOptionality @@ -62,7 +60,6 @@ import software.amazon.smithy.rust.codegen.core.util.toSnakeCase */ class PubCrateConstrainedShapeSymbolProvider( private val base: RustSymbolProvider, - private val model: Model, private val serviceShape: ServiceShape, ) : WrappingSymbolProvider(base) { private val nullableIndex = NullableIndex.of(model) @@ -74,7 +71,7 @@ class PubCrateConstrainedShapeSymbolProvider( val module = RustModule.new( RustReservedWords.escapeIfNeeded(name.toSnakeCase()), visibility = Visibility.PUBCRATE, - parent = ConstrainedModule, + parent = ServerRustModule.ConstrainedModule, inline = true, ) val rustType = RustType.Opaque(name, module.fullyQualifiedPath()) @@ -110,7 +107,7 @@ class PubCrateConstrainedShapeSymbolProvider( handleRustBoxing(targetSymbol, shape), shape, nullableIndex, - base.config().nullabilityCheckMode, + base.config.nullabilityCheckMode, ) } } diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/RangeTraitValidationErrorMessage.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/RangeTraitValidationErrorMessage.kt index 5512da8470..9cde66e6e0 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/RangeTraitValidationErrorMessage.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/RangeTraitValidationErrorMessage.kt @@ -8,12 +8,14 @@ package software.amazon.smithy.rust.codegen.server.smithy import software.amazon.smithy.model.traits.RangeTrait fun RangeTrait.validationErrorMessage(): String { - val beginning = "Value {} at '{}' failed to satisfy constraint: Member must be " + val beginning = "Value at '{}' failed to satisfy constraint: Member must be " val ending = if (this.min.isPresent && this.max.isPresent) { "between ${this.min.get()} and ${this.max.get()}, inclusive" - } else if (this.min.isPresent) ( - "greater than or equal to ${this.min.get()}" - ) else { + } else if (this.min.isPresent) { + ( + "greater than or equal to ${this.min.get()}" + ) + } else { check(this.max.isPresent) "less than or equal to ${this.max.get()}" } 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 new file mode 100644 index 0000000000..32abe41d69 --- /dev/null +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/RustCrateInlineModuleComposingWriter.kt @@ -0,0 +1,350 @@ +/* + * 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.codegen.core.Symbol +import software.amazon.smithy.model.shapes.Shape +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 +import software.amazon.smithy.rust.codegen.core.smithy.ModuleDocProvider +import software.amazon.smithy.rust.codegen.core.smithy.RustCrate +import software.amazon.smithy.rust.codegen.core.smithy.module +import java.util.concurrent.ConcurrentHashMap + +typealias DocWriter = () -> Any +typealias InlineModuleCreator = (Symbol, Writable) -> Unit + +/** + * Initializes RustCrate -> InnerModule data structure. + */ +fun RustCrate.initializeInlineModuleWriter(debugMode: Boolean): InnerModule = + crateToInlineModule + .getOrPut(this) { InnerModule(moduleDocProvider, debugMode) } + +/** + * Returns the InnerModule for the given RustCrate + */ +fun RustCrate.getInlineModuleWriter(): InnerModule { + return crateToInlineModule.getOrPut(this) { InnerModule(moduleDocProvider, false) } +} + +/** + * Returns a function that can be used to create an inline module writer. + */ +fun RustCrate.createInlineModuleCreator(): InlineModuleCreator { + return { symbol: Symbol, writable: Writable -> + this.getInlineModuleWriter().withInlineModuleHierarchyUsingCrate(this, symbol.module()) { + writable() + } + } +} + +/** + * If the passed in `shape` is a synthetic extracted shape resulting from a constrained struct member, + * the `Writable` is called using the structure's builder module. Otherwise, the `Writable` is called + * using the given `module`. + */ +fun RustCrate.withModuleOrWithStructureBuilderModule( + module: RustModule, + shape: Shape, + codegenContext: ServerCodegenContext, + codeWritable: Writable, +) { + // All structure constrained-member-shapes code is generated inside the structure builder's module. + val parentAndInlineModuleInfo = + shape.getParentAndInlineModuleForConstrainedMember(codegenContext.symbolProvider, codegenContext.settings.codegenConfig.publicConstrainedTypes) + if (parentAndInlineModuleInfo == null) { + this.withModule(module, codeWritable) + } else { + val (parent, inline) = parentAndInlineModuleInfo + val inlineWriter = this.getInlineModuleWriter() + + inlineWriter.withInlineModuleHierarchyUsingCrate(this, parent) { + inlineWriter.withInlineModuleHierarchy(this, inline) { + codeWritable(this) + } + } + } +} + +/** + * If the passed in `shape` is a synthetic extracted shape resulting from a constrained struct member, + * the `Writable` is called using the structure's builder module. Otherwise, the `Writable` is called + * using shape's `module`. + */ +fun RustCrate.useShapeWriterOrUseWithStructureBuilder( + shape: Shape, + codegenContext: ServerCodegenContext, + docWriter: DocWriter? = null, + writable: Writable, +) { + // All structure constrained-member-shapes code is generated inside the structure builder's module. + val parentAndInlineModuleInfo = + shape.getParentAndInlineModuleForConstrainedMember(codegenContext.symbolProvider, codegenContext.settings.codegenConfig.publicConstrainedTypes) + if (parentAndInlineModuleInfo == null) { + docWriter?.invoke() + this.useShapeWriter(shape, writable) + } else { + val (parent, inline) = parentAndInlineModuleInfo + val inlineWriter = this.getInlineModuleWriter() + + inlineWriter.withInlineModuleHierarchyUsingCrate(this, parent) { + inlineWriter.withInlineModuleHierarchy(this, inline) { + writable(this) + } + } + } +} + +fun RustCrate.renderInlineMemoryModules() { + val inlineModule = crateToInlineModule[this] + check(inlineModule != null) { + "InlineModule writer has not been registered for this crate" + } + inlineModule.render() +} + +/** + * Given a `RustWriter` calls the `Writable` using a `RustWriter` for the `inlineModule` + */ +fun RustCrate.withInMemoryInlineModule( + outerWriter: RustWriter, + inlineModule: RustModule.LeafModule, + docWriter: DocWriter?, + codeWritable: Writable, +) { + check(inlineModule.isInline()) { + "Module has to be an inline module for it to be used with the InlineModuleWriter" + } + this.getInlineModuleWriter().withInlineModuleHierarchy(outerWriter, inlineModule, docWriter) { + codeWritable(this) + } +} + +fun RustWriter.createTestInlineModuleCreator(): InlineModuleCreator { + return { symbol: Symbol, writable: Writable -> + this.withInlineModule(symbol.module(), null) { + writable() + } + } +} + +/** + * Maintains the `RustWriter` that has been created for a `RustModule.LeafModule`. + */ +private data class InlineModuleWithWriter(val inlineModule: RustModule.LeafModule, val writer: RustWriter) + +/** + * For each RustCrate a separate mapping of inline-module to `RustWriter` is maintained. + */ +private val crateToInlineModule: ConcurrentHashMap = + ConcurrentHashMap() + +class InnerModule(private val moduleDocProvider: ModuleDocProvider, debugMode: Boolean) { + // Holds the root modules to start rendering the descendents from. + private val topLevelModuleWriters: ConcurrentHashMap = ConcurrentHashMap() + private val inlineModuleWriters: ConcurrentHashMap> = ConcurrentHashMap() + private val docWriters: ConcurrentHashMap> = ConcurrentHashMap() + private val writerCreator = RustWriter.factory(debugMode) + + // By default, when a RustWriter is rendered, it prints a comment on top + // indicating that it contains generated code and should not be manually edited. This comment + // appears on each descendent inline module. To remove those comments, each time an inline + // module is rendered, first `emptyLineCount` characters are removed from it. + private val emptyLineCount: Int = writerCreator + .apply("lines-it-always-writes.rs", "crate") + .toString() + .split("\n")[0] + .length + + fun withInlineModule(outerWriter: RustWriter, innerModule: RustModule.LeafModule, docWriter: DocWriter? = null, writable: Writable) { + if (docWriter != null) { + val moduleDocWriterList = docWriters.getOrPut(innerModule) { mutableListOf() } + moduleDocWriterList.add(docWriter) + } + writable(getWriter(outerWriter, innerModule)) + } + + /** + * Given a `RustCrate` and a `RustModule.LeafModule()`, it creates a writer to that module and calls the writable. + */ + fun withInlineModuleHierarchyUsingCrate(rustCrate: RustCrate, inlineModule: RustModule.LeafModule, docWriter: DocWriter? = null, writable: Writable) { + val hierarchy = getHierarchy(inlineModule).toMutableList() + check(!hierarchy.first().isInline()) { + "When adding a `RustModule.LeafModule` to the crate, the topmost module in the hierarchy cannot be an inline module." + } + // The last in the hierarchy is the one we will return the writer for. + val bottomMost = hierarchy.removeLast() + + // In case it is a top level module that has been passed (e.g. ModelsModule, OutputsModule) then + // register it with the topLevel writers and call the writable on it. Otherwise, go over the + // complete hierarchy, registering each of the inner modules and then call the `Writable` + // with the bottom most inline module that has been passed. + if (hierarchy.isNotEmpty()) { + val topMost = hierarchy.removeFirst() + + // Create an intermediate writer for all inner modules in the hierarchy. + rustCrate.withModule(topMost) { + var writer = this + hierarchy.forEach { + writer = getWriter(writer, it) + } + + withInlineModule(writer, bottomMost, docWriter, writable) + } + } else { + check(!bottomMost.isInline()) { + "There is only one module in the hierarchy, so it has to be non-inlined." + } + rustCrate.withModule(bottomMost) { + registerTopMostWriter(this) + writable(this) + } + } + } + + /** + * Given a `Writer` to a module and an inline `RustModule.LeafModule()`, it creates a writer to that module and calls the writable. + * It registers the complete hierarchy including the `outerWriter` if that is not already registrered. + */ + fun withInlineModuleHierarchy(outerWriter: RustWriter, inlineModule: RustModule.LeafModule, docWriter: DocWriter? = null, writable: Writable) { + val hierarchy = getHierarchy(inlineModule).toMutableList() + if (!hierarchy.first().isInline()) { + hierarchy.removeFirst() + } + check(hierarchy.isNotEmpty()) { + "An inline module should always have one parent besides itself." + } + + // The last in the hierarchy is the module under which the new inline module resides. + val bottomMost = hierarchy.removeLast() + + // Create an entry in the HashMap for all the descendent modules in the hierarchy. + var writer = outerWriter + hierarchy.forEach { + writer = getWriter(writer, it) + } + + withInlineModule(writer, bottomMost, docWriter, writable) + } + + /** + * Creates an in memory writer and registers it with a map of RustWriter -> listOf(Inline descendent modules) + */ + private fun createNewInlineModule(): RustWriter { + val writer = writerCreator.apply("unknown-module-would-never-be-written.rs", "crate") + // Register the new RustWriter in the map to allow further descendent inline modules to be created inside it. + inlineModuleWriters[writer] = mutableListOf() + return writer + } + + /** + * Returns the complete hierarchy of a `RustModule.LeafModule` from top to bottom + */ + private fun getHierarchy(module: RustModule.LeafModule): List { + var current: RustModule = module + var hierarchy = listOf() + + while (current is RustModule.LeafModule) { + hierarchy = listOf(current) + hierarchy + current = current.parent + } + + return hierarchy + } + + /** + * Writes out each inline module's code (`toString`) to the respective top level `RustWriter`. + */ + fun render() { + var writerToAddDependencies: RustWriter? = null + + fun writeInlineCode(rustWriter: RustWriter, code: String) { + val inlineCode = code.drop(emptyLineCount) + rustWriter.writeWithNoFormatting(inlineCode) + } + + fun renderDescendents(topLevelWriter: RustWriter, inMemoryWriter: RustWriter) { + // Traverse all descendent inline modules and render them. + inlineModuleWriters[inMemoryWriter]!!.forEach { + writeDocs(it.inlineModule) + + topLevelWriter.withInlineModule(it.inlineModule, moduleDocProvider) { + writeInlineCode(this, it.writer.toString()) + renderDescendents(this, it.writer) + } + + // Add dependencies introduced by the inline module to the top most RustWriter. + it.writer.dependencies.forEach { dep -> writerToAddDependencies!!.addDependency(dep) } + } + } + + // Go over all the top level modules, create an `inlineModule` on the `RustWriter` + // and call the descendent hierarchy renderer using the `inlineModule::RustWriter`. + topLevelModuleWriters.keys.forEach { + writerToAddDependencies = it + + check(inlineModuleWriters[it] != null) { + "There must be a registered RustWriter for this module." + } + + renderDescendents(it, it) + } + } + + /** + * Given the inline-module returns an existing `RustWriter`, or if that inline module + * has never been registered before then a new `RustWriter` is created and returned. + */ + private fun getWriter(outerWriter: RustWriter, inlineModule: RustModule.LeafModule): RustWriter { + val nestedModuleWriter = inlineModuleWriters[outerWriter] + if (nestedModuleWriter != null) { + return findOrAddToList(nestedModuleWriter, inlineModule) + } + + val inlineWriters = registerTopMostWriter(outerWriter) + return findOrAddToList(inlineWriters, inlineModule) + } + + /** + * Records the root of a dependency graph of inline modules. + */ + private fun registerTopMostWriter(outerWriter: RustWriter): MutableList { + topLevelModuleWriters[outerWriter] = Unit + return inlineModuleWriters.getOrPut(outerWriter) { mutableListOf() } + } + + /** + * Either gets a new `RustWriter` for the inline module or creates a new one and adds it to + * the list of inline modules. + */ + private fun findOrAddToList( + inlineModuleList: MutableList, + lookForModule: RustModule.LeafModule, + ): RustWriter { + val inlineModuleAndWriter = inlineModuleList.firstOrNull() { + it.inlineModule.name == lookForModule.name + } + return if (inlineModuleAndWriter == null) { + val inlineWriter = createNewInlineModule() + inlineModuleList.add(InlineModuleWithWriter(lookForModule, inlineWriter)) + inlineWriter + } else { + check(inlineModuleAndWriter.inlineModule == lookForModule) { + "The two inline modules have the same name but different attributes on them." + } + + inlineModuleAndWriter.writer + } + } + + private fun writeDocs(innerModule: RustModule.LeafModule) { + docWriters[innerModule]?.forEach { + it() + } + } +} diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/RustCodegenServerPlugin.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/RustServerCodegenPlugin.kt similarity index 56% rename from codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/RustCodegenServerPlugin.kt rename to codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/RustServerCodegenPlugin.kt index 8a1dc17e54..aa310ccc71 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/RustCodegenServerPlugin.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/RustServerCodegenPlugin.kt @@ -6,7 +6,6 @@ package software.amazon.smithy.rust.codegen.server.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 @@ -14,12 +13,16 @@ import software.amazon.smithy.rust.codegen.core.rustlang.RustReservedWordSymbolP 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.StreamingShapeMetadataProvider import software.amazon.smithy.rust.codegen.core.smithy.StreamingShapeSymbolProvider import software.amazon.smithy.rust.codegen.core.smithy.SymbolVisitor -import software.amazon.smithy.rust.codegen.core.smithy.SymbolVisitorConfig +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.smithy.testutil.ServerDecoratableBuildPlugin import java.util.logging.Level import java.util.logging.Logger @@ -30,57 +33,65 @@ import java.util.logging.Logger * `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 Smithy plugin context + models. */ -class RustCodegenServerPlugin : SmithyBuildPlugin { +class RustServerCodegenPlugin : ServerDecoratableBuildPlugin() { private val logger = Logger.getLogger(javaClass.name) override fun getName(): String = "rust-server-codegen" - override fun execute(context: PluginContext) { - // Suppress extremely noisy logs about reserved words + /** + * See [software.amazon.smithy.rust.codegen.client.smithy.RustClientCodegenPlugin]. + */ + override fun executeWithDecorator( + context: PluginContext, + vararg decorator: ServerCodegenDecorator, + ) { Logger.getLogger(ReservedWordSymbolProvider::class.java.name).level = Level.OFF - // Discover [RustCodegenDecorators] on the classpath. [RustCodegenDecorator] returns different types of - // customizations. 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()) - - // ServerCodegenVisitor is the main driver of code generation that traverses the model and generates code + val codegenDecorator = + CombinedServerCodegenDecorator.fromClasspath( + context, + ServerRequiredCustomizations(), + SmithyValidationExceptionDecorator(), + CustomValidationExceptionWithReasonDecorator(), + *decorator, + ) logger.info("Loaded plugin to generate pure Rust bindings for the server SDK") ServerCodegenVisitor(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. + * See [software.amazon.smithy.rust.codegen.client.smithy.RustClientCodegenPlugin]. */ fun baseSymbolProvider( + settings: ServerRustSettings, model: Model, serviceShape: ServiceShape, - symbolVisitorConfig: SymbolVisitorConfig, + rustSymbolProviderConfig: RustSymbolProviderConfig, constrainedTypes: Boolean = true, + includeConstrainedShapeProvider: Boolean = true, + codegenDecorator: ServerCodegenDecorator, ) = - SymbolVisitor(model, serviceShape = serviceShape, config = symbolVisitorConfig) + SymbolVisitor(settings, model, serviceShape = serviceShape, config = rustSymbolProviderConfig) // Generate public constrained types for directly constrained shapes. - .let { if (constrainedTypes) ConstrainedShapeSymbolProvider(it, model, serviceShape) else it } + .let { + if (includeConstrainedShapeProvider) ConstrainedShapeSymbolProvider(it, serviceShape, constrainedTypes) else it + } // Generate different types for EventStream shapes (e.g. transcribe streaming) - .let { EventStreamSymbolProvider(symbolVisitorConfig.runtimeConfig, it, model, CodegenTarget.SERVER) } + .let { EventStreamSymbolProvider(rustSymbolProviderConfig.runtimeConfig, it, CodegenTarget.SERVER) } // Generate [ByteStream] instead of `Blob` for streaming binary shapes (e.g. S3 GetObject) - .let { StreamingShapeSymbolProvider(it, model) } + .let { StreamingShapeSymbolProvider(it) } // Add Rust attributes (like `#[derive(PartialEq)]`) to generated shapes - .let { BaseSymbolMetadataProvider(it, model, additionalAttributes = listOf()) } + .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, model, constrainedTypes) } + .let { ConstrainedShapeSymbolMetadataProvider(it, constrainedTypes) } // Streaming shapes need different derives (e.g. they cannot derive `PartialEq`) - .let { StreamingShapeMetadataProvider(it, model) } + .let { StreamingShapeMetadataProvider(it) } // Derive `Eq` and `Hash` if possible. - .let { DeriveEqAndHashSymbolMetadataProvider(it, model) } + .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, model) } + .let { RustReservedWordSymbolProvider(it, ServerReservedWords) } + // Allows decorators to inject a custom symbol provider + .let { codegenDecorator.symbolProvider(it) } } } 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 cb4e62fe9a..1726057a9f 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,13 +17,12 @@ 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")) val PinProjectLite: CargoDependency = CargoDependency("pin-project-lite", CratesIo("0.2")) val Tower: CargoDependency = CargoDependency("tower", CratesIo("0.4")) - val TokioDev: CargoDependency = CargoDependency("tokio", CratesIo("1.8.4"), scope = DependencyScope.Dev) + val TokioDev: CargoDependency = CargoDependency("tokio", CratesIo("1.23.1"), scope = DependencyScope.Dev) val Regex: CargoDependency = CargoDependency("regex", CratesIo("1.5.5")) val HyperDev: CargoDependency = CargoDependency("hyper", CratesIo("0.14.12"), DependencyScope.Dev) diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/ServerCodegenContext.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/ServerCodegenContext.kt index a0ad38f04f..d5c2a7084a 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/ServerCodegenContext.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/ServerCodegenContext.kt @@ -10,10 +10,11 @@ import software.amazon.smithy.model.shapes.ServiceShape import software.amazon.smithy.model.shapes.ShapeId 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 /** - * [ServerCodegenContext] contains code-generation context that is _specific_ to the [RustCodegenServerPlugin] plugin + * [ServerCodegenContext] contains code-generation context that is _specific_ to the [RustServerCodegenPlugin] plugin * from the `rust-codegen-server` subproject. * * It inherits from [CodegenContext], which contains code-generation context that is common to _all_ smithy-rs plugins. @@ -24,6 +25,7 @@ import software.amazon.smithy.rust.codegen.core.smithy.RustSymbolProvider data class ServerCodegenContext( override val model: Model, override val symbolProvider: RustSymbolProvider, + override val moduleDocProvider: ModuleDocProvider?, override val serviceShape: ServiceShape, override val protocol: ShapeId, override val settings: ServerRustSettings, @@ -32,5 +34,5 @@ data class ServerCodegenContext( val constraintViolationSymbolProvider: ConstraintViolationSymbolProvider, val pubCrateConstrainedShapeSymbolProvider: PubCrateConstrainedShapeSymbolProvider, ) : CodegenContext( - model, symbolProvider, serviceShape, protocol, settings, CodegenTarget.SERVER, + model, symbolProvider, moduleDocProvider, serviceShape, protocol, settings, CodegenTarget.SERVER, ) 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 607aad2158..52381ed5c8 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 @@ -16,6 +16,7 @@ import software.amazon.smithy.model.shapes.IntegerShape import software.amazon.smithy.model.shapes.ListShape import software.amazon.smithy.model.shapes.LongShape import software.amazon.smithy.model.shapes.MapShape +import software.amazon.smithy.model.shapes.NumberShape import software.amazon.smithy.model.shapes.OperationShape import software.amazon.smithy.model.shapes.ServiceShape import software.amazon.smithy.model.shapes.SetShape @@ -26,29 +27,26 @@ 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.model.traits.LengthTrait import software.amazon.smithy.model.transform.ModelTransformer -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.implBlock import software.amazon.smithy.rust.codegen.core.smithy.CodegenTarget -import software.amazon.smithy.rust.codegen.core.smithy.ConstrainedModule import software.amazon.smithy.rust.codegen.core.smithy.CoreRustSettings import software.amazon.smithy.rust.codegen.core.smithy.DirectedWalker -import software.amazon.smithy.rust.codegen.core.smithy.ModelsModule import software.amazon.smithy.rust.codegen.core.smithy.RustCrate -import software.amazon.smithy.rust.codegen.core.smithy.SymbolVisitorConfig -import software.amazon.smithy.rust.codegen.core.smithy.UnconstrainedModule +import software.amazon.smithy.rust.codegen.core.smithy.RustSymbolProviderConfig +import software.amazon.smithy.rust.codegen.core.smithy.generators.EnumGenerator import software.amazon.smithy.rust.codegen.core.smithy.generators.StructureGenerator import software.amazon.smithy.rust.codegen.core.smithy.generators.UnionGenerator -import software.amazon.smithy.rust.codegen.core.smithy.generators.error.eventStreamErrorSymbol -import software.amazon.smithy.rust.codegen.core.smithy.generators.implBlock +import software.amazon.smithy.rust.codegen.core.smithy.generators.error.ErrorImplGenerator import software.amazon.smithy.rust.codegen.core.smithy.protocols.ProtocolGeneratorFactory 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.smithy.transformers.eventStreamErrors -import software.amazon.smithy.rust.codegen.core.smithy.transformers.operationErrors import software.amazon.smithy.rust.codegen.core.util.CommandFailed +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 import software.amazon.smithy.rust.codegen.core.util.isEventStream @@ -69,16 +67,23 @@ import software.amazon.smithy.rust.codegen.server.smithy.generators.ServerBuilde import software.amazon.smithy.rust.codegen.server.smithy.generators.ServerBuilderGeneratorWithoutPublicConstrainedTypes import software.amazon.smithy.rust.codegen.server.smithy.generators.ServerEnumGenerator import software.amazon.smithy.rust.codegen.server.smithy.generators.ServerOperationErrorGenerator +import software.amazon.smithy.rust.codegen.server.smithy.generators.ServerOperationGenerator +import software.amazon.smithy.rust.codegen.server.smithy.generators.ServerRootGenerator +import software.amazon.smithy.rust.codegen.server.smithy.generators.ServerRuntimeTypesReExportsGenerator import software.amazon.smithy.rust.codegen.server.smithy.generators.ServerServiceGenerator import software.amazon.smithy.rust.codegen.server.smithy.generators.ServerStructureConstrainedTraitImpl import software.amazon.smithy.rust.codegen.server.smithy.generators.UnconstrainedCollectionGenerator import software.amazon.smithy.rust.codegen.server.smithy.generators.UnconstrainedMapGenerator import software.amazon.smithy.rust.codegen.server.smithy.generators.UnconstrainedUnionGenerator +import software.amazon.smithy.rust.codegen.server.smithy.generators.ValidationExceptionConversionGenerator import software.amazon.smithy.rust.codegen.server.smithy.generators.protocol.ServerProtocol import software.amazon.smithy.rust.codegen.server.smithy.generators.protocol.ServerProtocolGenerator +import software.amazon.smithy.rust.codegen.server.smithy.generators.protocol.ServerProtocolTestGenerator import software.amazon.smithy.rust.codegen.server.smithy.protocols.ServerProtocolLoader import software.amazon.smithy.rust.codegen.server.smithy.traits.isReachableFromOperationInput import software.amazon.smithy.rust.codegen.server.smithy.transformers.AttachValidationExceptionToConstrainedOperationInputsInAllowList +import software.amazon.smithy.rust.codegen.server.smithy.transformers.ConstrainedMemberTransform +import software.amazon.smithy.rust.codegen.server.smithy.transformers.RecursiveConstraintViolationBoxer import software.amazon.smithy.rust.codegen.server.smithy.transformers.RemoveEbsModelValidationException import software.amazon.smithy.rust.codegen.server.smithy.transformers.ShapesReachableFromOperationInputTagger import java.util.logging.Logger @@ -101,14 +106,15 @@ open class ServerCodegenVisitor( protected var codegenContext: ServerCodegenContext protected var protocolGeneratorFactory: ProtocolGeneratorFactory protected var protocolGenerator: ServerProtocolGenerator + protected var validationExceptionConversionGenerator: ValidationExceptionConversionGenerator init { - val symbolVisitorConfig = - SymbolVisitorConfig( - runtimeConfig = settings.runtimeConfig, - renameExceptions = false, - nullabilityCheckMode = NullableIndex.CheckMode.SERVER, - ) + val rustSymbolProviderConfig = RustSymbolProviderConfig( + runtimeConfig = settings.runtimeConfig, + renameExceptions = false, + nullabilityCheckMode = NullableIndex.CheckMode.SERVER, + moduleProvider = ServerModuleProvider, + ) val baseModel = baselineTransform(context.model) val service = settings.getService(baseModel) @@ -125,16 +131,19 @@ open class ServerCodegenVisitor( model = codegenDecorator.transformModel(service, baseModel) val serverSymbolProviders = ServerSymbolProviders.from( + settings, model, service, - symbolVisitorConfig, + rustSymbolProviderConfig, settings.codegenConfig.publicConstrainedTypes, - RustCodegenServerPlugin::baseSymbolProvider, + codegenDecorator, + RustServerCodegenPlugin::baseSymbolProvider, ) codegenContext = ServerCodegenContext( model, serverSymbolProviders.symbolProvider, + null, service, protocol, settings, @@ -144,7 +153,22 @@ open class ServerCodegenVisitor( serverSymbolProviders.pubCrateConstrainedShapeSymbolProvider, ) - rustCrate = RustCrate(context.fileManifest, codegenContext.symbolProvider, settings.codegenConfig) + // We can use a not-null assertion because [CombinedServerCodegenDecorator] returns a not null value. + validationExceptionConversionGenerator = codegenDecorator.validationExceptionConversion(codegenContext)!! + + codegenContext = codegenContext.copy( + moduleDocProvider = codegenDecorator.moduleDocumentationCustomization( + codegenContext, + ServerModuleDocProvider(codegenContext), + ), + ) + + rustCrate = RustCrate( + context.fileManifest, + codegenContext.symbolProvider, + settings.codegenConfig, + codegenContext.expectModuleDocProvider(), + ) protocolGenerator = protocolGeneratorFactory.buildProtocolGenerator(codegenContext) } @@ -159,9 +183,14 @@ open class ServerCodegenVisitor( // Add errors attached at the service level to the models .let { ModelTransformer.create().copyServiceErrorsToOperations(it, settings.getService(it)) } // Add `Box` to recursive shapes as necessary - .let(RecursiveShapeBoxer::transform) + .let(RecursiveShapeBoxer()::transform) + // Add `Box` to recursive constraint violations as necessary + .let(RecursiveConstraintViolationBoxer::transform) // Normalize operations by adding synthetic input and output shapes to every operation .let(OperationNormalizer::transform) + // Transforms constrained member shapes into non-constrained member shapes targeting a new shape that + // has the member's constraints. + .let(ConstrainedMemberTransform::transform) // Remove the EBS model's own `ValidationException`, which collides with `smithy.framework#ValidationException` .let(RemoveEbsModelValidationException::transform) // Attach the `smithy.framework#ValidationException` error to operations whose inputs are constrained, @@ -195,10 +224,14 @@ open class ServerCodegenVisitor( "[rust-server-codegen] Generating Rust server for service $service, protocol ${codegenContext.protocol}", ) + val validationExceptionShapeId = validationExceptionConversionGenerator.shapeId for (validationResult in listOf( - validateOperationsWithConstrainedInputHaveValidationExceptionAttached( - model, - service, + codegenDecorator.postprocessValidationExceptionNotAttachedErrorMessage( + validateOperationsWithConstrainedInputHaveValidationExceptionAttached( + model, + service, + validationExceptionShapeId, + ), ), validateUnsupportedConstraints(model, service, codegenContext.settings.codegenConfig), )) { @@ -207,13 +240,18 @@ open class ServerCodegenVisitor( logger.log(logMessage.level, logMessage.message) } if (validationResult.shouldAbort) { - throw CodegenException("Unsupported constraints feature used; see error messages above for resolution") + throw CodegenException("Unsupported constraints feature used; see error messages above for resolution", validationResult) } } + rustCrate.initializeInlineModuleWriter(codegenContext.settings.codegenConfig.debugMode) + val serviceShapes = DirectedWalker(model).walkShapes(service) serviceShapes.forEach { it.accept(this) } codegenDecorator.extras(codegenContext, rustCrate) + + rustCrate.getInlineModuleWriter().render() + rustCrate.finalize( settings, model, @@ -249,7 +287,24 @@ open class ServerCodegenVisitor( override fun structureShape(shape: StructureShape) { logger.info("[rust-server-codegen] Generating a structure $shape") rustCrate.useShapeWriter(shape) { - StructureGenerator(model, codegenContext.symbolProvider, this, shape).render(CodegenTarget.SERVER) + StructureGenerator( + model, + codegenContext.symbolProvider, + this, + shape, + codegenDecorator.structureCustomizations(codegenContext, emptyList()), + ).render() + + shape.getTrait()?.also { errorTrait -> + ErrorImplGenerator( + model, + codegenContext.symbolProvider, + this, + shape, + errorTrait, + codegenDecorator.errorImplCustomizations(codegenContext, emptyList()), + ).render(CodegenTarget.SERVER) + } renderStructureShapeBuilder(shape, this) } @@ -260,11 +315,11 @@ open class ServerCodegenVisitor( writer: RustWriter, ) { if (codegenContext.settings.codegenConfig.publicConstrainedTypes || shape.isReachableFromOperationInput()) { - val serverBuilderGenerator = ServerBuilderGenerator(codegenContext, shape) - serverBuilderGenerator.render(writer) + val serverBuilderGenerator = ServerBuilderGenerator(codegenContext, shape, validationExceptionConversionGenerator) + serverBuilderGenerator.render(rustCrate, writer) if (codegenContext.settings.codegenConfig.publicConstrainedTypes) { - writer.implBlock(shape, codegenContext.symbolProvider) { + writer.implBlock(codegenContext.symbolProvider.toSymbol(shape)) { serverBuilderGenerator.renderConvenienceMethod(this) } } @@ -281,10 +336,10 @@ open class ServerCodegenVisitor( if (!codegenContext.settings.codegenConfig.publicConstrainedTypes) { val serverBuilderGeneratorWithoutPublicConstrainedTypes = - ServerBuilderGeneratorWithoutPublicConstrainedTypes(codegenContext, shape) - serverBuilderGeneratorWithoutPublicConstrainedTypes.render(writer) + ServerBuilderGeneratorWithoutPublicConstrainedTypes(codegenContext, shape, validationExceptionConversionGenerator) + serverBuilderGeneratorWithoutPublicConstrainedTypes.render(rustCrate, writer) - writer.implBlock(shape, codegenContext.symbolProvider) { + writer.implBlock(codegenContext.symbolProvider.toSymbol(shape)) { serverBuilderGeneratorWithoutPublicConstrainedTypes.renderConvenienceMethod(this) } } @@ -303,25 +358,29 @@ open class ServerCodegenVisitor( if (renderUnconstrainedList) { logger.info("[rust-server-codegen] Generating an unconstrained type for collection shape $shape") - rustCrate.withModule(UnconstrainedModule) { + rustCrate.withModuleOrWithStructureBuilderModule(ServerRustModule.UnconstrainedModule, shape, codegenContext) { UnconstrainedCollectionGenerator( codegenContext, - this, + rustCrate.createInlineModuleCreator(), shape, ).render() } if (!isDirectlyConstrained) { logger.info("[rust-server-codegen] Generating a constrained type for collection shape $shape") - rustCrate.withModule(ConstrainedModule) { - PubCrateConstrainedCollectionGenerator(codegenContext, this, shape).render() + rustCrate.withModuleOrWithStructureBuilderModule(ServerRustModule.ConstrainedModule, shape, codegenContext) { + PubCrateConstrainedCollectionGenerator( + codegenContext, + rustCrate.createInlineModuleCreator(), + shape, + ).render() } } } val constraintsInfo = CollectionTraitInfo.fromShape(shape, codegenContext.constrainedShapeSymbolProvider) if (isDirectlyConstrained) { - rustCrate.withModule(ModelsModule) { + rustCrate.withModuleOrWithStructureBuilderModule(ServerRustModule.Model, shape, codegenContext) { ConstrainedCollectionGenerator( codegenContext, this, @@ -333,8 +392,13 @@ open class ServerCodegenVisitor( } if (isDirectlyConstrained || renderUnconstrainedList) { - rustCrate.withModule(ModelsModule) { - CollectionConstraintViolationGenerator(codegenContext, this, shape, constraintsInfo).render() + rustCrate.withModuleOrWithStructureBuilderModule(ServerRustModule.Model, shape, codegenContext) { + CollectionConstraintViolationGenerator( + codegenContext, + rustCrate.createInlineModuleCreator(), + shape, constraintsInfo, + validationExceptionConversionGenerator, + ).render() } } } @@ -349,20 +413,28 @@ open class ServerCodegenVisitor( if (renderUnconstrainedMap) { logger.info("[rust-server-codegen] Generating an unconstrained type for map $shape") - rustCrate.withModule(UnconstrainedModule) { - UnconstrainedMapGenerator(codegenContext, this, shape).render() + rustCrate.withModuleOrWithStructureBuilderModule(ServerRustModule.UnconstrainedModule, shape, codegenContext) { + UnconstrainedMapGenerator( + codegenContext, + rustCrate.createInlineModuleCreator(), + shape, + ).render() } if (!isDirectlyConstrained) { logger.info("[rust-server-codegen] Generating a constrained type for map $shape") - rustCrate.withModule(ConstrainedModule) { - PubCrateConstrainedMapGenerator(codegenContext, this, shape).render() + rustCrate.withModuleOrWithStructureBuilderModule(ServerRustModule.ConstrainedModule, shape, codegenContext) { + PubCrateConstrainedMapGenerator( + codegenContext, + rustCrate.createInlineModuleCreator(), + shape, + ).render() } } } if (isDirectlyConstrained) { - rustCrate.withModule(ModelsModule) { + rustCrate.withModuleOrWithStructureBuilderModule(ServerRustModule.Model, shape, codegenContext) { ConstrainedMapGenerator( codegenContext, this, @@ -373,8 +445,13 @@ open class ServerCodegenVisitor( } if (isDirectlyConstrained || renderUnconstrainedMap) { - rustCrate.withModule(ModelsModule) { - MapConstraintViolationGenerator(codegenContext, this, shape).render() + rustCrate.withModuleOrWithStructureBuilderModule(ServerRustModule.Model, shape, codegenContext) { + MapConstraintViolationGenerator( + codegenContext, + rustCrate.createInlineModuleCreator(), + shape, + validationExceptionConversionGenerator, + ).render() } } } @@ -385,55 +462,37 @@ open class ServerCodegenVisitor( * Although raw strings require no code generation, enums are actually [EnumTrait] applied to string shapes. */ override fun stringShape(shape: StringShape) { - fun serverEnumGeneratorFactory(codegenContext: ServerCodegenContext, writer: RustWriter, shape: StringShape) = - ServerEnumGenerator(codegenContext, writer, shape) + fun serverEnumGeneratorFactory(codegenContext: ServerCodegenContext, shape: StringShape) = + ServerEnumGenerator(codegenContext, shape, validationExceptionConversionGenerator) stringShape(shape, ::serverEnumGeneratorFactory) } - override fun integerShape(shape: IntegerShape) { - if (shape.isDirectlyConstrained(codegenContext.symbolProvider)) { - logger.info("[rust-server-codegen] Generating a constrained integer $shape") - rustCrate.withModule(ModelsModule) { - ConstrainedNumberGenerator(codegenContext, this, shape).render() - } - } - } - - override fun shortShape(shape: ShortShape) { - if (shape.isDirectlyConstrained(codegenContext.symbolProvider)) { - logger.info("[rust-server-codegen] Generating a constrained short $shape") - rustCrate.withModule(ModelsModule) { - ConstrainedNumberGenerator(codegenContext, this, shape).render() - } - } - } - - override fun longShape(shape: LongShape) { + override fun integerShape(shape: IntegerShape) = integralShape(shape) + override fun shortShape(shape: ShortShape) = integralShape(shape) + override fun longShape(shape: LongShape) = integralShape(shape) + override fun byteShape(shape: ByteShape) = integralShape(shape) + private fun integralShape(shape: NumberShape) { if (shape.isDirectlyConstrained(codegenContext.symbolProvider)) { - logger.info("[rust-server-codegen] Generating a constrained long $shape") - rustCrate.withModule(ModelsModule) { - ConstrainedNumberGenerator(codegenContext, this, shape).render() - } - } - } - - override fun byteShape(shape: ByteShape) { - if (shape.isDirectlyConstrained(codegenContext.symbolProvider)) { - logger.info("[rust-server-codegen] Generating a constrained byte $shape") - rustCrate.withModule(ModelsModule) { - ConstrainedNumberGenerator(codegenContext, this, shape).render() + logger.info("[rust-server-codegen] Generating a constrained integral $shape") + rustCrate.withModuleOrWithStructureBuilderModule(ServerRustModule.Model, shape, codegenContext) { + ConstrainedNumberGenerator( + codegenContext, rustCrate.createInlineModuleCreator(), + this, + shape, + validationExceptionConversionGenerator, + ).render() } } } protected fun stringShape( shape: StringShape, - enumShapeGeneratorFactory: (codegenContext: ServerCodegenContext, writer: RustWriter, shape: StringShape) -> ServerEnumGenerator, + enumShapeGeneratorFactory: (codegenContext: ServerCodegenContext, shape: StringShape) -> EnumGenerator, ) { if (shape.hasTrait()) { logger.info("[rust-server-codegen] Generating an enum $shape") - rustCrate.useShapeWriter(shape) { - enumShapeGeneratorFactory(codegenContext, this, shape).render() + rustCrate.useShapeWriterOrUseWithStructureBuilder(shape, codegenContext) { + enumShapeGeneratorFactory(codegenContext, shape).render(this) ConstrainedTraitForEnumGenerator(model, codegenContext.symbolProvider, this, shape).render() } } @@ -449,8 +508,14 @@ open class ServerCodegenVisitor( ) } else if (!shape.hasTrait() && shape.isDirectlyConstrained(codegenContext.symbolProvider)) { logger.info("[rust-server-codegen] Generating a constrained string $shape") - rustCrate.withModule(ModelsModule) { - ConstrainedStringGenerator(codegenContext, this, shape).render() + rustCrate.withModuleOrWithStructureBuilderModule(ServerRustModule.Model, shape, codegenContext) { + ConstrainedStringGenerator( + codegenContext, + rustCrate.createInlineModuleCreator(), + this, + shape, + validationExceptionConversionGenerator, + ).render() } } } @@ -474,32 +539,32 @@ open class ServerCodegenVisitor( ) ) { logger.info("[rust-server-codegen] Generating an unconstrained type for union shape $shape") - rustCrate.withModule(UnconstrainedModule) unconstrainedModuleWriter@{ - rustCrate.withModule(ModelsModule) modelsModuleWriter@{ - UnconstrainedUnionGenerator( - codegenContext, - this@unconstrainedModuleWriter, - this@modelsModuleWriter, - shape, - ).render() - } + rustCrate.withModule(ServerRustModule.UnconstrainedModule) modelsModuleWriter@{ + UnconstrainedUnionGenerator( + codegenContext, + rustCrate.createInlineModuleCreator(), + this@modelsModuleWriter, + shape, + ).render() } } if (shape.isEventStream()) { - val errors = shape.eventStreamErrors() - .map { model.expectShape(it.asMemberShape().get().target, StructureShape::class.java) } - if (errors.isNotEmpty()) { - rustCrate.withModule(RustModule.Error) { - val symbol = codegenContext.symbolProvider.toSymbol(shape) - val errorSymbol = shape.eventStreamErrorSymbol(codegenContext.symbolProvider) - ServerOperationErrorGenerator(model, codegenContext.symbolProvider, symbol, errors) - .renderErrors(this, errorSymbol, symbol) - } + rustCrate.withModule(ServerRustModule.Error) { + ServerOperationErrorGenerator(model, codegenContext.symbolProvider, shape).render(this) } } } + /** + * Generate protocol tests. This method can be overridden by other languages such has Python. + */ + open fun protocolTests() { + rustCrate.withModule(ServerRustModule.Operation) { + ServerProtocolTestGenerator(codegenContext, protocolGeneratorFactory.support(), protocolGenerator).render(this) + } + } + /** * Generate service-specific code for the model: * - Serializers @@ -511,28 +576,53 @@ open class ServerCodegenVisitor( */ override fun serviceShape(shape: ServiceShape) { logger.info("[rust-server-codegen] Generating a service $shape") - ServerServiceGenerator( - rustCrate, - protocolGenerator, - protocolGeneratorFactory.support(), - protocolGeneratorFactory.protocol(codegenContext) as ServerProtocol, - codegenContext, - ) - .render() + val serverProtocol = protocolGeneratorFactory.protocol(codegenContext) as ServerProtocol + + // Generate root + rustCrate.lib { + ServerRootGenerator( + serverProtocol, + codegenContext, + ).render(this) + } + + // Generate server re-exports + rustCrate.withModule(ServerRustModule.Server) { + ServerRuntimeTypesReExportsGenerator(codegenContext).render(this) + } + + // Generate protocol tests + protocolTests() + + // Generate service module + rustCrate.withModule(ServerRustModule.Service) { + ServerServiceGenerator( + codegenContext, + serverProtocol, + ).render(this) + } } /** - * Generate errors for operation shapes + * For each operation shape generate: + * - Operations ser/de + * - Errors via `ServerOperationErrorGenerator` + * - OperationShapes via `ServerOperationGenerator` */ override fun operationShape(shape: OperationShape) { - rustCrate.withModule(RustModule.Error) { - val symbol = codegenContext.symbolProvider.toSymbol(shape) - ServerOperationErrorGenerator( - model, - codegenContext.symbolProvider, - symbol, - shape.operationErrors(model).map { it.asStructureShape().get() }, - ).render(this) + // Generate errors. + rustCrate.withModule(ServerRustModule.Error) { + ServerOperationErrorGenerator(model, codegenContext.symbolProvider, shape).render(this) + } + + // Generate operation shapes. + rustCrate.withModule(ServerRustModule.OperationShape) { + ServerOperationGenerator(shape, codegenContext).render(this) + } + + // Generate operations ser/de. + rustCrate.withModule(ServerRustModule.Operation) { + protocolGenerator.renderOperation(this, shape) } } @@ -543,8 +633,14 @@ open class ServerCodegenVisitor( } if (shape.isDirectlyConstrained(codegenContext.symbolProvider)) { - rustCrate.withModule(ModelsModule) { - ConstrainedBlobGenerator(codegenContext, this, shape).render() + rustCrate.withModuleOrWithStructureBuilderModule(ServerRustModule.Model, shape, codegenContext) { + ConstrainedBlobGenerator( + codegenContext, + rustCrate.createInlineModuleCreator(), + this, + shape, + validationExceptionConversionGenerator, + ).render() } } } diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/ServerReservedWords.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/ServerReservedWords.kt new file mode 100644 index 0000000000..64d9ea04f4 --- /dev/null +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/ServerReservedWords.kt @@ -0,0 +1,15 @@ +/* + * 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.rust.codegen.core.rustlang.RustReservedWordConfig +import software.amazon.smithy.rust.codegen.core.smithy.generators.StructureGenerator + +val ServerReservedWords = RustReservedWordConfig( + structureMemberMap = StructureGenerator.structureMemberNameMap, + unionMemberMap = emptyMap(), + enumMemberMap = emptyMap(), +) 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 new file mode 100644 index 0000000000..15fde9837f --- /dev/null +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/ServerRustModule.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.server.smithy + +import software.amazon.smithy.codegen.core.Symbol +import software.amazon.smithy.model.knowledge.TopDownIndex +import software.amazon.smithy.model.shapes.OperationShape +import software.amazon.smithy.model.shapes.Shape +import software.amazon.smithy.model.shapes.StructureShape +import software.amazon.smithy.model.shapes.UnionShape +import software.amazon.smithy.model.traits.ErrorTrait +import software.amazon.smithy.rust.codegen.core.rustlang.RustModule +import software.amazon.smithy.rust.codegen.core.rustlang.RustReservedWords +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.docs +import software.amazon.smithy.rust.codegen.core.rustlang.escape +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.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.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 +object ServerRustModule { + val root = RustModule.LibRs + + val Error = RustModule.public("error") + val Operation = RustModule.public("operation") + val OperationShape = RustModule.public("operation_shape") + val Model = RustModule.public("model") + val Input = RustModule.public("input") + val Output = RustModule.public("output") + val Types = RustModule.public("types") + val Server = RustModule.public("server") + val Service = RustModule.private("service") + + val UnconstrainedModule = + software.amazon.smithy.rust.codegen.core.smithy.UnconstrainedModule + val ConstrainedModule = + software.amazon.smithy.rust.codegen.core.smithy.ConstrainedModule +} + +class ServerModuleDocProvider(private val codegenContext: ServerCodegenContext) : ModuleDocProvider { + override fun docsWriter(module: RustModule.LeafModule): Writable? { + val strDoc: (String) -> Writable = { str -> writable { docs(escape(str)) } } + return when (module) { + ServerRustModule.Error -> strDoc("All error types that operations can return. Documentation on these types is copied from the model.") + ServerRustModule.Operation -> strDoc("All operations that this crate can perform.") + ServerRustModule.OperationShape -> operationShapeModuleDoc() + ServerRustModule.Model -> strDoc("Data structures used by operation inputs/outputs. Documentation on these types is copied from the model.") + ServerRustModule.Input -> strDoc("Input structures for operations. Documentation on these types is copied from the model.") + ServerRustModule.Output -> strDoc("Output structures for operations. Documentation on these types is copied from the model.") + ServerRustModule.Types -> strDoc("Data primitives referenced by other data types.") + ServerRustModule.Server -> strDoc("Contains the types that are re-exported from the `aws-smithy-http-server` crate.") + ServerRustModule.UnconstrainedModule -> strDoc("Unconstrained types for constrained shapes.") + ServerRustModule.ConstrainedModule -> strDoc("Constrained types for constrained shapes.") + else -> TODO("Document this module: $module") + } + } + + private fun operationShapeModuleDoc(): Writable = writable { + val index = TopDownIndex.of(codegenContext.model) + 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 + /// [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 + /// 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, + "HandlerImports" to handlerImports(crateName, operations, commentToken = "///"), + ) + } +} + +object ServerModuleProvider : ModuleProvider { + override fun moduleForShape(context: ModuleProviderContext, shape: Shape): RustModule.LeafModule = when (shape) { + is OperationShape -> ServerRustModule.Operation + is StructureShape -> when { + shape.hasTrait() -> ServerRustModule.Error + shape.hasTrait() -> ServerRustModule.Input + shape.hasTrait() -> ServerRustModule.Output + else -> ServerRustModule.Model + } + else -> ServerRustModule.Model + } + + override fun moduleForOperationError( + context: ModuleProviderContext, + operation: OperationShape, + ): RustModule.LeafModule = ServerRustModule.Error + + override fun moduleForEventStreamError( + context: ModuleProviderContext, + eventStream: UnionShape, + ): RustModule.LeafModule = ServerRustModule.Error + + override fun moduleForBuilder(context: ModuleProviderContext, shape: Shape, symbol: Symbol): RustModule.LeafModule { + val pubCrate = !(context.settings as ServerRustSettings).codegenConfig.publicConstrainedTypes + val builderNamespace = RustReservedWords.escapeIfNeeded(symbol.name.toSnakeCase()) + + if (pubCrate) { + "_internal" + } else { + "" + } + val visibility = when (pubCrate) { + true -> Visibility.PUBCRATE + false -> Visibility.PUBLIC + } + return RustModule.new( + builderNamespace, + visibility, + parent = symbol.module(), + inline = true, + documentationOverride = "", + ) + } +} diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/ServerRustSettings.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/ServerRustSettings.kt index dbfc8356a2..67fbea91cd 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/ServerRustSettings.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/ServerRustSettings.kt @@ -25,7 +25,7 @@ import java.util.Optional */ /** - * Settings used by [RustCodegenServerPlugin]. + * Settings used by [RustServerCodegenPlugin]. */ data class ServerRustSettings( override val service: ShapeId, @@ -83,12 +83,20 @@ data class ServerCodegenConfig( override val debugMode: Boolean = defaultDebugMode, val publicConstrainedTypes: Boolean = defaultPublicConstrainedTypes, val ignoreUnsupportedConstraints: Boolean = defaultIgnoreUnsupportedConstraints, + /** + * A flag to enable _experimental_ support for custom validation exceptions via the + * [CustomValidationExceptionWithReasonDecorator] decorator. + * TODO(https://github.com/awslabs/smithy-rs/pull/2053): this will go away once we implement the RFC, when users will be + * able to define the converters in their Rust application code. + */ + val experimentalCustomValidationExceptionWithReasonPleaseDoNotUse: String? = defaultExperimentalCustomValidationExceptionWithReasonPleaseDoNotUse, ) : CoreCodegenConfig( formatTimeoutSeconds, debugMode, ) { companion object { private const val defaultPublicConstrainedTypes = true private const val defaultIgnoreUnsupportedConstraints = false + private val defaultExperimentalCustomValidationExceptionWithReasonPleaseDoNotUse = null fun fromCodegenConfigAndNode(coreCodegenConfig: CoreCodegenConfig, node: Optional) = if (node.isPresent) { @@ -97,6 +105,7 @@ data class ServerCodegenConfig( debugMode = coreCodegenConfig.debugMode, publicConstrainedTypes = node.get().getBooleanMemberOrDefault("publicConstrainedTypes", defaultPublicConstrainedTypes), ignoreUnsupportedConstraints = node.get().getBooleanMemberOrDefault("ignoreUnsupportedConstraints", defaultIgnoreUnsupportedConstraints), + experimentalCustomValidationExceptionWithReasonPleaseDoNotUse = node.get().getStringMemberOrDefault("experimentalCustomValidationExceptionWithReasonPleaseDoNotUse", defaultExperimentalCustomValidationExceptionWithReasonPleaseDoNotUse), ) } else { ServerCodegenConfig( diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/ServerSymbolProviders.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/ServerSymbolProviders.kt index 0e368d8517..a2693050a3 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/ServerSymbolProviders.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/ServerSymbolProviders.kt @@ -8,7 +8,8 @@ package software.amazon.smithy.rust.codegen.server.smithy import software.amazon.smithy.model.Model import software.amazon.smithy.model.shapes.ServiceShape import software.amazon.smithy.rust.codegen.core.smithy.RustSymbolProvider -import software.amazon.smithy.rust.codegen.core.smithy.SymbolVisitorConfig +import software.amazon.smithy.rust.codegen.core.smithy.RustSymbolProviderConfig +import software.amazon.smithy.rust.codegen.server.smithy.customize.ServerCodegenDecorator /** * Just a handy class to centralize initialization all the symbol providers required by the server code generators, to @@ -24,38 +25,44 @@ class ServerSymbolProviders private constructor( ) { companion object { fun from( + settings: ServerRustSettings, model: Model, service: ServiceShape, - symbolVisitorConfig: SymbolVisitorConfig, + rustSymbolProviderConfig: RustSymbolProviderConfig, publicConstrainedTypes: Boolean, - baseSymbolProviderFactory: (model: Model, service: ServiceShape, symbolVisitorConfig: SymbolVisitorConfig, publicConstrainedTypes: Boolean) -> RustSymbolProvider, + codegenDecorator: ServerCodegenDecorator, + baseSymbolProviderFactory: (settings: ServerRustSettings, model: Model, service: ServiceShape, rustSymbolProviderConfig: RustSymbolProviderConfig, publicConstrainedTypes: Boolean, includeConstraintShapeProvider: Boolean, codegenDecorator: ServerCodegenDecorator) -> RustSymbolProvider, ): ServerSymbolProviders { - val baseSymbolProvider = baseSymbolProviderFactory(model, service, symbolVisitorConfig, publicConstrainedTypes) + val baseSymbolProvider = baseSymbolProviderFactory(settings, model, service, rustSymbolProviderConfig, publicConstrainedTypes, publicConstrainedTypes, codegenDecorator) return ServerSymbolProviders( symbolProvider = baseSymbolProvider, constrainedShapeSymbolProvider = baseSymbolProviderFactory( + settings, model, service, - symbolVisitorConfig, + rustSymbolProviderConfig, + publicConstrainedTypes, true, + codegenDecorator, ), unconstrainedShapeSymbolProvider = UnconstrainedShapeSymbolProvider( baseSymbolProviderFactory( + settings, model, service, - symbolVisitorConfig, + rustSymbolProviderConfig, + false, false, + codegenDecorator, ), - model, publicConstrainedTypes, service, + publicConstrainedTypes, service, ), pubCrateConstrainedShapeSymbolProvider = PubCrateConstrainedShapeSymbolProvider( baseSymbolProvider, - model, service, ), constraintViolationSymbolProvider = ConstraintViolationSymbolProvider( baseSymbolProvider, - model, publicConstrainedTypes, service, ), diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/UnconstrainedShapeSymbolProvider.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/UnconstrainedShapeSymbolProvider.kt index 3da3129387..711f35e462 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/UnconstrainedShapeSymbolProvider.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/UnconstrainedShapeSymbolProvider.kt @@ -6,7 +6,6 @@ package software.amazon.smithy.rust.codegen.server.smithy import software.amazon.smithy.codegen.core.Symbol -import software.amazon.smithy.model.Model import software.amazon.smithy.model.knowledge.NullableIndex import software.amazon.smithy.model.shapes.CollectionShape import software.amazon.smithy.model.shapes.MapShape @@ -22,7 +21,6 @@ import software.amazon.smithy.rust.codegen.core.rustlang.RustType import software.amazon.smithy.rust.codegen.core.rustlang.Visibility import software.amazon.smithy.rust.codegen.core.smithy.Default import software.amazon.smithy.rust.codegen.core.smithy.RustSymbolProvider -import software.amazon.smithy.rust.codegen.core.smithy.UnconstrainedModule import software.amazon.smithy.rust.codegen.core.smithy.WrappingSymbolProvider import software.amazon.smithy.rust.codegen.core.smithy.contextName import software.amazon.smithy.rust.codegen.core.smithy.handleOptionality @@ -78,7 +76,6 @@ import software.amazon.smithy.rust.codegen.server.smithy.generators.serverBuilde */ class UnconstrainedShapeSymbolProvider( private val base: RustSymbolProvider, - private val model: Model, private val publicConstrainedTypes: Boolean, private val serviceShape: ServiceShape, ) : WrappingSymbolProvider(base) { @@ -101,10 +98,12 @@ class UnconstrainedShapeSymbolProvider( check(shape is CollectionShape || shape is MapShape || shape is UnionShape) val name = unconstrainedTypeNameForCollectionOrMapOrUnionShape(shape) + val parent = shape.getParentAndInlineModuleForConstrainedMember(this, publicConstrainedTypes)?.second ?: ServerRustModule.UnconstrainedModule + val module = RustModule.new( RustReservedWords.escapeIfNeeded(name.toSnakeCase()), visibility = Visibility.PUBCRATE, - parent = UnconstrainedModule, + parent = parent, inline = true, ) val rustType = RustType.Opaque(name, module.fullyQualifiedPath()) @@ -167,7 +166,7 @@ class UnconstrainedShapeSymbolProvider( handleRustBoxing(targetSymbol, shape), shape, nullableIndex, - base.config().nullabilityCheckMode, + base.config.nullabilityCheckMode, ) } else { base.toSymbol(shape) 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 8bf6f928d9..3a6cdabcd0 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 @@ -10,7 +10,9 @@ import software.amazon.smithy.model.shapes.BlobShape import software.amazon.smithy.model.shapes.ByteShape import software.amazon.smithy.model.shapes.EnumShape import software.amazon.smithy.model.shapes.IntegerShape +import software.amazon.smithy.model.shapes.ListShape import software.amazon.smithy.model.shapes.LongShape +import software.amazon.smithy.model.shapes.MapShape import software.amazon.smithy.model.shapes.MemberShape import software.amazon.smithy.model.shapes.OperationShape import software.amazon.smithy.model.shapes.ServiceShape @@ -36,13 +38,17 @@ private sealed class UnsupportedConstraintMessageKind { private val constraintTraitsUberIssue = "https://github.com/awslabs/smithy-rs/issues/1401" fun intoLogMessage(ignoreUnsupportedConstraints: Boolean): LogMessage { - fun buildMessage(intro: String, willSupport: Boolean, trackingIssue: String, canBeIgnored: Boolean = true): String { + fun buildMessage(intro: String, willSupport: Boolean, trackingIssue: String? = null, canBeIgnored: Boolean = true): String { var msg = """ $intro This is not supported in the smithy-rs server SDK.""" if (willSupport) { msg += """ - It will be supported in the future. See the tracking issue ($trackingIssue).""" + It will be supported in the future.""" + } + if (trackingIssue != null) { + msg += """ + For more information, and to report if you're affected by this, please use the tracking issue: $trackingIssue.""" } if (canBeIgnored) { msg += """ @@ -106,6 +112,19 @@ private sealed class UnsupportedConstraintMessageKind { level, buildMessageShapeHasUnsupportedConstraintTrait(shape, uniqueItemsTrait, constraintTraitsUberIssue), ) + + is UnsupportedMapShapeReachableFromUniqueItemsList -> LogMessage( + Level.SEVERE, + buildMessage( + """ + The map shape `${mapShape.id}` is reachable from the list shape `${listShape.id}`, which has the + `@uniqueItems` trait attached. + """.trimIndent().replace("\n", " "), + willSupport = false, + trackingIssue = "https://github.com/awslabs/smithy/issues/1567", + canBeIgnored = false, + ), + ) } } } @@ -129,14 +148,25 @@ private data class UnsupportedRangeTraitOnShape(val shape: Shape, val rangeTrait private data class UnsupportedUniqueItemsTraitOnShape(val shape: Shape, val uniqueItemsTrait: UniqueItemsTrait) : UnsupportedConstraintMessageKind() +private data class UnsupportedMapShapeReachableFromUniqueItemsList( + val listShape: ListShape, + val uniqueItemsTrait: UniqueItemsTrait, + val mapShape: MapShape, +) : UnsupportedConstraintMessageKind() + data class LogMessage(val level: Level, val message: String) -data class ValidationResult(val shouldAbort: Boolean, val messages: List) +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. + */ fun validateOperationsWithConstrainedInputHaveValidationExceptionAttached( model: Model, service: ServiceShape, + validationExceptionShapeId: ShapeId, ): ValidationResult { // Traverse the model and error out if an operation uses constrained input, but it does not have // `ValidationException` attached in `errors`. https://github.com/awslabs/smithy-rs/pull/1199#discussion_r809424783 @@ -151,7 +181,7 @@ fun validateOperationsWithConstrainedInputHaveValidationExceptionAttached( walker.walkShapes(operationShape.inputShape(model)) .any { it is SetShape || it is EnumShape || it.hasConstraintTrait() } } - .filter { !it.errors.contains(ShapeId.from("smithy.framework#ValidationException")) } + .filter { !it.errors.contains(validationExceptionShapeId) } .map { OperationWithConstrainedInputWithoutValidationException(it) } .toSet() @@ -167,11 +197,11 @@ fun validateOperationsWithConstrainedInputHaveValidationExceptionAttached( """ ```smithy - use smithy.framework#ValidationException + use $validationExceptionShapeId operation ${it.shape.id.name} { ... - errors: [..., ValidationException] // <-- Add this. + errors: [..., ${validationExceptionShapeId.name}] // <-- Add this. } ``` """.trimIndent(), @@ -189,18 +219,7 @@ fun validateUnsupportedConstraints( // Traverse the model and error out if: val walker = DirectedWalker(model) - // 1. Constraint traits on member shapes are used. [Constraint trait precedence] has not been implemented yet. - // TODO(https://github.com/awslabs/smithy-rs/issues/1401) - // [Constraint trait precedence]: https://awslabs.github.io/smithy/2.0/spec/model.html#applying-traits - val unsupportedConstraintOnMemberShapeSet = walker - .walkShapes(service) - .asSequence() - .filterIsInstance() - .filterMapShapesToTraits(unsupportedConstraintsOnMemberShapes) - .map { (shape, trait) -> UnsupportedConstraintOnMemberShape(shape as MemberShape, trait) } - .toSet() - - // 2. Constraint traits on streaming blob shapes are used. Their semantics are unclear. + // 1. Constraint traits on streaming blob shapes are used. Their semantics are unclear. // TODO(https://github.com/awslabs/smithy/issues/1389) val unsupportedLengthTraitOnStreamingBlobShapeSet = walker .walkShapes(service) @@ -210,7 +229,7 @@ fun validateUnsupportedConstraints( .map { UnsupportedLengthTraitOnStreamingBlobShape(it, it.expectTrait(), it.expectTrait()) } .toSet() - // 3. Constraint traits in event streams are used. Their semantics are unclear. + // 2. Constraint traits in event streams are used. Their semantics are unclear. // TODO(https://github.com/awslabs/smithy/issues/1388) val eventStreamShapes = walker .walkShapes(service) @@ -221,7 +240,9 @@ fun validateUnsupportedConstraints( .filterMapShapesToTraits(allConstraintTraits) .map { (shape, trait) -> UnsupportedConstraintOnShapeReachableViaAnEventStream(shape, trait) } .toSet() - val eventStreamErrors = eventStreamShapes.map { it.expectTrait() }.map { it.errorMembers } + val eventStreamErrors = eventStreamShapes.map { + it.expectTrait() + }.map { it.errorMembers } val unsupportedConstraintErrorShapeReachableViaAnEventStreamSet = eventStreamErrors .flatMap { it } .flatMap { walker.walkShapes(it) } @@ -231,7 +252,7 @@ fun validateUnsupportedConstraints( val unsupportedConstraintShapeReachableViaAnEventStreamSet = unsupportedConstraintOnNonErrorShapeReachableViaAnEventStreamSet + unsupportedConstraintErrorShapeReachableViaAnEventStreamSet - // 4. Range trait used on unsupported shapes. + // 3. Range trait used on unsupported shapes. // TODO(https://github.com/awslabs/smithy-rs/issues/1401) val unsupportedRangeTraitOnShapeSet = walker .walkShapes(service) @@ -241,11 +262,34 @@ fun validateUnsupportedConstraints( .map { (shape, rangeTrait) -> UnsupportedRangeTraitOnShape(shape, rangeTrait as RangeTrait) } .toSet() + // 5. `@uniqueItems` cannot reach a map shape. + // See https://github.com/awslabs/smithy/issues/1567. + val mapShapeReachableFromUniqueItemsListShapeSet = walker + .walkShapes(service) + .asSequence() + .filterMapShapesToTraits(setOf(UniqueItemsTrait::class.java)) + .flatMap { (listShape, uniqueItemsTrait) -> + walker.walkShapes(listShape).filterIsInstance().map { mapShape -> + UnsupportedMapShapeReachableFromUniqueItemsList( + listShape as ListShape, + uniqueItemsTrait as UniqueItemsTrait, + mapShape, + ) + } + } + .toSet() + val messages = - unsupportedConstraintOnMemberShapeSet.map { it.intoLogMessage(codegenConfig.ignoreUnsupportedConstraints) } + - unsupportedLengthTraitOnStreamingBlobShapeSet.map { it.intoLogMessage(codegenConfig.ignoreUnsupportedConstraints) } + - unsupportedConstraintShapeReachableViaAnEventStreamSet.map { it.intoLogMessage(codegenConfig.ignoreUnsupportedConstraints) } + - unsupportedRangeTraitOnShapeSet.map { it.intoLogMessage(codegenConfig.ignoreUnsupportedConstraints) } + unsupportedLengthTraitOnStreamingBlobShapeSet.map { + it.intoLogMessage(codegenConfig.ignoreUnsupportedConstraints) + } + + unsupportedConstraintShapeReachableViaAnEventStreamSet.map { + it.intoLogMessage(codegenConfig.ignoreUnsupportedConstraints) + } + + unsupportedRangeTraitOnShapeSet.map { it.intoLogMessage(codegenConfig.ignoreUnsupportedConstraints) } + + mapShapeReachableFromUniqueItemsListShapeSet.map { + it.intoLogMessage(codegenConfig.ignoreUnsupportedConstraints) + } 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/CustomValidationExceptionWithReasonDecorator.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/customizations/CustomValidationExceptionWithReasonDecorator.kt new file mode 100644 index 0000000000..cebde262b8 --- /dev/null +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/customizations/CustomValidationExceptionWithReasonDecorator.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.smithy.customizations + +import software.amazon.smithy.model.Model +import software.amazon.smithy.model.shapes.MapShape +import software.amazon.smithy.model.shapes.Shape +import software.amazon.smithy.model.shapes.ShapeId +import software.amazon.smithy.model.shapes.StringShape +import software.amazon.smithy.model.traits.EnumTrait +import software.amazon.smithy.model.traits.LengthTrait +import software.amazon.smithy.rust.codegen.core.rustlang.Writable +import software.amazon.smithy.rust.codegen.core.rustlang.join +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.RustSymbolProvider +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.ServerRuntimeType +import software.amazon.smithy.rust.codegen.server.smithy.customize.ServerCodegenDecorator +import software.amazon.smithy.rust.codegen.server.smithy.generators.BlobLength +import software.amazon.smithy.rust.codegen.server.smithy.generators.CollectionTraitInfo +import software.amazon.smithy.rust.codegen.server.smithy.generators.ConstraintViolation +import software.amazon.smithy.rust.codegen.server.smithy.generators.Length +import software.amazon.smithy.rust.codegen.server.smithy.generators.Pattern +import software.amazon.smithy.rust.codegen.server.smithy.generators.Range +import software.amazon.smithy.rust.codegen.server.smithy.generators.StringTraitInfo +import software.amazon.smithy.rust.codegen.server.smithy.generators.ValidationExceptionConversionGenerator +import software.amazon.smithy.rust.codegen.server.smithy.generators.isKeyConstrained +import software.amazon.smithy.rust.codegen.server.smithy.generators.isValueConstrained +import software.amazon.smithy.rust.codegen.server.smithy.validationErrorMessage + +/** + * A decorator that adds code to convert from constraint violations to a custom `ValidationException` shape that is very + * similar to `smithy.framework#ValidationException`, with an additional `reason` field. + * + * The shape definition is in [CustomValidationExceptionWithReasonDecoratorTest]. + * + * This is just an example to showcase experimental support for custom validation exceptions. + * TODO(https://github.com/awslabs/smithy-rs/pull/2053): this will go away once we implement the RFC, when users will be + * able to define the converters in their Rust application code. + */ +class CustomValidationExceptionWithReasonDecorator : ServerCodegenDecorator { + override val name: String + get() = "CustomValidationExceptionWithReasonDecorator" + override val order: Byte + get() = -69 + + override fun validationExceptionConversion(codegenContext: ServerCodegenContext): + ValidationExceptionConversionGenerator? = + if (codegenContext.settings.codegenConfig.experimentalCustomValidationExceptionWithReasonPleaseDoNotUse != null) { + ValidationExceptionWithReasonConversionGenerator(codegenContext) + } else { + null + } +} + +class ValidationExceptionWithReasonConversionGenerator(private val codegenContext: ServerCodegenContext) : + ValidationExceptionConversionGenerator { + override val shapeId: ShapeId = + ShapeId.from(codegenContext.settings.codegenConfig.experimentalCustomValidationExceptionWithReasonPleaseDoNotUse) + + override fun renderImplFromConstraintViolationForRequestRejection(): Writable = writable { + val codegenScope = arrayOf( + "RequestRejection" to ServerRuntimeType.requestRejection(codegenContext.runtimeConfig), + "From" to RuntimeType.From, + ) + rustTemplate( + """ + impl #{From} for #{RequestRejection} { + fn from(constraint_violation: ConstraintViolation) -> Self { + let first_validation_exception_field = constraint_violation.as_validation_exception_field("".to_owned()); + let validation_exception = crate::error::ValidationException { + message: format!("1 validation error detected. {}", &first_validation_exception_field.message), + reason: crate::model::ValidationExceptionReason::FieldValidationFailed, + fields: Some(vec![first_validation_exception_field]), + }; + Self::ConstraintViolation( + crate::protocol_serde::shape_validation_exception::ser_validation_exception_error(&validation_exception) + .expect("validation exceptions should never fail to serialize; please file a bug report under https://github.com/awslabs/smithy-rs/issues") + ) + } + } + """, + *codegenScope, + ) + } + + override fun stringShapeConstraintViolationImplBlock(stringConstraintsInfo: Collection): Writable = writable { + val validationExceptionFields = + stringConstraintsInfo.map { + writable { + when (it) { + is Pattern -> { + rustTemplate( + """ + Self::Pattern(_) => crate::model::ValidationExceptionField { + message: #{MessageWritable:W}, + name: path, + reason: crate::model::ValidationExceptionFieldReason::PatternNotValid, + }, + """, + "MessageWritable" to it.errorMessage(), + ) + } + is Length -> { + rust( + """ + Self::Length(length) => crate::model::ValidationExceptionField { + message: format!("${it.lengthTrait.validationErrorMessage()}", length, &path), + name: path, + reason: crate::model::ValidationExceptionFieldReason::LengthNotValid, + }, + """, + ) + } + } + } + }.join("\n") + + rustTemplate( + """ + pub(crate) fn as_validation_exception_field(self, path: #{String}) -> crate::model::ValidationExceptionField { + match self { + #{ValidationExceptionFields:W} + } + } + """, + "String" to RuntimeType.String, + "ValidationExceptionFields" to validationExceptionFields, + ) + } + + override fun enumShapeConstraintViolationImplBlock(enumTrait: EnumTrait) = writable { + val enumValueSet = enumTrait.enumDefinitionValues.joinToString(", ") + val message = "Value at '{}' failed to satisfy constraint: Member must satisfy enum value set: [$enumValueSet]" + rustTemplate( + """ + pub(crate) fn as_validation_exception_field(self, path: #{String}) -> crate::model::ValidationExceptionField { + crate::model::ValidationExceptionField { + message: format!(r##"$message"##, &path), + name: path, + reason: crate::model::ValidationExceptionFieldReason::ValueNotValid, + } + } + """, + "String" to RuntimeType.String, + ) + } + + override fun numberShapeConstraintViolationImplBlock(rangeInfo: Range) = writable { + rustTemplate( + """ + pub(crate) fn as_validation_exception_field(self, path: #{String}) -> crate::model::ValidationExceptionField { + match self { + Self::Range(_) => crate::model::ValidationExceptionField { + message: format!("${rangeInfo.rangeTrait.validationErrorMessage()}", &path), + name: path, + reason: crate::model::ValidationExceptionFieldReason::ValueNotValid, + } + } + } + """, + "String" to RuntimeType.String, + ) + } + + override fun blobShapeConstraintViolationImplBlock(blobConstraintsInfo: Collection) = writable { + val validationExceptionFields = + blobConstraintsInfo.map { + writable { + rust( + """ + Self::Length(length) => crate::model::ValidationExceptionField { + message: format!("${it.lengthTrait.validationErrorMessage()}", length, &path), + name: path, + reason: crate::model::ValidationExceptionFieldReason::LengthNotValid, + }, + """, + ) + } + }.join("\n") + + rustTemplate( + """ + pub(crate) fn as_validation_exception_field(self, path: #{String}) -> crate::model::ValidationExceptionField { + match self { + #{ValidationExceptionFields:W} + } + } + """, + "String" to RuntimeType.String, + "ValidationExceptionFields" to validationExceptionFields, + ) + } + + override fun mapShapeConstraintViolationImplBlock( + shape: MapShape, + keyShape: StringShape, + valueShape: Shape, + symbolProvider: RustSymbolProvider, + model: Model, + ) = writable { + rustBlockTemplate( + "pub(crate) fn as_validation_exception_field(self, path: #{String}) -> crate::model::ValidationExceptionField", + "String" to RuntimeType.String, + ) { + rustBlock("match self") { + shape.getTrait()?.also { + rust( + """ + Self::Length(length) => crate::model::ValidationExceptionField { + message: format!("${it.validationErrorMessage()}", length, &path), + name: path, + reason: crate::model::ValidationExceptionFieldReason::LengthNotValid, + }, + """, + ) + } + if (isKeyConstrained(keyShape, symbolProvider)) { + rust("""Self::Key(key_constraint_violation) => key_constraint_violation.as_validation_exception_field(path),""") + } + if (isValueConstrained(valueShape, model, symbolProvider)) { + rust("""Self::Value(key, value_constraint_violation) => value_constraint_violation.as_validation_exception_field(path + "/" + key.as_str()),""") + } + } + } + } + + override fun builderConstraintViolationImplBlock(constraintViolations: Collection) = writable { + rustBlock("match self") { + constraintViolations.forEach { + if (it.hasInner()) { + rust("""ConstraintViolation::${it.name()}(inner) => inner.as_validation_exception_field(path + "/${it.forMember.memberName}"),""") + } else { + rust( + """ + ConstraintViolation::${it.name()} => crate::model::ValidationExceptionField { + message: format!("Value at '{}/${it.forMember.memberName}' failed to satisfy constraint: Member must not be null", path), + name: path + "/${it.forMember.memberName}", + reason: crate::model::ValidationExceptionFieldReason::Other, + }, + """, + ) + } + } + } + } + + override fun collectionShapeConstraintViolationImplBlock( + collectionConstraintsInfo: + Collection, + isMemberConstrained: Boolean, + ) = writable { + val validationExceptionFields = collectionConstraintsInfo.map { + writable { + when (it) { + is CollectionTraitInfo.Length -> { + rust( + """ + Self::Length(length) => crate::model::ValidationExceptionField { + message: format!("${it.lengthTrait.validationErrorMessage()}", length, &path), + name: path, + reason: crate::model::ValidationExceptionFieldReason::LengthNotValid, + }, + """, + ) + } + is CollectionTraitInfo.UniqueItems -> { + rust( + """ + Self::UniqueItems { duplicate_indices, .. } => + crate::model::ValidationExceptionField { + message: format!("${it.uniqueItemsTrait.validationErrorMessage()}", &duplicate_indices, &path), + name: path, + reason: crate::model::ValidationExceptionFieldReason::ValueNotValid, + }, + """, + ) + } + } + } + }.toMutableList() + + if (isMemberConstrained) { + validationExceptionFields += { + rust( + """ + Self::Member(index, member_constraint_violation) => + member_constraint_violation.as_validation_exception_field(path + "/" + &index.to_string()) + """, + ) + } + } + rustTemplate( + """ + pub(crate) fn as_validation_exception_field(self, path: #{String}) -> crate::model::ValidationExceptionField { + match self { + #{AsValidationExceptionFields:W} + } + } + """, + "String" to RuntimeType.String, + "AsValidationExceptionFields" to validationExceptionFields.join("\n"), + ) + } +} diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/customizations/ServerRequiredCustomizations.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/customizations/ServerRequiredCustomizations.kt index 90b3550b98..ceba38691b 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/customizations/ServerRequiredCustomizations.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/customizations/ServerRequiredCustomizations.kt @@ -9,9 +9,11 @@ import software.amazon.smithy.rust.codegen.core.rustlang.Feature import software.amazon.smithy.rust.codegen.core.smithy.RustCrate import software.amazon.smithy.rust.codegen.core.smithy.customizations.AllowLintsCustomization import software.amazon.smithy.rust.codegen.core.smithy.customizations.CrateVersionCustomization -import software.amazon.smithy.rust.codegen.core.smithy.customizations.pubUseSmithyTypes +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.generators.LibRsCustomization import software.amazon.smithy.rust.codegen.server.smithy.ServerCodegenContext +import software.amazon.smithy.rust.codegen.server.smithy.ServerRustModule import software.amazon.smithy.rust.codegen.server.smithy.customize.ServerCodegenDecorator /** @@ -29,12 +31,19 @@ class ServerRequiredCustomizations : ServerCodegenDecorator { codegenContext: ServerCodegenContext, baseCustomizations: List, ): List = - baseCustomizations + CrateVersionCustomization() + AllowLintsCustomization() + baseCustomizations + AllowLintsCustomization() override fun extras(codegenContext: ServerCodegenContext, rustCrate: RustCrate) { // Add rt-tokio feature for `ByteStream::from_path` rustCrate.mergeFeature(Feature("rt-tokio", true, listOf("aws-smithy-http/rt-tokio"))) - pubUseSmithyTypes(codegenContext.runtimeConfig, codegenContext.model, rustCrate) + rustCrate.withModule(ServerRustModule.Types) { + pubUseSmithyPrimitives(codegenContext, codegenContext.model)(this) + pubUseSmithyErrorTypes(codegenContext)(this) + } + + rustCrate.withModule(ServerRustModule.root) { + CrateVersionCustomization.extras(rustCrate, ServerRustModule.root) + } } } diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/customizations/SmithyValidationExceptionDecorator.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/customizations/SmithyValidationExceptionDecorator.kt new file mode 100644 index 0000000000..7923f8bb3e --- /dev/null +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/customizations/SmithyValidationExceptionDecorator.kt @@ -0,0 +1,239 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +package software.amazon.smithy.rust.codegen.server.smithy.customizations + +import software.amazon.smithy.model.Model +import software.amazon.smithy.model.shapes.MapShape +import software.amazon.smithy.model.shapes.Shape +import software.amazon.smithy.model.shapes.ShapeId +import software.amazon.smithy.model.shapes.StringShape +import software.amazon.smithy.model.traits.EnumTrait +import software.amazon.smithy.model.traits.LengthTrait +import software.amazon.smithy.rust.codegen.core.rustlang.Writable +import software.amazon.smithy.rust.codegen.core.rustlang.join +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.RustSymbolProvider +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.ServerRuntimeType +import software.amazon.smithy.rust.codegen.server.smithy.customize.ServerCodegenDecorator +import software.amazon.smithy.rust.codegen.server.smithy.generators.BlobLength +import software.amazon.smithy.rust.codegen.server.smithy.generators.CollectionTraitInfo +import software.amazon.smithy.rust.codegen.server.smithy.generators.ConstraintViolation +import software.amazon.smithy.rust.codegen.server.smithy.generators.Range +import software.amazon.smithy.rust.codegen.server.smithy.generators.StringTraitInfo +import software.amazon.smithy.rust.codegen.server.smithy.generators.TraitInfo +import software.amazon.smithy.rust.codegen.server.smithy.generators.ValidationExceptionConversionGenerator +import software.amazon.smithy.rust.codegen.server.smithy.generators.isKeyConstrained +import software.amazon.smithy.rust.codegen.server.smithy.generators.isValueConstrained +import software.amazon.smithy.rust.codegen.server.smithy.validationErrorMessage + +/** + * A decorator that adds code to convert from constraint violations to Smithy's `smithy.framework#ValidationException`, + * defined in [0]. This is Smithy's recommended shape to return when validation fails. + * + * This decorator is always enabled when using the `rust-server-codegen` plugin. + * + * [0]: https://github.com/awslabs/smithy/tree/main/smithy-validation-model + * + * TODO(https://github.com/awslabs/smithy-rs/pull/2053): once the RFC is implemented, consider moving this back into the + * generators. + */ +class SmithyValidationExceptionDecorator : ServerCodegenDecorator { + override val name: String + get() = "SmithyValidationExceptionDecorator" + override val order: Byte + get() = 69 + + override fun validationExceptionConversion(codegenContext: ServerCodegenContext): ValidationExceptionConversionGenerator = + SmithyValidationExceptionConversionGenerator(codegenContext) +} + +class SmithyValidationExceptionConversionGenerator(private val codegenContext: ServerCodegenContext) : + ValidationExceptionConversionGenerator { + + // Define a companion object so that we can refer to this shape id globally. + companion object { + val SHAPE_ID: ShapeId = ShapeId.from("smithy.framework#ValidationException") + } + override val shapeId: ShapeId = SHAPE_ID + + override fun renderImplFromConstraintViolationForRequestRejection(): Writable = writable { + val codegenScope = arrayOf( + "RequestRejection" to ServerRuntimeType.requestRejection(codegenContext.runtimeConfig), + "From" to RuntimeType.From, + ) + rustTemplate( + """ + impl #{From} for #{RequestRejection} { + fn from(constraint_violation: ConstraintViolation) -> Self { + let first_validation_exception_field = constraint_violation.as_validation_exception_field("".to_owned()); + let validation_exception = crate::error::ValidationException { + message: format!("1 validation error detected. {}", &first_validation_exception_field.message), + field_list: Some(vec![first_validation_exception_field]), + }; + Self::ConstraintViolation( + crate::protocol_serde::shape_validation_exception::ser_validation_exception_error(&validation_exception) + .expect("validation exceptions should never fail to serialize; please file a bug report under https://github.com/awslabs/smithy-rs/issues") + ) + } + } + """, + *codegenScope, + ) + } + + override fun stringShapeConstraintViolationImplBlock(stringConstraintsInfo: Collection): Writable = writable { + val constraintsInfo: List = stringConstraintsInfo.map(StringTraitInfo::toTraitInfo) + + rustTemplate( + """ + pub(crate) fn as_validation_exception_field(self, path: #{String}) -> crate::model::ValidationExceptionField { + match self { + #{ValidationExceptionFields:W} + } + } + """, + "String" to RuntimeType.String, + "ValidationExceptionFields" to constraintsInfo.map { it.asValidationExceptionField }.join("\n"), + ) + } + + override fun blobShapeConstraintViolationImplBlock(blobConstraintsInfo: Collection): Writable = writable { + val constraintsInfo: List = blobConstraintsInfo.map(BlobLength::toTraitInfo) + + rustTemplate( + """ + pub(crate) fn as_validation_exception_field(self, path: #{String}) -> crate::model::ValidationExceptionField { + match self { + #{ValidationExceptionFields:W} + } + } + """, + "String" to RuntimeType.String, + "ValidationExceptionFields" to constraintsInfo.map { it.asValidationExceptionField }.join("\n"), + ) + } + + override fun mapShapeConstraintViolationImplBlock( + shape: MapShape, + keyShape: StringShape, + valueShape: Shape, + symbolProvider: RustSymbolProvider, + model: Model, + ) = writable { + rustBlockTemplate( + "pub(crate) fn as_validation_exception_field(self, path: #{String}) -> crate::model::ValidationExceptionField", + "String" to RuntimeType.String, + ) { + rustBlock("match self") { + shape.getTrait()?.also { + rust( + """ + Self::Length(length) => crate::model::ValidationExceptionField { + message: format!("${it.validationErrorMessage()}", length, &path), + path, + },""", + ) + } + if (isKeyConstrained(keyShape, symbolProvider)) { + // Note how we _do not_ append the key's member name to the path. This is intentional, as + // per the `RestJsonMalformedLengthMapKey` test. Note keys are always strings. + // https://github.com/awslabs/smithy/blob/ee0b4ff90daaaa5101f32da936c25af8c91cc6e9/smithy-aws-protocol-tests/model/restJson1/validation/malformed-length.smithy#L296-L295 + rust("""Self::Key(key_constraint_violation) => key_constraint_violation.as_validation_exception_field(path),""") + } + if (isValueConstrained(valueShape, model, symbolProvider)) { + // `as_str()` works with regular `String`s and constrained string shapes. + rust("""Self::Value(key, value_constraint_violation) => value_constraint_violation.as_validation_exception_field(path + "/" + key.as_str()),""") + } + } + } + } + + override fun enumShapeConstraintViolationImplBlock(enumTrait: EnumTrait) = writable { + val enumValueSet = enumTrait.enumDefinitionValues.joinToString(", ") + val message = "Value at '{}' failed to satisfy constraint: Member must satisfy enum value set: [$enumValueSet]" + rustTemplate( + """ + pub(crate) fn as_validation_exception_field(self, path: #{String}) -> crate::model::ValidationExceptionField { + crate::model::ValidationExceptionField { + message: format!(r##"$message"##, &path), + path, + } + } + """, + "String" to RuntimeType.String, + ) + } + + override fun numberShapeConstraintViolationImplBlock(rangeInfo: Range) = writable { + rustTemplate( + """ + pub(crate) fn as_validation_exception_field(self, path: #{String}) -> crate::model::ValidationExceptionField { + match self { + #{ValidationExceptionFields:W} + } + } + """, + "String" to RuntimeType.String, + "ValidationExceptionFields" to rangeInfo.toTraitInfo().asValidationExceptionField, + ) + } + + override fun builderConstraintViolationImplBlock(constraintViolations: Collection) = writable { + rustBlock("match self") { + constraintViolations.forEach { + if (it.hasInner()) { + rust("""ConstraintViolation::${it.name()}(inner) => inner.as_validation_exception_field(path + "/${it.forMember.memberName}"),""") + } else { + rust( + """ + ConstraintViolation::${it.name()} => crate::model::ValidationExceptionField { + message: format!("Value at '{}/${it.forMember.memberName}' failed to satisfy constraint: Member must not be null", path), + path: path + "/${it.forMember.memberName}", + }, + """, + ) + } + } + } + } + + override fun collectionShapeConstraintViolationImplBlock( + collectionConstraintsInfo: + Collection, + isMemberConstrained: Boolean, + ) = writable { + val validationExceptionFields = collectionConstraintsInfo.map { + it.toTraitInfo().asValidationExceptionField + }.toMutableList() + if (isMemberConstrained) { + validationExceptionFields += { + rust( + """Self::Member(index, member_constraint_violation) => + member_constraint_violation.as_validation_exception_field(path + "/" + &index.to_string()) + """, + ) + } + } + rustTemplate( + """ + pub(crate) fn as_validation_exception_field(self, path: #{String}) -> crate::model::ValidationExceptionField { + match self { + #{AsValidationExceptionFields:W} + } + } + """, + "String" to RuntimeType.String, + "AsValidationExceptionFields" to validationExceptionFields.join(""), + ) + } +} 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 b5b0f19295..8e771cb122 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,8 @@ 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.ValidationResult +import software.amazon.smithy.rust.codegen.server.smithy.generators.ValidationExceptionConversionGenerator import software.amazon.smithy.rust.codegen.server.smithy.generators.protocol.ServerProtocolGenerator import java.util.logging.Logger @@ -21,6 +23,13 @@ typealias ServerProtocolMap = ProtocolMap { fun protocols(serviceId: ShapeId, currentProtocols: ServerProtocolMap): ServerProtocolMap = currentProtocols + fun validationExceptionConversion(codegenContext: ServerCodegenContext): ValidationExceptionConversionGenerator? = null + + /** + * Injection point to allow a decorator to postprocess the error message that arises when an operation is + * constrained but the `ValidationException` shape is not attached to the operation's errors. + */ + fun postprocessValidationExceptionNotAttachedErrorMessage(validationResult: ValidationResult) = validationResult } /** @@ -28,9 +37,12 @@ interface ServerCodegenDecorator : CoreCodegenDecorator { * * This makes the actual concrete codegen simpler by not needing to deal with multiple separate decorators. */ -class CombinedServerCodegenDecorator(decorators: List) : +class CombinedServerCodegenDecorator(private val decorators: List) : CombinedCoreCodegenDecorator(decorators), ServerCodegenDecorator { + + private val orderedDecorators = decorators.sortedBy { it.order } + override val name: String get() = "CombinedServerCodegenDecorator" override val order: Byte @@ -41,6 +53,16 @@ class CombinedServerCodegenDecorator(decorators: List) : decorator.protocols(serviceId, protocolMap) } + override fun validationExceptionConversion(codegenContext: ServerCodegenContext): ValidationExceptionConversionGenerator = + // We use `firstNotNullOf` instead of `firstNotNullOfOrNull` because the [SmithyValidationExceptionDecorator] + // is registered. + orderedDecorators.firstNotNullOf { it.validationExceptionConversion(codegenContext) } + + override fun postprocessValidationExceptionNotAttachedErrorMessage(validationResult: ValidationResult): ValidationResult = + orderedDecorators.foldRight(validationResult) { decorator, accumulated -> + decorator.postprocessValidationExceptionNotAttachedErrorMessage(accumulated) + } + companion object { fun fromClasspath( context: PluginContext, 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 7867b045c4..e2a177f536 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 @@ -6,23 +6,25 @@ package software.amazon.smithy.rust.codegen.server.smithy.generators import software.amazon.smithy.model.shapes.CollectionShape -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.join -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.module +import software.amazon.smithy.rust.codegen.core.smithy.makeRustBoxed +import software.amazon.smithy.rust.codegen.core.util.hasTrait +import software.amazon.smithy.rust.codegen.core.util.letIf +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.canReachConstrainedShape +import software.amazon.smithy.rust.codegen.server.smithy.traits.ConstraintViolationRustBoxTrait import software.amazon.smithy.rust.codegen.server.smithy.traits.isReachableFromOperationInput class CollectionConstraintViolationGenerator( codegenContext: ServerCodegenContext, - private val modelsModuleWriter: RustWriter, + private val inlineModuleCreator: InlineModuleCreator, private val shape: CollectionShape, - private val constraintsInfo: List, + private val collectionConstraintsInfo: List, + private val validationExceptionConversionGenerator: ValidationExceptionConversionGenerator, ) { private val model = codegenContext.model private val symbolProvider = codegenContext.symbolProvider @@ -35,18 +37,25 @@ class CollectionConstraintViolationGenerator( PubCrateConstraintViolationSymbolProvider(this) } } + private val constraintsInfo: List = collectionConstraintsInfo.map { it.toTraitInfo() } fun render() { - val memberShape = model.expectShape(shape.member.target) + val targetShape = model.expectShape(shape.member.target) val constraintViolationSymbol = constraintViolationSymbolProvider.toSymbol(shape) val constraintViolationName = constraintViolationSymbol.name - val isMemberConstrained = memberShape.canReachConstrainedShape(model, symbolProvider) + val isMemberConstrained = targetShape.canReachConstrainedShape(model, symbolProvider) val constraintViolationVisibility = Visibility.publicIf(publicConstrainedTypes, Visibility.PUBCRATE) - modelsModuleWriter.withInlineModule(constraintViolationSymbol.module()) { + inlineModuleCreator(constraintViolationSymbol) { val constraintViolationVariants = constraintsInfo.map { it.constraintViolationVariant }.toMutableList() if (isMemberConstrained) { constraintViolationVariants += { + val memberConstraintViolationSymbol = + constraintViolationSymbolProvider.toSymbol(targetShape).letIf( + shape.member.hasTrait(), + ) { + it.makeRustBoxed() + } rustTemplate( """ /// Constraint violation error when an element doesn't satisfy its own constraints. @@ -55,7 +64,7 @@ class CollectionConstraintViolationGenerator( ##[doc(hidden)] Member(usize, #{MemberConstraintViolationSymbol}) """, - "MemberConstraintViolationSymbol" to constraintViolationSymbolProvider.toSymbol(memberShape), + "MemberConstraintViolationSymbol" to memberConstraintViolationSymbol, ) } } @@ -66,6 +75,7 @@ class CollectionConstraintViolationGenerator( // and is for use by the framework. rustTemplate( """ + ##[allow(clippy::enum_variant_names)] ##[derive(Debug, PartialEq)] ${constraintViolationVisibility.toRustQualifier()} enum $constraintViolationName { #{ConstraintViolationVariants:W} @@ -75,30 +85,13 @@ class CollectionConstraintViolationGenerator( ) if (shape.isReachableFromOperationInput()) { - val validationExceptionFields = constraintsInfo.map { it.asValidationExceptionField }.toMutableList() - if (isMemberConstrained) { - validationExceptionFields += { - rust( - """ - Self::Member(index, member_constraint_violation) => - member_constraint_violation.as_validation_exception_field(path + "/" + &index.to_string()) - """, - ) - } - } - rustTemplate( """ impl $constraintViolationName { - pub(crate) fn as_validation_exception_field(self, path: #{String}) -> crate::model::ValidationExceptionField { - match self { - #{AsValidationExceptionFields:W} - } - } + #{CollectionShapeConstraintViolationImplBlock} } """, - "String" to RuntimeType.String, - "AsValidationExceptionFields" to validationExceptionFields.join("\n"), + "CollectionShapeConstraintViolationImplBlock" to validationExceptionConversionGenerator.collectionShapeConstraintViolationImplBlock(collectionConstraintsInfo, isMemberConstrained), ) } } diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ConstrainedBlobGenerator.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ConstrainedBlobGenerator.kt index 41fec1cec7..5a1c3bc4e1 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ConstrainedBlobGenerator.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ConstrainedBlobGenerator.kt @@ -21,9 +21,9 @@ 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.expectRustMetadata import software.amazon.smithy.rust.codegen.core.smithy.makeMaybeConstrained -import software.amazon.smithy.rust.codegen.core.smithy.module import software.amazon.smithy.rust.codegen.core.smithy.rustType import software.amazon.smithy.rust.codegen.core.util.orNull +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.traits.isReachableFromOperationInput @@ -31,8 +31,10 @@ import software.amazon.smithy.rust.codegen.server.smithy.validationErrorMessage class ConstrainedBlobGenerator( val codegenContext: ServerCodegenContext, + private val inlineModuleCreator: InlineModuleCreator, val writer: RustWriter, val shape: BlobShape, + private val validationExceptionConversionGenerator: ValidationExceptionConversionGenerator, ) { val model = codegenContext.model val constrainedShapeSymbolProvider = codegenContext.constrainedShapeSymbolProvider @@ -45,9 +47,10 @@ class ConstrainedBlobGenerator( PubCrateConstraintViolationSymbolProvider(this) } } - private val constraintsInfo: List = listOf(LengthTrait::class.java) + private val blobConstraintsInfo: List = listOf(LengthTrait::class.java) .mapNotNull { shape.getTrait(it).orNull() } - .map { BlobLength(it).toTraitInfo() } + .map { BlobLength(it) } + private val constraintsInfo: List = blobConstraintsInfo.map { it.toTraitInfo() } fun render() { val symbol = constrainedShapeSymbolProvider.toSymbol(shape) @@ -108,7 +111,7 @@ class ConstrainedBlobGenerator( "From" to RuntimeType.From, ) - writer.withInlineModule(constraintViolation.module()) { + inlineModuleCreator(constraintViolation) { renderConstraintViolationEnum(this, shape, constraintViolation) } } @@ -128,21 +131,16 @@ class ConstrainedBlobGenerator( writer.rustTemplate( """ impl ${constraintViolation.name} { - pub(crate) fn as_validation_exception_field(self, path: #{String}) -> crate::model::ValidationExceptionField { - match self { - #{ValidationExceptionFields:W} - } - } + #{BlobShapeConstraintViolationImplBlock} } """, - "String" to RuntimeType.String, - "ValidationExceptionFields" to constraintsInfo.map { it.asValidationExceptionField }.join("\n"), + "BlobShapeConstraintViolationImplBlock" to validationExceptionConversionGenerator.blobShapeConstraintViolationImplBlock(blobConstraintsInfo), ) } } } -private data class BlobLength(val lengthTrait: LengthTrait) { +data class BlobLength(val lengthTrait: LengthTrait) { fun toTraitInfo(): TraitInfo = TraitInfo( { rust("Self::check_length(&value)?;") }, { @@ -155,8 +153,7 @@ private data class BlobLength(val lengthTrait: LengthTrait) { Self::Length(length) => crate::model::ValidationExceptionField { message: format!("${lengthTrait.validationErrorMessage()}", length, &path), path, - }, - """, + },""", ) }, this::renderValidationFunction, 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 4463220a96..9b5775478e 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 @@ -8,6 +8,7 @@ package software.amazon.smithy.rust.codegen.server.smithy.generators import software.amazon.smithy.codegen.core.Symbol import software.amazon.smithy.codegen.core.SymbolProvider import software.amazon.smithy.model.shapes.CollectionShape +import software.amazon.smithy.model.shapes.EnumShape import software.amazon.smithy.model.shapes.StructureShape import software.amazon.smithy.model.shapes.UnionShape import software.amazon.smithy.model.traits.LengthTrait @@ -48,7 +49,7 @@ class ConstrainedCollectionGenerator( val codegenContext: ServerCodegenContext, val writer: RustWriter, val shape: CollectionShape, - private val constraintsInfo: List, + collectionConstraintsInfo: List, private val unconstrainedSymbol: Symbol? = null, ) { private val model = codegenContext.model @@ -63,6 +64,7 @@ class ConstrainedCollectionGenerator( } } private val symbolProvider = codegenContext.symbolProvider + private val constraintsInfo = collectionConstraintsInfo.map { it.toTraitInfo() } fun render() { check(constraintsInfo.isNotEmpty()) { @@ -114,7 +116,9 @@ class ConstrainedCollectionGenerator( #{ValidationFunctions:W} """, *codegenScope, - "ValidationFunctions" to constraintsInfo.map { it.validationFunctionDefinition(constraintViolation, inner) }.join("\n"), + "ValidationFunctions" to constraintsInfo.map { + it.validationFunctionDefinition(constraintViolation, inner) + }.join("\n"), ) } @@ -145,7 +149,8 @@ class ConstrainedCollectionGenerator( if (!publicConstrainedTypes && innerShape.canReachConstrainedShape(model, symbolProvider) && innerShape !is StructureShape && - innerShape !is UnionShape + innerShape !is UnionShape && + innerShape !is EnumShape ) { writer.rustTemplate( """ @@ -178,7 +183,7 @@ class ConstrainedCollectionGenerator( } } -internal sealed class CollectionTraitInfo { +sealed class CollectionTraitInfo { data class UniqueItems(val uniqueItemsTrait: UniqueItemsTrait, val memberSymbol: Symbol) : CollectionTraitInfo() { override fun toTraitInfo(): TraitInfo = TraitInfo( @@ -245,7 +250,7 @@ internal sealed class CollectionTraitInfo { // [1]: https://github.com/awslabs/smithy-typescript/blob/517c85f8baccf0e5334b4e66d8786bdb5791c595/smithy-typescript-ssdk-libs/server-common/src/validation/index.ts#L106-L111 rust( """ - Self::UniqueItems { duplicate_indices, .. } => + Self::UniqueItems { duplicate_indices, .. } => crate::model::ValidationExceptionField { message: format!("${uniqueItemsTrait.validationErrorMessage()}", &duplicate_indices, &path), path, @@ -365,11 +370,10 @@ internal sealed class CollectionTraitInfo { } } - fun fromShape(shape: CollectionShape, symbolProvider: SymbolProvider): List = + fun fromShape(shape: CollectionShape, symbolProvider: SymbolProvider): List = supportedCollectionConstraintTraits .mapNotNull { shape.getTrait(it).orNull() } .map { trait -> fromTrait(trait, shape, symbolProvider) } - .map(CollectionTraitInfo::toTraitInfo) } abstract fun toTraitInfo(): TraitInfo diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ConstrainedMapGenerator.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ConstrainedMapGenerator.kt index e5721e7741..28b0d9f8d7 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ConstrainedMapGenerator.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ConstrainedMapGenerator.kt @@ -7,6 +7,7 @@ package software.amazon.smithy.rust.codegen.server.smithy.generators import software.amazon.smithy.codegen.core.Symbol import software.amazon.smithy.model.shapes.MapShape +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.LengthTrait @@ -21,6 +22,7 @@ import software.amazon.smithy.rust.codegen.core.smithy.expectRustMetadata import software.amazon.smithy.rust.codegen.core.util.expectTrait 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.typeNameContainsNonPublicType /** * [ConstrainedMapGenerator] generates a wrapper tuple newtype holding a constrained `std::collections::HashMap`. @@ -130,6 +132,14 @@ class ConstrainedMapGenerator( valueShape !is StructureShape && valueShape !is UnionShape ) { + val keyShape = model.expectShape(shape.key.target, StringShape::class.java) + val keyNeedsConversion = keyShape.typeNameContainsNonPublicType(model, symbolProvider, publicConstrainedTypes) + val key = if (keyNeedsConversion) { + "k.into()" + } else { + "k" + } + writer.rustTemplate( """ impl #{From}<$name> for #{FullyUnconstrainedSymbol} { @@ -137,7 +147,7 @@ class ConstrainedMapGenerator( value .into_inner() .into_iter() - .map(|(k, v)| (k, v.into())) + .map(|(k, v)| ($key, v.into())) .collect() } } diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ConstrainedNumberGenerator.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ConstrainedNumberGenerator.kt index 281f0005c1..9680865a91 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ConstrainedNumberGenerator.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ConstrainedNumberGenerator.kt @@ -21,16 +21,14 @@ 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.smithy.RuntimeType import software.amazon.smithy.rust.codegen.core.smithy.expectRustMetadata import software.amazon.smithy.rust.codegen.core.smithy.makeMaybeConstrained -import software.amazon.smithy.rust.codegen.core.smithy.module import software.amazon.smithy.rust.codegen.core.util.UNREACHABLE import software.amazon.smithy.rust.codegen.core.util.expectTrait import software.amazon.smithy.rust.codegen.core.util.redactIfNecessary +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.traits.isReachableFromOperationInput @@ -43,8 +41,10 @@ import software.amazon.smithy.rust.codegen.server.smithy.validationErrorMessage */ class ConstrainedNumberGenerator( val codegenContext: ServerCodegenContext, - val writer: RustWriter, + private val inlineModuleCreator: InlineModuleCreator, + private val writer: RustWriter, val shape: NumberShape, + private val validationExceptionConversionGenerator: ValidationExceptionConversionGenerator, ) { val model = codegenContext.model val constrainedShapeSymbolProvider = codegenContext.constrainedShapeSymbolProvider @@ -74,7 +74,8 @@ class ConstrainedNumberGenerator( val name = symbol.name val unconstrainedTypeName = unconstrainedType.render() val constraintViolation = constraintViolationSymbolProvider.toSymbol(shape) - val constraintsInfo = listOf(Range(rangeTrait).toTraitInfo(unconstrainedTypeName)) + val rangeInfo = Range(rangeTrait) + val constraintsInfo = listOf(rangeInfo.toTraitInfo()) writer.documentShape(shape, model) writer.docs(rustDocsConstrainedTypeEpilogue(name)) @@ -132,7 +133,7 @@ class ConstrainedNumberGenerator( writer.renderTryFrom(unconstrainedTypeName, name, constraintViolation, constraintsInfo) - writer.withInlineModule(constraintViolation.module()) { + inlineModuleCreator(constraintViolation) { rust( """ ##[derive(Debug, PartialEq)] @@ -143,40 +144,28 @@ class ConstrainedNumberGenerator( ) if (shape.isReachableFromOperationInput()) { - rustBlock("impl ${constraintViolation.name}") { - rustBlockTemplate( - "pub(crate) fn as_validation_exception_field(self, path: #{String}) -> crate::model::ValidationExceptionField", - "String" to RuntimeType.String, - ) { - rustBlock("match self") { - rust( - """ - Self::Range(value) => crate::model::ValidationExceptionField { - message: format!("${rangeTrait.validationErrorMessage()}", value, &path), - path, - }, - """, - ) - } + rustTemplate( + """ + impl ${constraintViolation.name} { + #{NumberShapeConstraintViolationImplBlock} } - } + """, + "NumberShapeConstraintViolationImplBlock" to validationExceptionConversionGenerator.numberShapeConstraintViolationImplBlock(rangeInfo), + ) } } } } -private data class Range(val rangeTrait: RangeTrait) { - fun toTraitInfo(unconstrainedTypeName: String): TraitInfo = TraitInfo( +data class Range(val rangeTrait: RangeTrait) { + fun toTraitInfo(): TraitInfo = TraitInfo( { rust("Self::check_range(value)?;") }, - { - docs("Error when a number doesn't satisfy its `@range` requirements.") - rust("Range($unconstrainedTypeName)") - }, + { docs("Error when a number doesn't satisfy its `@range` requirements.") }, { rust( """ - Self::Range(value) => crate::model::ValidationExceptionField { - message: format!("${rangeTrait.validationErrorMessage()}", value, &path), + Self::Range(_) => crate::model::ValidationExceptionField { + message: format!("${rangeTrait.validationErrorMessage()}", &path), path, }, """, diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ConstrainedStringGenerator.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ConstrainedStringGenerator.kt index 20a6746aa8..3e61ab6804 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ConstrainedStringGenerator.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ConstrainedStringGenerator.kt @@ -10,6 +10,7 @@ import software.amazon.smithy.model.shapes.Shape import software.amazon.smithy.model.shapes.StringShape import software.amazon.smithy.model.traits.LengthTrait import software.amazon.smithy.model.traits.PatternTrait +import software.amazon.smithy.model.traits.SensitiveTrait import software.amazon.smithy.model.traits.Trait import software.amazon.smithy.rust.codegen.core.rustlang.Attribute import software.amazon.smithy.rust.codegen.core.rustlang.RustType @@ -22,15 +23,17 @@ 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.rustlang.writable 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.smithy.makeMaybeConstrained -import software.amazon.smithy.rust.codegen.core.smithy.module import software.amazon.smithy.rust.codegen.core.smithy.testModuleForShape import software.amazon.smithy.rust.codegen.core.testutil.unitTest import software.amazon.smithy.rust.codegen.core.util.PANIC +import software.amazon.smithy.rust.codegen.core.util.hasTrait import software.amazon.smithy.rust.codegen.core.util.orNull import software.amazon.smithy.rust.codegen.core.util.redactIfNecessary +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.ServerCargoDependency import software.amazon.smithy.rust.codegen.server.smithy.ServerCodegenContext @@ -45,8 +48,10 @@ import software.amazon.smithy.rust.codegen.server.smithy.validationErrorMessage */ class ConstrainedStringGenerator( val codegenContext: ServerCodegenContext, - val writer: RustWriter, + private val inlineModuleCreator: InlineModuleCreator, + private val writer: RustWriter, val shape: StringShape, + private val validationExceptionConversionGenerator: ValidationExceptionConversionGenerator, ) { val model = codegenContext.model val constrainedShapeSymbolProvider = codegenContext.constrainedShapeSymbolProvider @@ -60,10 +65,12 @@ class ConstrainedStringGenerator( } } private val symbol = constrainedShapeSymbolProvider.toSymbol(shape) - private val constraintsInfo: List = + private val stringConstraintsInfo: List = supportedStringConstraintTraits .mapNotNull { shape.getTrait(it).orNull() } - .map { StringTraitInfo.fromTrait(symbol, it) } + .map { StringTraitInfo.fromTrait(symbol, it, isSensitive = shape.hasTrait()) } + private val constraintsInfo: List = + stringConstraintsInfo .map(StringTraitInfo::toTraitInfo) fun render() { @@ -133,7 +140,7 @@ class ConstrainedStringGenerator( "From" to RuntimeType.From, ) - writer.withInlineModule(constraintViolation.module()) { + inlineModuleCreator(constraintViolation) { renderConstraintViolationEnum(this, shape, constraintViolation) } @@ -155,15 +162,10 @@ class ConstrainedStringGenerator( writer.rustTemplate( """ impl ${constraintViolation.name} { - pub(crate) fn as_validation_exception_field(self, path: #{String}) -> crate::model::ValidationExceptionField { - match self { - #{ValidationExceptionFields:W} - } - } + #{StringShapeConstraintViolationImplBlock:W} } """, - "String" to RuntimeType.String, - "ValidationExceptionFields" to constraintsInfo.map { it.asValidationExceptionField }.join("\n"), + "StringShapeConstraintViolationImplBlock" to validationExceptionConversionGenerator.stringShapeConstraintViolationImplBlock(stringConstraintsInfo), ) } } @@ -173,7 +175,7 @@ class ConstrainedStringGenerator( if (testCases.isNotEmpty()) { val testModule = constrainedShapeSymbolProvider.testModuleForShape(shape) - writer.withInlineModule(testModule) { + writer.withInlineModule(testModule, null) { rustTemplate( """ #{TestCases:W} @@ -184,7 +186,7 @@ class ConstrainedStringGenerator( } } } -private data class Length(val lengthTrait: LengthTrait) : StringTraitInfo() { +data class Length(val lengthTrait: LengthTrait) : StringTraitInfo() { override fun toTraitInfo(): TraitInfo = TraitInfo( tryFromCheck = { rust("Self::check_length(&value)?;") }, constraintViolationVariant = { @@ -229,10 +231,8 @@ private data class Length(val lengthTrait: LengthTrait) : StringTraitInfo() { } } -private data class Pattern(val symbol: Symbol, val patternTrait: PatternTrait) : StringTraitInfo() { +data class Pattern(val symbol: Symbol, val patternTrait: PatternTrait, val isSensitive: Boolean) : StringTraitInfo() { override fun toTraitInfo(): TraitInfo { - val pattern = patternTrait.pattern - return TraitInfo( tryFromCheck = { rust("let value = Self::check_pattern(value)?;") }, constraintViolationVariant = { @@ -241,13 +241,15 @@ private data class Pattern(val symbol: Symbol, val patternTrait: PatternTrait) : rust("Pattern(String)") }, asValidationExceptionField = { - rust( + Attribute.AllowUnusedVariables.render(this) + rustTemplate( """ - Self::Pattern(string) => crate::model::ValidationExceptionField { - message: format!("${patternTrait.validationErrorMessage()}", &string, &path, r##"$pattern"##), + Self::Pattern(_) => crate::model::ValidationExceptionField { + message: #{ErrorMessage:W}, path }, """, + "ErrorMessage" to errorMessage(), ) }, this::renderValidationFunction, @@ -264,6 +266,18 @@ private data class Pattern(val symbol: Symbol, val patternTrait: PatternTrait) : ) } + fun errorMessage(): Writable { + val pattern = patternTrait.pattern + + return writable { + rust( + """ + format!("Value at '{}' failed to satisfy constraint: Member must satisfy regular expression pattern: {}", &path, r##"$pattern"##) + """, + ) + } + } + /** * Renders a `check_pattern` function to validate the string matches the * supplied regex in the `@pattern` trait. @@ -301,16 +315,18 @@ private data class Pattern(val symbol: Symbol, val patternTrait: PatternTrait) : } } -private sealed class StringTraitInfo { +sealed class StringTraitInfo { companion object { - fun fromTrait(symbol: Symbol, trait: Trait) = + fun fromTrait(symbol: Symbol, trait: Trait, isSensitive: Boolean) = when (trait) { is PatternTrait -> { - Pattern(symbol, trait) + Pattern(symbol, trait, isSensitive) } + is LengthTrait -> { Length(trait) } + else -> PANIC("StringTraitInfo.fromTrait called with unsupported trait $trait") } } 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 759f887088..a0dcf07ba9 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 @@ -12,12 +12,11 @@ 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.ErrorsModule -import software.amazon.smithy.rust.codegen.core.smithy.InputsModule -import software.amazon.smithy.rust.codegen.core.smithy.OutputsModule -import software.amazon.smithy.rust.codegen.core.smithy.generators.error.errorSymbol import software.amazon.smithy.rust.codegen.core.util.inputShape import software.amazon.smithy.rust.codegen.core.util.outputShape +import software.amazon.smithy.rust.codegen.server.smithy.ServerRustModule.Error as ErrorModule +import software.amazon.smithy.rust.codegen.server.smithy.ServerRustModule.Input as InputModule +import software.amazon.smithy.rust.codegen.server.smithy.ServerRustModule.Output as OutputModule /** * Generates a handler implementation stub for use within documentation. @@ -33,22 +32,22 @@ class DocHandlerGenerator( private val inputSymbol = symbolProvider.toSymbol(operation.inputShape(model)) private val outputSymbol = symbolProvider.toSymbol(operation.outputShape(model)) - private val errorSymbol = operation.errorSymbol(symbolProvider) + private val errorSymbol = symbolProvider.symbolForOperationError(operation) /** * Returns the function signature for an operation handler implementation. Used in the documentation. */ fun docSignature(): Writable { val outputT = if (operation.errors.isEmpty()) { - "${OutputsModule.name}::${outputSymbol.name}" + "${OutputModule.name}::${outputSymbol.name}" } else { - "Result<${OutputsModule.name}::${outputSymbol.name}, ${ErrorsModule.name}::${errorSymbol.name}>" + "Result<${OutputModule.name}::${outputSymbol.name}, ${ErrorModule.name}::${errorSymbol.name}>" } return writable { rust( """ - $commentToken async fn $handlerName(input: ${InputsModule.name}::${inputSymbol.name}) -> $outputT { + $commentToken async fn $handlerName(input: ${InputModule.name}::${inputSymbol.name}) -> $outputT { $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 cfcf5a53e1..065a4067c7 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 @@ -8,25 +8,22 @@ package software.amazon.smithy.rust.codegen.server.smithy.generators import software.amazon.smithy.model.shapes.MapShape import software.amazon.smithy.model.shapes.StringShape import software.amazon.smithy.model.traits.LengthTrait -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.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.module -import software.amazon.smithy.rust.codegen.core.util.getTrait +import software.amazon.smithy.rust.codegen.core.smithy.makeRustBoxed import software.amazon.smithy.rust.codegen.core.util.hasTrait +import software.amazon.smithy.rust.codegen.core.util.letIf +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.traits.ConstraintViolationRustBoxTrait import software.amazon.smithy.rust.codegen.server.smithy.traits.isReachableFromOperationInput -import software.amazon.smithy.rust.codegen.server.smithy.validationErrorMessage class MapConstraintViolationGenerator( codegenContext: ServerCodegenContext, - private val modelsModuleWriter: RustWriter, + private val inlineModuleCreator: InlineModuleCreator, val shape: MapShape, + private val validationExceptionConversionGenerator: ValidationExceptionConversionGenerator, ) { private val model = codegenContext.model private val constrainedShapeSymbolProvider = codegenContext.constrainedShapeSymbolProvider @@ -52,7 +49,14 @@ class MapConstraintViolationGenerator( constraintViolationCodegenScopeMutableList.add("KeyConstraintViolationSymbol" to constraintViolationSymbolProvider.toSymbol(keyShape)) } if (isValueConstrained(valueShape, model, symbolProvider)) { - constraintViolationCodegenScopeMutableList.add("ValueConstraintViolationSymbol" to constraintViolationSymbolProvider.toSymbol(valueShape)) + constraintViolationCodegenScopeMutableList.add( + "ValueConstraintViolationSymbol" to + constraintViolationSymbolProvider.toSymbol(valueShape).letIf( + shape.value.hasTrait(), + ) { + it.makeRustBoxed() + }, + ) constraintViolationCodegenScopeMutableList.add("KeySymbol" to constrainedShapeSymbolProvider.toSymbol(keyShape)) } val constraintViolationCodegenScope = constraintViolationCodegenScopeMutableList.toTypedArray() @@ -62,13 +66,15 @@ class MapConstraintViolationGenerator( } else { Visibility.PUBCRATE } - modelsModuleWriter.withInlineModule(constraintViolationSymbol.module()) { + + inlineModuleCreator(constraintViolationSymbol) { // TODO(https://github.com/awslabs/smithy-rs/issues/1401) We should really have two `ConstraintViolation` // types here. One will just have variants for each constraint trait on the map shape, for use by the user. // The other one will have variants if the shape's key or value is directly or transitively constrained, // and is for use by the framework. rustTemplate( """ + ##[allow(clippy::enum_variant_names)] ##[derive(Debug, PartialEq)] pub${ if (constraintViolationVisibility == Visibility.PUBCRATE) " (crate) " else "" } enum $constraintViolationName { ${if (shape.hasTrait()) "Length(usize)," else ""} @@ -80,35 +86,20 @@ class MapConstraintViolationGenerator( ) if (shape.isReachableFromOperationInput()) { - rustBlock("impl $constraintViolationName") { - rustBlockTemplate( - "pub(crate) fn as_validation_exception_field(self, path: #{String}) -> crate::model::ValidationExceptionField", - "String" to RuntimeType.String, - ) { - rustBlock("match self") { - shape.getTrait()?.also { - rust( - """ - Self::Length(length) => crate::model::ValidationExceptionField { - message: format!("${it.validationErrorMessage()}", length, &path), - path, - }, - """, - ) - } - if (isKeyConstrained(keyShape, symbolProvider)) { - // Note how we _do not_ append the key's member name to the path. This is intentional, as - // per the `RestJsonMalformedLengthMapKey` test. Note keys are always strings. - // https://github.com/awslabs/smithy/blob/ee0b4ff90daaaa5101f32da936c25af8c91cc6e9/smithy-aws-protocol-tests/model/restJson1/validation/malformed-length.smithy#L296-L295 - rust("""Self::Key(key_constraint_violation) => key_constraint_violation.as_validation_exception_field(path),""") - } - if (isValueConstrained(valueShape, model, symbolProvider)) { - // `as_str()` works with regular `String`s and constrained string shapes. - rust("""Self::Value(key, value_constraint_violation) => value_constraint_violation.as_validation_exception_field(path + "/" + key.as_str()),""") - } - } + rustTemplate( + """ + impl $constraintViolationName { + #{MapShapeConstraintViolationImplBlock} } - } + """, + "MapShapeConstraintViolationImplBlock" to validationExceptionConversionGenerator.mapShapeConstraintViolationImplBlock( + shape, + keyShape, + valueShape, + symbolProvider, + model, + ), + ) } } } diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/PubCrateConstrainedCollectionGenerator.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/PubCrateConstrainedCollectionGenerator.kt index 09f9352cde..1a563c25b9 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/PubCrateConstrainedCollectionGenerator.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/PubCrateConstrainedCollectionGenerator.kt @@ -7,7 +7,6 @@ package software.amazon.smithy.rust.codegen.server.smithy.generators import software.amazon.smithy.model.shapes.CollectionShape import software.amazon.smithy.model.shapes.MapShape -import software.amazon.smithy.rust.codegen.core.rustlang.RustWriter import software.amazon.smithy.rust.codegen.core.rustlang.conditionalBlock import software.amazon.smithy.rust.codegen.core.rustlang.rust import software.amazon.smithy.rust.codegen.core.rustlang.rustBlock @@ -16,7 +15,7 @@ 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.smithy.RuntimeType import software.amazon.smithy.rust.codegen.core.smithy.isOptional -import software.amazon.smithy.rust.codegen.core.smithy.module +import software.amazon.smithy.rust.codegen.server.smithy.InlineModuleCreator import software.amazon.smithy.rust.codegen.server.smithy.ServerCodegenContext import software.amazon.smithy.rust.codegen.server.smithy.canReachConstrainedShape import software.amazon.smithy.rust.codegen.server.smithy.isDirectlyConstrained @@ -41,7 +40,7 @@ import software.amazon.smithy.rust.codegen.server.smithy.typeNameContainsNonPubl */ class PubCrateConstrainedCollectionGenerator( val codegenContext: ServerCodegenContext, - val writer: RustWriter, + private val inlineModuleCreator: InlineModuleCreator, val shape: CollectionShape, ) { private val model = codegenContext.model @@ -74,7 +73,7 @@ class PubCrateConstrainedCollectionGenerator( "From" to RuntimeType.From, ) - writer.withInlineModule(constrainedSymbol.module()) { + inlineModuleCreator(constrainedSymbol) { rustTemplate( """ ##[derive(Debug, Clone)] @@ -109,11 +108,11 @@ class PubCrateConstrainedCollectionGenerator( impl #{From}<#{Symbol}> for $name { fn from(v: #{Symbol}) -> Self { ${ - if (innerNeedsConstraining) { - "Self(v.into_iter().map(|item| item.into()).collect())" - } else { - "Self(v)" - } + if (innerNeedsConstraining) { + "Self(v.into_iter().map(|item| item.into()).collect())" + } else { + "Self(v)" + } } } } @@ -121,11 +120,11 @@ class PubCrateConstrainedCollectionGenerator( impl #{From}<$name> for #{Symbol} { fn from(v: $name) -> Self { ${ - if (innerNeedsConstraining) { - "v.0.into_iter().map(|item| item.into()).collect()" - } else { - "v.0" - } + if (innerNeedsConstraining) { + "v.0.into_iter().map(|item| item.into()).collect()" + } else { + "v.0" + } } } } diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/PubCrateConstrainedMapGenerator.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/PubCrateConstrainedMapGenerator.kt index 9d5ad81125..838d4da085 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/PubCrateConstrainedMapGenerator.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/PubCrateConstrainedMapGenerator.kt @@ -8,7 +8,6 @@ package software.amazon.smithy.rust.codegen.server.smithy.generators import software.amazon.smithy.model.shapes.CollectionShape import software.amazon.smithy.model.shapes.MapShape import software.amazon.smithy.model.shapes.StringShape -import software.amazon.smithy.rust.codegen.core.rustlang.RustWriter import software.amazon.smithy.rust.codegen.core.rustlang.conditionalBlock import software.amazon.smithy.rust.codegen.core.rustlang.rust import software.amazon.smithy.rust.codegen.core.rustlang.rustBlock @@ -17,7 +16,7 @@ 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.smithy.RuntimeType import software.amazon.smithy.rust.codegen.core.smithy.isOptional -import software.amazon.smithy.rust.codegen.core.smithy.module +import software.amazon.smithy.rust.codegen.server.smithy.InlineModuleCreator import software.amazon.smithy.rust.codegen.server.smithy.ServerCodegenContext import software.amazon.smithy.rust.codegen.server.smithy.canReachConstrainedShape import software.amazon.smithy.rust.codegen.server.smithy.isDirectlyConstrained @@ -40,7 +39,7 @@ import software.amazon.smithy.rust.codegen.server.smithy.typeNameContainsNonPubl */ class PubCrateConstrainedMapGenerator( val codegenContext: ServerCodegenContext, - val writer: RustWriter, + private val inlineModuleCreator: InlineModuleCreator, val shape: MapShape, ) { private val model = codegenContext.model @@ -75,7 +74,7 @@ class PubCrateConstrainedMapGenerator( "From" to RuntimeType.From, ) - writer.withInlineModule(constrainedSymbol.module()) { + inlineModuleCreator(constrainedSymbol) { rustTemplate( """ ##[derive(Debug, Clone)] diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerBuilderConstraintViolations.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerBuilderConstraintViolations.kt index 55eae76c51..1ecb71d878 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerBuilderConstraintViolations.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerBuilderConstraintViolations.kt @@ -17,17 +17,16 @@ 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.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.isOptional import software.amazon.smithy.rust.codegen.core.smithy.makeRustBoxed -import software.amazon.smithy.rust.codegen.core.smithy.traits.RustBoxTrait import software.amazon.smithy.rust.codegen.core.util.hasTrait import software.amazon.smithy.rust.codegen.core.util.letIf import software.amazon.smithy.rust.codegen.core.util.toPascalCase 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.targetCanReachConstrainedShape +import software.amazon.smithy.rust.codegen.server.smithy.traits.ConstraintViolationRustBoxTrait /** * Renders constraint violation types that arise when building a structure shape builder. @@ -38,6 +37,7 @@ class ServerBuilderConstraintViolations( codegenContext: ServerCodegenContext, private val shape: StructureShape, private val builderTakesInUnconstrainedTypes: Boolean, + private val validationExceptionConversionGenerator: ValidationExceptionConversionGenerator, ) { private val model = codegenContext.model private val symbolProvider = codegenContext.symbolProvider @@ -71,7 +71,11 @@ class ServerBuilderConstraintViolations( writer.docs("Holds one variant for each of the ways the builder can fail.") if (nonExhaustive) Attribute.NonExhaustive.render(writer) val constraintViolationSymbolName = constraintViolationSymbolProvider.toSymbol(shape).name - writer.rustBlock("pub${if (visibility == Visibility.PUBCRATE) " (crate) " else ""} enum $constraintViolationSymbolName") { + writer.rustBlock( + """ + ##[allow(clippy::enum_variant_names)] + pub${if (visibility == Visibility.PUBCRATE) " (crate) " else ""} enum $constraintViolationSymbolName""", + ) { renderConstraintViolations(writer) } @@ -129,7 +133,11 @@ class ServerBuilderConstraintViolations( for (constraintViolation in all) { when (constraintViolation.kind) { ConstraintViolationKind.MISSING_MEMBER -> { - writer.docs("${constraintViolation.message(symbolProvider, model).replaceFirstChar { it.uppercaseChar() }}.") + writer.docs( + "${constraintViolation.message(symbolProvider, model).replaceFirstChar { + it.uppercaseChar() + }}.", + ) writer.rust("${constraintViolation.name()},") } @@ -138,14 +146,18 @@ class ServerBuilderConstraintViolations( val constraintViolationSymbol = constraintViolationSymbolProvider.toSymbol(targetShape) - // If the corresponding structure's member is boxed, box this constraint violation symbol too. - .letIf(constraintViolation.forMember.hasTrait()) { + // Box this constraint violation symbol if necessary. + .letIf(constraintViolation.forMember.hasTrait()) { it.makeRustBoxed() } // Note we cannot express the inner constraint violation as `>::Error`, because `T` might // be `pub(crate)` and that would leak `T` in a public interface. - writer.docs("${constraintViolation.message(symbolProvider, model)}.".replaceFirstChar { it.uppercaseChar() }) + writer.docs( + "${constraintViolation.message(symbolProvider, model)}.".replaceFirstChar { + it.uppercaseChar() + }, + ) Attribute.DocHidden.render(writer) writer.rust("${constraintViolation.name()}(#T),", constraintViolationSymbol) } @@ -154,25 +166,6 @@ class ServerBuilderConstraintViolations( } private fun renderAsValidationExceptionFieldList(writer: RustWriter) { - val validationExceptionFieldWritable = writable { - rustBlock("match self") { - all.forEach { - if (it.hasInner()) { - rust("""ConstraintViolation::${it.name()}(inner) => inner.as_validation_exception_field(path + "/${it.forMember.memberName}"),""") - } else { - rust( - """ - ConstraintViolation::${it.name()} => crate::model::ValidationExceptionField { - message: format!("Value null at '{}/${it.forMember.memberName}' failed to satisfy constraint: Member must not be null", path), - path: path + "/${it.forMember.memberName}", - }, - """, - ) - } - } - } - } - writer.rustTemplate( """ impl ConstraintViolation { @@ -181,7 +174,7 @@ class ServerBuilderConstraintViolations( } } """, - "ValidationExceptionFieldWritable" to validationExceptionFieldWritable, + "ValidationExceptionFieldWritable" to validationExceptionConversionGenerator.builderConstraintViolationImplBlock((all)), "String" to RuntimeType.String, ) } diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerBuilderGenerator.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerBuilderGenerator.kt index 27bfa69d32..2c15fa571f 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerBuilderGenerator.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerBuilderGenerator.kt @@ -29,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.withBlock 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.expectRustMetadata import software.amazon.smithy.rust.codegen.core.smithy.isOptional import software.amazon.smithy.rust.codegen.core.smithy.isRustBoxed @@ -49,7 +50,9 @@ 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.hasConstraintTraitOrTargetHasConstraintTrait import software.amazon.smithy.rust.codegen.server.smithy.targetCanReachConstrainedShape +import software.amazon.smithy.rust.codegen.server.smithy.traits.ConstraintViolationRustBoxTrait import software.amazon.smithy.rust.codegen.server.smithy.traits.isReachableFromOperationInput +import software.amazon.smithy.rust.codegen.server.smithy.withInMemoryInlineModule import software.amazon.smithy.rust.codegen.server.smithy.wouldHaveConstrainedWrapperTupleTypeWerePublicConstrainedTypesEnabled /** @@ -86,8 +89,9 @@ import software.amazon.smithy.rust.codegen.server.smithy.wouldHaveConstrainedWra * [derive_builder]: https://docs.rs/derive_builder/latest/derive_builder/index.html */ class ServerBuilderGenerator( - codegenContext: ServerCodegenContext, + val codegenContext: ServerCodegenContext, private val shape: StructureShape, + private val customValidationExceptionWithReasonConversionGenerator: ValidationExceptionConversionGenerator, ) { companion object { /** @@ -141,7 +145,7 @@ class ServerBuilderGenerator( private val builderSymbol = shape.serverBuilderSymbol(codegenContext) private val isBuilderFallible = hasFallibleBuilder(shape, model, symbolProvider, takeInUnconstrainedTypes) private val serverBuilderConstraintViolations = - ServerBuilderConstraintViolations(codegenContext, shape, takeInUnconstrainedTypes) + ServerBuilderConstraintViolations(codegenContext, shape, takeInUnconstrainedTypes, customValidationExceptionWithReasonConversionGenerator) private val codegenScope = arrayOf( "RequestRejection" to ServerRuntimeType.requestRejection(runtimeConfig), @@ -151,9 +155,9 @@ class ServerBuilderGenerator( "MaybeConstrained" to RuntimeType.MaybeConstrained, ) - fun render(writer: RustWriter) { - writer.docs("See #D.", structureSymbol) - writer.withInlineModule(builderSymbol.module()) { + fun render(rustCrate: RustCrate, writer: RustWriter) { + val docWriter: () -> Unit = { writer.docs("See #D.", structureSymbol) } + rustCrate.withInMemoryInlineModule(writer, builderSymbol.module(), docWriter) { renderBuilder(this) } } @@ -187,7 +191,9 @@ class ServerBuilderGenerator( // since we are a builder and everything is optional. val baseDerives = structureSymbol.expectRustMetadata().derives // Filter out any derive that isn't Debug or Clone. Then add a Default derive - val builderDerives = baseDerives.filter { it == RuntimeType.Debug || it == RuntimeType.Clone } + RuntimeType.Default + val builderDerives = baseDerives.filter { + it == RuntimeType.Debug || it == RuntimeType.Clone + } + RuntimeType.Default Attribute(derive(builderDerives)).render(writer) writer.rustBlock("${visibility.toRustQualifier()} struct Builder") { members.forEach { renderBuilderMember(this, it) } @@ -214,21 +220,9 @@ class ServerBuilderGenerator( private fun renderImplFromConstraintViolationForRequestRejection(writer: RustWriter) { writer.rustTemplate( """ - impl #{From} for #{RequestRejection} { - fn from(constraint_violation: ConstraintViolation) -> Self { - let first_validation_exception_field = constraint_violation.as_validation_exception_field("".to_owned()); - let validation_exception = crate::error::ValidationException { - message: format!("1 validation error detected. {}", &first_validation_exception_field.message), - field_list: Some(vec![first_validation_exception_field]), - }; - Self::ConstraintViolation( - crate::operation_ser::serialize_structure_crate_error_validation_exception(&validation_exception) - .expect("impossible") - ) - } - } + #{Converter:W} """, - *codegenScope, + "Converter" to customValidationExceptionWithReasonConversionGenerator.renderImplFromConstraintViolationForRequestRejection(), ) } @@ -401,12 +395,12 @@ class ServerBuilderGenerator( rust( """ self.$memberName = ${ - // TODO(https://github.com/awslabs/smithy-rs/issues/1302, https://github.com/awslabs/smithy/issues/1179): See above. - if (symbolProvider.toSymbol(member).isOptional()) { - "input.map(|v| v.into())" - } else { - "Some(input.into())" - } + // TODO(https://github.com/awslabs/smithy-rs/issues/1302, https://github.com/awslabs/smithy/issues/1179): See above. + if (symbolProvider.toSymbol(member).isOptional()) { + "input.map(|v| v.into())" + } else { + "Some(input.into())" + } }; self """, @@ -552,6 +546,8 @@ class ServerBuilderGenerator( val hasBox = builderMemberSymbol(member) .mapRustType { it.stripOuter() } .isRustBoxed() + val errHasBox = member.hasTrait() + if (hasBox) { writer.rustTemplate( """ @@ -559,11 +555,6 @@ class ServerBuilderGenerator( #{MaybeConstrained}::Constrained(x) => Ok(Box::new(x)), #{MaybeConstrained}::Unconstrained(x) => Ok(Box::new(x.try_into()?)), }) - .map(|res| - res${ if (constrainedTypeHoldsFinalType(member)) "" else ".map(|v| v.into())" } - .map_err(|err| ConstraintViolation::${constraintViolation.name()}(Box::new(err))) - ) - .transpose()? """, *codegenScope, ) @@ -574,15 +565,21 @@ class ServerBuilderGenerator( #{MaybeConstrained}::Constrained(x) => Ok(x), #{MaybeConstrained}::Unconstrained(x) => x.try_into(), }) - .map(|res| - res${if (constrainedTypeHoldsFinalType(member)) "" else ".map(|v| v.into())"} - .map_err(ConstraintViolation::${constraintViolation.name()}) - ) - .transpose()? """, *codegenScope, ) } + val mapOk = if (constrainedTypeHoldsFinalType(member)) "" else ".map(|v| v.into())" + val mapErr = if (errHasBox) ".map_err(Box::new)" else "" + writer.rustTemplate( + """ + .map(|res| + res$mapOk$mapErr.map_err(ConstraintViolation::${constraintViolation.name()}) + ) + .transpose()? + """, + *codegenScope, + ) // Constrained types are not public and this is a member shape that would have generated a // public constrained type, were the setting to be enabled. diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerBuilderGeneratorCommon.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerBuilderGeneratorCommon.kt index f16e2640b2..389f0dc173 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerBuilderGeneratorCommon.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerBuilderGeneratorCommon.kt @@ -39,6 +39,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.RuntimeConfig import software.amazon.smithy.rust.codegen.core.smithy.RustSymbolProvider +import software.amazon.smithy.rust.codegen.core.smithy.generators.EnumMemberModel import software.amazon.smithy.rust.codegen.core.util.UNREACHABLE import software.amazon.smithy.rust.codegen.core.util.dq import software.amazon.smithy.rust.codegen.core.util.expectTrait @@ -143,7 +144,9 @@ fun defaultValue( .entries .filter { entry -> entry.value == value } .map { entry -> - symbolProvider.toEnumVariantName( + EnumMemberModel.toEnumVariantName( + symbolProvider, + target, EnumDefinition.builder().name(entry.key).value(entry.value.toString()).build(), )!! } diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerBuilderGeneratorWithoutPublicConstrainedTypes.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerBuilderGeneratorWithoutPublicConstrainedTypes.kt index b30252a8a7..2ae17d1a80 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerBuilderGeneratorWithoutPublicConstrainedTypes.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerBuilderGeneratorWithoutPublicConstrainedTypes.kt @@ -23,12 +23,14 @@ 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.smithy.RuntimeType +import software.amazon.smithy.rust.codegen.core.smithy.RustCrate import software.amazon.smithy.rust.codegen.core.smithy.expectRustMetadata 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.module 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.withInMemoryInlineModule /** * Generates a builder for the Rust type associated with the [StructureShape]. @@ -46,6 +48,7 @@ import software.amazon.smithy.rust.codegen.server.smithy.ServerRuntimeType class ServerBuilderGeneratorWithoutPublicConstrainedTypes( private val codegenContext: ServerCodegenContext, shape: StructureShape, + validationExceptionConversionGenerator: ValidationExceptionConversionGenerator, ) { companion object { /** @@ -79,7 +82,7 @@ class ServerBuilderGeneratorWithoutPublicConstrainedTypes( private val builderSymbol = shape.serverBuilderSymbol(symbolProvider, false) private val isBuilderFallible = hasFallibleBuilder(shape, symbolProvider) private val serverBuilderConstraintViolations = - ServerBuilderConstraintViolations(codegenContext, shape, builderTakesInUnconstrainedTypes = false) + ServerBuilderConstraintViolations(codegenContext, shape, builderTakesInUnconstrainedTypes = false, validationExceptionConversionGenerator) private val codegenScope = arrayOf( "RequestRejection" to ServerRuntimeType.requestRejection(codegenContext.runtimeConfig), @@ -89,12 +92,12 @@ class ServerBuilderGeneratorWithoutPublicConstrainedTypes( "MaybeConstrained" to RuntimeType.MaybeConstrained, ) - fun render(writer: RustWriter) { + fun render(rustCrate: RustCrate, writer: RustWriter) { check(!codegenContext.settings.codegenConfig.publicConstrainedTypes) { "ServerBuilderGeneratorWithoutPublicConstrainedTypes should only be used when `publicConstrainedTypes` is false" } - writer.docs("See #D.", structureSymbol) - writer.withInlineModule(builderSymbol.module()) { + val docWriter = { writer.docs("See #D.", structureSymbol) } + rustCrate.withInMemoryInlineModule(writer, builderSymbol.module(), docWriter) { renderBuilder(this) } } diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerBuilderSymbol.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerBuilderSymbol.kt index 9720717383..64d35e0a8f 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerBuilderSymbol.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerBuilderSymbol.kt @@ -2,7 +2,6 @@ * 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.codegen.core.Symbol @@ -17,13 +16,15 @@ import software.amazon.smithy.rust.codegen.core.smithy.rustType import software.amazon.smithy.rust.codegen.core.util.toSnakeCase import software.amazon.smithy.rust.codegen.server.smithy.ServerCodegenContext +// TODO(https://github.com/awslabs/smithy-rs/issues/2396): Replace this with `RustSymbolProvider.symbolForBuilder` fun StructureShape.serverBuilderSymbol(codegenContext: ServerCodegenContext): Symbol = this.serverBuilderSymbol( codegenContext.symbolProvider, !codegenContext.settings.codegenConfig.publicConstrainedTypes, ) -fun StructureShape.serverBuilderSymbol(symbolProvider: SymbolProvider, pubCrate: Boolean): Symbol { +// TODO(https://github.com/awslabs/smithy-rs/issues/2396): Replace this with `RustSymbolProvider.moduleForBuilder` +fun StructureShape.serverBuilderModule(symbolProvider: SymbolProvider, pubCrate: Boolean): RustModule.LeafModule { val structureSymbol = symbolProvider.toSymbol(this) val builderNamespace = RustReservedWords.escapeIfNeeded(structureSymbol.name.toSnakeCase()) + if (pubCrate) { @@ -35,7 +36,18 @@ fun StructureShape.serverBuilderSymbol(symbolProvider: SymbolProvider, pubCrate: true -> Visibility.PUBCRATE false -> Visibility.PUBLIC } - val builderModule = RustModule.new(builderNamespace, visibility, parent = structureSymbol.module(), inline = true) + return RustModule.new( + builderNamespace, + visibility, + parent = structureSymbol.module(), + inline = true, + documentationOverride = "", + ) +} + +// TODO(https://github.com/awslabs/smithy-rs/issues/2396): Replace this with `RustSymbolProvider.symbolForBuilder` +fun StructureShape.serverBuilderSymbol(symbolProvider: SymbolProvider, pubCrate: Boolean): Symbol { + val builderModule = serverBuilderModule(symbolProvider, pubCrate) val rustType = RustType.Opaque("Builder", builderModule.fullyQualifiedPath()) return Symbol.builder() .rustType(rustType) 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 1a55cb8879..cc811b80f2 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 @@ -5,27 +5,26 @@ package software.amazon.smithy.rust.codegen.server.smithy.generators import software.amazon.smithy.model.shapes.StringShape -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.rustBlock 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.rustlang.writable import software.amazon.smithy.rust.codegen.core.smithy.RuntimeType 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 import software.amazon.smithy.rust.codegen.core.smithy.module import software.amazon.smithy.rust.codegen.core.util.dq -import software.amazon.smithy.rust.codegen.core.util.expectTrait 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.traits.isReachableFromOperationInput -open class ServerEnumGenerator( - val codegenContext: ServerCodegenContext, - private val writer: RustWriter, - shape: StringShape, -) : EnumGenerator(codegenContext.model, codegenContext.symbolProvider, writer, shape, shape.expectTrait()) { - override var target: CodegenTarget = CodegenTarget.SERVER - +open class ConstrainedEnum( + private val codegenContext: ServerCodegenContext, + private val shape: StringShape, + private val validationExceptionConversionGenerator: ValidationExceptionConversionGenerator, +) : EnumType() { private val publicConstrainedTypes = codegenContext.settings.codegenConfig.publicConstrainedTypes private val constraintViolationSymbolProvider = with(codegenContext.constraintViolationSymbolProvider) { @@ -41,8 +40,8 @@ open class ServerEnumGenerator( "String" to RuntimeType.String, ) - override fun renderFromForStr() { - writer.withInlineModule(constraintViolationSymbol.module()) { + override fun implFromForStr(context: EnumGeneratorContext): Writable = writable { + withInlineModule(constraintViolationSymbol.module(), codegenContext.moduleDocProvider) { rustTemplate( """ ##[derive(Debug, PartialEq)] @@ -52,39 +51,33 @@ open class ServerEnumGenerator( ) if (shape.isReachableFromOperationInput()) { - val enumValueSet = enumTrait.enumDefinitionValues.joinToString(", ") - val message = "Value {} at '{}' failed to satisfy constraint: Member must satisfy enum value set: [$enumValueSet]" - rustTemplate( """ impl $constraintViolationName { - pub(crate) fn as_validation_exception_field(self, path: #{String}) -> crate::model::ValidationExceptionField { - crate::model::ValidationExceptionField { - message: format!(r##"$message"##, &self.0, &path), - path, - } - } + #{EnumShapeConstraintViolationImplBlock:W} } """, - *codegenScope, + "EnumShapeConstraintViolationImplBlock" to validationExceptionConversionGenerator.enumShapeConstraintViolationImplBlock( + context.enumTrait, + ), ) } } - writer.rustBlock("impl #T<&str> for $enumName", RuntimeType.TryFrom) { + rustBlock("impl #T<&str> for ${context.enumName}", RuntimeType.TryFrom) { rust("type Error = #T;", constraintViolationSymbol) rustBlock("fn try_from(s: &str) -> Result>::Error>", RuntimeType.TryFrom) { rustBlock("match s") { - sortedMembers.forEach { member -> - rust("${member.value.dq()} => Ok($enumName::${member.derivedName()}),") + context.sortedMembers.forEach { member -> + rust("${member.value.dq()} => Ok(${context.enumName}::${member.derivedName()}),") } rust("_ => Err(#T(s.to_owned()))", constraintViolationSymbol) } } } - writer.rustTemplate( + rustTemplate( """ - impl #{TryFrom}<#{String}> for $enumName { - type Error = #{UnknownVariantSymbol}; + impl #{TryFrom}<#{String}> for ${context.enumName} { + type Error = #{ConstraintViolation}; fn try_from(s: #{String}) -> std::result::Result>::Error> { s.as_str().try_into() } @@ -92,21 +85,32 @@ open class ServerEnumGenerator( """, "String" to RuntimeType.String, "TryFrom" to RuntimeType.TryFrom, - "UnknownVariantSymbol" to constraintViolationSymbol, + "ConstraintViolation" to constraintViolationSymbol, ) } - override fun renderFromStr() { - writer.rustTemplate( + override fun implFromStr(context: EnumGeneratorContext): Writable = writable { + rustTemplate( """ - impl std::str::FromStr for $enumName { - type Err = #{UnknownVariantSymbol}; + impl std::str::FromStr for ${context.enumName} { + type Err = #{ConstraintViolation}; fn from_str(s: &str) -> std::result::Result::Err> { Self::try_from(s) } } """, - "UnknownVariantSymbol" to constraintViolationSymbol, + "ConstraintViolation" to constraintViolationSymbol, ) } } + +class ServerEnumGenerator( + codegenContext: ServerCodegenContext, + shape: StringShape, + validationExceptionConversionGenerator: ValidationExceptionConversionGenerator, +) : EnumGenerator( + codegenContext.model, + codegenContext.symbolProvider, + shape, + enumType = ConstrainedEnum(codegenContext, shape, validationExceptionConversionGenerator), +) diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerHttpSensitivityGenerator.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerHttpSensitivityGenerator.kt index 47b42825fa..0163f094f5 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerHttpSensitivityGenerator.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerHttpSensitivityGenerator.kt @@ -85,10 +85,14 @@ class LabelSensitivity(internal val labelIndexes: List, internal val greedy private fun hasRedactions(): Boolean = labelIndexes.isNotEmpty() || greedyLabel != null /** Returns the type of the `MakeFmt`. */ - fun type(): Writable = if (hasRedactions()) writable { - rustTemplate("#{SmithyHttpServer}::instrumentation::sensitivity::uri::MakeLabel bool>", *codegenScope) - } else writable { - rustTemplate("#{SmithyHttpServer}::instrumentation::MakeIdentity", *codegenScope) + fun type(): Writable = if (hasRedactions()) { + writable { + rustTemplate("#{SmithyHttpServer}::instrumentation::sensitivity::uri::MakeLabel bool>", *codegenScope) + } + } else { + writable { + rustTemplate("#{SmithyHttpServer}::instrumentation::MakeIdentity", *codegenScope) + } } /** Returns the value of the `GreedyLabel`. */ @@ -105,9 +109,13 @@ class LabelSensitivity(internal val labelIndexes: List, internal val greedy } /** Returns the setter enclosing the closure or suffix position. */ - fun setter(): Writable = if (hasRedactions()) writable { - rustTemplate(".label(#{Closure:W}, #{GreedyLabel:W})", "Closure" to closure(), "GreedyLabel" to greedyLabelStruct()) - } else writable { } + fun setter(): Writable = if (hasRedactions()) { + writable { + rustTemplate(".label(#{Closure:W}, #{GreedyLabel:W})", "Closure" to closure(), "GreedyLabel" to greedyLabelStruct()) + } + } else { + writable { } + } } /** Models the ways headers can be bound and sensitive */ @@ -156,11 +164,15 @@ sealed class HeaderSensitivity( /** Returns the closure used during construction. */ internal fun closure(): Writable { - val nameMatch = if (headerKeys.isEmpty()) writable { - rust("false") - } else writable { - val matches = headerKeys.joinToString("|") { it.dq() } - rust("matches!(name.as_str(), $matches)") + val nameMatch = if (headerKeys.isEmpty()) { + writable { + rust("false") + } + } else { + writable { + val matches = headerKeys.joinToString("|") { it.dq() } + rust("matches!(name.as_str(), $matches)") + } } val suffixAndValue = when (this) { @@ -252,11 +264,15 @@ sealed class QuerySensitivity( is SensitiveMapValue -> writable { rust("true") } - is NotSensitiveMapValue -> if (queryKeys.isEmpty()) writable { - rust("false;") - } else writable { - val matches = queryKeys.joinToString("|") { it.dq() } - rust("matches!(name, $matches);") + is NotSensitiveMapValue -> if (queryKeys.isEmpty()) { + writable { + rust("false;") + } + } else { + writable { + val matches = queryKeys.joinToString("|") { it.dq() } + rust("matches!(name, $matches);") + } } } @@ -498,7 +514,9 @@ class ServerHttpSensitivityGenerator( ) } - val value = writable { rustTemplate("#{SmithyHttpServer}::instrumentation::sensitivity::RequestFmt::new()", *codegenScope) } + headerSensitivity.setter() + labelSensitivity.setter() + querySensitivity.setters() + val value = writable { + rustTemplate("#{SmithyHttpServer}::instrumentation::sensitivity::RequestFmt::new()", *codegenScope) + } + headerSensitivity.setter() + labelSensitivity.setter() + querySensitivity.setters() return MakeFmt(type, value) } diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerInstantiator.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerInstantiator.kt index 3179b5370e..3d3105f9c7 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerInstantiator.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerInstantiator.kt @@ -16,6 +16,7 @@ import software.amazon.smithy.rust.codegen.core.smithy.generators.Instantiator import software.amazon.smithy.rust.codegen.core.smithy.generators.InstantiatorCustomization import software.amazon.smithy.rust.codegen.core.smithy.generators.InstantiatorSection import software.amazon.smithy.rust.codegen.core.smithy.isOptional +import software.amazon.smithy.rust.codegen.server.smithy.ServerCodegenContext import software.amazon.smithy.rust.codegen.server.smithy.isDirectlyConstrained import software.amazon.smithy.rust.codegen.server.smithy.traits.isReachableFromOperationInput @@ -47,12 +48,26 @@ class ServerBuilderKindBehavior(val codegenContext: CodegenContext) : Instantiat override fun hasFallibleBuilder(shape: StructureShape): Boolean { // Only operation input builders take in unconstrained types. val takesInUnconstrainedTypes = shape.isReachableFromOperationInput() - return ServerBuilderGenerator.hasFallibleBuilder( - shape, - codegenContext.model, - codegenContext.symbolProvider, - takesInUnconstrainedTypes, - ) + + val publicConstrainedTypes = if (codegenContext is ServerCodegenContext) { + codegenContext.settings.codegenConfig.publicConstrainedTypes + } else { + true + } + + return if (publicConstrainedTypes) { + ServerBuilderGenerator.hasFallibleBuilder( + shape, + codegenContext.model, + codegenContext.symbolProvider, + takesInUnconstrainedTypes, + ) + } else { + ServerBuilderGeneratorWithoutPublicConstrainedTypes.hasFallibleBuilder( + shape, + codegenContext.symbolProvider, + ) + } } override fun setterName(memberShape: MemberShape): String = codegenContext.symbolProvider.toMemberName(memberShape) 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 44048276d8..ee09c47047 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 @@ -7,7 +7,10 @@ package software.amazon.smithy.rust.codegen.server.smithy.generators import software.amazon.smithy.codegen.core.Symbol import software.amazon.smithy.model.Model +import software.amazon.smithy.model.shapes.OperationShape +import software.amazon.smithy.model.shapes.Shape 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.RustWriter import software.amazon.smithy.rust.codegen.core.rustlang.Visibility @@ -18,6 +21,9 @@ 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.RustSymbolProvider +import software.amazon.smithy.rust.codegen.core.smithy.transformers.eventStreamErrors +import software.amazon.smithy.rust.codegen.core.smithy.transformers.operationErrors +import software.amazon.smithy.rust.codegen.core.util.UNREACHABLE import software.amazon.smithy.rust.codegen.core.util.toSnakeCase /** @@ -27,28 +33,32 @@ import software.amazon.smithy.rust.codegen.core.util.toSnakeCase open class ServerOperationErrorGenerator( private val model: Model, private val symbolProvider: RustSymbolProvider, - private val operationSymbol: Symbol, - private val errors: List, + private val operationOrEventStream: Shape, ) { - open fun render(writer: RustWriter) { - val symbol = RuntimeType("crate::error::${operationSymbol.name}Error") - if (errors.isNotEmpty()) { - renderErrors(writer, symbol, operationSymbol) - } - } + private val symbol = symbolProvider.toSymbol(operationOrEventStream) - fun renderErrors( - writer: RustWriter, - errorSymbol: RuntimeType, - operationSymbol: Symbol, - ) { + private fun operationErrors(): List = + (operationOrEventStream as OperationShape).operationErrors(model).map { it.asStructureShape().get() } + private fun eventStreamErrors(): List = + (operationOrEventStream as UnionShape).eventStreamErrors() + .map { model.expectShape(it.asMemberShape().get().target, StructureShape::class.java) } + + fun render(writer: RustWriter) { + val (errorSymbol, errors) = when (operationOrEventStream) { + is OperationShape -> symbolProvider.symbolForOperationError(operationOrEventStream) to operationErrors() + is UnionShape -> symbolProvider.symbolForEventStreamError(operationOrEventStream) to eventStreamErrors() + else -> UNREACHABLE("OperationErrorGenerator only supports operation or event stream shapes") + } + if (errors.isEmpty()) { + return + } val meta = RustMetadata( derives = setOf(RuntimeType.Debug), visibility = Visibility.PUBLIC, ) - writer.rust("/// Error type for the `${operationSymbol.name}` operation.") - writer.rust("/// Each variant represents an error that can occur for the `${operationSymbol.name}` operation.") + writer.rust("/// Error type for the `${symbol.name}` operation.") + writer.rust("/// Each variant represents an error that can occur for the `${symbol.name}` operation.") meta.render(writer) writer.rustBlock("enum ${errorSymbol.name}") { errors.forEach { errorVariant -> @@ -85,7 +95,7 @@ open class ServerOperationErrorGenerator( } writer.rustBlock("impl #T for ${errorSymbol.name}", RuntimeType.StdError) { - rustBlock("fn source(&self) -> Option<&(dyn #T + 'static)>", RuntimeType.StdError) { + rustBlock("fn source(&self) -> std::option::Option<&(dyn #T + 'static)>", RuntimeType.StdError) { delegateToVariants(errors, errorSymbol) { rust("Some(_inner)") } @@ -120,7 +130,7 @@ open class ServerOperationErrorGenerator( */ private fun RustWriter.delegateToVariants( errors: List, - symbol: RuntimeType, + symbol: Symbol, writable: Writable, ) { rustBlock("match &self") { 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 4589de74a5..451aa65a85 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 @@ -17,8 +17,8 @@ import software.amazon.smithy.rust.codegen.core.util.toPascalCase import software.amazon.smithy.rust.codegen.server.smithy.ServerCargoDependency class ServerOperationGenerator( - codegenContext: CodegenContext, private val operation: OperationShape, + codegenContext: CodegenContext, ) { private val runtimeConfig = codegenContext.runtimeConfig private val codegenScope = diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerOperationShapeGenerator.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerOperationShapeGenerator.kt deleted file mode 100644 index ecd38c9057..0000000000 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerOperationShapeGenerator.kt +++ /dev/null @@ -1,75 +0,0 @@ -/* - * 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.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.toPascalCase -import software.amazon.smithy.rust.codegen.core.util.toSnakeCase -import software.amazon.smithy.rust.codegen.server.smithy.ServerCargoDependency - -class ServerOperationShapeGenerator( - private val operations: List, - private val codegenContext: CodegenContext, -) { - - fun render(writer: RustWriter) { - if (operations.isEmpty()) { - return - } - - val firstOperation = codegenContext.symbolProvider.toSymbol(operations[0]) - val firstOperationName = firstOperation.name.toPascalCase() - val crateName = codegenContext.settings.moduleName.toSnakeCase() - - writer.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 - //! [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 - //! 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, operations[0], "handler", commentToken = "//!")::render, - "HandlerImports" to handlerImports(crateName, operations, commentToken = "//!"), - ) - for (operation in operations) { - ServerOperationGenerator(codegenContext, operation).render(writer) - } - } -} 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 new file mode 100644 index 0000000000..a8221b9327 --- /dev/null +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerRootGenerator.kt @@ -0,0 +1,240 @@ +/* + * 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.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.join +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.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 +import software.amazon.smithy.rust.codegen.server.smithy.generators.protocol.ServerProtocol +import software.amazon.smithy.rust.codegen.server.smithy.ServerRustModule.Error as ErrorModule +import software.amazon.smithy.rust.codegen.server.smithy.ServerRustModule.Input as InputModule +import software.amazon.smithy.rust.codegen.server.smithy.ServerRustModule.Output as OutputModule + +/** + * ServerRootGenerator + * + * Generates all code within `lib.rs`, this includes: + * - Crate documentation + * - Re-exports + */ +open class ServerRootGenerator( + val protocol: ServerProtocol, + private val codegenContext: ServerCodegenContext, +) { + private val index = TopDownIndex.of(codegenContext.model) + private val operations = index.getContainedOperations(codegenContext.serviceShape).toSortedSet( + compareBy { + it.id + }, + ).toList() + private val serviceName = codegenContext.serviceShape.id.name.toPascalCase() + + fun documentation(writer: RustWriter) { + val builderFieldNames = + operations.associateWith { + RustReservedWords.escapeIfNeeded(codegenContext.symbolProvider.toSymbol(it).name.toSnakeCase()) + } + .toSortedMap( + compareBy { it.id }, + ) + val crateName = codegenContext.moduleUseName() + val builderName = "${serviceName}Builder" + val hasErrors = operations.any { it.errors.isNotEmpty() } + val handlers: Writable = operations + .map { operation -> + DocHandlerGenerator(codegenContext, operation, builderFieldNames[operation]!!, "//!")::render + } + .join("//!\n") + + writer.rustTemplate( + """ + //! A fast and customizable Rust implementation of the $serviceName Smithy service. + //! + //! ## Using $serviceName + //! + //! The primary entrypoint is [`$serviceName`]: it satisfies the [`Service`](#{Tower}::Service) + //! trait and therefore can be handed to a [`hyper` server](https://github.com/hyperium/hyper) via [`$serviceName::into_make_service`] or used in Lambda via [`LambdaHandler`](#{SmithyHttpServer}::routing::LambdaHandler). + //! The [`crate::${InputModule.name}`], ${if (!hasErrors) "and " else ""}[`crate::${OutputModule.name}`], ${if (hasErrors) "and [`crate::${ErrorModule.name}`]" else "" } + //! modules provide the types used in each operation. + //! + //! ###### Running on Hyper + //! + //! ```rust,no_run + //! ## use std::net::SocketAddr; + //! ## async fn dummy() { + //! use $crateName::$serviceName; + //! + //! ## let app = $serviceName::builder_without_plugins().build_unchecked(); + //! let server = app.into_make_service(); + //! let bind: SocketAddr = "127.0.0.1:6969".parse() + //! .expect("unable to parse the server bind address and port"); + //! #{Hyper}::Server::bind(&bind).serve(server).await.unwrap(); + //! ## } + //! ``` + //! + //! ###### Running on Lambda + //! + //! This requires the `aws-lambda` feature flag to be passed to the [`#{SmithyHttpServer}`] crate. + //! + //! ```rust,ignore + //! use #{SmithyHttpServer}::routing::LambdaHandler; + //! use $crateName::$serviceName; + //! + //! ## async fn dummy() { + //! ## let app = $serviceName::builder_without_plugins().build_unchecked(); + //! let handler = LambdaHandler::new(app); + //! lambda_http::run(handler).await.unwrap(); + //! ## } + //! ``` + //! + //! ## Building the $serviceName + //! + //! To construct [`$serviceName`] we use [`$builderName`] returned by [`$serviceName::builder_without_plugins`] + //! or [`$serviceName::builder_with_plugins`]. + //! + //! #### Plugins + //! + //! The [`$serviceName::builder_with_plugins`] method, returning [`$builderName`], + //! accepts a [`Plugin`](aws_smithy_http_server::plugin::Plugin). + //! Plugins allow you to build middleware which is aware of the operation it is being applied to. + //! + //! ```rust + //! ## use #{SmithyHttpServer}::plugin::IdentityPlugin as LoggingPlugin; + //! ## use #{SmithyHttpServer}::plugin::IdentityPlugin as MetricsPlugin; + //! ## use #{Hyper}::Body; + //! use #{SmithyHttpServer}::plugin::PluginPipeline; + //! use $crateName::{$serviceName, $builderName}; + //! + //! let plugins = PluginPipeline::new() + //! .push(LoggingPlugin) + //! .push(MetricsPlugin); + //! let builder: $builderName = $serviceName::builder_with_plugins(plugins); + //! ``` + //! + //! Check out [`#{SmithyHttpServer}::plugin`] to learn more about plugins. + //! + //! #### Handlers + //! + //! [`$builderName`] provides a setter method for each operation in your Smithy model. The setter methods expect an async function as input, matching the signature for the corresponding operation in your Smithy model. + //! We call these async functions **handlers**. This is where your application business logic lives. + //! + //! Every handler must take an `Input`, and optional [`extractor arguments`](#{SmithyHttpServer}::request), while returning: + //! + //! * A `Result` if your operation has modeled errors, or + //! * An `Output` otherwise. + //! + //! ```rust + //! ## struct Input; + //! ## struct Output; + //! ## struct Error; + //! async fn infallible_handler(input: Input) -> Output { todo!() } + //! + //! async fn fallible_handler(input: Input) -> Result { todo!() } + //! ``` + //! + //! Handlers can accept up to 8 extractors: + //! + //! ```rust + //! ## struct Input; + //! ## struct Output; + //! ## struct Error; + //! ## struct State; + //! ## use std::net::SocketAddr; + //! use #{SmithyHttpServer}::request::{extension::Extension, connect_info::ConnectInfo}; + //! + //! async fn handler_with_no_extensions(input: Input) -> Output { + //! todo!() + //! } + //! + //! async fn handler_with_one_extractor(input: Input, ext: Extension) -> Output { + //! todo!() + //! } + //! + //! async fn handler_with_two_extractors( + //! input: Input, + //! ext0: Extension, + //! ext1: ConnectInfo, + //! ) -> Output { + //! todo!() + //! } + //! ``` + //! + //! See the [`operation module`](#{SmithyHttpServer}::operation) for information on precisely what constitutes a handler. + //! + //! #### Build + //! + //! You can convert [`$builderName`] into [`$serviceName`] using either [`$builderName::build`] or [`$builderName::build_unchecked`]. + //! + //! [`$builderName::build`] requires you to provide a handler for every single operation in your Smithy model. It will return an error if that is not the case. + //! + //! [`$builderName::build_unchecked`], instead, does not require exhaustiveness. The server will automatically return 500 Internal Server Error to all requests for operations that do not have a registered handler. + //! [`$builderName::build_unchecked`] is particularly useful if you are deploying your Smithy service as a collection of Lambda functions, where each Lambda is only responsible for a subset of the operations in the Smithy service (or even a single one!). + //! + //! ## Example + //! + //! ```rust + //! ## use std::net::SocketAddr; + //! use $crateName::$serviceName; + //! + //! ##[#{Tokio}::main] + //! pub async fn main() { + //! let app = $serviceName::builder_without_plugins() + ${builderFieldNames.values.joinToString("\n") { "//! .$it($it)" }} + //! .build() + //! .expect("failed to build an instance of $serviceName"); + //! + //! let bind: SocketAddr = "127.0.0.1:6969".parse() + //! .expect("unable to parse the server bind address and port"); + //! let server = #{Hyper}::Server::bind(&bind).serve(app.into_make_service()); + //! ## let server = async { Ok::<_, ()>(()) }; + //! + //! // Run your service! + //! if let Err(err) = server.await { + //! eprintln!("server error: {:?}", err); + //! } + //! } + //! + #{HandlerImports:W} + //! + #{Handlers:W} + //! + //! ``` + //! + //! [`serve`]: https://docs.rs/hyper/0.14.16/hyper/server/struct.Builder.html##method.serve + //! [`tower::make::MakeService`]: https://docs.rs/tower/latest/tower/make/trait.MakeService.html + //! [HTTP binding traits]: https://smithy.io/2.0/spec/http-bindings.html + //! [operations]: https://smithy.io/2.0/spec/service-types.html##operation + //! [hyper server]: https://docs.rs/hyper/latest/hyper/server/index.html + //! [Service]: https://docs.rs/tower-service/latest/tower_service/trait.Service.html + """, + "HandlerImports" to handlerImports(crateName, operations, commentToken = "//!"), + "Handlers" to handlers, + "ExampleHandler" to operations.take(1).map { operation -> DocHandlerGenerator(codegenContext, operation, builderFieldNames[operation]!!, "//!").docSignature() }, + "SmithyHttpServer" to ServerCargoDependency.smithyHttpServer(codegenContext.runtimeConfig).toType(), + "Hyper" to ServerCargoDependency.HyperDev.toType(), + "Tokio" to ServerCargoDependency.TokioDev.toType(), + "Tower" to ServerCargoDependency.Tower.toType(), + ) + } + + /** + * 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(rustWriter: RustWriter) { + documentation(rustWriter) + + rustWriter.rust("pub use crate::service::{$serviceName, ${serviceName}Builder, MissingOperationsError};") + } +} 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 cf405dc5e9..160f9fd685 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 @@ -6,293 +6,542 @@ package software.amazon.smithy.rust.codegen.server.smithy.generators import software.amazon.smithy.model.knowledge.TopDownIndex +import software.amazon.smithy.model.neighbor.Walker import software.amazon.smithy.model.shapes.OperationShape -import software.amazon.smithy.rust.codegen.core.rustlang.RustModule +import software.amazon.smithy.model.shapes.StringShape +import software.amazon.smithy.model.traits.PatternTrait 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.Visibility import software.amazon.smithy.rust.codegen.core.rustlang.Writable +import software.amazon.smithy.rust.codegen.core.rustlang.documentShape import software.amazon.smithy.rust.codegen.core.rustlang.join 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.ErrorsModule -import software.amazon.smithy.rust.codegen.core.smithy.InputsModule -import software.amazon.smithy.rust.codegen.core.smithy.OutputsModule -import software.amazon.smithy.rust.codegen.core.smithy.RustCrate -import software.amazon.smithy.rust.codegen.core.smithy.generators.protocol.ProtocolSupport +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.hasTrait +import software.amazon.smithy.rust.codegen.core.util.letIf +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 import software.amazon.smithy.rust.codegen.server.smithy.generators.protocol.ServerProtocol -import software.amazon.smithy.rust.codegen.server.smithy.generators.protocol.ServerProtocolGenerator -import software.amazon.smithy.rust.codegen.server.smithy.generators.protocol.ServerProtocolTestGenerator +import software.amazon.smithy.rust.codegen.server.smithy.ServerRustModule.Error as ErrorModule +import software.amazon.smithy.rust.codegen.server.smithy.ServerRustModule.Input as InputModule +import software.amazon.smithy.rust.codegen.server.smithy.ServerRustModule.Output as OutputModule -/** - * ServerServiceGenerator - * - * Service generator is the main code generation entry point for Smithy services. Individual structures and unions are - * generated in codegen visitor, but this class handles all protocol-specific code generation (i.e. operations). - */ -open class ServerServiceGenerator( - private val rustCrate: RustCrate, - private val protocolGenerator: ServerProtocolGenerator, - private val protocolSupport: ProtocolSupport, - val protocol: ServerProtocol, +class ServerServiceGenerator( private val codegenContext: ServerCodegenContext, + private val protocol: ServerProtocol, ) { + private val runtimeConfig = codegenContext.runtimeConfig + private val smithyHttpServer = ServerCargoDependency.smithyHttpServer(runtimeConfig).toType() + private val codegenScope = + arrayOf( + "Bytes" to RuntimeType.Bytes, + "Http" to RuntimeType.Http, + "SmithyHttp" to RuntimeType.smithyHttp(runtimeConfig), + "HttpBody" to RuntimeType.HttpBody, + "SmithyHttpServer" to smithyHttpServer, + "Tower" to RuntimeType.Tower, + ) + private val model = codegenContext.model + private val symbolProvider = codegenContext.symbolProvider + private val crateName = codegenContext.moduleUseName() + + private val service = codegenContext.serviceShape + private val serviceName = service.id.name.toPascalCase() + private val builderName = "${serviceName}Builder" + private val builderPluginGenericTypeName = "Plugin" + private val builderBodyGenericTypeName = "Body" + + /** Calculate all `operationShape`s contained within the `ServiceShape`. */ private val index = TopDownIndex.of(codegenContext.model) - protected val operations = index.getContainedOperations(codegenContext.serviceShape).sortedBy { it.id } - private val serviceName = codegenContext.serviceShape.id.name.toString() - - fun documentation(writer: RustWriter) { - val operations = index.getContainedOperations(codegenContext.serviceShape).toSortedSet(compareBy { it.id }) - val builderFieldNames = - operations.associateWith { RustReservedWords.escapeIfNeeded(codegenContext.symbolProvider.toSymbol(it).name.toSnakeCase()) } - .toSortedMap( - compareBy { it.id }, + private val operations = index.getContainedOperations(codegenContext.serviceShape).toSortedSet(compareBy { it.id }) + + /** Associate each operation with the corresponding field names in the builder struct. */ + private val builderFieldNames = + operations.associateWith { RustReservedWords.escapeIfNeeded(symbolProvider.toSymbol(it).name.toSnakeCase()) } + .toSortedMap( + compareBy { it.id }, + ) + + /** Associate each operation with the name of the corresponding Zero-Sized Type (ZST) struct name. */ + private val operationStructNames = operations.associateWith { symbolProvider.toSymbol(it).name.toPascalCase() } + + /** A `Writable` block of "field: Type" for the builder. */ + private val builderFields = + builderFieldNames.values.map { name -> "$name: Option<#{SmithyHttpServer}::routing::Route>" } + + /** The name of the local private module containing the functions that return the request for each operation */ + private val requestSpecsModuleName = "request_specs" + + /** Associate each operation with a function that returns its request spec. */ + private val requestSpecMap: Map> = + operations.associateWith { operationShape -> + val operationName = symbolProvider.toSymbol(operationShape).name + val spec = protocol.serverRouterRequestSpec( + operationShape, + operationName, + serviceName, + smithyHttpServer.resolve("routing::request_spec"), + ) + val functionName = RustReservedWords.escapeIfNeeded(operationName.toSnakeCase()) + val functionBody = writable { + rustTemplate( + """ + fn $functionName() -> #{SpecType} { + #{Spec:W} + } + """, + "Spec" to spec, + "SpecType" to protocol.serverRouterRequestSpecType(smithyHttpServer.resolve("routing::request_spec")), ) - val crateName = codegenContext.moduleUseName() - val builderName = "${serviceName}Builder" - val hasErrors = operations.any { it.errors.isNotEmpty() } - val handlers: Writable = operations - .map { operation -> - DocHandlerGenerator(codegenContext, operation, builderFieldNames[operation]!!, "//!")::render } - .join("//!\n") + Pair(functionName, functionBody) + } - writer.rustTemplate( + /** A `Writable` block containing all the `Handler` and `Operation` setters for the builder. */ + private fun builderSetters(): Writable = writable { + for ((operationShape, structName) in operationStructNames) { + val fieldName = builderFieldNames[operationShape] + rustTemplate( + """ + /// Sets the [`$structName`](crate::operation_shape::$structName) operation. + /// + /// 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} + /// + #{Handler:W} + /// + /// let app = $serviceName::builder_without_plugins() + /// .$fieldName(handler) + /// /* Set other handlers */ + /// .build() + /// .unwrap(); + /// ## let app: $serviceName<#{SmithyHttpServer}::routing::Route<#{SmithyHttp}::body::SdkBody>> = app; + /// ``` + /// + 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, + > + { + use #{SmithyHttpServer}::operation::OperationShapeExt; + self.${fieldName}_operation(crate::operation_shape::$structName::from_handler(handler)) + } + + /// 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 + where + Operation: #{SmithyHttpServer}::operation::Upgradable< + #{Protocol}, + crate::operation_shape::$structName, + Extractors, + $builderBodyGenericTypeName, + $builderPluginGenericTypeName, + > + { + self.$fieldName = Some(operation.upgrade(&self.plugin)); + self + } + """, + "Protocol" to protocol.markerStruct(), + "Handler" to DocHandlerGenerator(codegenContext, operationShape, "handler", "///")::render, + "HandlerImports" to handlerImports(crateName, operations), + *codegenScope, + ) + + // Adds newline between setters. + rust("") + } + } + + private fun buildMethod(): Writable = writable { + val missingOperationsVariableName = "missing_operation_names" + val expectMessageVariableName = "unexpected_error_msg" + + val nullabilityChecks = writable { + for (operationShape in operations) { + val fieldName = builderFieldNames[operationShape]!! + val operationZstTypeName = operationStructNames[operationShape]!! + rust( + """ + if self.$fieldName.is_none() { + $missingOperationsVariableName.insert(crate::operation_shape::$operationZstTypeName::NAME, ".$fieldName()"); + } + """, + ) + } + } + val routesArrayElements = writable { + for (operationShape in operations) { + val fieldName = builderFieldNames[operationShape]!! + val (specBuilderFunctionName, _) = requestSpecMap.getValue(operationShape) + rust( + """ + ($requestSpecsModuleName::$specBuilderFunctionName(), self.$fieldName.expect($expectMessageVariableName)), + """, + ) + } + } + + rustTemplate( """ - //! A fast and customizable Rust implementation of the $serviceName Smithy service. - //! - //! ## Using $serviceName - //! - //! The primary entrypoint is [`$serviceName`]: it satisfies the [`Service`](#{Tower}::Service) - //! trait and therefore can be handed to a [`hyper` server](https://github.com/hyperium/hyper) via [`$serviceName::into_make_service`] or used in Lambda via [`LambdaHandler`](#{SmithyHttpServer}::routing::LambdaHandler). - //! The [`crate::${InputsModule.name}`], ${if (!hasErrors) "and " else ""}[`crate::${OutputsModule.name}`], ${if (hasErrors) "and [`crate::${ErrorsModule.name}`]" else "" } - //! modules provide the types used in each operation. - //! - //! ###### Running on Hyper - //! - //! ```rust,no_run - //! ## use std::net::SocketAddr; - //! ## async fn dummy() { - //! use $crateName::$serviceName; - //! - //! ## let app = $serviceName::builder_without_plugins().build_unchecked(); - //! let server = app.into_make_service(); - //! let bind: SocketAddr = "127.0.0.1:6969".parse() - //! .expect("unable to parse the server bind address and port"); - //! #{Hyper}::Server::bind(&bind).serve(server).await.unwrap(); - //! ## } - //! ``` - //! - //! ###### Running on Lambda - //! - //! This requires the `aws-lambda` feature flag to be passed to the [`#{SmithyHttpServer}`] crate. - //! - //! ```rust,ignore - //! use #{SmithyHttpServer}::routing::LambdaHandler; - //! use $crateName::$serviceName; - //! - //! ## async fn dummy() { - //! ## let app = $serviceName::builder_without_plugins().build_unchecked(); - //! let handler = LambdaHandler::new(app); - //! lambda_http::run(handler).await.unwrap(); - //! ## } - //! ``` - //! - //! ## Building the $serviceName - //! - //! To construct [`$serviceName`] we use [`$builderName`] returned by [`$serviceName::builder_without_plugins`] - //! or [`$serviceName::builder_with_plugins`]. - //! - //! #### Plugins - //! - //! The [`$serviceName::builder_with_plugins`] method, returning [`$builderName`], - //! accepts a [`Plugin`](aws_smithy_http_server::plugin::Plugin). - //! Plugins allow you to build middleware which is aware of the operation it is being applied to. - //! - //! ```rust - //! ## use #{SmithyHttpServer}::plugin::IdentityPlugin as LoggingPlugin; - //! ## use #{SmithyHttpServer}::plugin::IdentityPlugin as MetricsPlugin; - //! ## use #{Hyper}::Body; - //! use #{SmithyHttpServer}::plugin::PluginPipeline; - //! use $crateName::{$serviceName, $builderName}; - //! - //! let plugins = PluginPipeline::new() - //! .push(LoggingPlugin) - //! .push(MetricsPlugin); - //! let builder: $builderName = $serviceName::builder_with_plugins(plugins); - //! ``` - //! - //! Check out [`#{SmithyHttpServer}::plugin`] to learn more about plugins. - //! - //! #### Handlers - //! - //! [`$builderName`] provides a setter method for each operation in your Smithy model. The setter methods expect an async function as input, matching the signature for the corresponding operation in your Smithy model. - //! We call these async functions **handlers**. This is where your application business logic lives. - //! - //! Every handler must take an `Input`, and optional [`extractor arguments`](#{SmithyHttpServer}::request), while returning: - //! - //! * A `Result` if your operation has modeled errors, or - //! * An `Output` otherwise. - //! - //! ```rust - //! ## struct Input; - //! ## struct Output; - //! ## struct Error; - //! async fn infallible_handler(input: Input) -> Output { todo!() } - //! - //! async fn fallible_handler(input: Input) -> Result { todo!() } - //! ``` - //! - //! Handlers can accept up to 8 extractors: - //! - //! ```rust - //! ## struct Input; - //! ## struct Output; - //! ## struct Error; - //! ## struct State; - //! ## use std::net::SocketAddr; - //! use #{SmithyHttpServer}::request::{extension::Extension, connect_info::ConnectInfo}; - //! - //! async fn handler_with_no_extensions(input: Input) -> Output { - //! todo!() - //! } - //! - //! async fn handler_with_one_extractor(input: Input, ext: Extension) -> Output { - //! todo!() - //! } - //! - //! async fn handler_with_two_extractors( - //! input: Input, - //! ext0: Extension, - //! ext1: ConnectInfo, - //! ) -> Output { - //! todo!() - //! } - //! ``` - //! - //! See the [`operation module`](#{SmithyHttpServer}::operation) for information on precisely what constitutes a handler. - //! - //! #### Build - //! - //! You can convert [`$builderName`] into [`$serviceName`] using either [`$builderName::build`] or [`$builderName::build_unchecked`]. - //! - //! [`$builderName::build`] requires you to provide a handler for every single operation in your Smithy model. It will return an error if that is not the case. - //! - //! [`$builderName::build_unchecked`], instead, does not require exhaustiveness. The server will automatically return 500 Internal Server Error to all requests for operations that do not have a registered handler. - //! [`$builderName::build_unchecked`] is particularly useful if you are deploying your Smithy service as a collection of Lambda functions, where each Lambda is only responsible for a subset of the operations in the Smithy service (or even a single one!). - //! - //! ## Example - //! - //! ```rust - //! ## use std::net::SocketAddr; - //! use $crateName::$serviceName; - //! - //! ##[#{Tokio}::main] - //! pub async fn main() { - //! let app = $serviceName::builder_without_plugins() - ${builderFieldNames.values.joinToString("\n") { "//! .$it($it)" }} - //! .build() - //! .expect("failed to build an instance of $serviceName"); - //! - //! let bind: SocketAddr = "127.0.0.1:6969".parse() - //! .expect("unable to parse the server bind address and port"); - //! let server = #{Hyper}::Server::bind(&bind).serve(app.into_make_service()); - //! ## let server = async { Ok::<_, ()>(()) }; - //! - //! // Run your service! - //! if let Err(err) = server.await { - //! eprintln!("server error: {:?}", err); - //! } - //! } - //! - #{HandlerImports:W} - //! - #{Handlers:W} - //! - //! ``` - //! - //! [`serve`]: https://docs.rs/hyper/0.14.16/hyper/server/struct.Builder.html##method.serve - //! [`tower::make::MakeService`]: https://docs.rs/tower/latest/tower/make/trait.MakeService.html - //! [HTTP binding traits]: https://smithy.io/2.0/spec/http-bindings.html - //! [operations]: https://smithy.io/2.0/spec/service-types.html##operation - //! [hyper server]: https://docs.rs/hyper/latest/hyper/server/index.html - //! [Service]: https://docs.rs/tower-service/latest/tower_service/trait.Service.html + /// Constructs a [`$serviceName`] from the arguments provided to the builder. + /// + /// Forgetting to register a handler for one or more operations will result in an error. + /// + /// Check out [`$builderName::build_unchecked`] if you'd prefer the service to return status code 500 when an + /// unspecified route requested. + pub fn build(self) -> Result<$serviceName<#{SmithyHttpServer}::routing::Route<$builderBodyGenericTypeName>>, MissingOperationsError> + { + let router = { + use #{SmithyHttpServer}::operation::OperationShape; + let mut $missingOperationsVariableName = std::collections::HashMap::new(); + #{NullabilityChecks:W} + if !$missingOperationsVariableName.is_empty() { + return Err(MissingOperationsError { + operation_names2setter_methods: $missingOperationsVariableName, + }); + } + let $expectMessageVariableName = "this should never panic since we are supposed to check beforehand that a handler has been registered for this operation; please file a bug report under https://github.com/awslabs/smithy-rs/issues"; + + #{PatternInitializations:W} + + #{Router}::from_iter([#{RoutesArrayElements:W}]) + }; + Ok($serviceName { + router: #{SmithyHttpServer}::routing::RoutingService::new(router), + }) + } """, - "HandlerImports" to handlerImports(crateName, operations, commentToken = "//!"), - "Handlers" to handlers, - "ExampleHandler" to operations.take(1).map { operation -> DocHandlerGenerator(codegenContext, operation, builderFieldNames[operation]!!, "//!").docSignature() }, - "SmithyHttpServer" to ServerCargoDependency.smithyHttpServer(codegenContext.runtimeConfig).toType(), - "Hyper" to ServerCargoDependency.HyperDev.toType(), - "Tokio" to ServerCargoDependency.TokioDev.toType(), - "Tower" to ServerCargoDependency.Tower.toType(), + "Router" to protocol.routerType(), + "NullabilityChecks" to nullabilityChecks, + "RoutesArrayElements" to routesArrayElements, + "SmithyHttpServer" to smithyHttpServer, + "PatternInitializations" to patternInitializations(), ) } /** - * Render Service Specific code. Code will end up in different files via [useShapeWriter]. See `SymbolVisitor.kt` - * which assigns a symbol location to each shape. + * Renders `PatternString::compile_regex()` function calls for every + * `@pattern`-constrained string shape in the service closure. */ - fun render() { - rustCrate.lib { - documentation(this) + @Suppress("DEPRECATION") + private fun patternInitializations(): Writable { + val patterns = Walker(model).walkShapes(service) + .filter { shape -> shape is StringShape && shape.hasTrait() && !shape.hasTrait() } + .map { shape -> codegenContext.constrainedShapeSymbolProvider.toSymbol(shape) } + .map { symbol -> + writable { + rustTemplate("#{Type}::compile_regex();", "Type" to symbol) + } + } - rust("pub use crate::service::{$serviceName, ${serviceName}Builder, MissingOperationsError};") - } + patterns.letIf(patterns.isNotEmpty()) { + val docs = listOf(writable { rust("// Eagerly initialize regexes for `@pattern` strings.") }) - rustCrate.withModule(RustModule.operation(Visibility.PRIVATE)) { - ServerProtocolTestGenerator(codegenContext, protocolSupport, protocolGenerator).render(this) + docs + patterns } - for (operation in operations) { - if (operation.errors.isNotEmpty()) { - rustCrate.withModule(RustModule.Error) { - renderCombinedErrors(this, operation) - } + return patterns.join("") + } + + private fun buildUncheckedMethod(): Writable = writable { + val pairs = writable { + 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)) + }) + ), + """, + "SmithyHttpServer" to smithyHttpServer, + "Protocol" to protocol.markerStruct(), + ) } } + rustTemplate( + """ + /// Constructs a [`$serviceName`] from the arguments provided to the builder. + /// Operations without a handler default to returning 500 Internal Server Error to the caller. + /// + /// Check out [`$builderName::build`] if you'd prefer the builder to fail if one or more operations do + /// not have a registered handler. + pub fn build_unchecked(self) -> $serviceName<#{SmithyHttpServer}::routing::Route<$builderBodyGenericTypeName>> + where + $builderBodyGenericTypeName: Send + 'static + { + let router = #{Router}::from_iter([#{Pairs:W}]); + $serviceName { + router: #{SmithyHttpServer}::routing::RoutingService::new(router), + } + } + """, + "Router" to protocol.routerType(), + "Pairs" to pairs, + "SmithyHttpServer" to smithyHttpServer, + ) + } + + /** Returns a `Writable` containing the builder struct definition and its implementations. */ + private fun builder(): Writable = writable { + val builderGenerics = listOf(builderBodyGenericTypeName, builderPluginGenericTypeName).joinToString(", ") + rustTemplate( + """ + /// The service builder for [`$serviceName`]. + /// + /// Constructed via [`$serviceName::builder_with_plugins`] or [`$serviceName::builder_without_plugins`]. + pub struct $builderName<$builderGenerics> { + ${builderFields.joinToString(", ")}, + plugin: $builderPluginGenericTypeName, + } + + impl<$builderGenerics> $builderName<$builderGenerics> { + #{Setters:W} + } + + impl<$builderGenerics> $builderName<$builderGenerics> { + #{BuildMethod:W} + + #{BuildUncheckedMethod:W} + } + """, + "Setters" to builderSetters(), + "BuildMethod" to buildMethod(), + "BuildUncheckedMethod" to buildUncheckedMethod(), + *codegenScope, + ) + } - rustCrate.withModule( - RustModule.public("operation_shape"), - ) { - ServerOperationShapeGenerator(operations, codegenContext).render(this) + private fun requestSpecsModule(): Writable = writable { + val functions = writable { + for ((_, function) in requestSpecMap.values) { + rustTemplate( + """ + pub(super) #{Function:W} + """, + "Function" to function, + ) + } } + rustTemplate( + """ + mod $requestSpecsModuleName { + #{SpecFunctions:W} + } + """, + "SpecFunctions" to functions, + ) + } - rustCrate.withModule( - RustModule.private("service"), - ) { - ServerServiceGeneratorV2( - codegenContext, - protocol, - ).render(this) + /** Returns a `Writable` comma delimited sequence of `builder_field: None`. */ + private val notSetFields = builderFieldNames.values.map { + writable { + rustTemplate( + "$it: None", + *codegenScope, + ) } + } - renderExtras(operations) + /** Returns a `Writable` containing the service struct definition and its implementations. */ + private fun serviceStruct(): Writable = writable { + documentShape(service, model) - rustCrate.withModule( - RustModule.public( - "server", - """ - Contains the types that are re-exported from the `aws-smithy-http-server` create. - """, - ), - ) { - renderServerReExports(this) - } + rustTemplate( + """ + /// + /// See the [root](crate) documentation for more information. + ##[derive(Clone)] + pub struct $serviceName { + router: #{SmithyHttpServer}::routing::RoutingService<#{Router}, #{Protocol}>, + } + + impl $serviceName<()> { + /// Constructs a builder for [`$serviceName`]. + /// You must specify what plugins should be applied to the operations in this service. + /// + /// Use [`$serviceName::builder_without_plugins`] if you don't need to apply plugins. + /// + /// Check out [`PluginPipeline`](#{SmithyHttpServer}::plugin::PluginPipeline) if you need to apply + /// multiple plugins. + pub fn builder_with_plugins(plugin: Plugin) -> $builderName { + $builderName { + #{NotSetFields:W}, + 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) + } + } + + impl $serviceName { + /// Converts [`$serviceName`] into a [`MakeService`](tower::make::MakeService). + pub fn into_make_service(self) -> #{SmithyHttpServer}::routing::IntoMakeService { + #{SmithyHttpServer}::routing::IntoMakeService::new(self) + } + + + /// Converts [`$serviceName`] into a [`MakeService`](tower::make::MakeService) with [`ConnectInfo`](#{SmithyHttpServer}::request::connect_info::ConnectInfo). + pub fn into_make_service_with_connect_info(self) -> #{SmithyHttpServer}::routing::IntoMakeServiceWithConnectInfo { + #{SmithyHttpServer}::routing::IntoMakeServiceWithConnectInfo::new(self) + } + + /// Applies a [`Layer`](#{Tower}::Layer) uniformly to all routes. + pub fn layer(self, layer: &L) -> $serviceName + where + L: #{Tower}::Layer + { + $serviceName { + router: self.router.map(|s| s.layer(layer)) + } + } + + /// Applies [`Route::new`](#{SmithyHttpServer}::routing::Route::new) to all routes. + /// + /// This has the effect of erasing all types accumulated via [`layer`]($serviceName::layer). + pub fn boxed(self) -> $serviceName<#{SmithyHttpServer}::routing::Route> + where + S: #{Tower}::Service< + #{Http}::Request, + Response = #{Http}::Response<#{SmithyHttpServer}::body::BoxBody>, + Error = std::convert::Infallible>, + S: Clone + Send + 'static, + S::Future: Send + 'static, + { + self.layer(&#{Tower}::layer::layer_fn(#{SmithyHttpServer}::routing::Route::new)) + } + } + + impl #{Tower}::Service<#{Http}::Request> for $serviceName + where + S: #{Tower}::Service<#{Http}::Request, Response = #{Http}::Response> + Clone, + RespB: #{HttpBody}::Body + Send + 'static, + RespB::Error: Into> + { + type Response = #{Http}::Response<#{SmithyHttpServer}::body::BoxBody>; + type Error = S::Error; + type Future = #{SmithyHttpServer}::routing::RoutingFuture; + + fn poll_ready(&mut self, cx: &mut std::task::Context) -> std::task::Poll> { + self.router.poll_ready(cx) + } + + fn call(&mut self, request: #{Http}::Request) -> Self::Future { + self.router.call(request) + } + } + """, + "NotSetFields" to notSetFields.join(", "), + "Router" to protocol.routerType(), + "Protocol" to protocol.markerStruct(), + *codegenScope, + ) + } + + private fun missingOperationsError(): Writable = writable { + rust( + """ + /// 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>, + } + + impl std::fmt::Display for MissingOperationsError { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!( + f, + "You must specify a handler for all operations attached to `$serviceName`.\n\ + We are missing handlers for the following operations:\n", + )?; + for operation_name in self.operation_names2setter_methods.keys() { + writeln!(f, "- {}", operation_name)?; + } + + writeln!(f, "\nUse the dedicated methods on `$builderName` to register the missing handlers:")?; + for setter_name in self.operation_names2setter_methods.values() { + writeln!(f, "- {}", setter_name)?; + } + Ok(()) + } + } + + impl std::error::Error for MissingOperationsError {} + """, + ) } - // Render any extra section needed by subclasses of `ServerServiceGenerator`. - open fun renderExtras(operations: List) { } + fun render(writer: RustWriter) { + writer.rustTemplate( + """ + #{Builder:W} + + #{MissingOperationsError:W} - // Render combined errors. - open fun renderCombinedErrors(writer: RustWriter, operation: OperationShape) { - /* Subclasses can override */ + #{RequestSpecs:W} + + #{Struct:W} + """, + "Builder" to builder(), + "MissingOperationsError" to missingOperationsError(), + "RequestSpecs" to requestSpecsModule(), + "Struct" to serviceStruct(), + *codegenScope, + ) } +} - // Render `server` crate, re-exporting types. - private fun renderServerReExports(writer: RustWriter) { - ServerRuntimeTypesReExportsGenerator(codegenContext).render(writer) +/** + * Returns a writable to import the necessary modules used by a handler implementation stub. + * + * ```rust + * use my_service::{input, output, error}; + * ``` + */ +fun handlerImports(crateName: String, operations: Collection, commentToken: String = "///") = writable { + val hasErrors = operations.any { it.errors.isNotEmpty() } + val errorImport = if (hasErrors) ", ${ErrorModule.name}" else "" + if (operations.isNotEmpty()) { + rust("$commentToken use $crateName::{${InputModule.name}, ${OutputModule.name}$errorImport};") } } diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerServiceGeneratorV2.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerServiceGeneratorV2.kt deleted file mode 100644 index e8483f4a20..0000000000 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerServiceGeneratorV2.kt +++ /dev/null @@ -1,547 +0,0 @@ -/* - * 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.model.neighbor.Walker -import software.amazon.smithy.model.shapes.OperationShape -import software.amazon.smithy.model.shapes.StringShape -import software.amazon.smithy.model.traits.PatternTrait -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.documentShape -import software.amazon.smithy.rust.codegen.core.rustlang.join -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.ErrorsModule -import software.amazon.smithy.rust.codegen.core.smithy.InputsModule -import software.amazon.smithy.rust.codegen.core.smithy.OutputsModule -import software.amazon.smithy.rust.codegen.core.smithy.RuntimeType -import software.amazon.smithy.rust.codegen.core.util.hasTrait -import software.amazon.smithy.rust.codegen.core.util.letIf -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 -import software.amazon.smithy.rust.codegen.server.smithy.generators.protocol.ServerProtocol - -class ServerServiceGeneratorV2( - private val codegenContext: ServerCodegenContext, - private val protocol: ServerProtocol, -) { - private val runtimeConfig = codegenContext.runtimeConfig - private val smithyHttpServer = ServerCargoDependency.smithyHttpServer(runtimeConfig).toType() - private val codegenScope = - arrayOf( - "Bytes" to RuntimeType.Bytes, - "Http" to RuntimeType.Http, - "SmithyHttp" to RuntimeType.smithyHttp(runtimeConfig), - "HttpBody" to RuntimeType.HttpBody, - "SmithyHttpServer" to smithyHttpServer, - "Tower" to RuntimeType.Tower, - ) - private val model = codegenContext.model - private val symbolProvider = codegenContext.symbolProvider - private val crateName = codegenContext.moduleUseName() - - private val service = codegenContext.serviceShape - private val serviceName = service.id.name.toPascalCase() - private val builderName = "${serviceName}Builder" - private val builderPluginGenericTypeName = "Plugin" - private val builderBodyGenericTypeName = "Body" - - /** 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 }) - - /** Associate each operation with the corresponding field names in the builder struct. */ - private val builderFieldNames = - operations.associateWith { RustReservedWords.escapeIfNeeded(symbolProvider.toSymbol(it).name.toSnakeCase()) } - .toSortedMap( - compareBy { it.id }, - ) - - /** Associate each operation with the name of the corresponding Zero-Sized Type (ZST) struct name. */ - private val operationStructNames = operations.associateWith { symbolProvider.toSymbol(it).name.toPascalCase() } - - /** A `Writable` block of "field: Type" for the builder. */ - private val builderFields = - builderFieldNames.values.map { name -> "$name: Option<#{SmithyHttpServer}::routing::Route>" } - - /** The name of the local private module containing the functions that return the request for each operation */ - private val requestSpecsModuleName = "request_specs" - - /** Associate each operation with a function that returns its request spec. */ - private val requestSpecMap: Map> = - operations.associateWith { operationShape -> - val operationName = symbolProvider.toSymbol(operationShape).name - val spec = protocol.serverRouterRequestSpec( - operationShape, - operationName, - serviceName, - smithyHttpServer.resolve("routing::request_spec"), - ) - val functionName = RustReservedWords.escapeIfNeeded(operationName.toSnakeCase()) - val functionBody = writable { - rustTemplate( - """ - fn $functionName() -> #{SpecType} { - #{Spec:W} - } - """, - "Spec" to spec, - "SpecType" to protocol.serverRouterRequestSpecType(smithyHttpServer.resolve("routing::request_spec")), - ) - } - Pair(functionName, functionBody) - } - - /** A `Writable` block containing all the `Handler` and `Operation` setters for the builder. */ - private fun builderSetters(): Writable = writable { - for ((operationShape, structName) in operationStructNames) { - val fieldName = builderFieldNames[operationShape] - rustTemplate( - """ - /// Sets the [`$structName`](crate::operation_shape::$structName) operation. - /// - /// 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} - /// - #{Handler:W} - /// - /// let app = $serviceName::builder_without_plugins() - /// .$fieldName(handler) - /// /* Set other handlers */ - /// .build() - /// .unwrap(); - /// ## let app: $serviceName<#{SmithyHttpServer}::routing::Route<#{SmithyHttp}::body::SdkBody>> = app; - /// ``` - /// - 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, - Extensions, - $builderBodyGenericTypeName, - $builderPluginGenericTypeName, - > - { - use #{SmithyHttpServer}::operation::OperationShapeExt; - self.${fieldName}_operation(crate::operation_shape::$structName::from_handler(handler)) - } - - /// 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 - where - Operation: #{SmithyHttpServer}::operation::Upgradable< - #{Protocol}, - crate::operation_shape::$structName, - Extensions, - $builderBodyGenericTypeName, - $builderPluginGenericTypeName, - > - { - self.$fieldName = Some(operation.upgrade(&self.plugin)); - self - } - """, - "Protocol" to protocol.markerStruct(), - "Handler" to DocHandlerGenerator(codegenContext, operationShape, "handler", "///")::render, - "HandlerImports" to handlerImports(crateName, operations), - *codegenScope, - ) - - // Adds newline between setters. - rust("") - } - } - - private fun buildMethod(): Writable = writable { - val missingOperationsVariableName = "missing_operation_names" - val expectMessageVariableName = "unexpected_error_msg" - - val nullabilityChecks = writable { - for (operationShape in operations) { - val fieldName = builderFieldNames[operationShape]!! - val operationZstTypeName = operationStructNames[operationShape]!! - rust( - """ - if self.$fieldName.is_none() { - $missingOperationsVariableName.insert(crate::operation_shape::$operationZstTypeName::NAME, ".$fieldName()"); - } - """, - ) - } - } - val routesArrayElements = writable { - for (operationShape in operations) { - val fieldName = builderFieldNames[operationShape]!! - val (specBuilderFunctionName, _) = requestSpecMap.getValue(operationShape) - rust( - """ - ($requestSpecsModuleName::$specBuilderFunctionName(), self.$fieldName.expect($expectMessageVariableName)), - """, - ) - } - } - - rustTemplate( - """ - /// Constructs a [`$serviceName`] from the arguments provided to the builder. - /// - /// Forgetting to register a handler for one or more operations will result in an error. - /// - /// Check out [`$builderName::build_unchecked`] if you'd prefer the service to return status code 500 when an - /// unspecified route requested. - pub fn build(self) -> Result<$serviceName<#{SmithyHttpServer}::routing::Route<$builderBodyGenericTypeName>>, MissingOperationsError> - { - let router = { - use #{SmithyHttpServer}::operation::OperationShape; - let mut $missingOperationsVariableName = std::collections::HashMap::new(); - #{NullabilityChecks:W} - if !$missingOperationsVariableName.is_empty() { - return Err(MissingOperationsError { - operation_names2setter_methods: $missingOperationsVariableName, - }); - } - let $expectMessageVariableName = "this should never panic since we are supposed to check beforehand that a handler has been registered for this operation; please file a bug report under https://github.com/awslabs/smithy-rs/issues"; - - #{PatternInitializations:W} - - #{Router}::from_iter([#{RoutesArrayElements:W}]) - }; - Ok($serviceName { - router: #{SmithyHttpServer}::routing::RoutingService::new(router), - }) - } - """, - "Router" to protocol.routerType(), - "NullabilityChecks" to nullabilityChecks, - "RoutesArrayElements" to routesArrayElements, - "SmithyHttpServer" to smithyHttpServer, - "PatternInitializations" to patternInitializations(), - ) - } - - /** - * Renders `PatternString::compile_regex()` function calls for every - * `@pattern`-constrained string shape in the service closure. - */ - @Suppress("DEPRECATION") - private fun patternInitializations(): Writable { - val patterns = Walker(model).walkShapes(service) - .filter { shape -> shape is StringShape && shape.hasTrait() && !shape.hasTrait() } - .map { shape -> codegenContext.constrainedShapeSymbolProvider.toSymbol(shape) } - .map { symbol -> - writable { - rustTemplate("#{Type}::compile_regex();", "Type" to symbol) - } - } - - patterns.letIf(patterns.isNotEmpty()) { - val docs = listOf(writable { rust("// Eagerly initialize regexes for `@pattern` strings.") }) - - docs + patterns - } - - return patterns.join("") - } - - private fun buildUncheckedMethod(): Writable = writable { - val pairs = writable { - 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)) - }) - ), - """, - "SmithyHttpServer" to smithyHttpServer, - "Protocol" to protocol.markerStruct(), - ) - } - } - rustTemplate( - """ - /// Constructs a [`$serviceName`] from the arguments provided to the builder. - /// Operations without a handler default to returning 500 Internal Server Error to the caller. - /// - /// Check out [`$builderName::build`] if you'd prefer the builder to fail if one or more operations do - /// not have a registered handler. - pub fn build_unchecked(self) -> $serviceName<#{SmithyHttpServer}::routing::Route<$builderBodyGenericTypeName>> - where - $builderBodyGenericTypeName: Send + 'static - { - let router = #{Router}::from_iter([#{Pairs:W}]); - $serviceName { - router: #{SmithyHttpServer}::routing::RoutingService::new(router), - } - } - """, - "Router" to protocol.routerType(), - "Pairs" to pairs, - "SmithyHttpServer" to smithyHttpServer, - ) - } - - /** Returns a `Writable` containing the builder struct definition and its implementations. */ - private fun builder(): Writable = writable { - val builderGenerics = listOf(builderBodyGenericTypeName, builderPluginGenericTypeName).joinToString(", ") - rustTemplate( - """ - /// The service builder for [`$serviceName`]. - /// - /// Constructed via [`$serviceName::builder_with_plugins`] or [`$serviceName::builder_without_plugins`]. - pub struct $builderName<$builderGenerics> { - ${builderFields.joinToString(", ")}, - plugin: $builderPluginGenericTypeName, - } - - impl<$builderGenerics> $builderName<$builderGenerics> { - #{Setters:W} - } - - impl<$builderGenerics> $builderName<$builderGenerics> { - #{BuildMethod:W} - - #{BuildUncheckedMethod:W} - } - """, - "Setters" to builderSetters(), - "BuildMethod" to buildMethod(), - "BuildUncheckedMethod" to buildUncheckedMethod(), - *codegenScope, - ) - } - - private fun requestSpecsModule(): Writable = writable { - val functions = writable { - for ((_, function) in requestSpecMap.values) { - rustTemplate( - """ - pub(super) #{Function:W} - """, - "Function" to function, - ) - } - } - rustTemplate( - """ - mod $requestSpecsModuleName { - #{SpecFunctions:W} - } - """, - "SpecFunctions" to functions, - ) - } - - /** Returns a `Writable` comma delimited sequence of `builder_field: None`. */ - private val notSetFields = builderFieldNames.values.map { - writable { - rustTemplate( - "$it: None", - *codegenScope, - ) - } - } - - /** Returns a `Writable` containing the service struct definition and its implementations. */ - private fun serviceStruct(): Writable = writable { - documentShape(service, model) - - rustTemplate( - """ - /// - /// See the [root](crate) documentation for more information. - ##[derive(Clone)] - pub struct $serviceName { - router: #{SmithyHttpServer}::routing::RoutingService<#{Router}, #{Protocol}>, - } - - impl $serviceName<()> { - /// Constructs a builder for [`$serviceName`]. - /// You must specify what plugins should be applied to the operations in this service. - /// - /// Use [`$serviceName::builder_without_plugins`] if you don't need to apply plugins. - /// - /// Check out [`PluginPipeline`](#{SmithyHttpServer}::plugin::PluginPipeline) if you need to apply - /// multiple plugins. - pub fn builder_with_plugins(plugin: Plugin) -> $builderName { - $builderName { - #{NotSetFields:W}, - 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) - } - } - - impl $serviceName { - /// Converts [`$serviceName`] into a [`MakeService`](tower::make::MakeService). - pub fn into_make_service(self) -> #{SmithyHttpServer}::routing::IntoMakeService { - #{SmithyHttpServer}::routing::IntoMakeService::new(self) - } - - - /// Converts [`$serviceName`] into a [`MakeService`](tower::make::MakeService) with [`ConnectInfo`](#{SmithyHttpServer}::request::connect_info::ConnectInfo). - pub fn into_make_service_with_connect_info(self) -> #{SmithyHttpServer}::routing::IntoMakeServiceWithConnectInfo { - #{SmithyHttpServer}::routing::IntoMakeServiceWithConnectInfo::new(self) - } - - /// Applies a [`Layer`](#{Tower}::Layer) uniformly to all routes. - pub fn layer(self, layer: &L) -> $serviceName - where - L: #{Tower}::Layer - { - $serviceName { - router: self.router.map(|s| s.layer(layer)) - } - } - - /// Applies [`Route::new`](#{SmithyHttpServer}::routing::Route::new) to all routes. - /// - /// This has the effect of erasing all types accumulated via [`layer`]($serviceName::layer). - pub fn boxed(self) -> $serviceName<#{SmithyHttpServer}::routing::Route> - where - S: #{Tower}::Service< - #{Http}::Request, - Response = #{Http}::Response<#{SmithyHttpServer}::body::BoxBody>, - Error = std::convert::Infallible>, - S: Clone + Send + 'static, - S::Future: Send + 'static, - { - self.layer(&#{Tower}::layer::layer_fn(#{SmithyHttpServer}::routing::Route::new)) - } - } - - impl #{Tower}::Service<#{Http}::Request> for $serviceName - where - S: #{Tower}::Service<#{Http}::Request, Response = #{Http}::Response> + Clone, - RespB: #{HttpBody}::Body + Send + 'static, - RespB::Error: Into> - { - type Response = #{Http}::Response<#{SmithyHttpServer}::body::BoxBody>; - type Error = S::Error; - type Future = #{SmithyHttpServer}::routing::RoutingFuture; - - fn poll_ready(&mut self, cx: &mut std::task::Context) -> std::task::Poll> { - self.router.poll_ready(cx) - } - - fn call(&mut self, request: #{Http}::Request) -> Self::Future { - self.router.call(request) - } - } - """, - "NotSetFields" to notSetFields.join(", "), - "Router" to protocol.routerType(), - "Protocol" to protocol.markerStruct(), - *codegenScope, - ) - } - - private fun missingOperationsError(): Writable = writable { - rust( - """ - /// 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>, - } - - impl std::fmt::Display for MissingOperationsError { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - write!( - f, - "You must specify a handler for all operations attached to `$serviceName`.\n\ - We are missing handlers for the following operations:\n", - )?; - for operation_name in self.operation_names2setter_methods.keys() { - writeln!(f, "- {}", operation_name)?; - } - - writeln!(f, "\nUse the dedicated methods on `$builderName` to register the missing handlers:")?; - for setter_name in self.operation_names2setter_methods.values() { - writeln!(f, "- {}", setter_name)?; - } - Ok(()) - } - } - - impl std::error::Error for MissingOperationsError {} - """, - ) - } - - fun render(writer: RustWriter) { - writer.rustTemplate( - """ - #{Builder:W} - - #{MissingOperationsError:W} - - #{RequestSpecs:W} - - #{Struct:W} - """, - "Builder" to builder(), - "MissingOperationsError" to missingOperationsError(), - "RequestSpecs" to requestSpecsModule(), - "Struct" to serviceStruct(), - *codegenScope, - ) - } -} - -/** - * Returns a writable to import the necessary modules used by a handler implementation stub. - * - * ```rust - * use my_service::{input, output, error}; - * ``` - */ -fun handlerImports(crateName: String, operations: Collection, commentToken: String = "///") = writable { - val hasErrors = operations.any { it.errors.isNotEmpty() } - val errorImport = if (hasErrors) ", ${ErrorsModule.name}" else "" - if (operations.isNotEmpty()) { - rust("$commentToken use $crateName::{${InputsModule.name}, ${OutputsModule.name}$errorImport};") - } -} diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/UnconstrainedCollectionGenerator.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/UnconstrainedCollectionGenerator.kt index b5a9d45895..6916617b6f 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/UnconstrainedCollectionGenerator.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/UnconstrainedCollectionGenerator.kt @@ -18,12 +18,14 @@ 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.isOptional import software.amazon.smithy.rust.codegen.core.smithy.makeMaybeConstrained -import software.amazon.smithy.rust.codegen.core.smithy.module +import software.amazon.smithy.rust.codegen.core.util.hasTrait +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.UnconstrainedShapeSymbolProvider import software.amazon.smithy.rust.codegen.server.smithy.canReachConstrainedShape import software.amazon.smithy.rust.codegen.server.smithy.isDirectlyConstrained +import software.amazon.smithy.rust.codegen.server.smithy.traits.ConstraintViolationRustBoxTrait /** * Generates a Rust type for a constrained collection shape that is able to hold values for the corresponding @@ -38,7 +40,7 @@ import software.amazon.smithy.rust.codegen.server.smithy.isDirectlyConstrained */ class UnconstrainedCollectionGenerator( val codegenContext: ServerCodegenContext, - private val unconstrainedModuleWriter: RustWriter, + private val inlineModuleCreator: InlineModuleCreator, val shape: CollectionShape, ) { private val model = codegenContext.model @@ -70,7 +72,7 @@ class UnconstrainedCollectionGenerator( val innerMemberSymbol = unconstrainedShapeSymbolProvider.toSymbol(shape.member) - unconstrainedModuleWriter.withInlineModule(symbol.module()) { + inlineModuleCreator(symbol) { rustTemplate( """ ##[derive(Debug, Clone)] @@ -107,7 +109,11 @@ class UnconstrainedCollectionGenerator( constrainedShapeSymbolProvider.toSymbol(shape.member) } val innerConstraintViolationSymbol = constraintViolationSymbolProvider.toSymbol(innerShape) - + val boxErr = if (shape.member.hasTrait()) { + ".map_err(|(idx, inner_violation)| (idx, Box::new(inner_violation)))" + } else { + "" + } val constrainValueWritable = writable { conditionalBlock("inner.map(|inner| ", ").transpose()", constrainedMemberSymbol.isOptional()) { rust("inner.try_into().map_err(|inner_violation| (idx, inner_violation))") @@ -124,7 +130,9 @@ class UnconstrainedCollectionGenerator( #{ConstrainValueWritable:W} }) .collect(); - let inner = res.map_err(|(idx, inner_violation)| Self::Error::Member(idx, inner_violation))?; + let inner = res + $boxErr + .map_err(|(idx, inner_violation)| Self::Error::Member(idx, inner_violation))?; """, "Vec" to RuntimeType.Vec, "ConstrainedMemberSymbol" to constrainedMemberSymbol, diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/UnconstrainedMapGenerator.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/UnconstrainedMapGenerator.kt index e18d372c75..0862a26987 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/UnconstrainedMapGenerator.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/UnconstrainedMapGenerator.kt @@ -19,11 +19,13 @@ 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.isOptional import software.amazon.smithy.rust.codegen.core.smithy.makeMaybeConstrained -import software.amazon.smithy.rust.codegen.core.smithy.module +import software.amazon.smithy.rust.codegen.core.util.hasTrait +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.canReachConstrainedShape import software.amazon.smithy.rust.codegen.server.smithy.isDirectlyConstrained +import software.amazon.smithy.rust.codegen.server.smithy.traits.ConstraintViolationRustBoxTrait /** * Generates a Rust type for a constrained map shape that is able to hold values for the corresponding @@ -38,7 +40,7 @@ import software.amazon.smithy.rust.codegen.server.smithy.isDirectlyConstrained */ class UnconstrainedMapGenerator( val codegenContext: ServerCodegenContext, - private val unconstrainedModuleWriter: RustWriter, + private val inlineModuleCreator: InlineModuleCreator, val shape: MapShape, ) { private val model = codegenContext.model @@ -72,7 +74,7 @@ class UnconstrainedMapGenerator( val keySymbol = unconstrainedShapeSymbolProvider.toSymbol(keyShape) val valueMemberSymbol = unconstrainedShapeSymbolProvider.toSymbol(shape.value) - unconstrainedModuleWriter.withInlineModule(symbol.module()) { + inlineModuleCreator(symbol) { rustTemplate( """ ##[derive(Debug, Clone)] @@ -125,6 +127,11 @@ class UnconstrainedMapGenerator( ) } val constrainValueWritable = writable { + val boxErr = if (shape.value.hasTrait()) { + ".map_err(Box::new)" + } else { + "" + } if (constrainedMemberValueSymbol.isOptional()) { // The map is `@sparse`. rustBlock("match v") { @@ -133,7 +140,7 @@ class UnconstrainedMapGenerator( // DRYing this up with the else branch below would make this less understandable. rustTemplate( """ - match #{ConstrainedValueSymbol}::try_from(v) { + match #{ConstrainedValueSymbol}::try_from(v)$boxErr { Ok(v) => Ok((k, Some(v))), Err(inner_constraint_violation) => Err(Self::Error::Value(k, inner_constraint_violation)), } @@ -145,7 +152,7 @@ class UnconstrainedMapGenerator( } else { rustTemplate( """ - match #{ConstrainedValueSymbol}::try_from(v) { + match #{ConstrainedValueSymbol}::try_from(v)$boxErr { Ok(v) => #{Epilogue:W}, Err(inner_constraint_violation) => Err(Self::Error::Value(k, inner_constraint_violation)), } @@ -214,9 +221,10 @@ class UnconstrainedMapGenerator( // ``` rustTemplate( """ - let hm: std::collections::HashMap<#{KeySymbol}, #{ValueSymbol}> = + let hm: #{HashMap}<#{KeySymbol}, #{ValueSymbol}> = hm.into_iter().map(|(k, v)| (k, v.into())).collect(); """, + "HashMap" to RuntimeType.HashMap, "KeySymbol" to symbolProvider.toSymbol(keyShape), "ValueSymbol" to symbolProvider.toSymbol(valueShape), ) diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/UnconstrainedUnionGenerator.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/UnconstrainedUnionGenerator.kt index 72655675a0..d4f6cd4860 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/UnconstrainedUnionGenerator.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/UnconstrainedUnionGenerator.kt @@ -23,16 +23,17 @@ 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.makeMaybeConstrained import software.amazon.smithy.rust.codegen.core.smithy.makeRustBoxed -import software.amazon.smithy.rust.codegen.core.smithy.module import software.amazon.smithy.rust.codegen.core.smithy.traits.RustBoxTrait import software.amazon.smithy.rust.codegen.core.util.hasTrait import software.amazon.smithy.rust.codegen.core.util.letIf import software.amazon.smithy.rust.codegen.core.util.toPascalCase +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.canReachConstrainedShape import software.amazon.smithy.rust.codegen.server.smithy.isDirectlyConstrained import software.amazon.smithy.rust.codegen.server.smithy.targetCanReachConstrainedShape +import software.amazon.smithy.rust.codegen.server.smithy.traits.ConstraintViolationRustBoxTrait import software.amazon.smithy.rust.codegen.server.smithy.traits.isReachableFromOperationInput /** @@ -48,7 +49,7 @@ import software.amazon.smithy.rust.codegen.server.smithy.traits.isReachableFromO */ class UnconstrainedUnionGenerator( val codegenContext: ServerCodegenContext, - private val unconstrainedModuleWriter: RustWriter, + private val inlineModuleCreator: InlineModuleCreator, private val modelsModuleWriter: RustWriter, val shape: UnionShape, ) { @@ -76,7 +77,7 @@ class UnconstrainedUnionGenerator( val constraintViolationSymbol = constraintViolationSymbolProvider.toSymbol(shape) val constraintViolationName = constraintViolationSymbol.name - unconstrainedModuleWriter.withInlineModule(symbol.module()) { + inlineModuleCreator(symbol) { rustBlock( """ ##[allow(clippy::enum_variant_names)] @@ -132,11 +133,16 @@ class UnconstrainedUnionGenerator( } else { Visibility.PUBCRATE } - modelsModuleWriter.withInlineModule( - constraintViolationSymbol.module(), + + inlineModuleCreator( + constraintViolationSymbol, ) { Attribute(derive(RuntimeType.Debug, RuntimeType.PartialEq)).render(this) - rustBlock("pub${if (constraintViolationVisibility == Visibility.PUBCRATE) " (crate)" else ""} enum $constraintViolationName") { + rustBlock( + """ + ##[allow(clippy::enum_variant_names)] + pub${if (constraintViolationVisibility == Visibility.PUBCRATE) " (crate)" else ""} enum $constraintViolationName""", + ) { constraintViolations().forEach { renderConstraintViolation(this, it) } } @@ -171,8 +177,8 @@ class UnconstrainedUnionGenerator( val constraintViolationSymbol = constraintViolationSymbolProvider.toSymbol(targetShape) - // If the corresponding union's member is boxed, box this constraint violation symbol too. - .letIf(constraintViolation.forMember.hasTrait()) { + // Box this constraint violation symbol if necessary. + .letIf(constraintViolation.forMember.hasTrait()) { it.makeRustBoxed() } @@ -201,10 +207,15 @@ class UnconstrainedUnionGenerator( (!publicConstrainedTypes || !targetShape.isDirectlyConstrained(symbolProvider)) val (unconstrainedVar, boxIt) = if (member.hasTrait()) { - "(*unconstrained)" to ".map(Box::new).map_err(Box::new)" + "(*unconstrained)" to ".map(Box::new)" } else { "unconstrained" to "" } + val boxErr = if (member.hasTrait()) { + ".map_err(Box::new)" + } else { + "" + } if (resolveToNonPublicConstrainedType) { val constrainedSymbol = @@ -217,8 +228,7 @@ class UnconstrainedUnionGenerator( """ { let constrained: #{ConstrainedSymbol} = $unconstrainedVar - .try_into() - $boxIt + .try_into()$boxIt$boxErr .map_err(Self::Error::${ConstraintViolation(member).name()})?; constrained.into() } @@ -231,6 +241,7 @@ class UnconstrainedUnionGenerator( $unconstrainedVar .try_into() $boxIt + $boxErr .map_err(Self::Error::${ConstraintViolation(member).name()})? """, ) diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ValidationExceptionConversionGenerator.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ValidationExceptionConversionGenerator.kt new file mode 100644 index 0000000000..7db4426586 --- /dev/null +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ValidationExceptionConversionGenerator.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.smithy.generators + +import software.amazon.smithy.model.Model +import software.amazon.smithy.model.shapes.MapShape +import software.amazon.smithy.model.shapes.Shape +import software.amazon.smithy.model.shapes.ShapeId +import software.amazon.smithy.model.shapes.StringShape +import software.amazon.smithy.model.traits.EnumTrait +import software.amazon.smithy.rust.codegen.core.rustlang.Writable +import software.amazon.smithy.rust.codegen.core.smithy.RustSymbolProvider + +/** + * Collection of methods that will be invoked by the respective generators to generate code to convert constraint + * violations to validation exceptions. + * This is only rendered for shapes that lie in a constrained operation's closure. + */ +interface ValidationExceptionConversionGenerator { + val shapeId: ShapeId + + /** + * Convert from a top-level operation input's constraint violation into + * `aws_smithy_http_server::rejection::RequestRejection`. + */ + fun renderImplFromConstraintViolationForRequestRejection(): Writable + + // Simple shapes. + fun stringShapeConstraintViolationImplBlock(stringConstraintsInfo: Collection): Writable + fun enumShapeConstraintViolationImplBlock(enumTrait: EnumTrait): Writable + fun numberShapeConstraintViolationImplBlock(rangeInfo: Range): Writable + fun blobShapeConstraintViolationImplBlock(blobConstraintsInfo: Collection): Writable + + // Aggregate shapes. + fun mapShapeConstraintViolationImplBlock( + shape: MapShape, + keyShape: StringShape, + valueShape: Shape, + symbolProvider: RustSymbolProvider, + model: Model, + ): Writable + fun builderConstraintViolationImplBlock(constraintViolations: Collection): Writable + fun collectionShapeConstraintViolationImplBlock( + collectionConstraintsInfo: Collection, + isMemberConstrained: Boolean, + ): Writable +} 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 b01c2f633e..41201b8695 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 @@ -7,7 +7,6 @@ package software.amazon.smithy.rust.codegen.server.smithy.generators.http import software.amazon.smithy.codegen.core.Symbol import software.amazon.smithy.model.shapes.OperationShape -import software.amazon.smithy.model.shapes.StructureShape 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 @@ -23,25 +22,19 @@ 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.Protocol import software.amazon.smithy.rust.codegen.server.smithy.ServerCodegenContext -import software.amazon.smithy.rust.codegen.server.smithy.generators.serverBuilderSymbol import software.amazon.smithy.rust.codegen.server.smithy.targetCanReachConstrainedShape class ServerRequestBindingGenerator( protocol: Protocol, - private val codegenContext: ServerCodegenContext, + codegenContext: ServerCodegenContext, operationShape: OperationShape, ) { - private fun serverBuilderSymbol(shape: StructureShape): Symbol = shape.serverBuilderSymbol( - codegenContext.symbolProvider, - !codegenContext.settings.codegenConfig.publicConstrainedTypes, - ) private val httpBindingGenerator = HttpBindingGenerator( protocol, codegenContext, codegenContext.unconstrainedShapeSymbolProvider, operationShape, - ::serverBuilderSymbol, listOf( ServerRequestAfterDeserializingIntoAHashMapOfHttpPrefixHeadersWrapInUnconstrainedMapHttpBindingCustomization( codegenContext, @@ -54,11 +47,11 @@ class ServerRequestBindingGenerator( fun generateDeserializePayloadFn( binding: HttpBindingDescriptor, - errorT: RuntimeType, + errorSymbol: Symbol, structuredHandler: RustWriter.(String) -> Unit, ): RuntimeType = httpBindingGenerator.generateDeserializePayloadFn( binding, - errorT, + errorSymbol, structuredHandler, HttpMessageType.REQUEST, ) @@ -82,7 +75,9 @@ class ServerRequestAfterDeserializingIntoAHashMapOfHttpPrefixHeadersWrapInUncons if (section.memberShape.targetCanReachConstrainedShape(codegenContext.model, codegenContext.unconstrainedShapeSymbolProvider)) { rust( "let out = out.map(#T);", - codegenContext.unconstrainedShapeSymbolProvider.toSymbol(section.memberShape).mapRustType { it.stripOuter() }, + codegenContext.unconstrainedShapeSymbolProvider.toSymbol(section.memberShape).mapRustType { + it.stripOuter() + }, ) } } 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 e30d9cc633..cc47830054 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 @@ -5,7 +5,6 @@ package software.amazon.smithy.rust.codegen.server.smithy.generators.http -import software.amazon.smithy.codegen.core.Symbol import software.amazon.smithy.model.shapes.ByteShape import software.amazon.smithy.model.shapes.CollectionShape import software.amazon.smithy.model.shapes.IntegerShape @@ -13,7 +12,6 @@ import software.amazon.smithy.model.shapes.LongShape import software.amazon.smithy.model.shapes.OperationShape import software.amazon.smithy.model.shapes.Shape import software.amazon.smithy.model.shapes.ShortShape -import software.amazon.smithy.model.shapes.StructureShape 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 @@ -25,7 +23,6 @@ import software.amazon.smithy.rust.codegen.core.smithy.generators.http.HttpMessa import software.amazon.smithy.rust.codegen.core.smithy.protocols.Protocol import software.amazon.smithy.rust.codegen.core.smithy.protocols.serialize.ValueExpression import software.amazon.smithy.rust.codegen.server.smithy.ServerCodegenContext -import software.amazon.smithy.rust.codegen.server.smithy.generators.serverBuilderSymbol import software.amazon.smithy.rust.codegen.server.smithy.workingWithPublicConstrainedWrapperTupleType class ServerResponseBindingGenerator( @@ -33,15 +30,12 @@ class ServerResponseBindingGenerator( private val codegenContext: ServerCodegenContext, operationShape: OperationShape, ) { - private fun builderSymbol(shape: StructureShape): Symbol = shape.serverBuilderSymbol(codegenContext) - private val httpBindingGenerator = HttpBindingGenerator( protocol, codegenContext, codegenContext.symbolProvider, operationShape, - ::builderSymbol, listOf( ServerResponseBeforeIteratingOverMapBoundWithHttpPrefixHeadersUnwrapConstrainedMapHttpBindingCustomization( codegenContext, 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 09eb45a5e6..ece9b26b42 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,7 +5,6 @@ package software.amazon.smithy.rust.codegen.server.smithy.generators.protocol -import software.amazon.smithy.codegen.core.Symbol import software.amazon.smithy.model.knowledge.TopDownIndex import software.amazon.smithy.model.shapes.OperationShape import software.amazon.smithy.model.shapes.Shape @@ -34,7 +33,6 @@ 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.serverBuilderSymbol 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 @@ -87,8 +85,6 @@ class ServerAwsJsonProtocol( private val runtimeConfig = codegenContext.runtimeConfig override fun structuredDataParser(operationShape: OperationShape): StructuredDataParserGenerator { - fun builderSymbol(shape: StructureShape): Symbol = - shape.serverBuilderSymbol(serverCodegenContext) fun returnSymbolToParse(shape: Shape): ReturnSymbolToParse = if (shape.canReachConstrainedShape(codegenContext.model, serverCodegenContext.symbolProvider)) { ReturnSymbolToParse(serverCodegenContext.unconstrainedShapeSymbolProvider.toSymbol(shape), true) @@ -99,7 +95,6 @@ class ServerAwsJsonProtocol( codegenContext, httpBindingResolver, ::awsJsonFieldName, - ::builderSymbol, ::returnSymbolToParse, listOf( ServerRequestBeforeBoxingDeserializedMemberConvertToMaybeConstrainedJsonParserCustomization(serverCodegenContext), @@ -156,8 +151,6 @@ class ServerRestJsonProtocol( val runtimeConfig = codegenContext.runtimeConfig override fun structuredDataParser(operationShape: OperationShape): StructuredDataParserGenerator { - fun builderSymbol(shape: StructureShape): Symbol = - shape.serverBuilderSymbol(serverCodegenContext) fun returnSymbolToParse(shape: Shape): ReturnSymbolToParse = if (shape.canReachConstrainedShape(codegenContext.model, codegenContext.symbolProvider)) { ReturnSymbolToParse(serverCodegenContext.unconstrainedShapeSymbolProvider.toSymbol(shape), true) @@ -168,7 +161,6 @@ class ServerRestJsonProtocol( codegenContext, httpBindingResolver, ::restJsonFieldName, - ::builderSymbol, ::returnSymbolToParse, listOf( ServerRequestBeforeBoxingDeserializedMemberConvertToMaybeConstrainedJsonParserCustomization( 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 9727ca580b..2a2564808a 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 @@ -138,7 +138,6 @@ class ServerProtocolTestGenerator( fun render(writer: RustWriter) { for (operation in operations) { - protocolGenerator.renderOperation(writer, operation) renderOperationTestCases(operation, writer) } } @@ -175,7 +174,7 @@ class ServerProtocolTestGenerator( ), inline = true, ) - writer.withInlineModule(module) { + writer.withInlineModule(module, null) { renderAllTestCases(operationShape, allTests) } } @@ -344,8 +343,8 @@ class ServerProtocolTestGenerator( val operationErrorName = "crate::error::${operationSymbol.name}Error" if (!protocolSupport.responseSerialization || ( - !protocolSupport.errorSerialization && shape.hasTrait() - ) + !protocolSupport.errorSerialization && shape.hasTrait() + ) ) { rust("/* test case disabled for this protocol (not yet supported) */") return @@ -387,7 +386,12 @@ class ServerProtocolTestGenerator( renderHttpRequest(uri.get(), method, headers, body.orNull(), queryParams, host.orNull()) } - makeRequest(operationShape, operationSymbol, this, writable("""panic!("$panicMessage", &input) as $outputT""")) + makeRequest( + operationShape, + operationSymbol, + this, + writable("""panic!("$panicMessage", &input) as $outputT"""), + ) checkResponse(this, testCase.response) } } @@ -415,22 +419,22 @@ class ServerProtocolTestGenerator( rustTemplate( """ .body(${ - if (body != null) { - // The `replace` is necessary to fix the malformed request test `RestJsonInvalidJsonBody`. - // https://github.com/awslabs/smithy/blob/887ae4f6d118e55937105583a07deb90d8fabe1c/smithy-aws-protocol-tests/model/restJson1/malformedRequests/malformed-request-body.smithy#L47 - // - // Smithy is written in Java, which parses `\u000c` within a `String` as a single char given by the - // corresponding Unicode code point. That is the "form feed" 0x0c character. When printing it, - // it gets written as "\f", which is an invalid Rust escape sequence: https://static.rust-lang.org/doc/master/reference.html#literals - // So we need to write the corresponding Rust Unicode escape sequence to make the program compile. - // - // We also escape to avoid interactions with templating in the case where the body contains `#`. - val sanitizedBody = escape(body.replace("\u000c", "\\u{000c}")).dq() - - "#{SmithyHttpServer}::body::Body::from(#{Bytes}::from_static($sanitizedBody.as_bytes()))" - } else { - "#{SmithyHttpServer}::body::Body::empty()" - } + if (body != null) { + // The `replace` is necessary to fix the malformed request test `RestJsonInvalidJsonBody`. + // https://github.com/awslabs/smithy/blob/887ae4f6d118e55937105583a07deb90d8fabe1c/smithy-aws-protocol-tests/model/restJson1/malformedRequests/malformed-request-body.smithy#L47 + // + // Smithy is written in Java, which parses `\u000c` within a `String` as a single char given by the + // corresponding Unicode code point. That is the "form feed" 0x0c character. When printing it, + // it gets written as "\f", which is an invalid Rust escape sequence: https://static.rust-lang.org/doc/master/reference.html#literals + // So we need to write the corresponding Rust Unicode escape sequence to make the program compile. + // + // We also escape to avoid interactions with templating in the case where the body contains `#`. + val sanitizedBody = escape(body.replace("\u000c", "\\u{000c}")).dq() + + "#{SmithyHttpServer}::body::Body::from(#{Bytes}::from_static($sanitizedBody.as_bytes()))" + } else { + "#{SmithyHttpServer}::body::Body::empty()" + } }).unwrap(); """, *codegenScope, @@ -643,7 +647,7 @@ class ServerProtocolTestGenerator( assertOk(rustWriter) { rustWriter.rust( "#T(&body, ${ - rustWriter.escape(body).dq() + rustWriter.escape(body).dq() }, #T::from(${(mediaType ?: "unknown").dq()}))", RuntimeType.protocolTest(codegenContext.runtimeConfig, "validate_body"), RuntimeType.protocolTest(codegenContext.runtimeConfig, "MediaType"), @@ -757,11 +761,7 @@ class ServerProtocolTestGenerator( private const val AwsJson11 = "aws.protocoltests.json#JsonProtocol" private const val RestJson = "aws.protocoltests.restjson#RestJson" private const val RestJsonValidation = "aws.protocoltests.restjson.validation#RestJsonValidation" - private const val MalformedRangeValidation = "aws.protocoltests.extras.restjson.validation#MalformedRangeValidation" private val ExpectFail: Set = setOf( - // Pending merge from the Smithy team: see https://github.com/awslabs/smithy/pull/1477. - FailingTest(RestJson, "RestJsonWithPayloadExpectsImpliedContentType", TestType.MalformedRequest), - // Pending resolution from the Smithy team, see https://github.com/awslabs/smithy/issues/1068. FailingTest(RestJson, "RestJsonHttpWithHeadersButNoPayload", TestType.Request), @@ -772,90 +772,19 @@ class ServerProtocolTestGenerator( FailingTest(RestJson, "RestJsonEndpointTrait", TestType.Request), FailingTest(RestJson, "RestJsonEndpointTraitWithHostLabel", TestType.Request), - FailingTest(RestJson, "RestJsonWithBodyExpectsApplicationJsonContentType", TestType.MalformedRequest), - - // Tests involving constraint traits, which are not yet fully implemented. - // See https://github.com/awslabs/smithy-rs/issues/1401. + FailingTest(RestJson, "RestJsonOmitsEmptyListQueryValues", TestType.Request), + // Tests involving `@range` on floats. + // Pending resolution from the Smithy team, see https://github.com/awslabs/smithy-rs/issues/2007. FailingTest(RestJsonValidation, "RestJsonMalformedRangeFloat_case0", TestType.MalformedRequest), FailingTest(RestJsonValidation, "RestJsonMalformedRangeFloat_case1", TestType.MalformedRequest), FailingTest(RestJsonValidation, "RestJsonMalformedRangeMaxFloat", TestType.MalformedRequest), FailingTest(RestJsonValidation, "RestJsonMalformedRangeMinFloat", TestType.MalformedRequest), - FailingTest(RestJsonValidation, "RestJsonMalformedPatternSensitiveString", TestType.MalformedRequest), - - // See https://github.com/awslabs/smithy-rs/issues/1969 - FailingTest(MalformedRangeValidation, "RestJsonMalformedRangeShortOverride_case0", TestType.MalformedRequest), - FailingTest(MalformedRangeValidation, "RestJsonMalformedRangeShortOverride_case1", TestType.MalformedRequest), - FailingTest( - MalformedRangeValidation, - "RestJsonMalformedRangeIntegerOverride_case0", - TestType.MalformedRequest, - ), - FailingTest( - MalformedRangeValidation, - "RestJsonMalformedRangeIntegerOverride_case1", - TestType.MalformedRequest, - ), - FailingTest( - MalformedRangeValidation, - "RestJsonMalformedRangeLongOverride_case0", - TestType.MalformedRequest, - ), - FailingTest( - MalformedRangeValidation, - "RestJsonMalformedRangeLongOverride_case1", - TestType.MalformedRequest, - ), - FailingTest(MalformedRangeValidation, "RestJsonMalformedRangeMaxShortOverride", TestType.MalformedRequest), - FailingTest( - MalformedRangeValidation, - "RestJsonMalformedRangeMaxIntegerOverride", - TestType.MalformedRequest, - ), - FailingTest(MalformedRangeValidation, "RestJsonMalformedRangeMaxLongOverride", TestType.MalformedRequest), - FailingTest(MalformedRangeValidation, "RestJsonMalformedRangeMinShortOverride", TestType.MalformedRequest), - FailingTest( - MalformedRangeValidation, - "RestJsonMalformedRangeMinIntegerOverride", - TestType.MalformedRequest, - ), - FailingTest(MalformedRangeValidation, "RestJsonMalformedRangeMinLongOverride", TestType.MalformedRequest), - FailingTest(RestJsonValidation, "RestJsonMalformedRangeByteOverride_case0", TestType.MalformedRequest), - FailingTest(RestJsonValidation, "RestJsonMalformedRangeByteOverride_case1", TestType.MalformedRequest), + + // Tests involving floating point shapes and the `@range` trait; see https://github.com/awslabs/smithy-rs/issues/2007 FailingTest(RestJsonValidation, "RestJsonMalformedRangeFloatOverride_case0", TestType.MalformedRequest), FailingTest(RestJsonValidation, "RestJsonMalformedRangeFloatOverride_case1", TestType.MalformedRequest), - FailingTest(RestJsonValidation, "RestJsonMalformedLengthMaxStringOverride", TestType.MalformedRequest), - FailingTest(RestJsonValidation, "RestJsonMalformedLengthMinStringOverride", TestType.MalformedRequest), - FailingTest(RestJsonValidation, "RestJsonMalformedRangeMaxByteOverride", TestType.MalformedRequest), FailingTest(RestJsonValidation, "RestJsonMalformedRangeMaxFloatOverride", TestType.MalformedRequest), - FailingTest(RestJsonValidation, "RestJsonMalformedRangeMinByteOverride", TestType.MalformedRequest), FailingTest(RestJsonValidation, "RestJsonMalformedRangeMinFloatOverride", TestType.MalformedRequest), - FailingTest(RestJsonValidation, "RestJsonMalformedPatternListOverride_case0", TestType.MalformedRequest), - FailingTest(RestJsonValidation, "RestJsonMalformedPatternListOverride_case1", TestType.MalformedRequest), - FailingTest(RestJsonValidation, "RestJsonMalformedPatternMapKeyOverride_case0", TestType.MalformedRequest), - FailingTest(RestJsonValidation, "RestJsonMalformedPatternMapKeyOverride_case1", TestType.MalformedRequest), - FailingTest( - RestJsonValidation, - "RestJsonMalformedPatternMapValueOverride_case0", - TestType.MalformedRequest, - ), - FailingTest( - RestJsonValidation, - "RestJsonMalformedPatternMapValueOverride_case1", - TestType.MalformedRequest, - ), - FailingTest(RestJsonValidation, "RestJsonMalformedPatternStringOverride_case0", TestType.MalformedRequest), - FailingTest(RestJsonValidation, "RestJsonMalformedPatternStringOverride_case1", TestType.MalformedRequest), - FailingTest(RestJsonValidation, "RestJsonMalformedPatternUnionOverride_case0", TestType.MalformedRequest), - FailingTest(RestJsonValidation, "RestJsonMalformedPatternUnionOverride_case1", TestType.MalformedRequest), - FailingTest(RestJsonValidation, "RestJsonMalformedLengthBlobOverride_case0", TestType.MalformedRequest), - FailingTest(RestJsonValidation, "RestJsonMalformedLengthBlobOverride_case1", TestType.MalformedRequest), - FailingTest(RestJsonValidation, "RestJsonMalformedLengthListOverride_case0", TestType.MalformedRequest), - FailingTest(RestJsonValidation, "RestJsonMalformedLengthListOverride_case1", TestType.MalformedRequest), - FailingTest(RestJsonValidation, "RestJsonMalformedLengthMapOverride_case0", TestType.MalformedRequest), - FailingTest(RestJsonValidation, "RestJsonMalformedLengthMapOverride_case1", TestType.MalformedRequest), - FailingTest(RestJsonValidation, "RestJsonMalformedLengthStringOverride_case0", TestType.MalformedRequest), - FailingTest(RestJsonValidation, "RestJsonMalformedLengthStringOverride_case1", TestType.MalformedRequest), - FailingTest(RestJsonValidation, "RestJsonMalformedLengthStringOverride_case2", TestType.MalformedRequest), // Some tests for the S3 service (restXml). FailingTest("com.amazonaws.s3#AmazonS3", "GetBucketLocationUnwrappedOutput", TestType.Response), @@ -872,16 +801,41 @@ class ServerProtocolTestGenerator( FailingTest("aws.protocoltests.json10#JsonRpc10", "AwsJson10EndpointTrait", TestType.Request), // AwsJson1.1 failing tests. - FailingTest("aws.protocoltests.json#JsonProtocol", "AwsJson11EndpointTraitWithHostLabel", TestType.Request), - FailingTest("aws.protocoltests.json#JsonProtocol", "AwsJson11EndpointTrait", TestType.Request), - FailingTest("aws.protocoltests.json#JsonProtocol", "parses_httpdate_timestamps", TestType.Response), - FailingTest("aws.protocoltests.json#JsonProtocol", "parses_iso8601_timestamps", TestType.Response), - FailingTest( - "aws.protocoltests.json#JsonProtocol", - "parses_the_request_id_from_the_response", - TestType.Response, - ), - + FailingTest(AwsJson11, "AwsJson11EndpointTraitWithHostLabel", TestType.Request), + FailingTest(AwsJson11, "AwsJson11EndpointTrait", TestType.Request), + FailingTest(AwsJson11, "parses_the_request_id_from_the_response", TestType.Response), + + // TODO(https://github.com/awslabs/smithy/issues/1683): This has been marked as failing until resolution of said issue + FailingTest(RestJsonValidation, "RestJsonMalformedUniqueItemsBlobList", TestType.MalformedRequest), + FailingTest(RestJsonValidation, "RestJsonMalformedUniqueItemsBooleanList_case0", TestType.MalformedRequest), + FailingTest(RestJsonValidation, "RestJsonMalformedUniqueItemsBooleanList_case1", TestType.MalformedRequest), + FailingTest(RestJsonValidation, "RestJsonMalformedUniqueItemsStringList", TestType.MalformedRequest), + FailingTest(RestJsonValidation, "RestJsonMalformedUniqueItemsByteList", TestType.MalformedRequest), + FailingTest(RestJsonValidation, "RestJsonMalformedUniqueItemsShortList", TestType.MalformedRequest), + FailingTest(RestJsonValidation, "RestJsonMalformedUniqueItemsIntegerList", TestType.MalformedRequest), + 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, "RestJsonMalformedUniqueItemsEnumList", TestType.MalformedRequest), + FailingTest(RestJsonValidation, "RestJsonMalformedUniqueItemsIntEnumList", TestType.MalformedRequest), + FailingTest(RestJsonValidation, "RestJsonMalformedUniqueItemsListList", TestType.MalformedRequest), + FailingTest(RestJsonValidation, "RestJsonMalformedUniqueItemsStructureList", TestType.MalformedRequest), + FailingTest(RestJsonValidation, "RestJsonMalformedUniqueItemsUnionList_case0", TestType.MalformedRequest), + FailingTest(RestJsonValidation, "RestJsonMalformedUniqueItemsUnionList_case1", TestType.MalformedRequest), + + // TODO(https://github.com/awslabs/smithy-rs/issues/2472): We don't respect the `@internal` trait + FailingTest(RestJsonValidation, "RestJsonMalformedEnumList_case0", TestType.MalformedRequest), + FailingTest(RestJsonValidation, "RestJsonMalformedEnumList_case1", TestType.MalformedRequest), + FailingTest(RestJsonValidation, "RestJsonMalformedEnumMapKey_case0", TestType.MalformedRequest), + FailingTest(RestJsonValidation, "RestJsonMalformedEnumMapKey_case1", TestType.MalformedRequest), + FailingTest(RestJsonValidation, "RestJsonMalformedEnumMapValue_case0", TestType.MalformedRequest), + FailingTest(RestJsonValidation, "RestJsonMalformedEnumMapValue_case1", TestType.MalformedRequest), + FailingTest(RestJsonValidation, "RestJsonMalformedEnumString_case0", TestType.MalformedRequest), + FailingTest(RestJsonValidation, "RestJsonMalformedEnumString_case1", TestType.MalformedRequest), + FailingTest(RestJsonValidation, "RestJsonMalformedEnumUnion_case0", TestType.MalformedRequest), + FailingTest(RestJsonValidation, "RestJsonMalformedEnumUnion_case1", TestType.MalformedRequest), ) private val RunOnly: Set? = null @@ -915,6 +869,8 @@ class ServerProtocolTestGenerator( "queryTimestamp": 1, "queryTimestampList": [1, 2, 3], "queryEnum": "Foo", + "queryIntegerEnum": 1, + "queryIntegerEnumList": [1,2,3], "queryEnumList": ["Foo", "Baz", "Bar"], "queryParamsMapOfStringList": { "String": ["Hello there"], @@ -961,8 +917,10 @@ class ServerProtocolTestGenerator( 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() @@ -975,8 +933,8 @@ class ServerProtocolTestGenerator( .contents( """ { - "message" : "1 validation error detected. Value 000000000000000000000000000000000000000000000000000000000000000000000000000000000000! at '/evilString' failed to satisfy constraint: Member must satisfy regular expression pattern: ^([0-9]+)+${'$'}", - "fieldList" : [{"message": "Value 000000000000000000000000000000000000000000000000000000000000000000000000000000000000! at '/evilString' failed to satisfy constraint: Member must satisfy regular expression pattern: ^([0-9]+)+${'$'}", "path": "/evilString"}] + "message" : "1 validation error detected. Value at '/evilString' failed to satisfy constraint: Member must satisfy regular expression pattern: ^([0-9]+)+${'$'}", + "fieldList" : [{"message": "Value at '/evilString' failed to satisfy constraint: Member must satisfy regular expression pattern: ^([0-9]+)+${'$'}", "path": "/evilString"}] } """.trimIndent(), ) @@ -992,7 +950,7 @@ class ServerProtocolTestGenerator( // 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, + // Pair(RestJson, "RestJsonAllQueryStringTypes") to ::fixRestJsonAllQueryStringTypes, // TODO(https://github.com/awslabs/smithy/pull/1562) Pair(RestJson, "RestJsonQueryStringEscaping") to ::fixRestJsonQueryStringEscaping, ) @@ -1008,7 +966,10 @@ class ServerProtocolTestGenerator( private val BrokenMalformedRequestTests: Map, KFunction1> = // TODO(https://github.com/awslabs/smithy/issues/1506) mapOf( - Pair(RestJsonValidation, "RestJsonMalformedPatternReDOSString") to ::fixRestJsonMalformedPatternReDOSString, + Pair( + RestJsonValidation, + "RestJsonMalformedPatternReDOSString", + ) to ::fixRestJsonMalformedPatternReDOSString, ) } } 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 ff7496b973..16441ac0c2 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 @@ -27,7 +27,6 @@ import software.amazon.smithy.model.traits.HttpPayloadTrait import software.amazon.smithy.model.traits.HttpTrait import software.amazon.smithy.model.traits.MediaTypeTrait import software.amazon.smithy.rust.codegen.core.rustlang.Attribute -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.RustWriter import software.amazon.smithy.rust.codegen.core.rustlang.Writable @@ -41,10 +40,10 @@ 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.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.generators.error.errorSymbol import software.amazon.smithy.rust.codegen.core.smithy.generators.http.HttpMessageType import software.amazon.smithy.rust.codegen.core.smithy.generators.protocol.ProtocolTraitImplGenerator import software.amazon.smithy.rust.codegen.core.smithy.generators.setterName @@ -53,6 +52,8 @@ 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.ProtocolFunctions +import software.amazon.smithy.rust.codegen.core.smithy.protocols.RestJson import software.amazon.smithy.rust.codegen.core.smithy.protocols.parse.StructuredDataParserGenerator import software.amazon.smithy.rust.codegen.core.smithy.traits.SyntheticInputTrait import software.amazon.smithy.rust.codegen.core.smithy.transformers.operationErrors @@ -66,7 +67,6 @@ 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.isStreaming import software.amazon.smithy.rust.codegen.core.util.outputShape -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 import software.amazon.smithy.rust.codegen.server.smithy.ServerRuntimeType @@ -113,9 +113,8 @@ private class ServerHttpBoundProtocolTraitImplGenerator( private val model = codegenContext.model private val runtimeConfig = codegenContext.runtimeConfig private val httpBindingResolver = protocol.httpBindingResolver - private val operationDeserModule = RustModule.private("operation_deser") - private val operationSerModule = RustModule.private("operation_ser") private val typeConversionGenerator = TypeConversionGenerator(model, symbolProvider, runtimeConfig) + private val protocolFunctions = ProtocolFunctions(codegenContext) private val codegenScope = arrayOf( "AsyncTrait" to ServerCargoDependency.AsyncTrait.toType(), @@ -253,7 +252,7 @@ private class ServerHttpBoundProtocolTraitImplGenerator( ) // Implement `into_response` for output types. - val errorSymbol = operationShape.errorSymbol(symbolProvider) + val errorSymbol = symbolProvider.symbolForOperationError(operationShape) rustTemplate( """ @@ -296,11 +295,10 @@ private class ServerHttpBoundProtocolTraitImplGenerator( } private fun serverParseRequest(operationShape: OperationShape): RuntimeType { - val fnName = "parse_${operationShape.id.name.toSnakeCase()}_request" val inputShape = operationShape.inputShape(model) val inputSymbol = symbolProvider.toSymbol(inputShape) - return RuntimeType.forInlineFun(fnName, operationDeserModule) { + return protocolFunctions.deserializeFn(operationShape, fnNameSuffix = "http_request") { fnName -> Attribute.AllowClippyUnnecessaryWraps.render(this) // The last conversion trait bound is needed by the `hyper::body::to_bytes(body).await?` call. rustBlockTemplate( @@ -331,11 +329,10 @@ private class ServerHttpBoundProtocolTraitImplGenerator( } private fun serverSerializeResponse(operationShape: OperationShape): RuntimeType { - val fnName = "serialize_${operationShape.id.name.toSnakeCase()}_response" val outputShape = operationShape.outputShape(model) val outputSymbol = symbolProvider.toSymbol(outputShape) - return RuntimeType.forInlineFun(fnName, operationSerModule) { + return protocolFunctions.serializeFn(operationShape, fnNameSuffix = "http_response") { fnName -> Attribute.AllowClippyUnnecessaryWraps.render(this) // Note we only need to take ownership of the output in the case that it contains streaming members. @@ -364,9 +361,8 @@ private class ServerHttpBoundProtocolTraitImplGenerator( } private fun serverSerializeError(operationShape: OperationShape): RuntimeType { - val fnName = "serialize_${operationShape.id.name.toSnakeCase()}_error" - val errorSymbol = operationShape.errorSymbol(symbolProvider) - return RuntimeType.forInlineFun(fnName, operationSerModule) { + val errorSymbol = symbolProvider.symbolForOperationError(operationShape) + return protocolFunctions.serializeFn(operationShape, fnNameSuffix = "http_error") { fnName -> Attribute.AllowClippyUnnecessaryWraps.render(this) rustBlockTemplate( "pub fn $fnName(error: &#{E}) -> std::result::Result<#{SmithyHttpServer}::response::Response, #{ResponseRejection}>", @@ -385,7 +381,7 @@ private class ServerHttpBoundProtocolTraitImplGenerator( private fun RustWriter.serverRenderErrorShapeResponseSerializer( operationShape: OperationShape, - errorSymbol: RuntimeType, + errorSymbol: Symbol, ) { val operationName = symbolProvider.toSymbol(operationShape).name val structuredDataSerializer = protocol.structuredDataSerializer(operationShape) @@ -628,7 +624,20 @@ private class ServerHttpBoundProtocolTraitImplGenerator( """ let bytes = #{Hyper}::body::to_bytes(body).await?; if !bytes.is_empty() { - input = #{parser}(bytes.as_ref(), input)?; + """, + *codegenScope, + ) + if (protocol is RestJson) { + rustTemplate( + """ + #{SmithyHttpServer}::protocols::content_type_header_classifier(&parts.headers, Some("application/json"))?; + """, + *codegenScope, + ) + } + rustTemplate( + """ + input = #{parser}(bytes.as_ref(), input)?; } """, *codegenScope, @@ -645,11 +654,11 @@ private class ServerHttpBoundProtocolTraitImplGenerator( """ { input = input.${member.setterName()}(${ - if (symbolProvider.toSymbol(binding.member).isOptional()) { - "Some(value)" - } else { - "value" - } + if (symbolProvider.toSymbol(binding.member).isOptional()) { + "Some(value)" + } else { + "value" + } }); } """, @@ -677,7 +686,9 @@ private class ServerHttpBoundProtocolTraitImplGenerator( ) ) { "?" - } else "" + } else { + "" + } rustTemplate("input.build()$err", *codegenScope) } @@ -827,7 +838,7 @@ private class ServerHttpBoundProtocolTraitImplGenerator( // * a map of list of string; or // * a map of set of string. enum class QueryParamsTargetMapValueType { - STRING, LIST, SET; + STRING, LIST, SET } private fun queryParamsTargetMapValueType(targetMapValue: Shape): QueryParamsTargetMapValueType = @@ -935,7 +946,7 @@ private class ServerHttpBoundProtocolTraitImplGenerator( it.location, protocol.defaultTimestampFormat, ) - val timestampFormatType = RuntimeType.timestampFormat(runtimeConfig, timestampFormat) + val timestampFormatType = RuntimeType.parseTimestampFormat(CodegenTarget.SERVER, runtimeConfig, timestampFormat) rustTemplate( """ let v = #{DateTime}::from_str(&v, #{format})?#{ConvertInto:W}; @@ -1010,7 +1021,7 @@ private class ServerHttpBoundProtocolTraitImplGenerator( rustBlock("if !$memberName.is_empty()") { withBlock( "input = input.${ - binding.member.setterName() + binding.member.setterName() }(", ");", ) { @@ -1019,7 +1030,9 @@ private class ServerHttpBoundProtocolTraitImplGenerator( "#T(", ")", conditional = hasConstrainedTarget, - unconstrainedShapeSymbolProvider.toSymbol(binding.member).mapRustType { it.stripOuter() }, + unconstrainedShapeSymbolProvider.toSymbol(binding.member).mapRustType { + it.stripOuter() + }, ) { write(memberName) } @@ -1058,8 +1071,7 @@ private class ServerHttpBoundProtocolTraitImplGenerator( private fun generateParseStrFn(binding: HttpBindingDescriptor, percentDecoding: Boolean): RuntimeType { val output = unconstrainedShapeSymbolProvider.toSymbol(binding.member) - val fnName = generateParseStrFnName(binding) - return RuntimeType.forInlineFun(fnName, operationDeserModule) { + return protocolFunctions.deserializeFn(binding.member) { fnName -> rustBlockTemplate( "pub fn $fnName(value: &str) -> std::result::Result<#{O}, #{RequestRejection}>", *codegenScope, @@ -1088,7 +1100,7 @@ private class ServerHttpBoundProtocolTraitImplGenerator( binding.location, protocol.defaultTimestampFormat, ) - val timestampFormatType = RuntimeType.timestampFormat(runtimeConfig, timestampFormat) + val timestampFormatType = RuntimeType.parseTimestampFormat(CodegenTarget.SERVER, runtimeConfig, timestampFormat) if (percentDecoding) { rustTemplate( @@ -1130,28 +1142,22 @@ private class ServerHttpBoundProtocolTraitImplGenerator( } } - private fun generateParseStrFnName(binding: HttpBindingDescriptor): String { - val containerName = binding.member.container.name.toSnakeCase() - val memberName = binding.memberName.toSnakeCase() - return "parse_str_${containerName}_$memberName" - } - /** * Returns the error type of the function that deserializes a non-streaming HTTP payload (a byte slab) into the * shape targeted by the `httpPayload` trait. */ - private fun getDeserializePayloadErrorSymbol(binding: HttpBindingDescriptor): RuntimeType { + private fun getDeserializePayloadErrorSymbol(binding: HttpBindingDescriptor): Symbol { check(binding.location == HttpLocation.PAYLOAD) if (model.expectShape(binding.member.target) is StringShape) { - return ServerRuntimeType.requestRejection(runtimeConfig) + return ServerRuntimeType.requestRejection(runtimeConfig).toSymbol() } return when (codegenContext.protocol) { RestJson1Trait.ID, AwsJson1_0Trait.ID, AwsJson1_1Trait.ID -> { - RuntimeType.smithyJson(runtimeConfig).resolve("deserialize::error::DeserializeError") + RuntimeType.smithyJson(runtimeConfig).resolve("deserialize::error::DeserializeError").toSymbol() } RestXmlTrait.ID -> { - RuntimeType.smithyXml(runtimeConfig).resolve("decode::XmlDecodeError") + RuntimeType.smithyXml(runtimeConfig).resolve("decode::XmlDecodeError").toSymbol() } else -> { TODO("Protocol ${codegenContext.protocol} not supported yet") diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/testutil/ServerCodegenIntegrationTest.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/testutil/ServerCodegenIntegrationTest.kt new file mode 100644 index 0000000000..fc83f1392b --- /dev/null +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/testutil/ServerCodegenIntegrationTest.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.testutil + +import software.amazon.smithy.build.PluginContext +import software.amazon.smithy.build.SmithyBuildPlugin +import software.amazon.smithy.model.Model +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.codegenIntegrationTest +import software.amazon.smithy.rust.codegen.server.smithy.RustServerCodegenPlugin +import software.amazon.smithy.rust.codegen.server.smithy.ServerCodegenContext +import software.amazon.smithy.rust.codegen.server.smithy.customize.ServerCodegenDecorator +import java.nio.file.Path + +/** + * This file is entirely analogous to [software.amazon.smithy.rust.codegen.client.testutil.ClientCodegenIntegrationTest.kt]. + */ + +fun serverIntegrationTest( + model: Model, + params: IntegrationTestParams = IntegrationTestParams(), + additionalDecorators: List = listOf(), + test: (ServerCodegenContext, RustCrate) -> Unit = { _, _ -> }, +): Path { + fun invokeRustCodegenPlugin(ctx: PluginContext) { + val codegenDecorator = object : ServerCodegenDecorator { + override val name: String = "Add tests" + override val order: Byte = 0 + + override fun classpathDiscoverable(): Boolean = false + + override fun extras(codegenContext: ServerCodegenContext, rustCrate: RustCrate) { + test(codegenContext, rustCrate) + } + } + RustServerCodegenPlugin().executeWithDecorator(ctx, codegenDecorator, *additionalDecorators.toTypedArray()) + } + return codegenIntegrationTest(model, params, invokePlugin = ::invokeRustCodegenPlugin) +} + +abstract class ServerDecoratableBuildPlugin : SmithyBuildPlugin { + abstract fun executeWithDecorator( + context: PluginContext, + vararg decorator: ServerCodegenDecorator, + ) + + override fun execute(context: PluginContext) { + executeWithDecorator(context) + } +} diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/testutil/ServerTestHelpers.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/testutil/ServerTestHelpers.kt index 9d49dfb9fe..d7c3c7fb5c 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/testutil/ServerTestHelpers.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/testutil/ServerTestHelpers.kt @@ -12,25 +12,30 @@ import software.amazon.smithy.model.shapes.ServiceShape import software.amazon.smithy.model.shapes.ShapeId import software.amazon.smithy.model.shapes.StructureShape import software.amazon.smithy.rust.codegen.core.rustlang.RustWriter -import software.amazon.smithy.rust.codegen.core.smithy.CodegenTarget +import software.amazon.smithy.rust.codegen.core.rustlang.implBlock 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.smithy.SymbolVisitorConfig +import software.amazon.smithy.rust.codegen.core.smithy.RustSymbolProviderConfig import software.amazon.smithy.rust.codegen.core.smithy.generators.StructureGenerator -import software.amazon.smithy.rust.codegen.core.smithy.generators.implBlock +import software.amazon.smithy.rust.codegen.core.testutil.TestModuleDocProvider import software.amazon.smithy.rust.codegen.core.testutil.TestRuntimeConfig -import software.amazon.smithy.rust.codegen.server.smithy.RustCodegenServerPlugin +import software.amazon.smithy.rust.codegen.server.smithy.RustServerCodegenPlugin import software.amazon.smithy.rust.codegen.server.smithy.ServerCodegenConfig import software.amazon.smithy.rust.codegen.server.smithy.ServerCodegenContext +import software.amazon.smithy.rust.codegen.server.smithy.ServerModuleProvider 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.customizations.SmithyValidationExceptionConversionGenerator +import software.amazon.smithy.rust.codegen.server.smithy.customize.ServerCodegenDecorator import software.amazon.smithy.rust.codegen.server.smithy.generators.ServerBuilderGenerator // These are the settings we default to if the user does not override them in their `smithy-build.json`. -val ServerTestSymbolVisitorConfig = SymbolVisitorConfig( +val ServerTestRustSymbolProviderConfig = RustSymbolProviderConfig( runtimeConfig = TestRuntimeConfig, renameExceptions = false, nullabilityCheckMode = NullableIndex.CheckMode.SERVER, + moduleProvider = ServerModuleProvider, ) private fun testServiceShapeFor(model: Model) = @@ -39,21 +44,28 @@ private fun testServiceShapeFor(model: Model) = fun serverTestSymbolProvider(model: Model, serviceShape: ServiceShape? = null) = serverTestSymbolProviders(model, serviceShape).symbolProvider +private class ServerTestCodegenDecorator : ServerCodegenDecorator { + override val name = "test" + override val order: Byte = 0 +} + fun serverTestSymbolProviders( model: Model, serviceShape: ServiceShape? = null, settings: ServerRustSettings? = null, ) = ServerSymbolProviders.from( + serverTestRustSettings(), model, serviceShape ?: testServiceShapeFor(model), - ServerTestSymbolVisitorConfig, + ServerTestRustSymbolProviderConfig, ( settings ?: serverTestRustSettings( (serviceShape ?: testServiceShapeFor(model)).id, ) ).codegenConfig.publicConstrainedTypes, - RustCodegenServerPlugin::baseSymbolProvider, + ServerTestCodegenDecorator(), + RustServerCodegenPlugin::baseSymbolProvider, ) fun serverTestRustSettings( @@ -94,16 +106,19 @@ fun serverTestCodegenContext( ?: ServiceShape.builder().version("test").id("test#Service").build() val protocol = protocolShapeId ?: ShapeId.from("test#Protocol") val serverSymbolProviders = ServerSymbolProviders.from( + settings, model, service, - ServerTestSymbolVisitorConfig, + ServerTestRustSymbolProviderConfig, settings.codegenConfig.publicConstrainedTypes, - RustCodegenServerPlugin::baseSymbolProvider, + ServerTestCodegenDecorator(), + RustServerCodegenPlugin::baseSymbolProvider, ) return ServerCodegenContext( model, serverSymbolProviders.symbolProvider, + TestModuleDocProvider, service, protocol, settings, @@ -117,14 +132,14 @@ fun serverTestCodegenContext( /** * In tests, we frequently need to generate a struct, a builder, and an impl block to access said builder. */ -fun StructureShape.serverRenderWithModelBuilder(model: Model, symbolProvider: RustSymbolProvider, writer: RustWriter) { - StructureGenerator(model, symbolProvider, writer, this).render(CodegenTarget.SERVER) +fun StructureShape.serverRenderWithModelBuilder(rustCrate: RustCrate, model: Model, symbolProvider: RustSymbolProvider, writer: RustWriter) { + StructureGenerator(model, symbolProvider, writer, this, emptyList()).render() val serverCodegenContext = serverTestCodegenContext(model) // Note that this always uses `ServerBuilderGenerator` and _not_ `ServerBuilderGeneratorWithoutPublicConstrainedTypes`, // regardless of the `publicConstrainedTypes` setting. - val modelBuilder = ServerBuilderGenerator(serverCodegenContext, this) - modelBuilder.render(writer) - writer.implBlock(this, symbolProvider) { + val modelBuilder = ServerBuilderGenerator(serverCodegenContext, this, SmithyValidationExceptionConversionGenerator(serverCodegenContext)) + modelBuilder.render(rustCrate, writer) + writer.implBlock(symbolProvider.toSymbol(this)) { modelBuilder.renderConvenienceMethod(this) } } diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/traits/ConstraintViolationRustBoxTrait.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/traits/ConstraintViolationRustBoxTrait.kt new file mode 100644 index 0000000000..9aee2b884e --- /dev/null +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/traits/ConstraintViolationRustBoxTrait.kt @@ -0,0 +1,25 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +package software.amazon.smithy.rust.codegen.server.smithy.traits + +import software.amazon.smithy.model.node.Node +import software.amazon.smithy.model.shapes.ShapeId +import software.amazon.smithy.model.traits.Trait + +/** + * This shape is analogous to [software.amazon.smithy.rust.codegen.core.smithy.traits.RustBoxTrait], but for the + * constraint violation graph. The sets of shapes we tag are different, and they are interpreted by the code generator + * differently, so we need a separate tag. + * + * This is used to handle recursive constraint violations. + * See [software.amazon.smithy.rust.codegen.server.smithy.transformers.RecursiveConstraintViolationBoxer]. + */ +class ConstraintViolationRustBoxTrait : Trait { + val ID = ShapeId.from("software.amazon.smithy.rust.codegen.smithy.rust.synthetic#constraintViolationBox") + override fun toNode(): Node = Node.objectNode() + + override fun toShapeId(): ShapeId = ID +} diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/traits/SyntheticStructureFromConstrainedMemberTrait.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/traits/SyntheticStructureFromConstrainedMemberTrait.kt new file mode 100644 index 0000000000..de5890c87a --- /dev/null +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/traits/SyntheticStructureFromConstrainedMemberTrait.kt @@ -0,0 +1,21 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +package software.amazon.smithy.rust.codegen.server.smithy.traits + +import software.amazon.smithy.model.node.Node +import software.amazon.smithy.model.shapes.MemberShape +import software.amazon.smithy.model.shapes.Shape +import software.amazon.smithy.model.shapes.ShapeId +import software.amazon.smithy.model.traits.AnnotationTrait + +/** + * Trait applied to an overridden shape indicating the member of this new shape type + */ +class SyntheticStructureFromConstrainedMemberTrait(val container: Shape, val member: MemberShape) : AnnotationTrait(SyntheticStructureFromConstrainedMemberTrait.ID, Node.objectNode()) { + companion object { + val ID: ShapeId = ShapeId.from("smithy.api.internal#overriddenMember") + } +} diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/transformers/AttachValidationExceptionToConstrainedOperationInputsInAllowList.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/transformers/AttachValidationExceptionToConstrainedOperationInputsInAllowList.kt index 02e1d4be64..68840bde20 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/transformers/AttachValidationExceptionToConstrainedOperationInputsInAllowList.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/transformers/AttachValidationExceptionToConstrainedOperationInputsInAllowList.kt @@ -13,6 +13,7 @@ import software.amazon.smithy.model.shapes.ShapeId import software.amazon.smithy.model.transform.ModelTransformer import software.amazon.smithy.rust.codegen.core.smithy.DirectedWalker import software.amazon.smithy.rust.codegen.core.util.inputShape +import software.amazon.smithy.rust.codegen.server.smithy.customizations.SmithyValidationExceptionConversionGenerator import software.amazon.smithy.rust.codegen.server.smithy.hasConstraintTrait /** @@ -60,11 +61,11 @@ object AttachValidationExceptionToConstrainedOperationInputsInAllowList { walker.walkShapes(operationShape.inputShape(model)) .any { it is SetShape || it is EnumShape || it.hasConstraintTrait() } } - .filter { !it.errors.contains(ShapeId.from("smithy.framework#ValidationException")) } + .filter { !it.errors.contains(SmithyValidationExceptionConversionGenerator.SHAPE_ID) } return ModelTransformer.create().mapShapes(model) { shape -> if (shape is OperationShape && operationsWithConstrainedInputWithoutValidationException.contains(shape)) { - shape.toBuilder().addError("smithy.framework#ValidationException").build() + shape.toBuilder().addError(SmithyValidationExceptionConversionGenerator.SHAPE_ID).build() } else { shape } diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/transformers/ConstrainedMemberTransform.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/transformers/ConstrainedMemberTransform.kt new file mode 100644 index 0000000000..3ab99ce64d --- /dev/null +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/transformers/ConstrainedMemberTransform.kt @@ -0,0 +1,226 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +package software.amazon.smithy.rust.codegen.server.smithy.transformers + +import software.amazon.smithy.model.Model +import software.amazon.smithy.model.shapes.AbstractShapeBuilder +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.Shape +import software.amazon.smithy.model.shapes.ShapeId +import software.amazon.smithy.model.shapes.StructureShape +import software.amazon.smithy.model.shapes.UnionShape +import software.amazon.smithy.model.traits.RequiredTrait +import software.amazon.smithy.model.traits.Trait +import software.amazon.smithy.model.transform.ModelTransformer +import software.amazon.smithy.rust.codegen.core.smithy.DirectedWalker +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.UNREACHABLE +import software.amazon.smithy.rust.codegen.core.util.orNull +import software.amazon.smithy.rust.codegen.server.smithy.allConstraintTraits +import software.amazon.smithy.rust.codegen.server.smithy.traits.SyntheticStructureFromConstrainedMemberTrait +import software.amazon.smithy.utils.ToSmithyBuilder +import java.lang.IllegalStateException +import java.util.* + +/** + * Transforms all member shapes that have constraints on them into equivalent non-constrained + * member shapes targeting synthetic constrained structure shapes with the member's constraints. + * + * E.g.: + * ``` + * structure A { + * @length(min: 1, max: 69) + * string: ConstrainedString + * } + * + * @length(min: 2, max: 10) + * @pattern("^[A-Za-z]+$") + * string ConstrainedString + * ``` + * + * to + * + * ``` + * structure A { + * string: OverriddenConstrainedString + * } + * + * @length(min: 1, max: 69) + * @pattern("^[A-Za-z]+$") + * OverriddenConstrainedString + * + * @length(min: 2, max: 10) + * @pattern("^[A-Za-z]+$") + * string ConstrainedString + * ``` + */ +object ConstrainedMemberTransform { + private data class MemberShapeTransformation( + val newShape: Shape, + val memberToChange: MemberShape, + val traitsToKeep: List, + ) + + private val memberConstraintTraitsToOverride = allConstraintTraits - RequiredTrait::class.java + + private fun Shape.hasMemberConstraintTrait() = + memberConstraintTraitsToOverride.any(this::hasTrait) + + fun transform(model: Model): Model { + val additionalNames = HashSet() + val walker = DirectedWalker(model) + + // Find all synthetic input / output structures that have been added by + // the OperationNormalizer, get constrained members out of those structures, + // convert them into non-constrained members and then pass them to the transformer. + // The transformer will add new shapes, and will replace existing member shapes' target + // with the newly added shapes. + val transformations = model.operationShapes + .flatMap { listOfNotNull(it.input.orNull(), it.output.orNull()) + it.errors } + .mapNotNull { model.expectShape(it).asStructureShape().orElse(null) } + .filter { it.hasTrait(SyntheticInputTrait.ID) || it.hasTrait(SyntheticOutputTrait.ID) } + .flatMap { walker.walkShapes(it) } + .filter { it is StructureShape || it is ListShape || it is UnionShape || it is MapShape } + .flatMap { it.constrainedMembers() } + .mapNotNull { + val transformation = it.makeNonConstrained(model, additionalNames) + // Keep record of new names that have been generated to ensure none of them regenerated. + additionalNames.add(transformation.newShape.id) + + transformation + } + + return applyTransformations(model, transformations) + } + + /*** + * Returns a Model that has all the transformations applied on the original model. + */ + private fun applyTransformations( + model: Model, + transformations: List, + ): Model { + val modelBuilder = model.toBuilder() + + val memberShapesToReplace = transformations.map { + // Add the new shape to the model. + modelBuilder.addShape(it.newShape) + + it.memberToChange.toBuilder() + .target(it.newShape.id) + .traits(it.traitsToKeep) + .build() + } + + // Change all original constrained member shapes with the new standalone shapes. + return ModelTransformer.create() + .replaceShapes(modelBuilder.build(), memberShapesToReplace) + } + + /** + * Returns a list of members that have constraint traits applied to them + */ + private fun Shape.constrainedMembers(): List = + this.allMembers.values.filter { + it.hasMemberConstraintTrait() + } + + /** + * Returns the unique (within the model) shape ID of the new shape + */ + private fun overriddenShapeId( + model: Model, + additionalNames: Set, + memberShape: ShapeId, + ): ShapeId { + val structName = memberShape.name + val memberName = memberShape.member.orElse(null) + .replaceFirstChar { if (it.isLowerCase()) it.titlecase(Locale.getDefault()) else it.toString() } + + fun makeStructName(suffix: String = "") = + ShapeId.from("${memberShape.namespace}#${structName}${memberName}$suffix") + + fun structNameIsUnique(newName: ShapeId) = + model.getShape(newName).isEmpty && !additionalNames.contains(newName) + + fun generateUniqueName(): ShapeId { + // Ensure the name does not already exist in the model, else make it unique + // by appending a new number as the suffix. + (0..100).forEach { + val extractedStructName = if (it == 0) makeStructName("") else makeStructName("$it") + if (structNameIsUnique(extractedStructName)) { + return extractedStructName + } + } + + throw IllegalStateException("A unique name for the overridden structure type could not be generated") + } + + return generateUniqueName() + } + + /** + * Returns the transformation that would be required to turn the given member shape + * into a non-constrained member shape. + */ + private fun MemberShape.makeNonConstrained( + model: Model, + additionalNames: MutableSet, + ): MemberShapeTransformation { + val (memberConstraintTraits, otherTraits) = this.allTraits.values + .partition { + memberConstraintTraitsToOverride.contains(it.javaClass) + } + + check(memberConstraintTraits.isNotEmpty()) { + "There must at least be one member constraint on the shape" + } + + // Build a new shape similar to the target of the constrained member shape. It should + // have all of the original constraints that have not been overridden, and the ones + // that this member shape overrides. + val targetShape = model.expectShape(this.target) + if (targetShape !is ToSmithyBuilder<*>) { + UNREACHABLE("Member target shapes will always be buildable") + } + + return when (val builder = targetShape.toBuilder()) { + is AbstractShapeBuilder<*, *> -> { + // Use the target builder to create a new standalone shape that would + // be added to the model later on. Keep all existing traits on the target + // but replace the ones that are overridden on the member shape. + val nonOverriddenConstraintTraits = + builder.allTraits.values.filter { existingTrait -> + memberConstraintTraits.none { it.toShapeId() == existingTrait.toShapeId() } + } + + // Add a synthetic constraint on all new shapes being defined, that would link + // the new shape to the root structure from which it is reachable. + val syntheticTrait = + SyntheticStructureFromConstrainedMemberTrait(model.expectShape(this.container), this) + + // Combine target traits, overridden traits and the synthetic trait + val newTraits = + nonOverriddenConstraintTraits + memberConstraintTraits + syntheticTrait + + // Create a new unique standalone shape that will be added to the model later on + val shapeId = overriddenShapeId(model, additionalNames, this.id) + val standaloneShape = builder.id(shapeId) + .traits(newTraits) + .build() + + // Since the new shape has not been added to the model as yet, the current + // memberShape's target cannot be changed to the new shape. + MemberShapeTransformation(standaloneShape, this, otherTraits) + } + + else -> UNREACHABLE("Constraint traits cannot to applied on ${this.id}") + } + } +} diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/transformers/RecursiveConstraintViolationBoxer.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/transformers/RecursiveConstraintViolationBoxer.kt new file mode 100644 index 0000000000..d2e41ead36 --- /dev/null +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/transformers/RecursiveConstraintViolationBoxer.kt @@ -0,0 +1,78 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +package software.amazon.smithy.rust.codegen.server.smithy.transformers + +import software.amazon.smithy.model.Model +import software.amazon.smithy.model.shapes.MemberShape +import software.amazon.smithy.model.shapes.Shape +import software.amazon.smithy.rust.codegen.core.smithy.transformers.RecursiveShapeBoxer +import software.amazon.smithy.rust.codegen.core.util.hasTrait +import software.amazon.smithy.rust.codegen.server.smithy.traits.ConstraintViolationRustBoxTrait + +object RecursiveConstraintViolationBoxer { + /** + * Transform a model which may contain recursive shapes into a model annotated with [ConstraintViolationRustBoxTrait]. + * + * See [RecursiveShapeBoxer] for how the tagging algorithm works. + * + * The constraint violation graph needs to box types in recursive paths more often. Since we don't collect + * constraint violations (yet, see [0]), the constraint violation graph never holds `Vec`s or `HashMap`s, + * only simple types. Indeed, the following simple recursive model: + * + * ```smithy + * union Recursive { + * list: List + * } + * + * @length(min: 69) + * list List { + * member: Recursive + * } + * ``` + * + * has a cycle that goes through a list shape, so no shapes in it need boxing in the regular shape graph. However, + * the constraint violation graph is infinitely recursive if we don't introduce boxing somewhere: + * + * ```rust + * pub mod model { + * pub mod list { + * pub enum ConstraintViolation { + * Length(usize), + * Member( + * usize, + * crate::model::recursive::ConstraintViolation, + * ), + * } + * } + * + * pub mod recursive { + * pub enum ConstraintViolation { + * List(crate::model::list::ConstraintViolation), + * } + * } + * } + * ``` + * + * So what we do to fix this is to configure the `RecursiveShapeBoxer` model transform so that the "cycles through + * lists and maps introduce indirection" assumption can be lifted. This allows this model transform to tag member + * shapes along recursive paths with a new trait, `ConstraintViolationRustBoxTrait`, that the constraint violation + * type generation then utilizes to ensure that no infinitely recursive constraint violation types get generated. + * Places where constraint violations are handled (like where unconstrained types are converted to constrained + * types) must account for the scenario where they now are or need to be boxed. + * + * [0] https://github.com/awslabs/smithy-rs/pull/2040 + */ + fun transform(model: Model): Model = RecursiveShapeBoxer( + containsIndirectionPredicate = ::constraintViolationLoopContainsIndirection, + boxShapeFn = ::addConstraintViolationRustBoxTrait, + ).transform(model) + + private fun constraintViolationLoopContainsIndirection(loop: Collection): Boolean = + loop.find { it.hasTrait() } != null + + private fun addConstraintViolationRustBoxTrait(memberShape: MemberShape): MemberShape = + memberShape.toBuilder().addTrait(ConstraintViolationRustBoxTrait()).build() +} diff --git a/codegen-server/src/main/resources/META-INF/services/software.amazon.smithy.build.SmithyBuildPlugin b/codegen-server/src/main/resources/META-INF/services/software.amazon.smithy.build.SmithyBuildPlugin index 00f891cb22..392b1d4392 100644 --- a/codegen-server/src/main/resources/META-INF/services/software.amazon.smithy.build.SmithyBuildPlugin +++ b/codegen-server/src/main/resources/META-INF/services/software.amazon.smithy.build.SmithyBuildPlugin @@ -2,4 +2,4 @@ # Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. # SPDX-License-Identifier: Apache-2.0 # -software.amazon.smithy.rust.codegen.server.smithy.RustCodegenServerPlugin +software.amazon.smithy.rust.codegen.server.smithy.RustServerCodegenPlugin diff --git a/codegen-server/src/test/kotlin/software/amazon/smithy/rust/codegen/server/smithy/ConstrainedShapeSymbolProviderTest.kt b/codegen-server/src/test/kotlin/software/amazon/smithy/rust/codegen/server/smithy/ConstrainedShapeSymbolProviderTest.kt index d96c761fa8..21d7e5c48f 100644 --- a/codegen-server/src/test/kotlin/software/amazon/smithy/rust/codegen/server/smithy/ConstrainedShapeSymbolProviderTest.kt +++ b/codegen-server/src/test/kotlin/software/amazon/smithy/rust/codegen/server/smithy/ConstrainedShapeSymbolProviderTest.kt @@ -90,7 +90,7 @@ class ConstrainedShapeSymbolProviderTest { private val model = baseModelString.asSmithyModel() private val serviceShape = model.lookup("test#TestService") private val symbolProvider = serverTestSymbolProvider(model, serviceShape) - private val constrainedShapeSymbolProvider = ConstrainedShapeSymbolProvider(symbolProvider, model, serviceShape) + private val constrainedShapeSymbolProvider = ConstrainedShapeSymbolProvider(symbolProvider, serviceShape, true) companion object { @JvmStatic diff --git a/codegen-server/src/test/kotlin/software/amazon/smithy/rust/codegen/server/smithy/ConstraintsMemberShapeTest.kt b/codegen-server/src/test/kotlin/software/amazon/smithy/rust/codegen/server/smithy/ConstraintsMemberShapeTest.kt new file mode 100644 index 0000000000..f5b619ea74 --- /dev/null +++ b/codegen-server/src/test/kotlin/software/amazon/smithy/rust/codegen/server/smithy/ConstraintsMemberShapeTest.kt @@ -0,0 +1,500 @@ +/* + * 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.matchers.shouldBe +import io.kotest.matchers.shouldNotBe +import org.junit.jupiter.api.Test +import software.amazon.smithy.aws.traits.DataTrait +import software.amazon.smithy.model.Model +import software.amazon.smithy.model.SourceLocation +import software.amazon.smithy.model.shapes.ShapeId +import software.amazon.smithy.model.traits.RequiredTrait +import software.amazon.smithy.model.traits.Trait +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 +import software.amazon.smithy.rust.codegen.core.smithy.RuntimeConfig +import software.amazon.smithy.rust.codegen.core.smithy.RuntimeCrateLocation +import software.amazon.smithy.rust.codegen.core.smithy.RustCrate +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.testutil.generatePluginContext +import software.amazon.smithy.rust.codegen.core.testutil.unitTest +import software.amazon.smithy.rust.codegen.core.util.runCommand +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.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.testutil.serverTestCodegenContext +import software.amazon.smithy.rust.codegen.server.smithy.transformers.ConstrainedMemberTransform +import java.io.File +import java.nio.file.Path + +class ConstraintsMemberShapeTest { + private val outputModelOnly = """ + namespace constrainedMemberShape + + use aws.protocols#restJson1 + use aws.api#data + + @restJson1 + service ConstrainedService { + operations: [OperationUsingGet] + } + + @http(uri: "/anOperation", method: "GET") + operation OperationUsingGet { + output : OperationUsingGetOutput + } + structure OperationUsingGetOutput { + plainLong : Long + plainInteger : Integer + plainShort : Short + plainByte : Byte + plainFloat: Float + plainString: String + + @range(min: 1, max:100) + constrainedLong : Long + @range(min: 2, max:100) + constrainedInteger : Integer + @range(min: 3, max:100) + constrainedShort : Short + @range(min: 4, max:100) + constrainedByte : Byte + @length(max: 100) + constrainedString: String + + @required + @range(min: 5, max:100) + requiredConstrainedLong : Long + @required + @range(min: 6, max:100) + requiredConstrainedInteger : Integer + @required + @range(min: 7, max:100) + requiredConstrainedShort : Short + @required + @range(min: 8, max:100) + requiredConstrainedByte : Byte + @required + @length(max: 101) + requiredConstrainedString: String + + patternString : PatternString + + @data("content") + @pattern("^[g-m]+${'$'}") + constrainedPatternString : PatternString + + plainStringList : PlainStringList + patternStringList : PatternStringList + patternStringListOverride : PatternStringListOverride + + plainStructField : PlainStructWithInteger + structWithConstrainedMember : StructWithConstrainedMember + structWithConstrainedMemberOverride : StructWithConstrainedMemberOverride + + patternUnion: PatternUnion + patternUnionOverride: PatternUnionOverride + patternMap : PatternMap + patternMapOverride: PatternMapOverride + } + list ListWithIntegerMemberStruct { + member: PlainStructWithInteger + } + structure PlainStructWithInteger { + lat : Integer + long : Integer + } + structure StructWithConstrainedMember { + @range(min: 100) + lat : Integer + long : Integer + } + structure StructWithConstrainedMemberOverride { + @range(min: 10) + lat : RangedInteger + @range(min: 10, max:100) + long : RangedInteger + } + list PlainStringList { + member: String + } + list PatternStringList { + member: PatternString + } + list PatternStringListOverride { + @pattern("^[g-m]+${'$'}") + member: PatternString + } + map PatternMap { + key: PatternString, + value: PatternString + } + map PatternMapOverride { + @pattern("^[g-m]+${'$'}") + key: PatternString, + @pattern("^[g-m]+${'$'}") + value: PatternString + } + union PatternUnion { + first: PatternString, + second: PatternString + } + union PatternUnionOverride { + @pattern("^[g-m]+${'$'}") + first: PatternString, + @pattern("^[g-m]+${'$'}") + second: PatternString + } + @pattern("^[a-m]+${'$'}") + string PatternString + @range(min: 0, max:1000) + integer RangedInteger + """.asSmithyModel() + + private fun loadModel(model: Model): Model = + ConstrainedMemberTransform.transform(OperationNormalizer.transform(model)) + + @Test + fun `non constrained fields should not be changed`() { + val transformedModel = loadModel(outputModelOnly) + + fun checkFieldTargetRemainsSame(fieldName: String) { + checkMemberShapeIsSame( + transformedModel, + outputModelOnly, + "constrainedMemberShape.synthetic#OperationUsingGetOutput\$$fieldName", + "constrainedMemberShape#OperationUsingGetOutput\$$fieldName", + ) { + "OperationUsingGetOutput$fieldName has changed whereas it is not constrained and should have remained same" + } + } + + setOf( + "plainInteger", + "plainLong", + "plainByte", + "plainShort", + "plainFloat", + "patternString", + "plainStringList", + "patternStringList", + "patternStringListOverride", + "plainStructField", + "structWithConstrainedMember", + "structWithConstrainedMemberOverride", + "patternUnion", + "patternUnionOverride", + "patternMap", + "patternMapOverride", + ).forEach(::checkFieldTargetRemainsSame) + + checkMemberShapeIsSame( + transformedModel, + outputModelOnly, + "constrainedMemberShape#StructWithConstrainedMember\$long", + "constrainedMemberShape#StructWithConstrainedMember\$long", + ) + } + + @Test + fun `constrained members should have a different target now`() { + val transformedModel = loadModel(outputModelOnly) + checkMemberShapeChanged( + transformedModel, + outputModelOnly, + "constrainedMemberShape#PatternStringListOverride\$member", + "constrainedMemberShape#PatternStringListOverride\$member", + ) + + fun checkSyntheticFieldTargetChanged(fieldName: String) { + checkMemberShapeChanged( + transformedModel, + outputModelOnly, + "constrainedMemberShape.synthetic#OperationUsingGetOutput\$$fieldName", + "constrainedMemberShape#OperationUsingGetOutput\$$fieldName", + ) { + "constrained member $fieldName should have been changed into a new type." + } + } + + fun checkFieldTargetChanged(memberNameWithContainer: String) { + checkMemberShapeChanged( + transformedModel, + outputModelOnly, + "constrainedMemberShape#$memberNameWithContainer", + "constrainedMemberShape#$memberNameWithContainer", + ) { + "constrained member $memberNameWithContainer should have been changed into a new type." + } + } + + setOf( + "constrainedLong", + "constrainedByte", + "constrainedShort", + "constrainedInteger", + "constrainedString", + "requiredConstrainedString", + "requiredConstrainedLong", + "requiredConstrainedByte", + "requiredConstrainedInteger", + "requiredConstrainedShort", + "constrainedPatternString", + ).forEach(::checkSyntheticFieldTargetChanged) + + setOf( + "StructWithConstrainedMember\$lat", + "PatternMapOverride\$key", + "PatternMapOverride\$value", + "PatternStringListOverride\$member", + ).forEach(::checkFieldTargetChanged) + } + + @Test + fun `extra trait on a constrained member should remain on it`() { + val transformedModel = loadModel(outputModelOnly) + checkShapeHasTrait( + transformedModel, + outputModelOnly, + "constrainedMemberShape.synthetic#OperationUsingGetOutput\$constrainedPatternString", + "constrainedMemberShape#OperationUsingGetOutput\$constrainedPatternString", + DataTrait("content", SourceLocation.NONE), + ) + } + + @Test + fun `required remains on constrained member shape`() { + val transformedModel = loadModel(outputModelOnly) + checkShapeHasTrait( + transformedModel, + outputModelOnly, + "constrainedMemberShape.synthetic#OperationUsingGetOutput\$requiredConstrainedString", + "constrainedMemberShape#OperationUsingGetOutput\$requiredConstrainedString", + RequiredTrait(), + ) + } + + private fun runServerCodeGen(model: Model, dirToUse: File? = null, writable: Writable): Path { + val runtimeConfig = + RuntimeConfig(runtimeCrateLocation = RuntimeCrateLocation.Path(File("../rust-runtime").absolutePath)) + + val (context, dir) = generatePluginContext( + model, + runtimeConfig = runtimeConfig, + overrideTestDir = dirToUse, + ) + val codegenDecorator = + CombinedServerCodegenDecorator.fromClasspath( + context, + ServerRequiredCustomizations(), + SmithyValidationExceptionDecorator(), + CustomValidationExceptionWithReasonDecorator(), + ) + + ServerCodegenVisitor(context, codegenDecorator) + .execute() + + val codegenContext = serverTestCodegenContext(model) + val settings = ServerRustSettings.from(context.model, context.settings) + val rustCrate = RustCrate( + context.fileManifest, + codegenContext.symbolProvider, + settings.codegenConfig, + codegenContext.expectModuleDocProvider(), + ) + + // We cannot write to the lib anymore as the RustWriter overwrites it, so writing code directly to check.rs + // and then adding a `mod check;` to the lib.rs + rustCrate.withModule(RustModule.public("check")) { + writable(this) + File("$dir/src/check.rs").writeText(toString()) + } + + val lib = File("$dir/src/lib.rs") + val libContents = lib.readText() + "\nmod check;" + lib.writeText(libContents) + + return dir + } + + @Test + fun `generate code and check member constrained shapes are in the right modules`() { + val dir = runServerCodeGen(outputModelOnly) { + fun RustWriter.testTypeExistsInBuilderModule(typeName: String) { + unitTest( + "builder_module_has_${typeName.toSnakeCase()}", + """ + #[allow(unused_imports)] use crate::output::operation_using_get_output::$typeName; + """, + ) + } + + // All directly constrained members of the output structure should be in the builder module + setOf( + "ConstrainedLong", + "ConstrainedByte", + "ConstrainedShort", + "ConstrainedInteger", + "ConstrainedString", + "RequiredConstrainedString", + "RequiredConstrainedLong", + "RequiredConstrainedByte", + "RequiredConstrainedInteger", + "RequiredConstrainedShort", + "ConstrainedPatternString", + ).forEach(::testTypeExistsInBuilderModule) + + fun Set.generateUseStatements(prefix: String) = + this.joinToString(separator = "\n") { + "#[allow(unused_imports)] use $prefix::$it;" + } + + unitTest( + "map_overridden_enum", + setOf( + "Value", + "value::ConstraintViolation as ValueCV", + "Key", + "key::ConstraintViolation as KeyCV", + ).generateUseStatements("crate::model::pattern_map_override"), + ) + + unitTest( + "union_overridden_enum", + setOf( + "First", + "first::ConstraintViolation as FirstCV", + "Second", + "second::ConstraintViolation as SecondCV", + ).generateUseStatements("crate::model::pattern_union_override"), + ) + + unitTest( + "list_overridden_enum", + setOf( + "Member", + "member::ConstraintViolation as MemberCV", + ).generateUseStatements("crate::model::pattern_string_list_override"), + ) + } + + val env = mapOf("RUSTFLAGS" to "-A dead_code") + "cargo test".runCommand(dir, env) + } + + /** + * Checks that the given member shape: + * 1. Has been changed to a new shape + * 2. New shape has the same type as the original shape's target e.g. float Centigrade, + * float newType + */ + private fun checkMemberShapeChanged( + model: Model, + baseModel: Model, + member: String, + orgModelMember: String, + lazyMessage: () -> Any = ::defaultError, + ) { + val memberId = ShapeId.from(member) + assert(model.getShape(memberId).isPresent) { lazyMessage } + + val memberShape = model.expectShape(memberId).asMemberShape().get() + val memberTargetShape = model.expectShape(memberShape.target) + val orgMemberId = ShapeId.from(orgModelMember) + assert(baseModel.getShape(orgMemberId).isPresent) { lazyMessage } + + val beforeTransformMemberShape = baseModel.expectShape(orgMemberId).asMemberShape().get() + val originalTargetShape = model.expectShape(beforeTransformMemberShape.target) + + val extractableConstraintTraits = allConstraintTraits - RequiredTrait::class.java + + // New member shape should not have the overridden constraints on it + assert(!extractableConstraintTraits.any(memberShape::hasTrait)) { lazyMessage } + + // Target shape has to be changed to a new shape + memberTargetShape.id.name shouldNotBe beforeTransformMemberShape.target.name + + // Target shape's name should match the expected name + val expectedName = memberShape.container.name.substringAfter('#') + + memberShape.memberName.substringBefore('#').toPascalCase() + + memberTargetShape.id.name shouldBe expectedName + + // New shape should have all the constraint traits that were on the member shape, + // and it should also have the traits that the target type contains. + val beforeTransformConstraintTraits = + beforeTransformMemberShape.allTraits.values.filter { allConstraintTraits.contains(it.javaClass) }.toSet() + val newShapeConstrainedTraits = + memberTargetShape.allTraits.values.filter { allConstraintTraits.contains(it.javaClass) }.toSet() + + val leftOutConstraintTrait = beforeTransformConstraintTraits - newShapeConstrainedTraits + assert( + leftOutConstraintTrait.isEmpty() || leftOutConstraintTrait.all { + it.toShapeId() == RequiredTrait.ID + }, + ) { lazyMessage } + + // In case the target shape has some more constraints, which the member shape did not override, + // then those still need to apply on the new standalone shape that has been defined. + val leftOverTraits = originalTargetShape.allTraits.values + .filter { beforeOverridingTrait -> + beforeTransformConstraintTraits.none { + beforeOverridingTrait.toShapeId() == it.toShapeId() + } + } + val allNewShapeTraits = memberTargetShape.allTraits.values.toList() + assert((leftOverTraits + newShapeConstrainedTraits).all { it in allNewShapeTraits }) { lazyMessage } + } + + private fun defaultError() = "test failed" + + /** + * Checks that the given shape has not changed in the transformed model and is exactly + * the same as the original model + */ + private fun checkMemberShapeIsSame( + model: Model, + baseModel: Model, + member: String, + orgModelMember: String, + lazyMessage: () -> Any = ::defaultError, + ) { + val memberId = ShapeId.from(member) + assert(model.getShape(memberId).isPresent) { lazyMessage } + + val memberShape = model.expectShape(memberId).asMemberShape().get() + val memberTargetShape = model.expectShape(memberShape.target) + val originalShape = baseModel.expectShape(ShapeId.from(orgModelMember)).asMemberShape().get() + + // Member shape should not have any constraints on it + assert(!memberShape.hasConstraintTrait()) { lazyMessage } + // Target shape has to be same as the original shape + memberTargetShape.id shouldBe originalShape.target + } + + private fun checkShapeHasTrait( + model: Model, + orgModel: Model, + member: String, + orgModelMember: String, + trait: Trait, + ) { + val memberId = ShapeId.from(member) + val memberShape = model.expectShape(memberId).asMemberShape().get() + val orgMemberShape = orgModel.expectShape(ShapeId.from(orgModelMember)).asMemberShape().get() + + val newMemberTrait = memberShape.expectTrait(trait::class.java) + val oldMemberTrait = orgMemberShape.expectTrait(trait::class.java) + + newMemberTrait shouldBe oldMemberTrait + } +} diff --git a/codegen-server/src/test/kotlin/software/amazon/smithy/rust/codegen/server/smithy/ConstraintsTest.kt b/codegen-server/src/test/kotlin/software/amazon/smithy/rust/codegen/server/smithy/ConstraintsTest.kt index 946027ce02..30e5e64813 100644 --- a/codegen-server/src/test/kotlin/software/amazon/smithy/rust/codegen/server/smithy/ConstraintsTest.kt +++ b/codegen-server/src/test/kotlin/software/amazon/smithy/rust/codegen/server/smithy/ConstraintsTest.kt @@ -8,6 +8,7 @@ package software.amazon.smithy.rust.codegen.server.smithy import io.kotest.inspectors.forAll import io.kotest.matchers.shouldBe import org.junit.jupiter.api.Test +import software.amazon.smithy.model.shapes.BooleanShape import software.amazon.smithy.model.shapes.ListShape import software.amazon.smithy.model.shapes.MapShape import software.amazon.smithy.model.shapes.MemberShape @@ -81,7 +82,12 @@ class ConstraintsTest { @length(min: 1, max: 5) mapAPrecedence: MapA } - """.asSmithyModel() + + structure StructWithInnerDefault { + @default(false) + inner: PrimitiveBoolean + } + """.asSmithyModel(smithyVersion = "2") private val symbolProvider = serverTestSymbolProvider(model) private val testInputOutput = model.lookup("test#TestInputOutput") @@ -93,6 +99,8 @@ class ConstraintsTest { private val structA = model.lookup("test#StructureA") private val structAInt = model.lookup("test#StructureA\$int") private val structAString = model.lookup("test#StructureA\$string") + private val structWithInnerDefault = model.lookup("test#StructWithInnerDefault") + private val primitiveBoolean = model.lookup("smithy.api#PrimitiveBoolean") @Test fun `it should detect supported constrained traits as constrained`() { @@ -119,4 +127,10 @@ class ConstraintsTest { mapB.canReachConstrainedShape(model, symbolProvider) shouldBe true recursiveShape.canReachConstrainedShape(model, symbolProvider) shouldBe true } + + @Test + fun `it should not consider shapes with the default trait as constrained`() { + structWithInnerDefault.canReachConstrainedShape(model, symbolProvider) shouldBe false + primitiveBoolean.isDirectlyConstrained(symbolProvider) shouldBe false + } } diff --git a/codegen-server/src/test/kotlin/software/amazon/smithy/rust/codegen/server/smithy/DeriveEqAndHashSymbolMetadataProviderTest.kt b/codegen-server/src/test/kotlin/software/amazon/smithy/rust/codegen/server/smithy/DeriveEqAndHashSymbolMetadataProviderTest.kt index 55c0c6e680..5f2dea66e2 100644 --- a/codegen-server/src/test/kotlin/software/amazon/smithy/rust/codegen/server/smithy/DeriveEqAndHashSymbolMetadataProviderTest.kt +++ b/codegen-server/src/test/kotlin/software/amazon/smithy/rust/codegen/server/smithy/DeriveEqAndHashSymbolMetadataProviderTest.kt @@ -171,8 +171,8 @@ internal class DeriveEqAndHashSymbolMetadataProviderTest { """.asSmithyModel(smithyVersion = "2.0") private val serviceShape = model.lookup("test#TestService") private val deriveEqAndHashSymbolMetadataProvider = serverTestSymbolProvider(model, serviceShape) - .let { BaseSymbolMetadataProvider(it, model, additionalAttributes = listOf()) } - .let { DeriveEqAndHashSymbolMetadataProvider(it, model) } + .let { BaseSymbolMetadataProvider(it, additionalAttributes = listOf()) } + .let { DeriveEqAndHashSymbolMetadataProvider(it) } companion object { @JvmStatic diff --git a/codegen-server/src/test/kotlin/software/amazon/smithy/rust/codegen/server/smithy/PubCrateConstrainedShapeSymbolProviderTest.kt b/codegen-server/src/test/kotlin/software/amazon/smithy/rust/codegen/server/smithy/PubCrateConstrainedShapeSymbolProviderTest.kt index f0b339a485..8d07a5959a 100644 --- a/codegen-server/src/test/kotlin/software/amazon/smithy/rust/codegen/server/smithy/PubCrateConstrainedShapeSymbolProviderTest.kt +++ b/codegen-server/src/test/kotlin/software/amazon/smithy/rust/codegen/server/smithy/PubCrateConstrainedShapeSymbolProviderTest.kt @@ -55,13 +55,17 @@ class PubCrateConstrainedShapeSymbolProviderTest { @Test fun `it should crash when provided with a shape that is directly constrained`() { val constrainedStringShape = model.lookup("test#ConstrainedString") - shouldThrow { pubCrateConstrainedShapeSymbolProvider.toSymbol(constrainedStringShape) } + shouldThrow { + pubCrateConstrainedShapeSymbolProvider.toSymbol(constrainedStringShape) + } } @Test fun `it should crash when provided with a shape that is unconstrained`() { val unconstrainedStringShape = model.lookup("test#UnconstrainedString") - shouldThrow { pubCrateConstrainedShapeSymbolProvider.toSymbol(unconstrainedStringShape) } + shouldThrow { + pubCrateConstrainedShapeSymbolProvider.toSymbol(unconstrainedStringShape) + } } @Test diff --git a/codegen-server/src/test/kotlin/software/amazon/smithy/rust/codegen/server/smithy/RecursiveConstraintViolationsTest.kt b/codegen-server/src/test/kotlin/software/amazon/smithy/rust/codegen/server/smithy/RecursiveConstraintViolationsTest.kt new file mode 100644 index 0000000000..7467d0d76f --- /dev/null +++ b/codegen-server/src/test/kotlin/software/amazon/smithy/rust/codegen/server/smithy/RecursiveConstraintViolationsTest.kt @@ -0,0 +1,185 @@ +/* + * 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.extension.ExtensionContext +import org.junit.jupiter.params.ParameterizedTest +import org.junit.jupiter.params.provider.Arguments +import org.junit.jupiter.params.provider.ArgumentsProvider +import org.junit.jupiter.params.provider.ArgumentsSource +import software.amazon.smithy.model.Model +import software.amazon.smithy.rust.codegen.core.testutil.asSmithyModel +import software.amazon.smithy.rust.codegen.server.smithy.testutil.serverIntegrationTest +import java.util.stream.Stream + +internal class RecursiveConstraintViolationsTest { + + data class TestCase( + /** The test name is only used in the generated report, to easily identify a failing test. **/ + val testName: String, + /** The model to generate **/ + val model: Model, + /** The shape ID of the member shape that should have the marker trait attached. **/ + val shapeIdWithConstraintViolationRustBoxTrait: String, + ) + + class RecursiveConstraintViolationsTestProvider : ArgumentsProvider { + private val baseModel = + """ + namespace com.amazonaws.recursiveconstraintviolations + + use aws.protocols#restJson1 + use smithy.framework#ValidationException + + @restJson1 + service RecursiveConstraintViolations { + operations: [ + Operation + ] + } + + @http(uri: "/operation", method: "POST") + operation Operation { + input: Recursive + output: Recursive + errors: [ValidationException] + } + """ + + private fun recursiveListModel(sparse: Boolean, listPrefix: String = ""): Pair = + """ + $baseModel + + structure Recursive { + list: ${listPrefix}List + } + + ${ if (sparse) { "@sparse" } else { "" } } + @length(min: 69) + list ${listPrefix}List { + member: Recursive + } + """.asSmithyModel() to if ("${listPrefix}List" < "Recursive") { + "com.amazonaws.recursiveconstraintviolations#${listPrefix}List\$member" + } else { + "com.amazonaws.recursiveconstraintviolations#Recursive\$list" + } + + private fun recursiveMapModel(sparse: Boolean, mapPrefix: String = ""): Pair = + """ + $baseModel + + structure Recursive { + map: ${mapPrefix}Map + } + + ${ if (sparse) { "@sparse" } else { "" } } + @length(min: 69) + map ${mapPrefix}Map { + key: String, + value: Recursive + } + """.asSmithyModel() to if ("${mapPrefix}Map" < "Recursive") { + "com.amazonaws.recursiveconstraintviolations#${mapPrefix}Map\$value" + } else { + "com.amazonaws.recursiveconstraintviolations#Recursive\$map" + } + + private fun recursiveUnionModel(unionPrefix: String = ""): Pair = + """ + $baseModel + + structure Recursive { + attributeValue: ${unionPrefix}AttributeValue + } + + // Named `${unionPrefix}AttributeValue` in honor of DynamoDB's famous `AttributeValue`. + // https://docs.rs/aws-sdk-dynamodb/latest/aws_sdk_dynamodb/model/enum.AttributeValue.html + union ${unionPrefix}AttributeValue { + set: SetAttribute + } + + @uniqueItems + list SetAttribute { + member: ${unionPrefix}AttributeValue + } + """.asSmithyModel() to + // The first loop the algorithm picks out to fix turns out to be the `list <-> union` loop: + // + // ``` + // [ + // ${unionPrefix}AttributeValue, + // ${unionPrefix}AttributeValue$set, + // SetAttribute, + // SetAttribute$member + // ] + // ``` + // + // Upon which, after fixing it, the other loop (`structure <-> list <-> union`) already contains + // indirection, so we disregard it. + // + // This is hence a good test in that it tests that `RecursiveConstraintViolationBoxer` does not + // superfluously add more indirection than strictly necessary. + // However, it is a bad test in that if the Smithy library ever returns the recursive paths in a + // different order, the (`structure <-> list <-> union`) loop might be fixed first, and this test might + // start to fail! So watch out for that. Nonetheless, `RecursiveShapeBoxer` calls out: + // + // This function MUST be deterministic (always choose the same shapes to `Box`). If it is not, that is a bug. + // + // So I think it's fair to write this test under the above assumption. + if ("${unionPrefix}AttributeValue" < "SetAttribute") { + "com.amazonaws.recursiveconstraintviolations#${unionPrefix}AttributeValue\$set" + } else { + "com.amazonaws.recursiveconstraintviolations#SetAttribute\$member" + } + + override fun provideArguments(context: ExtensionContext?): Stream { + val listModels = listOf(false, true).flatMap { isSparse -> + listOf("", "ZZZ").map { listPrefix -> + val (model, shapeIdWithConstraintViolationRustBoxTrait) = recursiveListModel(isSparse, listPrefix) + var testName = "${ if (isSparse) "sparse" else "non-sparse" } recursive list" + if (listPrefix.isNotEmpty()) { + testName += " with shape name prefix $listPrefix" + } + TestCase(testName, model, shapeIdWithConstraintViolationRustBoxTrait) + } + } + val mapModels = listOf(false, true).flatMap { isSparse -> + listOf("", "ZZZ").map { mapPrefix -> + val (model, shapeIdWithConstraintViolationRustBoxTrait) = recursiveMapModel(isSparse, mapPrefix) + var testName = "${ if (isSparse) "sparse" else "non-sparse" } recursive map" + if (mapPrefix.isNotEmpty()) { + testName += " with shape name prefix $mapPrefix" + } + TestCase(testName, model, shapeIdWithConstraintViolationRustBoxTrait) + } + } + val unionModels = listOf("", "ZZZ").map { unionPrefix -> + val (model, shapeIdWithConstraintViolationRustBoxTrait) = recursiveUnionModel(unionPrefix) + var testName = "recursive union" + if (unionPrefix.isNotEmpty()) { + testName += " with shape name prefix $unionPrefix" + } + TestCase(testName, model, shapeIdWithConstraintViolationRustBoxTrait) + } + return listOf(listModels, mapModels, unionModels) + .flatten() + .map { Arguments.of(it) }.stream() + } + } + + /** + * Ensures the models generate code that compiles. + * + * Make sure the tests in [software.amazon.smithy.rust.codegen.server.smithy.transformers.RecursiveConstraintViolationBoxerTest] + * are all passing before debugging any of these tests, since the former tests test preconditions for these. + */ + @ParameterizedTest + @ArgumentsSource(RecursiveConstraintViolationsTestProvider::class) + fun `recursive constraint violation code generation test`(testCase: TestCase) { + serverIntegrationTest(testCase.model) + } +} diff --git a/codegen-server/src/test/kotlin/software/amazon/smithy/rust/codegen/server/smithy/RustCrateInlineModuleComposingWriterTest.kt b/codegen-server/src/test/kotlin/software/amazon/smithy/rust/codegen/server/smithy/RustCrateInlineModuleComposingWriterTest.kt new file mode 100644 index 0000000000..d4358aa4c0 --- /dev/null +++ b/codegen-server/src/test/kotlin/software/amazon/smithy/rust/codegen/server/smithy/RustCrateInlineModuleComposingWriterTest.kt @@ -0,0 +1,274 @@ +/* + * 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.matchers.collections.shouldContain +import org.junit.jupiter.api.Test +import software.amazon.smithy.model.Model +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.comment +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.RuntimeConfig +import software.amazon.smithy.rust.codegen.core.smithy.RuntimeCrateLocation +import software.amazon.smithy.rust.codegen.core.smithy.RustCrate +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.generatePluginContext +import software.amazon.smithy.rust.codegen.core.testutil.unitTest +import software.amazon.smithy.rust.codegen.server.smithy.testutil.serverTestCodegenContext +import software.amazon.smithy.rust.codegen.server.smithy.testutil.serverTestSymbolProvider +import java.io.File + +class RustCrateInlineModuleComposingWriterTest { + private val rustCrate: RustCrate + private val codegenContext: ServerCodegenContext + private val model: Model = """ + ${'$'}version: "2.0" + namespace test + + use aws.api#data + use aws.protocols#restJson1 + + @title("Weather Service") + @restJson1 + service WeatherService { + operations: [MalformedPatternOverride] + } + + @suppress(["UnstableTrait"]) + @http(uri: "/MalformedPatternOverride", method: "GET") + operation MalformedPatternOverride { + output: MalformedPatternOverrideInput, + errors: [] + } + + structure MalformedPatternOverrideInput { + @pattern("^[g-m]+${'$'}") + string: PatternString, + } + + @pattern("^[a-m]+${'$'}") + string PatternString + """.trimIndent().asSmithyModel() + + init { + codegenContext = serverTestCodegenContext(model) + val runtimeConfig = + RuntimeConfig(runtimeCrateLocation = RuntimeCrateLocation.Path(File("../rust-runtime").absolutePath)) + + val (context, _) = generatePluginContext( + model, + runtimeConfig = runtimeConfig, + ) + val settings = ServerRustSettings.from(context.model, context.settings) + rustCrate = RustCrate( + context.fileManifest, + codegenContext.symbolProvider, + settings.codegenConfig, + codegenContext.expectModuleDocProvider(), + ) + } + + private fun createTestInlineModule(parentModule: RustModule, moduleName: String): RustModule.LeafModule = + RustModule.new( + moduleName, + visibility = Visibility.PUBLIC, + parent = parentModule, + inline = true, + ) + + private fun createTestOrphanInlineModule(moduleName: String): RustModule.LeafModule = + RustModule.new( + moduleName, + visibility = Visibility.PUBLIC, + parent = RustModule.LibRs, + inline = true, + ) + + private fun helloWorld(writer: RustWriter, moduleName: String) { + writer.rustBlock("pub fn hello_world()") { + writer.comment("Module $moduleName") + } + } + + private fun byeWorld(writer: RustWriter, moduleName: String) { + writer.rustBlock("pub fn bye_world()") { + writer.comment("Module $moduleName") + writer.rust("""println!("from inside $moduleName");""") + } + } + + @Test + fun `calling withModule multiple times returns same object on rustModule`() { + val testProject = TestWorkspace.testProject(serverTestSymbolProvider(model)) + val writers: MutableSet = mutableSetOf() + testProject.withModule(ServerRustModule.Model) { + writers.add(this) + } + testProject.withModule(ServerRustModule.Model) { + writers shouldContain this + } + } + + @Test + fun `simple inline module works`() { + val testProject = TestWorkspace.testProject(serverTestSymbolProvider(model)) + val moduleA = createTestInlineModule(ServerRustModule.Model, "a") + testProject.withModule(ServerRustModule.Model) { + testProject.getInlineModuleWriter().withInlineModule(this, moduleA) { + helloWorld(this, "a") + } + } + + testProject.getInlineModuleWriter().render() + testProject.withModule(ServerRustModule.Model) { + this.unitTest("test_a") { + rust("crate::model::a::hello_world();") + } + } + testProject.compileAndTest() + } + + @Test + fun `creating nested modules works from different rustWriters`() { + // Define the following functions in different inline modules. + // crate::model::a::hello_world(); + // crate::model::a::bye_world(); + // crate::model::b::hello_world(); + // crate::model::b::bye_world(); + // crate::model::b::c::hello_world(); + // crate::model::b::c::bye_world(); + // crate::input::e::hello_world(); + // crate::output::f::hello_world(); + // crate::output::f::g::hello_world(); + // crate::output::h::hello_world(); + // crate::output::h::i::hello_world(); + + val testProject = TestWorkspace.testProject(serverTestSymbolProvider(model)) + val modules = hashMapOf( + "a" to createTestOrphanInlineModule("a"), + "d" to createTestOrphanInlineModule("d"), + "e" to createTestOrphanInlineModule("e"), + "i" to createTestOrphanInlineModule("i"), + ) + + modules["b"] = createTestInlineModule(ServerRustModule.Model, "b") + modules["c"] = createTestInlineModule(modules["b"]!!, "c") + modules["f"] = createTestInlineModule(ServerRustModule.Output, "f") + modules["g"] = createTestInlineModule(modules["f"]!!, "g") + modules["h"] = createTestInlineModule(ServerRustModule.Output, "h") + // A different kotlin object but would still go in the right place + + testProject.withModule(ServerRustModule.Model) { + testProject.getInlineModuleWriter().withInlineModule(this, modules["a"]!!) { + helloWorld(this, "a") + } + testProject.getInlineModuleWriter().withInlineModule(this, modules["b"]!!) { + helloWorld(this, "b") + testProject.getInlineModuleWriter().withInlineModule(this, modules["c"]!!) { + byeWorld(this, "b::c") + } + } + // Writing to the same module crate::model::a second time should work. + testProject.getInlineModuleWriter().withInlineModule(this, modules["a"]!!) { + byeWorld(this, "a") + } + // Writing to model::b, when model::b and model::b::c have already been written to + // should work. + testProject.getInlineModuleWriter().withInlineModule(this, modules["b"]!!) { + byeWorld(this, "b") + } + } + + // Write directly to an inline module without specifying the immediate parent. crate::model::b::c + // should have a `hello_world` fn in it now. + testProject.withModule(ServerRustModule.Model) { + testProject.getInlineModuleWriter().withInlineModuleHierarchy(this, modules["c"]!!) { + helloWorld(this, "c") + } + } + // Write to a different top level module to confirm that works. + testProject.withModule(ServerRustModule.Input) { + testProject.getInlineModuleWriter().withInlineModuleHierarchy(this, modules["e"]!!) { + helloWorld(this, "e") + } + } + + // Create a descendent inner module crate::output::f::g and then try writing to the intermediate inner module + // that did not exist before the descendent was dded. + testProject.getInlineModuleWriter().withInlineModuleHierarchyUsingCrate(testProject, modules["f"]!!) { + testProject.getInlineModuleWriter().withInlineModuleHierarchyUsingCrate(testProject, modules["g"]!!) { + helloWorld(this, "g") + } + } + + testProject.getInlineModuleWriter().withInlineModuleHierarchyUsingCrate(testProject, modules["f"]!!) { + helloWorld(this, "f") + } + + // It should work even if the inner descendent module was added using `withInlineModuleHierarchy` and then + // code is added to the intermediate module using `withInlineModuleHierarchyUsingCrate` + testProject.withModule(ServerRustModule.Output) { + testProject.getInlineModuleWriter().withInlineModuleHierarchy(this, modules["h"]!!) { + testProject.getInlineModuleWriter().withInlineModuleHierarchy(this, modules["i"]!!) { + helloWorld(this, "i") + } + testProject.withModule(ServerRustModule.Model) { + // While writing to output::h::i, it should be able to a completely different module + testProject.getInlineModuleWriter().withInlineModuleHierarchy(this, modules["b"]!!) { + rustBlock("pub fn some_other_writer_wrote_this()") { + rust("""println!("from inside crate::model::b::some_other_writer_wrote_this");""") + } + } + } + } + } + testProject.getInlineModuleWriter().withInlineModuleHierarchyUsingCrate(testProject, modules["h"]!!) { + helloWorld(this, "h") + } + + // Render all of the code. + testProject.getInlineModuleWriter().render() + + testProject.withModule(ServerRustModule.Model) { + this.unitTest("test_a") { + rust("crate::model::a::hello_world();") + rust("crate::model::a::bye_world();") + } + this.unitTest("test_b") { + rust("crate::model::b::hello_world();") + rust("crate::model::b::bye_world();") + } + this.unitTest("test_someother_writer_wrote") { + rust("crate::model::b::some_other_writer_wrote_this();") + } + this.unitTest("test_b_c") { + rust("crate::model::b::c::hello_world();") + rust("crate::model::b::c::bye_world();") + } + this.unitTest("test_e") { + rust("crate::input::e::hello_world();") + } + this.unitTest("test_f") { + rust("crate::output::f::hello_world();") + } + this.unitTest("test_g") { + rust("crate::output::f::g::hello_world();") + } + this.unitTest("test_h") { + rust("crate::output::h::hello_world();") + } + this.unitTest("test_h_i") { + rust("crate::output::h::i::hello_world();") + } + } + testProject.compileAndTest() + } +} diff --git a/codegen-server/src/test/kotlin/software/amazon/smithy/rust/codegen/server/smithy/ServerCodegenVisitorTest.kt b/codegen-server/src/test/kotlin/software/amazon/smithy/rust/codegen/server/smithy/ServerCodegenVisitorTest.kt index 2f23c143f4..942e5f7617 100644 --- a/codegen-server/src/test/kotlin/software/amazon/smithy/rust/codegen/server/smithy/ServerCodegenVisitorTest.kt +++ b/codegen-server/src/test/kotlin/software/amazon/smithy/rust/codegen/server/smithy/ServerCodegenVisitorTest.kt @@ -11,6 +11,7 @@ import software.amazon.smithy.model.shapes.ShapeId import software.amazon.smithy.rust.codegen.core.testutil.asSmithyModel import software.amazon.smithy.rust.codegen.core.testutil.generatePluginContext 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 kotlin.io.path.writeText @@ -45,8 +46,12 @@ class ServerCodegenVisitorTest { """.asSmithyModel(smithyVersion = "2.0") val (ctx, testDir) = generatePluginContext(model) testDir.resolve("src/main.rs").writeText("fn main() {}") - val codegenDecorator: CombinedServerCodegenDecorator = - CombinedServerCodegenDecorator.fromClasspath(ctx, ServerRequiredCustomizations()) + val codegenDecorator = + CombinedServerCodegenDecorator.fromClasspath( + ctx, + ServerRequiredCustomizations(), + SmithyValidationExceptionDecorator(), + ) val visitor = ServerCodegenVisitor(ctx, codegenDecorator) val baselineModel = visitor.baselineTransformInternalTest(model) baselineModel.getShapesWithTrait(ShapeId.from("smithy.api#mixin")).isEmpty() shouldBe true 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 83ea701a97..68b88a7978 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 @@ -18,6 +18,7 @@ import software.amazon.smithy.model.shapes.ServiceShape import software.amazon.smithy.rust.codegen.core.smithy.transformers.EventStreamNormalizer 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.util.logging.Level internal class ValidateUnsupportedConstraintsAreNotUsedTest { @@ -26,7 +27,6 @@ internal class ValidateUnsupportedConstraintsAreNotUsedTest { namespace test service TestService { - version: "123", operations: [TestOperation] } @@ -53,7 +53,11 @@ internal class ValidateUnsupportedConstraintsAreNotUsedTest { } """.asSmithyModel() val service = model.lookup("test#TestService") - val validationResult = validateOperationsWithConstrainedInputHaveValidationExceptionAttached(model, service) + val validationResult = validateOperationsWithConstrainedInputHaveValidationExceptionAttached( + model, + service, + SmithyValidationExceptionConversionGenerator.SHAPE_ID, + ) validationResult.messages shouldHaveSize 1 @@ -71,39 +75,6 @@ internal class ValidateUnsupportedConstraintsAreNotUsedTest { """.trimIndent() } - @Test - fun `it should detect when unsupported constraint traits on member shapes are used`() { - val model = - """ - $baseModel - - structure TestInputOutput { - @length(min: 1, max: 69) - lengthString: String - } - """.asSmithyModel() - val validationResult = validateModel(model) - - validationResult.messages shouldHaveSize 1 - validationResult.messages[0].message shouldContain "The member shape `test#TestInputOutput\$lengthString` has the constraint trait `smithy.api#length` attached" - } - - @Test - fun `it should not detect when the required trait on a member shape is used`() { - val model = - """ - $baseModel - - structure TestInputOutput { - @required - string: String - } - """.asSmithyModel() - val validationResult = validateModel(model) - - validationResult.messages shouldHaveSize 0 - } - private val constraintTraitOnStreamingBlobShapeModel = """ $baseModel @@ -181,6 +152,49 @@ internal class ValidateUnsupportedConstraintsAreNotUsedTest { } } + private val mapShapeReachableFromUniqueItemsListShapeModel = + """ + $baseModel + + structure TestInputOutput { + uniqueItemsList: UniqueItemsList + } + + @uniqueItems + list UniqueItemsList { + member: Map + } + + map Map { + key: String + value: String + } + """.asSmithyModel() + + @Test + fun `it should detect when a map shape is reachable from a uniqueItems list shape`() { + val validationResult = validateModel(mapShapeReachableFromUniqueItemsListShapeModel) + + validationResult.messages shouldHaveSize 1 + validationResult.shouldAbort shouldBe true + validationResult.messages[0].message shouldContain( + """ + The map shape `test#Map` is reachable from the list shape `test#UniqueItemsList`, which has the + `@uniqueItems` trait attached. + """.trimIndent().replace("\n", " ") + ) + } + + @Test + fun `it should abort when a map shape is reachable from a uniqueItems list shape, despite opting into ignoreUnsupportedConstraintTraits`() { + val validationResult = validateModel( + mapShapeReachableFromUniqueItemsListShapeModel, + ServerCodegenConfig().copy(ignoreUnsupportedConstraints = true), + ) + + validationResult.shouldAbort shouldBe true + } + @Test fun `it should abort when constraint traits in event streams are used, despite opting into ignoreUnsupportedConstraintTraits`() { val validationResult = validateModel( diff --git a/codegen-server/src/test/kotlin/software/amazon/smithy/rust/codegen/server/smithy/customizations/CustomValidationExceptionWithReasonDecoratorTest.kt b/codegen-server/src/test/kotlin/software/amazon/smithy/rust/codegen/server/smithy/customizations/CustomValidationExceptionWithReasonDecoratorTest.kt new file mode 100644 index 0000000000..31b4602381 --- /dev/null +++ b/codegen-server/src/test/kotlin/software/amazon/smithy/rust/codegen/server/smithy/customizations/CustomValidationExceptionWithReasonDecoratorTest.kt @@ -0,0 +1,109 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +package software.amazon.smithy.rust.codegen.server.smithy.customizations + +import org.junit.jupiter.api.Test +import software.amazon.smithy.model.Model +import software.amazon.smithy.model.node.Node +import software.amazon.smithy.model.shapes.Shape +import software.amazon.smithy.model.shapes.ShapeId +import software.amazon.smithy.model.transform.ModelTransformer +import software.amazon.smithy.rust.codegen.core.testutil.IntegrationTestParams +import software.amazon.smithy.rust.codegen.core.testutil.asSmithyModel +import software.amazon.smithy.rust.codegen.server.smithy.testutil.serverIntegrationTest +import java.io.File +import kotlin.streams.toList + +fun swapOutSmithyValidationExceptionForCustomOne(model: Model): Model { + val customValidationExceptionModel = + """ + namespace com.amazonaws.constraints + + enum ValidationExceptionFieldReason { + LENGTH_NOT_VALID = "LengthNotValid" + PATTERN_NOT_VALID = "PatternNotValid" + SYNTAX_NOT_VALID = "SyntaxNotValid" + VALUE_NOT_VALID = "ValueNotValid" + OTHER = "Other" + } + + /// Stores information about a field passed inside a request that resulted in an exception. + structure ValidationExceptionField { + /// The field name. + @required + Name: String + + @required + Reason: ValidationExceptionFieldReason + + /// Message describing why the field failed validation. + @required + Message: String + } + + /// A list of fields. + list ValidationExceptionFieldList { + member: ValidationExceptionField + } + + enum ValidationExceptionReason { + FIELD_VALIDATION_FAILED = "FieldValidationFailed" + UNKNOWN_OPERATION = "UnknownOperation" + CANNOT_PARSE = "CannotParse" + OTHER = "Other" + } + + /// The input fails to satisfy the constraints specified by an AWS service. + @error("client") + @httpError(400) + structure ValidationException { + /// Description of the error. + @required + Message: String + + /// Reason the request failed validation. + @required + Reason: ValidationExceptionReason + + /// The field that caused the error, if applicable. If more than one field + /// caused the error, pick one and elaborate in the message. + Fields: ValidationExceptionFieldList + } + """.asSmithyModel(smithyVersion = "2.0") + + // Remove Smithy's `ValidationException`. + var model = ModelTransformer.create().removeShapes( + model, + listOf(model.expectShape(SmithyValidationExceptionConversionGenerator.SHAPE_ID)), + ) + // Add our custom one. + model = ModelTransformer.create().replaceShapes(model, customValidationExceptionModel.shapes().toList()) + // Make all operations use our custom one. + val newOperationShapes = model.operationShapes.map { operationShape -> + operationShape.toBuilder().addError(ShapeId.from("com.amazonaws.constraints#ValidationException")).build() + } + return ModelTransformer.create().replaceShapes(model, newOperationShapes) +} + +internal class CustomValidationExceptionWithReasonDecoratorTest { + @Test + fun `constraints model with the CustomValidationExceptionWithReasonDecorator applied compiles`() { + var model = File("../codegen-core/common-test-models/constraints.smithy").readText().asSmithyModel() + model = swapOutSmithyValidationExceptionForCustomOne(model) + + serverIntegrationTest( + model, + IntegrationTestParams( + additionalSettings = Node.objectNodeBuilder().withMember( + "codegen", + Node.objectNodeBuilder() + .withMember("experimentalCustomValidationExceptionWithReasonPleaseDoNotUse", "com.amazonaws.constraints#ValidationException") + .build(), + ).build(), + ), + ) + } +} diff --git a/codegen-server/src/test/kotlin/software/amazon/smithy/rust/codegen/server/smithy/customizations/PostprocessValidationExceptionNotAttachedErrorMessageDecoratorTest.kt b/codegen-server/src/test/kotlin/software/amazon/smithy/rust/codegen/server/smithy/customizations/PostprocessValidationExceptionNotAttachedErrorMessageDecoratorTest.kt new file mode 100644 index 0000000000..9130d30b33 --- /dev/null +++ b/codegen-server/src/test/kotlin/software/amazon/smithy/rust/codegen/server/smithy/customizations/PostprocessValidationExceptionNotAttachedErrorMessageDecoratorTest.kt @@ -0,0 +1,73 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +package software.amazon.smithy.rust.codegen.server.smithy.customizations + +import io.kotest.matchers.shouldBe +import io.kotest.matchers.string.shouldContain +import org.junit.jupiter.api.Test +import org.junit.jupiter.api.assertThrows +import software.amazon.smithy.codegen.core.CodegenException +import software.amazon.smithy.rust.codegen.core.testutil.asSmithyModel +import software.amazon.smithy.rust.codegen.server.smithy.LogMessage +import software.amazon.smithy.rust.codegen.server.smithy.ValidationResult +import software.amazon.smithy.rust.codegen.server.smithy.customize.ServerCodegenDecorator +import software.amazon.smithy.rust.codegen.server.smithy.testutil.serverIntegrationTest + +internal class PostprocessValidationExceptionNotAttachedErrorMessageDecoratorTest { + @Test + fun `validation exception not attached error message is postprocessed if decorator is registered`() { + val model = + """ + namespace test + use aws.protocols#restJson1 + + @restJson1 + service TestService { + operations: ["ConstrainedOperation"], + } + + operation ConstrainedOperation { + input: ConstrainedOperationInput + } + + structure ConstrainedOperationInput { + @required + requiredString: String + } + """.asSmithyModel() + + val validationExceptionNotAttachedErrorMessageDummyPostprocessorDecorator = object : ServerCodegenDecorator { + override val name: String + get() = "ValidationExceptionNotAttachedErrorMessageDummyPostprocessorDecorator" + override val order: Byte + get() = 69 + + override fun postprocessValidationExceptionNotAttachedErrorMessage(validationResult: ValidationResult): ValidationResult { + check(validationResult.messages.size == 1) + + val level = validationResult.messages.first().level + val message = + """ + ${validationResult.messages.first().message} + + There are three things all wise men fear: the sea in storm, a night with no moon, and the anger of a gentle man. + """ + + return validationResult.copy(messages = listOf(LogMessage(level, message))) + } + } + + val exception = assertThrows { + serverIntegrationTest( + model, + additionalDecorators = listOf(validationExceptionNotAttachedErrorMessageDummyPostprocessorDecorator), + ) + } + val exceptionCause = (exception.cause!! as ValidationResult) + exceptionCause.messages.size shouldBe 1 + exceptionCause.messages.first().message shouldContain "There are three things all wise men fear: the sea in storm, a night with no moon, and the anger of a gentle man." + } +} 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 cfb8bbd38b..060e0166a4 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 @@ -15,7 +15,6 @@ import org.junit.jupiter.params.provider.ArgumentsSource import software.amazon.smithy.model.Model import software.amazon.smithy.model.shapes.BlobShape import software.amazon.smithy.rust.codegen.core.rustlang.RustWriter -import software.amazon.smithy.rust.codegen.core.smithy.ModelsModule 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 @@ -23,6 +22,9 @@ 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.dq 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.testutil.serverTestCodegenContext import java.util.stream.Stream @@ -66,9 +68,16 @@ class ConstrainedBlobGeneratorTest { val project = TestWorkspace.testProject(symbolProvider) - project.withModule(ModelsModule) { + project.withModule(ServerRustModule.Model) { addDependency(RuntimeType.blob(codegenContext.runtimeConfig).toSymbol()) - ConstrainedBlobGenerator(codegenContext, this, constrainedBlobShape).render() + + ConstrainedBlobGenerator( + codegenContext, + this.createTestInlineModuleCreator(), + this, + constrainedBlobShape, + SmithyValidationExceptionConversionGenerator(codegenContext), + ).render() unitTest( name = "try_from_success", @@ -119,9 +128,15 @@ class ConstrainedBlobGeneratorTest { val codegenContext = serverTestCodegenContext(model) - val writer = RustWriter.forModule(ModelsModule.name) + val writer = RustWriter.forModule(ServerRustModule.Model.name) - ConstrainedBlobGenerator(codegenContext, writer, constrainedBlobShape).render() + ConstrainedBlobGenerator( + codegenContext, + writer.createTestInlineModuleCreator(), + writer, + constrainedBlobShape, + SmithyValidationExceptionConversionGenerator(codegenContext), + ).render() // Check that the wrapped type is `pub(crate)`. 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 8e043ec47c..cce7da4aa6 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 @@ -25,7 +25,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.ModelsModule 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 @@ -33,6 +32,9 @@ import software.amazon.smithy.rust.codegen.core.testutil.unitTest import software.amazon.smithy.rust.codegen.core.util.UNREACHABLE import software.amazon.smithy.rust.codegen.core.util.lookup import software.amazon.smithy.rust.codegen.server.smithy.ServerCodegenContext +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.testutil.serverTestCodegenContext import software.amazon.smithy.rust.codegen.server.smithy.transformers.ShapesReachableFromOperationInputTagger import java.util.stream.Stream @@ -174,7 +176,7 @@ class ConstrainedCollectionGeneratorTest { else -> UNREACHABLE("Shape is either list or set.") } - project.withModule(ModelsModule) { + project.withModule(ServerRustModule.Model) { render(codegenContext, this, shape) val instantiator = serverInstantiator(codegenContext) @@ -268,7 +270,7 @@ class ConstrainedCollectionGeneratorTest { """.asSmithyModel().let(ShapesReachableFromOperationInputTagger::transform) val constrainedCollectionShape = model.lookup("test#ConstrainedList") - val writer = RustWriter.forModule(ModelsModule.name) + val writer = RustWriter.forModule(ServerRustModule.Model.name) val codegenContext = serverTestCodegenContext(model) render(codegenContext, writer, constrainedCollectionShape) @@ -284,6 +286,12 @@ class ConstrainedCollectionGeneratorTest { ) { val constraintsInfo = CollectionTraitInfo.fromShape(constrainedCollectionShape, codegenContext.symbolProvider) ConstrainedCollectionGenerator(codegenContext, writer, constrainedCollectionShape, constraintsInfo).render() - CollectionConstraintViolationGenerator(codegenContext, writer, constrainedCollectionShape, constraintsInfo).render() + CollectionConstraintViolationGenerator( + codegenContext, + writer.createTestInlineModuleCreator(), + constrainedCollectionShape, + constraintsInfo, + SmithyValidationExceptionConversionGenerator(codegenContext), + ).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 2da058cde5..0eebb7e36b 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 @@ -17,13 +17,15 @@ import software.amazon.smithy.model.node.ObjectNode import software.amazon.smithy.model.shapes.MapShape 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.smithy.ModelsModule 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.server.smithy.ServerCodegenContext +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.testutil.serverTestCodegenContext import software.amazon.smithy.rust.codegen.server.smithy.transformers.ShapesReachableFromOperationInputTagger import java.util.stream.Stream @@ -76,7 +78,7 @@ class ConstrainedMapGeneratorTest { val project = TestWorkspace.testProject(symbolProvider) - project.withModule(ModelsModule) { + project.withModule(ServerRustModule.Model) { render(codegenContext, this, constrainedMapShape) val instantiator = serverInstantiator(codegenContext) @@ -138,7 +140,7 @@ class ConstrainedMapGeneratorTest { """.asSmithyModel().let(ShapesReachableFromOperationInputTagger::transform) val constrainedMapShape = model.lookup("test#ConstrainedMap") - val writer = RustWriter.forModule(ModelsModule.name) + val writer = RustWriter.forModule(ServerRustModule.Model.name) val codegenContext = serverTestCodegenContext(model) render(codegenContext, writer, constrainedMapShape) @@ -153,6 +155,11 @@ class ConstrainedMapGeneratorTest { constrainedMapShape: MapShape, ) { ConstrainedMapGenerator(codegenContext, writer, constrainedMapShape).render() - MapConstraintViolationGenerator(codegenContext, writer, constrainedMapShape).render() + MapConstraintViolationGenerator( + codegenContext, + writer.createTestInlineModuleCreator(), + constrainedMapShape, + SmithyValidationExceptionConversionGenerator(codegenContext), + ).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 681d0fffe3..5a78574c93 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 @@ -14,12 +14,14 @@ import org.junit.jupiter.params.provider.ArgumentsSource import software.amazon.smithy.model.Model import software.amazon.smithy.model.shapes.NumberShape import software.amazon.smithy.rust.codegen.core.rustlang.RustWriter -import software.amazon.smithy.rust.codegen.core.smithy.ModelsModule 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.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.testutil.serverTestCodegenContext import java.util.stream.Stream @@ -70,8 +72,14 @@ class ConstrainedNumberGeneratorTest { val project = TestWorkspace.testProject(symbolProvider) - project.withModule(ModelsModule) { - ConstrainedNumberGenerator(codegenContext, this, shape).render() + project.withModule(ServerRustModule.Model) { + ConstrainedNumberGenerator( + codegenContext, + this.createTestInlineModuleCreator(), + this, + shape, + SmithyValidationExceptionConversionGenerator(codegenContext), + ).render() unitTest( name = "try_from_success", @@ -131,8 +139,14 @@ class ConstrainedNumberGeneratorTest { val codegenContext = serverTestCodegenContext(model) - val writer = RustWriter.forModule(ModelsModule.name) - ConstrainedNumberGenerator(codegenContext, writer, constrainedShape).render() + val writer = RustWriter.forModule(ServerRustModule.Model.name) + ConstrainedNumberGenerator( + codegenContext, + writer.createTestInlineModuleCreator(), + writer, + constrainedShape, + SmithyValidationExceptionConversionGenerator(codegenContext), + ).render() // Check that the wrapped type is `pub(crate)`. writer.toString() shouldContain "pub struct $shapeName(pub(crate) $rustType);" 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 a8fa6e441d..62a7d061c3 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 @@ -16,13 +16,15 @@ import org.junit.jupiter.params.provider.ArgumentsSource import software.amazon.smithy.model.Model import software.amazon.smithy.model.shapes.StringShape import software.amazon.smithy.rust.codegen.core.rustlang.RustWriter -import software.amazon.smithy.rust.codegen.core.smithy.ModelsModule 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.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.testutil.serverTestCodegenContext import java.util.stream.Stream @@ -81,8 +83,14 @@ class ConstrainedStringGeneratorTest { val project = TestWorkspace.testProject(symbolProvider) - project.withModule(ModelsModule) { - ConstrainedStringGenerator(codegenContext, this, constrainedStringShape).render() + project.withModule(ServerRustModule.Model) { + ConstrainedStringGenerator( + codegenContext, + this.createTestInlineModuleCreator(), + this, + constrainedStringShape, + SmithyValidationExceptionConversionGenerator(codegenContext), + ).render() unitTest( name = "try_from_success", @@ -134,9 +142,15 @@ class ConstrainedStringGeneratorTest { val codegenContext = serverTestCodegenContext(model) - val writer = RustWriter.forModule(ModelsModule.name) + val writer = RustWriter.forModule(ServerRustModule.Model.name) - ConstrainedStringGenerator(codegenContext, writer, constrainedStringShape).render() + ConstrainedStringGenerator( + codegenContext, + writer.createTestInlineModuleCreator(), + writer, + constrainedStringShape, + SmithyValidationExceptionConversionGenerator(codegenContext), + ).render() // Check that the wrapped type is `pub(crate)`. writer.toString() shouldContain "pub struct ConstrainedString(pub(crate) std::string::String);" @@ -161,9 +175,22 @@ class ConstrainedStringGeneratorTest { val project = TestWorkspace.testProject(codegenContext.symbolProvider) - project.withModule(ModelsModule) { - ConstrainedStringGenerator(codegenContext, this, constrainedStringShape).render() - ConstrainedStringGenerator(codegenContext, this, sensitiveConstrainedStringShape).render() + project.withModule(ServerRustModule.Model) { + val validationExceptionConversionGenerator = SmithyValidationExceptionConversionGenerator(codegenContext) + ConstrainedStringGenerator( + codegenContext, + this.createTestInlineModuleCreator(), + this, + constrainedStringShape, + validationExceptionConversionGenerator, + ).render() + ConstrainedStringGenerator( + codegenContext, + this.createTestInlineModuleCreator(), + this, + sensitiveConstrainedStringShape, + validationExceptionConversionGenerator, + ).render() unitTest( name = "non_sensitive_string_display_implementation", @@ -200,8 +227,14 @@ class ConstrainedStringGeneratorTest { val codegenContext = serverTestCodegenContext(model) val project = TestWorkspace.testProject(codegenContext.symbolProvider) - project.withModule(ModelsModule) { - ConstrainedStringGenerator(codegenContext, this, constrainedStringShape).render() + project.withModule(ServerRustModule.Model) { + ConstrainedStringGenerator( + codegenContext, + this.createTestInlineModuleCreator(), + this, + constrainedStringShape, + SmithyValidationExceptionConversionGenerator(codegenContext), + ).render() } assertThrows { diff --git a/codegen-server/src/test/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerBuilderConstraintViolationsTest.kt b/codegen-server/src/test/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerBuilderConstraintViolationsTest.kt new file mode 100644 index 0000000000..1f685909f3 --- /dev/null +++ b/codegen-server/src/test/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerBuilderConstraintViolationsTest.kt @@ -0,0 +1,49 @@ +/* + * 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 org.junit.jupiter.api.Test +import software.amazon.smithy.rust.codegen.core.testutil.asSmithyModel +import software.amazon.smithy.rust.codegen.server.smithy.testutil.serverIntegrationTest + +class ServerBuilderConstraintViolationsTest { + + // This test exists not to regress on [this](https://github.com/awslabs/smithy-rs/issues/2343) issue. + // We generated constraint violation variants, pointing to a structure (StructWithInnerDefault below), + // but the structure was not constrained, because the structure's member have a default value + // and default values are validated at generation time from the model. + @Test + fun `it should not generate constraint violations for members with a default value`() { + val model = """ + namespace test + + use aws.protocols#restJson1 + use smithy.framework#ValidationException + + @restJson1 + service SimpleService { + operations: [Operation] + } + + @http(uri: "/operation", method: "POST") + operation Operation { + input: OperationInput + errors: [ValidationException] + } + + structure OperationInput { + @required + requiredStructureWithInnerDefault: StructWithInnerDefault + } + + structure StructWithInnerDefault { + @default(false) + inner: PrimitiveBoolean + } + """.asSmithyModel(smithyVersion = "2") + serverIntegrationTest(model) + } +} diff --git a/codegen-server/src/test/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerBuilderDefaultValuesTest.kt b/codegen-server/src/test/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerBuilderDefaultValuesTest.kt index 2219c2e653..580ebff60c 100644 --- a/codegen-server/src/test/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerBuilderDefaultValuesTest.kt +++ b/codegen-server/src/test/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerBuilderDefaultValuesTest.kt @@ -15,13 +15,14 @@ import software.amazon.smithy.model.shapes.StructureShape 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.conditionalBlock +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.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.RustCrate 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.generators.implBlock 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 @@ -31,6 +32,8 @@ import software.amazon.smithy.rust.codegen.core.util.lookup 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.ServerCodegenConfig +import software.amazon.smithy.rust.codegen.server.smithy.customizations.SmithyValidationExceptionConversionGenerator +import software.amazon.smithy.rust.codegen.server.smithy.renderInlineMemoryModules import software.amazon.smithy.rust.codegen.server.smithy.testutil.serverTestCodegenContext import software.amazon.smithy.rust.codegen.server.smithy.testutil.serverTestRustSettings import software.amazon.smithy.rust.codegen.server.smithy.testutil.serverTestSymbolProvider @@ -104,10 +107,10 @@ class ServerBuilderDefaultValuesTest { project.withModule(RustModule.public("model")) { when (builderGeneratorKind) { BuilderGeneratorKind.SERVER_BUILDER_GENERATOR -> { - writeServerBuilderGenerator(this, model, symbolProvider) + writeServerBuilderGenerator(project, this, model, symbolProvider) } BuilderGeneratorKind.SERVER_BUILDER_GENERATOR_WITHOUT_PUBLIC_CONSTRAINED_TYPES -> { - writeServerBuilderGeneratorWithoutPublicConstrainedTypes(this, model, symbolProvider) + writeServerBuilderGeneratorWithoutPublicConstrainedTypes(project, this, model, symbolProvider) } } @@ -143,6 +146,7 @@ class ServerBuilderDefaultValuesTest { ) } + project.renderInlineMemoryModules() // Run clippy because the builder's code for handling `@default` is prone to upset it. project.compileAndTest(runClippy = true) } @@ -167,7 +171,7 @@ class ServerBuilderDefaultValuesTest { .map { it.key to "${it.value}.into()" } } - private fun writeServerBuilderGeneratorWithoutPublicConstrainedTypes(writer: RustWriter, model: Model, symbolProvider: RustSymbolProvider) { + private fun writeServerBuilderGeneratorWithoutPublicConstrainedTypes(rustCrate: RustCrate, writer: RustWriter, model: Model, symbolProvider: RustSymbolProvider) { val struct = model.lookup("com.test#MyStruct") val codegenContext = serverTestCodegenContext( model, @@ -175,29 +179,37 @@ class ServerBuilderDefaultValuesTest { codegenConfig = ServerCodegenConfig(publicConstrainedTypes = false), ), ) - val builderGenerator = ServerBuilderGeneratorWithoutPublicConstrainedTypes(codegenContext, struct) + val builderGenerator = ServerBuilderGeneratorWithoutPublicConstrainedTypes(codegenContext, struct, SmithyValidationExceptionConversionGenerator(codegenContext)) - writer.implBlock(struct, symbolProvider) { + writer.implBlock(symbolProvider.toSymbol(struct)) { builderGenerator.renderConvenienceMethod(writer) } - builderGenerator.render(writer) - - ServerEnumGenerator(codegenContext, writer, model.lookup("com.test#Language")).render() - StructureGenerator(model, symbolProvider, writer, struct).render() + builderGenerator.render(rustCrate, writer) + + ServerEnumGenerator( + codegenContext, + model.lookup("com.test#Language"), + SmithyValidationExceptionConversionGenerator(codegenContext), + ).render(writer) + StructureGenerator(model, symbolProvider, writer, struct, emptyList()).render() } - private fun writeServerBuilderGenerator(writer: RustWriter, model: Model, symbolProvider: RustSymbolProvider) { + private fun writeServerBuilderGenerator(rustCrate: RustCrate, writer: RustWriter, model: Model, symbolProvider: RustSymbolProvider) { val struct = model.lookup("com.test#MyStruct") val codegenContext = serverTestCodegenContext(model) - val builderGenerator = ServerBuilderGenerator(codegenContext, struct) + val builderGenerator = ServerBuilderGenerator(codegenContext, struct, SmithyValidationExceptionConversionGenerator(codegenContext)) - writer.implBlock(struct, symbolProvider) { + writer.implBlock(symbolProvider.toSymbol(struct)) { builderGenerator.renderConvenienceMethod(writer) } - builderGenerator.render(writer) - - ServerEnumGenerator(codegenContext, writer, model.lookup("com.test#Language")).render() - StructureGenerator(model, symbolProvider, writer, struct).render() + builderGenerator.render(rustCrate, writer) + + ServerEnumGenerator( + codegenContext, + model.lookup("com.test#Language"), + SmithyValidationExceptionConversionGenerator(codegenContext), + ).render(writer) + StructureGenerator(model, symbolProvider, writer, struct, emptyList()).render() } private fun structSetters(values: Map, optional: Boolean) = writable { diff --git a/codegen-server/src/test/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerBuilderGeneratorTest.kt b/codegen-server/src/test/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerBuilderGeneratorTest.kt index 24688f3654..d26e6b35db 100644 --- a/codegen-server/src/test/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerBuilderGeneratorTest.kt +++ b/codegen-server/src/test/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerBuilderGeneratorTest.kt @@ -7,13 +7,17 @@ package software.amazon.smithy.rust.codegen.server.smithy.generators import org.junit.jupiter.api.Test import software.amazon.smithy.model.shapes.StructureShape -import software.amazon.smithy.rust.codegen.core.rustlang.RustWriter -import software.amazon.smithy.rust.codegen.core.smithy.CodegenTarget +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.smithy.generators.StructureGenerator -import software.amazon.smithy.rust.codegen.core.smithy.generators.implBlock +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.server.smithy.ServerRustModule +import software.amazon.smithy.rust.codegen.server.smithy.customizations.SmithyValidationExceptionConversionGenerator +import software.amazon.smithy.rust.codegen.server.smithy.renderInlineMemoryModules import software.amazon.smithy.rust.codegen.server.smithy.testutil.serverTestCodegenContext class ServerBuilderGeneratorTest { @@ -35,23 +39,40 @@ class ServerBuilderGeneratorTest { """.asSmithyModel() val codegenContext = serverTestCodegenContext(model) - val writer = RustWriter.forModule("model") - val shape = model.lookup("test#Credentials") - StructureGenerator(model, codegenContext.symbolProvider, writer, shape).render(CodegenTarget.SERVER) - val builderGenerator = ServerBuilderGenerator(codegenContext, shape) - builderGenerator.render(writer) - writer.implBlock(shape, codegenContext.symbolProvider) { - builderGenerator.renderConvenienceMethod(this) + val project = TestWorkspace.testProject() + project.withModule(ServerRustModule.Model) { + val writer = this + val shape = model.lookup("test#Credentials") + + StructureGenerator(model, codegenContext.symbolProvider, writer, shape, emptyList()).render() + val builderGenerator = ServerBuilderGenerator( + codegenContext, + shape, + SmithyValidationExceptionConversionGenerator(codegenContext), + ) + + builderGenerator.render(project, writer) + + writer.implBlock(codegenContext.symbolProvider.toSymbol(shape)) { + builderGenerator.renderConvenienceMethod(this) + } + + project.renderInlineMemoryModules() + } + + project.unitTest { + rust( + """ + use super::*; + use crate::model::*; + let builder = Credentials::builder() + .username(Some("admin".to_owned())) + .password(Some("pswd".to_owned())) + .secret_key(Some("12345".to_owned())); + assert_eq!(format!("{:?}", builder), "Builder { username: Some(\"admin\"), password: \"*** Sensitive Data Redacted ***\", secret_key: \"*** Sensitive Data Redacted ***\" }"); + """, + ) } - writer.compileAndTest( - """ - use super::*; - let builder = Credentials::builder() - .username(Some("admin".to_owned())) - .password(Some("pswd".to_owned())) - .secret_key(Some("12345".to_owned())); - assert_eq!(format!("{:?}", builder), "Builder { username: Some(\"admin\"), password: \"*** Sensitive Data Redacted ***\", secret_key: \"*** Sensitive Data Redacted ***\" }"); - """, - ) + project.compileAndTest() } } diff --git a/codegen-server/src/test/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerEnumGeneratorTest.kt b/codegen-server/src/test/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerEnumGeneratorTest.kt index 0e813cebbd..bffb82bb40 100644 --- a/codegen-server/src/test/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerEnumGeneratorTest.kt +++ b/codegen-server/src/test/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerEnumGeneratorTest.kt @@ -12,6 +12,7 @@ import software.amazon.smithy.rust.codegen.core.rustlang.RustWriter 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.util.lookup +import software.amazon.smithy.rust.codegen.server.smithy.customizations.SmithyValidationExceptionConversionGenerator import software.amazon.smithy.rust.codegen.server.smithy.testutil.serverTestCodegenContext class ServerEnumGeneratorTest { @@ -40,7 +41,11 @@ class ServerEnumGeneratorTest { @Test fun `it generates TryFrom, FromStr and errors for enums`() { - ServerEnumGenerator(codegenContext, writer, shape).render() + ServerEnumGenerator( + codegenContext, + shape, + SmithyValidationExceptionConversionGenerator(codegenContext), + ).render(writer) writer.compileAndTest( """ use std::str::FromStr; @@ -53,10 +58,14 @@ class ServerEnumGeneratorTest { @Test fun `it generates enums without the unknown variant`() { - ServerEnumGenerator(codegenContext, writer, shape).render() + ServerEnumGenerator( + codegenContext, + shape, + SmithyValidationExceptionConversionGenerator(codegenContext), + ).render(writer) writer.compileAndTest( """ - // check no unknown + // Check no `Unknown` variant. let instance = InstanceType::T2Micro; match instance { InstanceType::T2Micro => (), @@ -68,7 +77,11 @@ class ServerEnumGeneratorTest { @Test fun `it generates enums without non_exhaustive`() { - ServerEnumGenerator(codegenContext, writer, shape).render() + ServerEnumGenerator( + codegenContext, + shape, + SmithyValidationExceptionConversionGenerator(codegenContext), + ).render(writer) writer.toString() shouldNotContain "#[non_exhaustive]" } } diff --git a/codegen-server/src/test/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerHttpSensitivityGeneratorTest.kt b/codegen-server/src/test/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerHttpSensitivityGeneratorTest.kt index 9a8ae01cab..a90acec6e4 100644 --- a/codegen-server/src/test/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerHttpSensitivityGeneratorTest.kt +++ b/codegen-server/src/test/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerHttpSensitivityGeneratorTest.kt @@ -14,6 +14,7 @@ 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.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.getTrait import software.amazon.smithy.rust.codegen.core.util.inputShape @@ -57,7 +58,7 @@ class ServerHttpSensitivityGeneratorTest { assertEquals(listOf("query_b"), (querySensitivity as QuerySensitivity.NotSensitiveMapValue).queryKeys) val testProject = TestWorkspace.testProject(serverTestSymbolProvider(model)) - testProject.lib { + testProject.testModule { unitTest("query_closure") { rustTemplate( """ @@ -70,6 +71,7 @@ class ServerHttpSensitivityGeneratorTest { ) } } + testProject.compileAndTest() } @@ -104,7 +106,7 @@ class ServerHttpSensitivityGeneratorTest { querySensitivity as QuerySensitivity.SensitiveMapValue val testProject = TestWorkspace.testProject(serverTestSymbolProvider(model)) - testProject.lib { + testProject.testModule { unitTest("query_params_closure") { rustTemplate( """ @@ -152,7 +154,7 @@ class ServerHttpSensitivityGeneratorTest { assert((querySensitivity as QuerySensitivity.NotSensitiveMapValue).queryKeys.isEmpty()) val testProject = TestWorkspace.testProject(serverTestSymbolProvider(model)) - testProject.lib { + testProject.testModule { unitTest("query_params_special_closure") { rustTemplate( """ @@ -200,7 +202,7 @@ class ServerHttpSensitivityGeneratorTest { querySensitivity as QuerySensitivity.SensitiveMapValue val testProject = TestWorkspace.testProject(serverTestSymbolProvider(model)) - testProject.lib { + testProject.testModule { unitTest("query_params_special_closure") { rustTemplate( """ @@ -277,7 +279,7 @@ class ServerHttpSensitivityGeneratorTest { assertEquals(null, (headerData as HeaderSensitivity.NotSensitiveMapValue).prefixHeader) val testProject = TestWorkspace.testProject(serverTestSymbolProvider(model)) - testProject.lib { + testProject.testModule { unitTest("header_closure") { rustTemplate( """ @@ -325,7 +327,7 @@ class ServerHttpSensitivityGeneratorTest { assertEquals("prefix-", (headerData as HeaderSensitivity.SensitiveMapValue).prefixHeader) val testProject = TestWorkspace.testProject(serverTestSymbolProvider(model)) - testProject.lib { + testProject.testModule { unitTest("prefix_headers_closure") { rustTemplate( """ @@ -408,7 +410,7 @@ class ServerHttpSensitivityGeneratorTest { assertEquals("prefix-", asMapValue.prefixHeader) val testProject = TestWorkspace.testProject(serverTestSymbolProvider(model)) - testProject.lib { + testProject.testModule { unitTest("prefix_headers_special_closure") { rustTemplate( """ @@ -462,7 +464,7 @@ class ServerHttpSensitivityGeneratorTest { assert(!asSensitiveMapValue.keySensitive) val testProject = TestWorkspace.testProject(serverTestSymbolProvider(model)) - testProject.lib { + testProject.testModule { unitTest("prefix_headers_special_closure") { rustTemplate( """ @@ -514,7 +516,7 @@ class ServerHttpSensitivityGeneratorTest { assertEquals(listOf(1, 2), labelData.labelIndexes) val testProject = TestWorkspace.testProject(serverTestSymbolProvider(model)) - testProject.lib { + testProject.testModule { unitTest("uri_closure") { rustTemplate( """ diff --git a/codegen-server/src/test/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerInstantiatorTest.kt b/codegen-server/src/test/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerInstantiatorTest.kt index 1bfbd26e2f..bc7ee94c56 100644 --- a/codegen-server/src/test/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerInstantiatorTest.kt +++ b/codegen-server/src/test/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerInstantiatorTest.kt @@ -13,7 +13,6 @@ import software.amazon.smithy.model.shapes.UnionShape import software.amazon.smithy.rust.codegen.core.rustlang.RustModule 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.smithy.generators.EnumGenerator import software.amazon.smithy.rust.codegen.core.smithy.generators.UnionGenerator import software.amazon.smithy.rust.codegen.core.smithy.transformers.RecursiveShapeBoxer import software.amazon.smithy.rust.codegen.core.testutil.TestWorkspace @@ -21,8 +20,10 @@ 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.dq -import software.amazon.smithy.rust.codegen.core.util.expectTrait 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.customizations.SmithyValidationExceptionConversionGenerator +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 @@ -123,7 +124,7 @@ class ServerInstantiatorTest { }, ]) string NamedEnum - """.asSmithyModel().let { RecursiveShapeBoxer.transform(it) } + """.asSmithyModel().let { RecursiveShapeBoxer().transform(it) } private val codegenContext = serverTestCodegenContext(model) private val symbolProvider = codegenContext.symbolProvider @@ -138,45 +139,50 @@ class ServerInstantiatorTest { val data = Node.parse("{}") val project = TestWorkspace.testProject() - project.withModule(RustModule.Model) { - structure.serverRenderWithModelBuilder(model, symbolProvider, this) - inner.serverRenderWithModelBuilder(model, symbolProvider, this) - nestedStruct.serverRenderWithModelBuilder(model, symbolProvider, this) + + project.withModule(ServerRustModule.Model) { + structure.serverRenderWithModelBuilder(project, model, symbolProvider, this) + inner.serverRenderWithModelBuilder(project, model, symbolProvider, this) + nestedStruct.serverRenderWithModelBuilder(project, model, symbolProvider, this) UnionGenerator(model, symbolProvider, this, union).render() - unitTest("server_instantiator_test") { - withBlock("let result = ", ";") { - sut.render(this, structure, data) - } + withInlineModule(RustModule.inlineTests(), null) { + unitTest("server_instantiator_test") { + withBlock("let result = ", ";") { + sut.render(this, structure, data) + } - rust( - """ - use std::collections::HashMap; - use aws_smithy_types::{DateTime, Document}; - - let expected = MyStructRequired { - str: "".to_owned(), - primitive_int: 0, - int: 0, - ts: DateTime::from_secs(0), - byte: 0, - union: NestedUnion::Struct(NestedStruct { - str: "".to_owned(), - num: 0, - }), - structure: NestedStruct { + rust( + """ + use std::collections::HashMap; + use aws_smithy_types::{DateTime, Document}; + use super::*; + + let expected = MyStructRequired { str: "".to_owned(), - num: 0, - }, - list: Vec::new(), - map: HashMap::new(), - doc: Document::Object(HashMap::new()), - }; - assert_eq!(result, expected); - """, - ) + primitive_int: 0, + int: 0, + ts: DateTime::from_secs(0), + byte: 0, + union: NestedUnion::Struct(NestedStruct { + str: "".to_owned(), + num: 0, + }), + structure: NestedStruct { + str: "".to_owned(), + num: 0, + }, + list: Vec::new(), + map: HashMap::new(), + doc: Document::Object(HashMap::new()), + }; + assert_eq!(result, expected); + """, + ) + } } } + project.renderInlineMemoryModules() project.compileAndTest() } @@ -187,8 +193,12 @@ class ServerInstantiatorTest { val data = Node.parse("t2.nano".dq()) val project = TestWorkspace.testProject() - project.withModule(RustModule.Model) { - EnumGenerator(model, symbolProvider, this, shape, shape.expectTrait()).render() + project.withModule(ServerRustModule.Model) { + ServerEnumGenerator( + codegenContext, + shape, + SmithyValidationExceptionConversionGenerator(codegenContext), + ).render(this) unitTest("generate_named_enums") { withBlock("let result = ", ";") { sut.render(this, shape, data) @@ -206,8 +216,12 @@ class ServerInstantiatorTest { val data = Node.parse("t2.nano".dq()) val project = TestWorkspace.testProject() - project.withModule(RustModule.Model) { - EnumGenerator(model, symbolProvider, this, shape, shape.expectTrait()).render() + project.withModule(ServerRustModule.Model) { + ServerEnumGenerator( + codegenContext, + shape, + SmithyValidationExceptionConversionGenerator(codegenContext), + ).render(this) unitTest("generate_unnamed_enums") { withBlock("let result = ", ";") { sut.render(this, shape, data) 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 32f565feba..5851344d96 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 @@ -7,13 +7,14 @@ package software.amazon.smithy.rust.codegen.server.smithy.generators import org.junit.jupiter.api.Test import software.amazon.smithy.model.shapes.StructureShape -import software.amazon.smithy.rust.codegen.core.smithy.ErrorsModule import software.amazon.smithy.rust.codegen.core.smithy.transformers.OperationNormalizer 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.server.smithy.ServerRustModule +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.serverTestSymbolProvider @@ -51,16 +52,14 @@ class ServerOperationErrorGeneratorTest { @Test fun `generates combined error enums`() { val project = TestWorkspace.testProject(symbolProvider) - project.withModule(ErrorsModule) { + project.withModule(ServerRustModule.Error) { listOf("FooException", "ComplexError", "InvalidGreeting", "Deprecated").forEach { - model.lookup("error#$it").serverRenderWithModelBuilder(model, symbolProvider, this) + model.lookup("error#$it").serverRenderWithModelBuilder(project, model, symbolProvider, this) } - val errors = listOf("FooException", "ComplexError", "InvalidGreeting").map { model.lookup("error#$it") } ServerOperationErrorGenerator( model, symbolProvider, - symbolProvider.toSymbol(model.lookup("error#Greeting")), - errors, + model.lookup("error#Greeting"), ).render(this) unitTest( @@ -96,7 +95,7 @@ class ServerOperationErrorGeneratorTest { let error: GreetingError = variant.into(); """, ) - + project.renderInlineMemoryModules() project.compileAndTest() } } 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 6a02765c9a..28a8c1ef4c 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 @@ -8,14 +8,15 @@ 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.smithy.ConstrainedModule -import software.amazon.smithy.rust.codegen.core.smithy.ModelsModule -import software.amazon.smithy.rust.codegen.core.smithy.UnconstrainedModule 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.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.renderInlineMemoryModules import software.amazon.smithy.rust.codegen.server.smithy.testutil.serverRenderWithModelBuilder import software.amazon.smithy.rust.codegen.server.smithy.testutil.serverTestCodegenContext @@ -50,25 +51,35 @@ class UnconstrainedCollectionGeneratorTest { val project = TestWorkspace.testProject(symbolProvider) - project.withModule(ModelsModule) { - model.lookup("test#StructureC").serverRenderWithModelBuilder(model, symbolProvider, this) + project.withModule(ServerRustModule.Model) { + model.lookup("test#StructureC").serverRenderWithModelBuilder(project, model, symbolProvider, this) } - project.withModule(ConstrainedModule) { + project.withModule(ServerRustModule.ConstrainedModule) { listOf(listA, listB).forEach { - PubCrateConstrainedCollectionGenerator(codegenContext, this, it).render() + PubCrateConstrainedCollectionGenerator( + codegenContext, + this.createTestInlineModuleCreator(), + it, + ).render() } } - project.withModule(UnconstrainedModule) unconstrainedModuleWriter@{ - project.withModule(ModelsModule) modelsModuleWriter@{ + project.withModule(ServerRustModule.UnconstrainedModule) unconstrainedModuleWriter@{ + project.withModule(ServerRustModule.Model) modelsModuleWriter@{ listOf(listA, listB).forEach { UnconstrainedCollectionGenerator( codegenContext, - this@unconstrainedModuleWriter, + this@unconstrainedModuleWriter.createTestInlineModuleCreator(), it, ).render() - CollectionConstraintViolationGenerator(codegenContext, this@modelsModuleWriter, it, listOf()).render() + CollectionConstraintViolationGenerator( + codegenContext, + this@modelsModuleWriter.createTestInlineModuleCreator(), + it, + CollectionTraitInfo.fromShape(it, codegenContext.constrainedShapeSymbolProvider), + SmithyValidationExceptionConversionGenerator(codegenContext), + ).render() } this@unconstrainedModuleWriter.unitTest( @@ -121,6 +132,7 @@ class UnconstrainedCollectionGeneratorTest { ) } } + 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 75e176be39..dbb52b85a7 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 @@ -8,14 +8,17 @@ 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.smithy.ConstrainedModule -import software.amazon.smithy.rust.codegen.core.smithy.ModelsModule -import software.amazon.smithy.rust.codegen.core.smithy.UnconstrainedModule +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.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.renderInlineMemoryModules import software.amazon.smithy.rust.codegen.server.smithy.testutil.serverRenderWithModelBuilder import software.amazon.smithy.rust.codegen.server.smithy.testutil.serverTestCodegenContext @@ -50,23 +53,35 @@ class UnconstrainedMapGeneratorTest { val mapA = model.lookup("test#MapA") val mapB = model.lookup("test#MapB") - val project = TestWorkspace.testProject(symbolProvider, debugMode = true) + val project = TestWorkspace.testProject(symbolProvider, CoreCodegenConfig(debugMode = true)) - project.withModule(ModelsModule) { - model.lookup("test#StructureC").serverRenderWithModelBuilder(model, symbolProvider, this) + project.withModule(Model) { + model.lookup("test#StructureC").serverRenderWithModelBuilder(project, model, symbolProvider, this) } - project.withModule(ConstrainedModule) { + project.withModule(ServerRustModule.ConstrainedModule) { listOf(mapA, mapB).forEach { - PubCrateConstrainedMapGenerator(codegenContext, this, it).render() + PubCrateConstrainedMapGenerator( + codegenContext, + this.createTestInlineModuleCreator(), + it, + ).render() } } - project.withModule(UnconstrainedModule) unconstrainedModuleWriter@{ - project.withModule(ModelsModule) modelsModuleWriter@{ + project.withModule(ServerRustModule.UnconstrainedModule) unconstrainedModuleWriter@{ + project.withModule(Model) modelsModuleWriter@{ listOf(mapA, mapB).forEach { - UnconstrainedMapGenerator(codegenContext, this@unconstrainedModuleWriter, it).render() - - MapConstraintViolationGenerator(codegenContext, this@modelsModuleWriter, it).render() + UnconstrainedMapGenerator( + codegenContext, + this@unconstrainedModuleWriter.createTestInlineModuleCreator(), it, + ).render() + + MapConstraintViolationGenerator( + codegenContext, + this@modelsModuleWriter.createTestInlineModuleCreator(), + it, + SmithyValidationExceptionConversionGenerator(codegenContext), + ).render() } this@unconstrainedModuleWriter.unitTest( @@ -159,7 +174,7 @@ class UnconstrainedMapGeneratorTest { ) } } - + project.renderInlineMemoryModules() project.compileAndTest() } } diff --git a/codegen-server/src/test/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/UnconstrainedUnionGeneratorTest.kt b/codegen-server/src/test/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/UnconstrainedUnionGeneratorTest.kt index 4b5eca2d1e..bf1904757e 100644 --- a/codegen-server/src/test/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/UnconstrainedUnionGeneratorTest.kt +++ b/codegen-server/src/test/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/UnconstrainedUnionGeneratorTest.kt @@ -8,14 +8,15 @@ package software.amazon.smithy.rust.codegen.server.smithy.generators import org.junit.jupiter.api.Test import software.amazon.smithy.model.shapes.StructureShape import software.amazon.smithy.model.shapes.UnionShape -import software.amazon.smithy.rust.codegen.core.smithy.ModelsModule -import software.amazon.smithy.rust.codegen.core.smithy.UnconstrainedModule import software.amazon.smithy.rust.codegen.core.smithy.generators.UnionGenerator 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.server.smithy.ServerRustModule +import software.amazon.smithy.rust.codegen.server.smithy.createInlineModuleCreator +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 @@ -42,16 +43,17 @@ class UnconstrainedUnionGeneratorTest { val project = TestWorkspace.testProject(symbolProvider) - project.withModule(ModelsModule) { - model.lookup("test#Structure").serverRenderWithModelBuilder(model, symbolProvider, this) + project.withModule(ServerRustModule.Model) { + model.lookup("test#Structure").serverRenderWithModelBuilder(project, model, symbolProvider, this) } - project.withModule(ModelsModule) { + project.withModule(ServerRustModule.Model) { UnionGenerator(model, symbolProvider, this, unionShape, renderUnknownVariant = false).render() } - project.withModule(UnconstrainedModule) unconstrainedModuleWriter@{ - project.withModule(ModelsModule) modelsModuleWriter@{ - UnconstrainedUnionGenerator(codegenContext, this@unconstrainedModuleWriter, this@modelsModuleWriter, unionShape).render() + + project.withModule(ServerRustModule.UnconstrainedModule) unconstrainedModuleWriter@{ + project.withModule(ServerRustModule.Model) modelsModuleWriter@{ + UnconstrainedUnionGenerator(codegenContext, project.createInlineModuleCreator(), this@modelsModuleWriter, unionShape).render() this@unconstrainedModuleWriter.unitTest( name = "unconstrained_union_fail_to_constrain", @@ -97,6 +99,7 @@ class UnconstrainedUnionGeneratorTest { ) } } + project.renderInlineMemoryModules() project.compileAndTest() } } diff --git a/codegen-server/src/test/kotlin/software/amazon/smithy/rust/codegen/server/smithy/protocols/eventstream/ServerEventStreamBaseRequirements.kt b/codegen-server/src/test/kotlin/software/amazon/smithy/rust/codegen/server/smithy/protocols/eventstream/ServerEventStreamBaseRequirements.kt deleted file mode 100644 index e3ff924db8..0000000000 --- a/codegen-server/src/test/kotlin/software/amazon/smithy/rust/codegen/server/smithy/protocols/eventstream/ServerEventStreamBaseRequirements.kt +++ /dev/null @@ -1,96 +0,0 @@ -/* - * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. - * SPDX-License-Identifier: Apache-2.0 - */ - -package software.amazon.smithy.rust.codegen.server.smithy.protocols.eventstream - -import org.junit.jupiter.api.extension.ExtensionContext -import org.junit.jupiter.params.provider.Arguments -import org.junit.jupiter.params.provider.ArgumentsProvider -import software.amazon.smithy.codegen.core.Symbol -import software.amazon.smithy.model.Model -import software.amazon.smithy.model.shapes.ServiceShape -import software.amazon.smithy.model.shapes.ShapeId -import software.amazon.smithy.model.shapes.StructureShape -import software.amazon.smithy.rust.codegen.core.rustlang.RustWriter -import software.amazon.smithy.rust.codegen.core.smithy.CodegenTarget -import software.amazon.smithy.rust.codegen.core.smithy.RustSymbolProvider -import software.amazon.smithy.rust.codegen.core.smithy.generators.implBlock -import software.amazon.smithy.rust.codegen.core.testutil.EventStreamTestModels -import software.amazon.smithy.rust.codegen.core.testutil.EventStreamTestRequirements -import software.amazon.smithy.rust.codegen.server.smithy.ServerCodegenConfig -import software.amazon.smithy.rust.codegen.server.smithy.ServerCodegenContext -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.ServerOperationErrorGenerator -import software.amazon.smithy.rust.codegen.server.smithy.testutil.serverTestCodegenContext -import software.amazon.smithy.rust.codegen.server.smithy.testutil.serverTestRustSettings -import java.util.stream.Stream - -data class TestCase( - val eventStreamTestCase: EventStreamTestModels.TestCase, - val publicConstrainedTypes: Boolean, -) { - override fun toString(): String = "$eventStreamTestCase, publicConstrainedTypes = $publicConstrainedTypes" -} - -class TestCasesProvider : ArgumentsProvider { - override fun provideArguments(context: ExtensionContext?): Stream = - EventStreamTestModels.TEST_CASES - .flatMap { testCase -> - listOf( - TestCase(testCase, publicConstrainedTypes = false), - TestCase(testCase, publicConstrainedTypes = true), - ) - }.map { Arguments.of(it) }.stream() -} - -abstract class ServerEventStreamBaseRequirements : EventStreamTestRequirements { - abstract val publicConstrainedTypes: Boolean - - override fun createCodegenContext( - model: Model, - serviceShape: ServiceShape, - protocolShapeId: ShapeId, - codegenTarget: CodegenTarget, - ): ServerCodegenContext = serverTestCodegenContext( - model, serviceShape, - serverTestRustSettings( - codegenConfig = ServerCodegenConfig(publicConstrainedTypes = publicConstrainedTypes), - ), - protocolShapeId, - ) - - override fun renderBuilderForShape( - writer: RustWriter, - codegenContext: ServerCodegenContext, - shape: StructureShape, - ) { - if (codegenContext.settings.codegenConfig.publicConstrainedTypes) { - ServerBuilderGenerator(codegenContext, shape).apply { - render(writer) - writer.implBlock(shape, codegenContext.symbolProvider) { - renderConvenienceMethod(writer) - } - } - } else { - ServerBuilderGeneratorWithoutPublicConstrainedTypes(codegenContext, shape).apply { - render(writer) - writer.implBlock(shape, codegenContext.symbolProvider) { - renderConvenienceMethod(writer) - } - } - } - } - - override fun renderOperationError( - writer: RustWriter, - model: Model, - symbolProvider: RustSymbolProvider, - operationSymbol: Symbol, - errors: List, - ) { - ServerOperationErrorGenerator(model, symbolProvider, operationSymbol, errors).render(writer) - } -} diff --git a/codegen-server/src/test/kotlin/software/amazon/smithy/rust/codegen/server/smithy/protocols/eventstream/ServerEventStreamMarshallerGeneratorTest.kt b/codegen-server/src/test/kotlin/software/amazon/smithy/rust/codegen/server/smithy/protocols/eventstream/ServerEventStreamMarshallerGeneratorTest.kt index 860b451ee8..83d7b6c96d 100644 --- a/codegen-server/src/test/kotlin/software/amazon/smithy/rust/codegen/server/smithy/protocols/eventstream/ServerEventStreamMarshallerGeneratorTest.kt +++ b/codegen-server/src/test/kotlin/software/amazon/smithy/rust/codegen/server/smithy/protocols/eventstream/ServerEventStreamMarshallerGeneratorTest.kt @@ -5,45 +5,43 @@ package software.amazon.smithy.rust.codegen.server.smithy.protocols.eventstream +import org.junit.jupiter.api.extension.ExtensionContext import org.junit.jupiter.params.ParameterizedTest +import org.junit.jupiter.params.provider.Arguments +import org.junit.jupiter.params.provider.ArgumentsProvider import org.junit.jupiter.params.provider.ArgumentsSource -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.protocols.Protocol -import software.amazon.smithy.rust.codegen.core.smithy.protocols.serialize.EventStreamMarshallerGenerator -import software.amazon.smithy.rust.codegen.core.testutil.EventStreamTestTools -import software.amazon.smithy.rust.codegen.core.testutil.EventStreamTestVariety -import software.amazon.smithy.rust.codegen.core.testutil.TestEventStreamProject -import software.amazon.smithy.rust.codegen.core.testutil.TestRuntimeConfig -import software.amazon.smithy.rust.codegen.server.smithy.ServerCodegenContext +import software.amazon.smithy.rust.codegen.core.testutil.EventStreamMarshallTestCases.writeMarshallTestCases +import software.amazon.smithy.rust.codegen.core.testutil.EventStreamTestModels +import software.amazon.smithy.rust.codegen.core.testutil.testModule +import software.amazon.smithy.rust.codegen.server.smithy.testutil.serverIntegrationTest +import java.util.stream.Stream class ServerEventStreamMarshallerGeneratorTest { @ParameterizedTest @ArgumentsSource(TestCasesProvider::class) fun test(testCase: TestCase) { - EventStreamTestTools.runTestCase( - testCase.eventStreamTestCase, - object : ServerEventStreamBaseRequirements() { - override val publicConstrainedTypes: Boolean get() = testCase.publicConstrainedTypes - - override fun renderGenerator( - codegenContext: ServerCodegenContext, - project: TestEventStreamProject, - protocol: Protocol, - ): RuntimeType { - return EventStreamMarshallerGenerator( - project.model, - CodegenTarget.SERVER, - TestRuntimeConfig, - project.symbolProvider, - project.streamShape, - protocol.structuredDataSerializer(project.operationShape), - testCase.eventStreamTestCase.requestContentType, - ).render() - } - }, - CodegenTarget.SERVER, - EventStreamTestVariety.Marshall, - ) + serverIntegrationTest(testCase.eventStreamTestCase.model) { codegenContext, rustCrate -> + rustCrate.testModule { + writeMarshallTestCases(codegenContext, testCase.eventStreamTestCase, optionalBuilderInputs = true) + } + } } } + +data class TestCase( + val eventStreamTestCase: EventStreamTestModels.TestCase, + val publicConstrainedTypes: Boolean, +) { + override fun toString(): String = "$eventStreamTestCase, publicConstrainedTypes = $publicConstrainedTypes" +} + +class TestCasesProvider : ArgumentsProvider { + override fun provideArguments(context: ExtensionContext?): Stream = + EventStreamTestModels.TEST_CASES + .flatMap { testCase -> + listOf( + TestCase(testCase, publicConstrainedTypes = false), + TestCase(testCase, publicConstrainedTypes = true), + ) + }.map { Arguments.of(it) }.stream() +} diff --git a/codegen-server/src/test/kotlin/software/amazon/smithy/rust/codegen/server/smithy/protocols/eventstream/ServerEventStreamUnmarshallerGeneratorTest.kt b/codegen-server/src/test/kotlin/software/amazon/smithy/rust/codegen/server/smithy/protocols/eventstream/ServerEventStreamUnmarshallerGeneratorTest.kt index 08a5ef5f58..26f76db187 100644 --- a/codegen-server/src/test/kotlin/software/amazon/smithy/rust/codegen/server/smithy/protocols/eventstream/ServerEventStreamUnmarshallerGeneratorTest.kt +++ b/codegen-server/src/test/kotlin/software/amazon/smithy/rust/codegen/server/smithy/protocols/eventstream/ServerEventStreamUnmarshallerGeneratorTest.kt @@ -7,20 +7,10 @@ package software.amazon.smithy.rust.codegen.server.smithy.protocols.eventstream import org.junit.jupiter.params.ParameterizedTest import org.junit.jupiter.params.provider.ArgumentsSource -import software.amazon.smithy.codegen.core.Symbol -import software.amazon.smithy.model.shapes.StructureShape -import software.amazon.smithy.rust.codegen.core.rustlang.RustWriter -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.BuilderGenerator -import software.amazon.smithy.rust.codegen.core.smithy.generators.implBlock -import software.amazon.smithy.rust.codegen.core.smithy.protocols.Protocol -import software.amazon.smithy.rust.codegen.core.smithy.protocols.parse.EventStreamUnmarshallerGenerator -import software.amazon.smithy.rust.codegen.core.testutil.EventStreamTestTools -import software.amazon.smithy.rust.codegen.core.testutil.EventStreamTestVariety -import software.amazon.smithy.rust.codegen.core.testutil.TestEventStreamProject -import software.amazon.smithy.rust.codegen.server.smithy.ServerCodegenContext -import software.amazon.smithy.rust.codegen.server.smithy.generators.serverBuilderSymbol +import software.amazon.smithy.rust.codegen.core.testutil.EventStreamUnmarshallTestCases.writeUnmarshallTestCases +import software.amazon.smithy.rust.codegen.core.testutil.IntegrationTestParams +import software.amazon.smithy.rust.codegen.core.testutil.testModule +import software.amazon.smithy.rust.codegen.server.smithy.testutil.serverIntegrationTest class ServerEventStreamUnmarshallerGeneratorTest { @ParameterizedTest @@ -32,42 +22,17 @@ class ServerEventStreamUnmarshallerGeneratorTest { return } - EventStreamTestTools.runTestCase( - testCase.eventStreamTestCase, - object : ServerEventStreamBaseRequirements() { - override val publicConstrainedTypes: Boolean get() = testCase.publicConstrainedTypes - - override fun renderGenerator( - codegenContext: ServerCodegenContext, - project: TestEventStreamProject, - protocol: Protocol, - ): RuntimeType { - fun builderSymbol(shape: StructureShape): Symbol = shape.serverBuilderSymbol(codegenContext) - return EventStreamUnmarshallerGenerator( - protocol, - codegenContext, - project.operationShape, - project.streamShape, - ::builderSymbol, - ).render() - } - - // TODO(https://github.com/awslabs/smithy-rs/issues/1442): Delete this function override to use the correct builder from the parent class - override fun renderBuilderForShape( - writer: RustWriter, - codegenContext: ServerCodegenContext, - shape: StructureShape, - ) { - BuilderGenerator(codegenContext.model, codegenContext.symbolProvider, shape).apply { - render(writer) - writer.implBlock(shape, codegenContext.symbolProvider) { - renderConvenienceMethod(writer) - } - } - } - }, - CodegenTarget.SERVER, - EventStreamTestVariety.Unmarshall, - ) + serverIntegrationTest( + testCase.eventStreamTestCase.model, + IntegrationTestParams(service = "test#TestService", addModuleToEventStreamAllowList = true), + ) { codegenContext, rustCrate -> + rustCrate.testModule { + writeUnmarshallTestCases( + codegenContext, + testCase.eventStreamTestCase, + optionalBuilderInputs = true, + ) + } + } } } diff --git a/codegen-server/src/test/kotlin/software/amazon/smithy/rust/codegen/server/smithy/transformers/RecursiveConstraintViolationBoxerTest.kt b/codegen-server/src/test/kotlin/software/amazon/smithy/rust/codegen/server/smithy/transformers/RecursiveConstraintViolationBoxerTest.kt new file mode 100644 index 0000000000..622d380603 --- /dev/null +++ b/codegen-server/src/test/kotlin/software/amazon/smithy/rust/codegen/server/smithy/transformers/RecursiveConstraintViolationBoxerTest.kt @@ -0,0 +1,31 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +package software.amazon.smithy.rust.codegen.server.smithy.transformers + +import io.kotest.matchers.shouldBe +import org.junit.jupiter.params.ParameterizedTest +import org.junit.jupiter.params.provider.ArgumentsSource +import software.amazon.smithy.rust.codegen.core.util.hasTrait +import software.amazon.smithy.rust.codegen.core.util.lookup +import software.amazon.smithy.rust.codegen.server.smithy.RecursiveConstraintViolationsTest +import software.amazon.smithy.rust.codegen.server.smithy.traits.ConstraintViolationRustBoxTrait +import kotlin.streams.toList + +internal class RecursiveConstraintViolationBoxerTest { + @ParameterizedTest + @ArgumentsSource(RecursiveConstraintViolationsTest.RecursiveConstraintViolationsTestProvider::class) + fun `recursive constraint violation boxer test`(testCase: RecursiveConstraintViolationsTest.TestCase) { + val transformed = RecursiveConstraintViolationBoxer.transform(testCase.model) + + val shapesWithConstraintViolationRustBoxTrait = transformed.shapes().filter { + it.hasTrait() + }.toList() + + // Only the provided member shape should have the trait attached. + shapesWithConstraintViolationRustBoxTrait shouldBe + listOf(transformed.lookup(testCase.shapeIdWithConstraintViolationRustBoxTrait)) + } +} diff --git a/design/src/SUMMARY.md b/design/src/SUMMARY.md index 381b27987a..ae41ea5246 100644 --- a/design/src/SUMMARY.md +++ b/design/src/SUMMARY.md @@ -54,6 +54,9 @@ - [RFC-0029: Finding New Home for Credential Types](./rfcs/rfc0029_new_home_for_cred_types.md) - [RFC-0030: Serialization And Deserialization](./rfcs/rfc0030_serialization_and_deserialization.md) - [RFC-0031: Providing Fallback Credentials on Timeout](./rfcs/rfc0031_providing_fallback_credentials_on_timeout.md) + - [RFC-0032: Better Constraint Violations](./rfcs/rfc0032_better_constraint_violations.md) + - [RFC-0033: Improving access to request IDs in SDK clients](./rfcs/rfc0033_improve_sdk_request_id_access.md) + - [RFC-0034: Smithy Orchestrator](./rfcs/rfc0034_smithy_orchestrator.md) - [Contributing](./contributing/overview.md) - [Writing and debugging a low-level feature that relies on HTTP](./contributing/writing_and_debugging_a_low-level_feature_that_relies_on_HTTP.md) diff --git a/design/src/rfcs/overview.md b/design/src/rfcs/overview.md index 4109cd56fb..e388ec4929 100644 --- a/design/src/rfcs/overview.md +++ b/design/src/rfcs/overview.md @@ -41,3 +41,5 @@ - [RFC-0029: Finding New Home for Credential Types](./rfc0029_new_home_for_cred_types.md) - [RFC-0030: Serialization And Deserialization](./rfc0030_serialization_and_deserialization.md) - [RFC-0031: Providing Fallback Credentials on Timeout](./rfc0031_providing_fallback_credentials_on_timeout.md) +- [RFC-0032: Better Constraint Violations](./rfc0032_better_constraint_violations.md) +- [RFC-0033: Improving access to request IDs in SDK clients](./rfc0033_improve_sdk_request_id_access.md) diff --git a/design/src/rfcs/rfc0020_service_builder.md b/design/src/rfcs/rfc0020_service_builder.md index a0fad97e30..35bf97f1f9 100644 --- a/design/src/rfcs/rfc0020_service_builder.md +++ b/design/src/rfcs/rfc0020_service_builder.md @@ -862,4 +862,6 @@ A toy implementation of the combined proposal is presented in [this PR](https:// - - [x] Add code generation which outputs new service builder. - -- [ ] Deprecate `OperationRegistryBuilder`, `OperationRegistry` and `Router`. +- [x] Deprecate `OperationRegistryBuilder`, `OperationRegistry` and `Router`. + - + - diff --git a/design/src/rfcs/rfc0023_refine_builder.md b/design/src/rfcs/rfc0023_refine_builder.md index fb519ca4c7..d93cd6bf77 100644 --- a/design/src/rfcs/rfc0023_refine_builder.md +++ b/design/src/rfcs/rfc0023_refine_builder.md @@ -781,9 +781,12 @@ The API proposed in this RFC has been manually implemented for the Pokemon servi ## Changes checklist -- [ ] Update `codegen-server` to generate the proposed service builder API -- [ ] Implement `Pluggable` for `PluginStack` -- [ ] Evaluate the introduction of a `PluginBuilder` as the primary API to compose multiple plugins (instead of `PluginStack::new(IdentityPlugin, IdentityPlugin).apply(...)`) +- [x] Update `codegen-server` to generate the proposed service builder API + - +- [x] Implement `Pluggable` for `PluginStack` + - +- [x] Evaluate the introduction of a `PluginBuilder` as the primary API to compose multiple plugins (instead of `PluginStack::new(IdentityPlugin, IdentityPlugin).apply(...)`) + - [RFC 20]: rfc0020_service_builder.md [Pokemon service]: https://github.com/awslabs/smithy-rs/blob/c7ddb164b28b920313432789cfe05d8112a035cc/codegen-core/common-test-models/pokemon.smithy diff --git a/design/src/rfcs/rfc0026_client_crate_organization.md b/design/src/rfcs/rfc0026_client_crate_organization.md index 117d489e8d..672eed876d 100644 --- a/design/src/rfcs/rfc0026_client_crate_organization.md +++ b/design/src/rfcs/rfc0026_client_crate_organization.md @@ -1,7 +1,7 @@ RFC: Client Crate Organization ============================== -> Status: Accepted +> Status: Implemented > > Applies to: clients (and may impact servers due to shared codegen) @@ -374,19 +374,31 @@ All combined, the following is the new publicly visible organization: Changes Checklist ----------------- -- [ ] Move `crate::AppName`, `crate::Endpoint`, `crate::Credentials`, and `crate::Region` into `crate::config` -- [ ] Move `crate::PKG_VERSION` into a new `crate::meta` module -- [ ] Move `crate::operation::customize` into `crate::client` -- [ ] Organize code generated types by operation -- [ ] Reorganize builders that aren't per-operation -- [ ] Only re-export `aws_smithy_client::client::Builder` for non-SDK clients (remove from SDK clients) -- [ ] Rename `crate::types` to `crate::primitives` -- [ ] Rename `crate::model` to `crate::types` -- [ ] Move `crate::error` into `crate::types` -- [ ] Move `crate::ErrorExt` into `crate::error` -- [ ] Re-export `aws_smithy_types::error::display::DisplayErrorContext` and `aws_smithy_http::result::SdkError` in `crate::error` -- [ ] Move `crate::paginator` into `crate::operation` -- [ ] Flatten `crate::presigning` -- [ ] Remove/hide operation `ParseResponse` implementations in `crate::operation` -- [ ] Hide or remove `crate::lens` and `crate::http_body_checksum` -- [ ] Update "Crate Organization" top-level section in generated crate docs +- [x] Move `crate::AppName` into `crate::config` +- [x] Move `crate::PKG_VERSION` into a new `crate::meta` module +- [x] Move `crate::Endpoint` into `crate::config` +- [x] Move `crate::Credentials` into `crate::config` +- [x] Move `crate::Region` into `crate::config` +- [x] Move `crate::operation::customize` into `crate::client` +- [x] Finish refactor to decouple client/server modules +- [x] Organize code generated types by operation +- [x] Reorganize builders +- [x] Rename `crate::types` to `crate::primitives` +- [x] Rename `crate::model` to `crate::types` +- [x] Move `crate::error` into `crate::types` +- [x] Only re-export `aws_smithy_client::client::Builder` for non-SDK clients (remove from SDK clients) +- [x] Move `crate::ErrorExt` into `crate::error` +- [x] Re-export `aws_smithy_types::error::display::DisplayErrorContext` and `aws_smithy_http::result::SdkError` in `crate::error` +- [x] Move `crate::paginator` into `crate::operation` +- [x] Flatten `crate::presigning` +- [x] Hide or remove `crate::lens` and `crate::http_body_checksum` +- [x] Move fluent builders into `crate::operation::x::builders` +- [x] Remove/hide operation `ParseResponse` implementations in `crate::operation` +- [x] Update "Crate Organization" top-level section in generated crate docs +- [x] Update all module docs +- [x] Break up modules/files so that they're not 30k lines of code + - [x] models/types; each struct/enum should probably get its own file with pub-use + - [x] models/types::builders: now this needs to get split up + - [x] `client.rs` +- [x] Fix examples +- [x] Write changelog diff --git a/design/src/rfcs/rfc0027_endpoints_20.md b/design/src/rfcs/rfc0027_endpoints_20.md index 02ba3c3f2d..b5428c1ed1 100644 --- a/design/src/rfcs/rfc0027_endpoints_20.md +++ b/design/src/rfcs/rfc0027_endpoints_20.md @@ -523,7 +523,7 @@ Changes checklist - [x] Add a Smithy endpoint resolver to the service config, with a default that loads the default endpoint resolver. - [x] Update `SdkConfig` to accept a URI instead of an implementation of `ResolveAwsEndpoint`. This change can be done standalone. -- [ ] Remove/deprecate the `ResolveAwsEndpoint` trait and replace it with the vanilla Smithy trait. Potentially, provide +- [x] Remove/deprecate the `ResolveAwsEndpoint` trait and replace it with the vanilla Smithy trait. Potentially, provide a bridge. - [x] Update `make_operation` to write a [`smithy::Endpoint`](#the-endpoint-struct) into the property bag - [x] Update AWS Endpoint middleware to work off of a [`smithy::Endpoint`](#the-endpoint-struct) diff --git a/design/src/rfcs/rfc0032_better_constraint_violations.md b/design/src/rfcs/rfc0032_better_constraint_violations.md new file mode 100644 index 0000000000..d8648df316 --- /dev/null +++ b/design/src/rfcs/rfc0032_better_constraint_violations.md @@ -0,0 +1,825 @@ +RFC: Better Constraint Violations +================================= + +> Status: Accepted +> +> Applies to: server + +During and after [the design][constraint-traits-rfc] and [the core +implementation][builders-of-builders-pr] of [constraint traits] in the server +SDK, some problems relating to constraint violations were identified. This RFC +sets out to explain and address three of them: [impossible constraint +violations](#impossible-constraint-violations), [collecting constraint +violations](#collecting-constraint-violations), and ["tightness" of constraint +violations](#tightness-of-constraint-violations). The RFC explains each of them +in turn, solving them in an iterative and pedagogical manner, i.e. the solution +of a problem depends on the previous ones having been solved with their +proposed solutions. The three problems are meant to be addressed atomically in +one changeset (see the [Checklist](#checklist)) section. + +Note: code snippets from generated SDKs in this document are abridged so as to +be didactic and relevant to the point being made. They are accurate with +regards to commit [`2226fe`]. + +[constraint-traits-rfc]: https://github.com/awslabs/smithy-rs/pull/1199 +[builders-of-builders-pr]: https://github.com/awslabs/smithy-rs/pull/1342 +[`2226fe`]: https://github.com/awslabs/smithy-rs/tree/2226feff8f7fa884204f81a50d7e016386912acc +[constraint traits]: https://awslabs.github.io/smithy/2.0/spec/constraint-traits.html + +Terminology +----------- + +[The design][constraint-traits-rfc] and the description of [the +PR][builders-of-builders-pr] where the core implementation of constraint traits +was made are recommended prior reading to understand this RFC. + +- **Shape closure**: the set of shapes a shape can "reach", including itself. +- **Transitively constrained shape**: a shape whose closure includes: + 1. a shape with a [constraint trait][constraint traits] attached, + 2. a (member) shape with a [`required` trait] attached, + 3. an [`enum` shape]; or + 4. an [`intEnum` shape]. +- A **directly constrained shape** is any of these: + 1. a shape with a [constraint trait][constraint traits] attached, + 2. a (member) shape with a [`required` trait] attached, + 3. an [`enum` shape], + 4. an [`intEnum` shape]; or + 5. a [`structure` shape] with at least one `required` member shape. +- **Constrained type**: the Rust type a constrained shape gets rendered as. For + shapes that are not `structure`, `union`, `enum` or `intEnum` shapes, these + are wrapper [newtype]s. + +[`required` trait]: https://smithy.io/2.0/spec/type-refinement-traits.html#required-trait +[`enum` shape]: https://smithy.io/2.0/spec/simple-types.html#enum +[`intEnum` shape]: https://smithy.io/2.0/spec/simple-types.html#intenum +[`structure` shape]: https://smithy.io/2.0/spec/aggregate-types.html#structure +[newtype]: https://doc.rust-lang.org/rust-by-example/generics/new_types.html + +In the absence of a qualifier, "constrained shape" should be interpreted as +"transitively constrained shape". + +Impossible constraint violations +-------------------------------- + +### Background + +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 +impl TryFrom for ConstrainedType { + type Error = ConstraintViolation; + + fn try_from(value: UnconstrainedType) -> Result { + ... + } +} +``` + +The `ConstraintViolation` type is a Rust `enum` with one variant per way +"constraining" the input value may fail. So, for example, the following Smithy +model: + +```smithy +structure A { + @required + member: String, +} +``` + +Yields: + +```rust +/// See [`A`](crate::model::A). +pub mod a { + #[derive(std::cmp::PartialEq, std::fmt::Debug)] + /// Holds one variant for each of the ways the builder can fail. + pub enum ConstraintViolation { + /// `member` was not provided but it is required when building `A`. + MissingMember, + } +} +``` + +Constraint violations are always Rust `enum`s, even if they only have one +variant. + +Constraint violations can occur in application code: + +```rust +use my_server_sdk::model + +let res = model::a::Builder::default().build(); // We forgot to set `member`. + +match res { + Ok(a) => { ... }, + Err(e) => { + assert_eq!(model::a::ConstraintViolation::MissingMember, e); + } +} +``` + +[`TryFrom`]: https://doc.rust-lang.org/std/convert/trait.TryFrom.html + +### Problem + +Currently, the constraint violation types we generate are used by _both_: + +1. the server framework upon request deserialization; and +2. by users in application code. + +However, the kinds of constraint violations that can occur in application code +can sometimes be a _strict subset_ of those that can occur during request +deserialization. + +Consider the following model: + +```smithy +@length(min: 1, max: 69) +map LengthMap { + key: String, + value: LengthString +} + +@length(min: 2, max: 69) +string LengthString +``` + +This produces: + +```rust +pub struct LengthMap( + pub(crate) std::collections::HashMap, +); + +impl + std::convert::TryFrom< + std::collections::HashMap, + > for LengthMap +{ + type Error = crate::model::length_map::ConstraintViolation; + + /// Constructs a `LengthMap` from an + /// [`std::collections::HashMap`], failing when the provided value does not + /// satisfy the modeled constraints. + fn try_from( + value: std::collections::HashMap, + ) -> Result { + let length = value.len(); + if (1..=69).contains(&length) { + Ok(Self(value)) + } else { + Err(crate::model::length_map::ConstraintViolation::Length(length)) + } + } +} + +pub mod length_map { + pub enum ConstraintViolation { + Length(usize), + Value( + std::string::String, + crate::model::length_string::ConstraintViolation, + ), + } + ... +} +``` + +Observe how the `ConstraintViolation::Value` variant is never constructed. +Indeed, this variant is impossible to be constructed _in application code_: a +user has to provide a map whose values are already constrained `LengthString`s +to the `try_from` constructor, which only enforces the map's `@length` trait. + +The reason why these seemingly "impossible violations" are being generated is +because they can arise during request deserialization. Indeed, the server +framework deserializes requests into **fully unconstrained types**. These are +types holding unconstrained types all the way through their closures. For +instance, in the case of structure shapes, builder types (the unconstrained +type corresponding to the structure shape) [hold +builders][builders-of-builders-pr] all the way down. + +In the case of the above model, below is the alternate `pub(crate)` constructor +the server framework uses upon deserialization. Observe how +`LengthMapOfLengthStringsUnconstrained` is _fully unconstrained_ and how the +`try_from` constructor can yield `ConstraintViolation::Value`. + +```rust +pub(crate) mod length_map_of_length_strings_unconstrained { + #[derive(Debug, Clone)] + pub(crate) struct LengthMapOfLengthStringsUnconstrained( + pub(crate) std::collections::HashMap, + ); + + impl std::convert::TryFrom + for crate::model::LengthMapOfLengthStrings + { + type Error = crate::model::length_map_of_length_strings::ConstraintViolation; + fn try_from(value: LengthMapOfLengthStringsUnconstrained) -> Result { + let res: Result< + std::collections::HashMap, + Self::Error, + > = value + .0 + .into_iter() + .map(|(k, v)| { + let v: crate::model::LengthString = k.try_into().map_err(Self::Error::Key)?; + + Ok((k, v)) + }) + .collect(); + let hm = res?; + Self::try_from(hm) + } + } +} +``` + +In conclusion, the user is currently exposed to an internal detail of how the +framework operates that has no bearing on their application code. They +shouldn't be exposed to impossible constraint violation variants in their Rust +docs, nor have to `match` on these variants when handling errors. + +Note: [this comment] alludes to the problem described above. + +[this comment]: https://github.com/awslabs/smithy-rs/blob/27020be3421fb93e35692803f9a795f92feb1d19/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/MapConstraintViolationGenerator.kt#L66-L69 + +### Solution proposal + +The problem can be mitigated by adding `#[doc(hidden)]` to the internal +variants and `#[non_exhaustive]` to the enum. We're already doing this in some +constraint violation types. + +However, a "less leaky" solution is achieved by _splitting_ the constraint +violation type into two types, which this RFC proposes: + +1. one for use by the framework, with `pub(crate)` visibility, named + `ConstraintViolationException`; and +2. one for use by user application code, with `pub` visibility, named + `ConstraintViolation`. + +```rust +pub mod length_map { + pub enum ConstraintViolation { + Length(usize), + } + pub (crate) enum ConstraintViolationException { + Length(usize), + Value( + std::string::String, + crate::model::length_string::ConstraintViolation, + ), + } +} +``` + +Note that, to some extent, the spirit of this approach is [already currently +present](https://github.com/awslabs/smithy-rs/blob/9a4c1f304f6f5237d480cfb56dad2951d927d424/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerBuilderGenerator.kt#L78-L81) +in the case of builder types when `publicConstrainedTypes` is set to `false`: + +1. [`ServerBuilderGenerator.kt`] renders the usual builder type that enforces + constraint traits, setting its visibility to `pub (crate)`, for exclusive + use by the framework. +2. [`ServerBuilderGeneratorWithoutPublicConstrainedTypes.kt`] renders the + builder type the user is exposed to: this builder does not take in + constrained types and does not enforce all modeled constraints. + +[`ServerBuilderGenerator.kt`]: https://github.com/awslabs/smithy-rs/blob/2226feff8f7fa884204f81a50d7e016386912acc/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerBuilderGenerator.kt +[`ServerBuilderGeneratorWithoutPublicConstrainedTypes.kt`]: https://github.com/awslabs/smithy-rs/blob/2226feff8f7fa884204f81a50d7e016386912acc/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerBuilderGeneratorWithoutPublicConstrainedTypes.kt + +Collecting constraint violations +-------------------------------- + +### Background + +Constrained operations are currently required to have +`smithy.framework#ValidationException` as a member in their [`errors` +property](https://smithy.io/2.0/spec/service-types.html#operation). This is the +shape that is rendered in responses when a request contains data that violates +the modeled constraints. + +The shape is defined in the +[`smithy-validation-model`](https://search.maven.org/artifact/software.amazon.smithy/smithy-validation-model) +Maven package, [as +follows](https://github.com/awslabs/smithy/blob/main/smithy-validation-model/model/smithy.framework.validation.smithy): + +```smithy +$version: "2.0" + +namespace smithy.framework + +/// A standard error for input validation failures. +/// This should be thrown by services when a member of the input structure +/// falls outside of the modeled or documented constraints. +@error("client") +structure ValidationException { + + /// A summary of the validation failure. + @required + message: String, + + /// A list of specific failures encountered while validating the input. + /// A member can appear in this list more than once if it failed to satisfy multiple constraints. + fieldList: ValidationExceptionFieldList +} + +/// Describes one specific validation failure for an input member. +structure ValidationExceptionField { + /// A JSONPointer expression to the structure member whose value failed to satisfy the modeled constraints. + @required + path: String, + + /// A detailed description of the validation failure. + @required + message: String +} + +list ValidationExceptionFieldList { + member: ValidationExceptionField +} +``` + +It was mentioned in the [constraint traits +RFC](https://github.com/awslabs/smithy-rs/pull/1199#discussion_r809300673), and +implicit in the definition of Smithy's +[`smithy.framework.ValidationException`](https://github.com/awslabs/smithy/blob/main/smithy-validation-model/model/smithy.framework.validation.smithy) +shape, that server frameworks should respond with a _complete_ collection of +errors encountered during constraint trait enforcement to the client. + +### Problem + +As of writing, the `TryFrom` constructor of constrained types whose shapes have +more than one constraint trait attached can only yield a single error. For +example, the following shape: + +```smithy +@pattern("[a-f0-5]*") +@length(min: 5, max: 10) +string LengthPatternString +``` + +Yields: + +```rust +pub struct LengthPatternString(pub(crate) std::string::String); + +impl LengthPatternString { + fn check_length( + string: &str, + ) -> Result<(), crate::model::length_pattern_string::ConstraintViolation> { + let length = string.chars().count(); + + if (5..=10).contains(&length) { + Ok(()) + } else { + Err(crate::model::length_pattern_string::ConstraintViolation::Length(length)) + } + } + + fn check_pattern( + string: String, + ) -> Result { + let regex = Self::compile_regex(); + + if regex.is_match(&string) { + Ok(string) + } else { + Err(crate::model::length_pattern_string::ConstraintViolation::Pattern(string)) + } + } + + pub fn compile_regex() -> &'static regex::Regex { + static REGEX: once_cell::sync::Lazy = once_cell::sync::Lazy::new(|| { + regex::Regex::new(r#"[a-f0-5]*"#).expect(r#"The regular expression [a-f0-5]* is not supported by the `regex` crate; feel free to file an issue under https://github.com/awslabs/smithy-rs/issues for support"#) + }); + + ®EX + } +} + +impl std::convert::TryFrom for LengthPatternString { + type Error = crate::model::length_pattern_string::ConstraintViolation; + + /// Constructs a `LengthPatternString` from an [`std::string::String`], + /// failing when the provided value does not satisfy the modeled constraints. + fn try_from(value: std::string::String) -> Result { + Self::check_length(&value)?; + + let value = Self::check_pattern(value)?; + + Ok(Self(value)) + } +} +``` + +Observe how a failure to adhere to the `@length` trait will short-circuit the +evaluation of the constructor, when the value could technically also not adhere +with the `@pattern` trait. + +Similarly, constrained structures fail upon encountering the first member that +violates a constraint. + +Additionally, _in framework request deserialization code_: + +- collections whose members are constrained fail upon encountering the first + member that violates the constraint, +- maps whose keys and/or values are constrained fail upon encountering the + first violation; and +- structures whose members are constrained fail upon encountering the first + member that violates the constraint, + +In summary, any shape that is transitively constrained yields types whose +constructors (both the internal one and the user-facing one) currently +short-circuit upon encountering the first violation. + +### Solution proposal + +The deserializing architecture lends itself to be easily refactored so that we +can collect constraint violations before returning them. Indeed, note that +deserializers enforce constraint traits in a two-step phase: first, the +_entirety_ of the unconstrained value is deserialized, _then_ constraint traits +are enforced by feeding the entire value to the `TryFrom` constructor. + +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 +pub ConstraintViolations(pub(crate) Vec); + +impl IntoIterator for ConstraintViolations { ... } + +impl std::convert::TryFrom for LengthPatternString { + type Error = ConstraintViolations; + + fn try_from(value: std::string::String) -> Result { + // Check constraints and collect violations. + ... + } +} +``` + +- The main reason for wrapping a vector in `ConstraintViolations` as opposed to + directly returning the vector is forwards-compatibility: we may want to + expand `ConstraintViolations` with conveniences. +- If the constrained type can only ever yield a single violation, we will + dispense with `ConstraintViolations` and keep directly returning the + `crate::model::shape_name::ConstraintViolation` type. + +We will analogously introduce a `ConstraintViolationExceptions` type that +represents a collection of constraint violations that can occur _within the +framework's request deserialization code_. This type will be `pub(crate)` and +will be the one the framework will map to Smithy's `ValidationException` that +eventually gets serialized into the response. + +#### Collecting constraint violations may constitute a DOS attack vector + +This is a problem that _already_ exists as of writing, but that collecting +constraint violations highlights, so it is a good opportunity, from a +pedagogical perspective, to explain it here. Consider the following model: + +```smithy +@length(max: 3) +list ListOfPatternStrings { + member: PatternString +} + +@pattern("expensive regex to evaluate") +string PatternString +``` + +Our implementation currently enforces constraints _from the leaf to the root_: +when enforcing the `@length` constraint, the `TryFrom` constructor the server +framework uses gets a `Vec` and _first_ checks the members adhere to +the `@pattern` trait, and only _after_ is the `@length` trait checked. This +means that if a client sends a request with `n >>> 3` list members, the +expensive check runs `n` times, when a constant-time check inspecting the +length of the input vector would have sufficed to reject the request. +Additionally, we may want to avoid serializing `n` `ValidationExceptionField`s +due to performance concerns. + +1. A possibility to circumvent this is making the `@length` validator special, + having it bound the other validators via effectively permuting the order of + the checks and thus short-circuiting. + * In general, it's unclear what constraint traits should cause + short-circuiting. A probably reasonable rule of thumb is to include + traits that can be attached directly to aggregate shapes: as of writing, + that would be `@uniqueItems` on list shapes and `@length` on list shapes. +1. Another possiblity is to _do nothing_ and value _complete_ validation + exception response messages over trying to mitigate this with special + handling. One could argue that these kind of DOS attack vectors should be + taken care of with a separate solution e.g. a layer that bounds a request + body's size to a reasonable default (see [how Axum added + this](https://github.com/tokio-rs/axum/pull/1420)). We will provide a similar + request body limiting mechanism regardless. + +This RFC advocates for implementing the first option, arguing that [it's fair +to say that the framework should return an error that is as informative as +possible, but it doesn't necessarily have to be +complete](https://github.com/awslabs/smithy-rs/pull/2040#discussion_r1036226762). +However, we will also write a layer, applied by default to all server SDKs, +that bounds a request body's size to a reasonable (yet high) default. Relying +on users to manually apply the layer is dangerous, since such a configuration +is [trivially +exploitable]. +Users can always manually apply the layer again to their resulting service if +they want to further restrict a request's body size. + +[trivially exploitable]: https://jfrog.com/blog/watch-out-for-dos-when-using-rusts-popular-hyper-package/ + +"Tightness" of constraint violations +------------------------------------ + +### Problem + +`ConstraintViolationExceptions` [is not +"tight"](https://www.ecorax.net/tightness/) in that there's nothing in the type +system that indicates to the user, when writing the custom validation error +mapping function, that the iterator will not return a sequence of +`ConstraintViolationException`s that is actually impossible to occur in +practice. + +Recall that `ConstraintViolationException`s are `enum`s that model both direct +constraint violations as well as transitive ones. For example, given the model: + +```smithy +@length(min: 1, max: 69) +map LengthMap { + key: String, + value: LengthString +} + +@length(min: 2, max: 69) +string LengthString +``` + +The corresponding `ConstraintViolationException` Rust type for the `LengthMap` +shape is: + +```rust +pub mod length_map { + pub enum ConstraintViolation { + Length(usize), + } + pub (crate) enum ConstraintViolationException { + Length(usize), + Value( + std::string::String, + crate::model::length_string::ConstraintViolationException, + ), + } +} +``` + +`ConstraintViolationExceptions` is just a container over this type: + +```rust +pub ConstraintViolationExceptions(pub(crate) Vec); + +impl IntoIterator for ConstraintViolationExceptions { ... } +``` + +There might be multiple map values that fail to adhere to the constraints in +`LengthString`, which would make the iterator yield multiple +`length_map::ConstraintViolationException::Value`s; however, at most one +`length_map::ConstraintViolationException::Length` can be yielded _in +practice_. This might be obvious to the service owner when inspecting the model +and the Rust docs, but it's not expressed in the type system. + +The above tightness problem has been formulated in terms of +`ConstraintViolationExceptions`, because the fact that +`ConstraintViolationExceptions` contain transitive constraint violations +highlights the tightness problem. Note, however, that **the tightness problem +also afflicts `ConstraintViolations`**. + +Indeed, consider the following model: + +```smithy +@pattern("[a-f0-5]*") +@length(min: 5, max: 10) +string LengthPatternString +``` + +This would yield: + +```rust +pub ConstraintViolations(pub(crate) Vec); + +impl IntoIterator for ConstraintViolations { ... } + +pub mod length_pattern_string { + pub enum ConstraintViolation { + Length(usize), + Pattern(String) + } +} + +impl std::convert::TryFrom for LengthPatternString { + type Error = ConstraintViolations; + + fn try_from(value: std::string::String) -> Result { + // Check constraints and collect violations. + ... + } +} +``` + +Observe how the iterator of an instance of +`ConstraintViolations`, +may, a priori, yield e.g. the +`length_pattern_string::ConstraintViolation::Length` variant twice, when it's +clear that the iterator should contain _at most one_ of each of +`length_pattern_string::ConstraintViolation`'s variants. + +### Final solution proposal + +We propose a tighter API design. + +1. We substitute `enum`s for `struct`s whose members are all `Option`al, + representing all the constraint violations that can occur. +1. For list shapes and map shapes: + 1. we implement `IntoIterator` on an additional `struct` `Members` + representing only the violations that can occur on the collection's + members. + 2. we add a _non_ `Option`-al field to the `struct` representing the + constraint violations of type `Members`. + +Let's walk through an example. Take the last model: + +```smithy +@pattern("[a-f0-5]*") +@length(min: 5, max: 10) +string LengthPatternString +``` + +This would yield, as per the first substitution: + +```rust +pub mod length_pattern_string { + pub struct ConstraintViolations { + pub length: Option, + pub pattern: Option, + } + + pub mod constraint_violation { + pub struct Length(usize); + pub struct Pattern(String); + } +} + +impl std::convert::TryFrom for LengthPatternString { + type Error = length_pattern_string::ConstraintViolations; + + // The error type returned by this constructor, `ConstraintViolations`, + // will always have _at least_ one member set. + fn try_from(value: std::string::String) -> Result { + // Check constraints and collect violations. + ... + } +} +``` + +We now expand the model to highlight the second step of the algorithm: + +```smithy +@length(min: 1, max: 69) +map LengthMap { + key: String, + value: LengthString +} +``` + +This gives us: + +```rust +pub mod length_map { + pub struct ConstraintViolations { + pub length: Option, + + // Would be `Option` in the case of an aggregate shape that is _not_ a + // list shape or a map shape. + pub member_violations: constraint_violation::Members, + } + + pub mod constraint_violation { + // Note that this could now live outside the `length_map` module and be + // reused across all `@length`-constrained shapes, if we expanded it with + // another `usize` indicating the _modeled_ value in the `@length` trait; by + // keeping it inside `length_map` we can hardcode that value in the + // implementation of e.g. error messages. + pub struct Length(usize); + + pub struct Members { + pub(crate) Vec + } + + pub struct Member { + // If the map's key shape were constrained, we'd have a `key` + // field here too. + + value: Option + } + + pub struct Value( + std::string::String, + crate::model::length_string::ConstraintViolation, + ); + + impl IntoIterator for Members { ... } + } +} +``` + +--- + +The above examples have featured the tight API design with +`ConstraintViolation`s. Of course, we will apply the same design in the case of +`ConstraintViolationException`s. For the sake of completeness, let's expand our +model yet again with a structure shape: + +```smithy +structure A { + @required + member: String, + + @required + length_map: LengthMap, +} +``` + +And this time let's feature _both_ the resulting +`ConstraintViolationExceptions` and `ConstraintViolations` types: + +```rust +pub mod a { + pub struct ConstraintViolationExceptions { + // All fields must be `Option`, despite the members being `@required`, + // since no violations for their values might have occurred. + + pub missing_member_exception: Option, + pub missing_length_map_exception: Option, + pub length_map_exceptions: Option, + } + + pub mod constraint_violation_exception { + pub struct MissingMember; + pub struct MissingLengthMap; + } + + pub struct ConstraintViolations { + pub missing_member: Option, + pub missing_length_map: Option, + } + + pub mod constraint_violation { + pub struct MissingMember; + pub struct MissingLengthMap; + } +} +``` + +As can be intuited, the only differences are that: + +* `ConstraintViolationExceptions` hold transitive violations while + `ConstraintViolations` only need to expose direct violations (as explained in + the [Impossible constraint violations](#impossible-constraint-violations) + section), +* `ConstraintViolationExceptions` have members suffixed with `_exception`, as + is the module name. + +Note that while the constraint violation (exception) type names are plural, the +module names are always singular. + +We also make a conscious decision of, in this case of structure shapes, making +the types of all members `Option`s, for simplicity. Another choice would have +been to make `length_map_exceptions` not `Option`-al, and, in the case where no +violations in `LengthMap` values occurred, set +`length_map::ConstraintViolations::length` to `None` and +`length_map::ConstraintViolations::member_violations` eventually reach an empty +iterator. However, it's best that we use the expressiveness of `Option`s at the +earliest ("highest" in the shape hierarchy) opportunity: if a member is `Some`, +it means it (eventually) reaches data. + +Checklist +--------- + +Unfortunately, while this RFC _could_ be implemented iteratively (i.e. solve +each of the problems in turn), it would introduce too much churn and throwaway +work: solving the tightness problem requires a more or less complete overhaul +of the constraint violations code generator. It's best that all three problems +be solved in the same changeset. + +- [ ] Generate `ConstraintViolations` and `ConstraintViolationExceptions` types + so as to not reify [impossible constraint + violations](#impossible-constraint-violations), add the ability to [collect + constraint + violations](#collecting-constraint-violations), and solve the ["tightness" problem of constraint violations](#tightness-of-constraint-violations). +- [ ] Special-case generated request deserialization code for operations + using `@length` and `@uniqueItems` constrained shapes whose closures reach + other constrained shapes so that the validators for these two traits + short-circuit upon encountering a number of inner constraint violations + above a certain threshold. +- [ ] Write and expose a layer, applied by default to all generated server SDKs, + that bounds a request body's size to a reasonable (yet high) default, to prevent [trivial DoS attacks][trivially exploitable]. diff --git a/design/src/rfcs/rfc0033_improve_sdk_request_id_access.md b/design/src/rfcs/rfc0033_improve_sdk_request_id_access.md new file mode 100644 index 0000000000..3e78f4070c --- /dev/null +++ b/design/src/rfcs/rfc0033_improve_sdk_request_id_access.md @@ -0,0 +1,245 @@ +RFC: Improving access to request IDs in SDK clients +=================================================== + +> Status: Implemented in [#2129](https://github.com/awslabs/smithy-rs/pull/2129) +> +> Applies to: AWS SDK clients + +At time of writing, customers can retrieve a request ID in one of four ways in the Rust SDK: + +1. For error cases where the response parsed successfully, the request ID can be retrieved + via accessor method on operation error. This also works for unmodeled errors so long as + the response parsing succeeds. +2. For error cases where a response was received but parsing fails, the response headers + can be retrieved from the raw response on the error, but customers have to manually extract + the request ID from those headers (there's no convenient accessor method). +3. For all error cases where the request ID header was sent in the response, customers can + call `SdkError::into_service_error` to transform the `SdkError` into an operation error, + which has a `request_id` accessor on it. +4. For success cases, the customer can't retrieve the request ID at all if they use the fluent + client. Instead, they must manually make the operation and call the underlying Smithy client + so that they have access to `SdkSuccess`, which provides the raw response where the request ID + can be manually extracted from headers. + +Only one of these mechanisms is convenient and ergonomic. The rest need considerable improvements. +Additionally, the request ID should be attached to tracing events where possible so that enabling +debug logging reveals the request IDs without any code changes being necessary. + +This RFC proposes changes to make the request ID easier to access. + +Terminology +----------- + +- **Request ID:** A unique identifier assigned to and associated with a request to AWS that is + sent back in the response headers. This identifier is useful to customers when requesting support. +- **Operation Error:** Operation errors are code generated for each operation in a Smithy model. + They are an enum of every possible modeled error that that operation can respond with, as well + as an `Unhandled` variant for any unmodeled or unrecognized errors. +- **Modeled Errors:** Any error that is represented in a Smithy model with the `@error` trait. +- **Unmodeled Errors:** Errors that a service responds with that do not appear in the Smithy model. +- **SDK Clients:** Clients generated for the AWS SDK, including "adhoc" or "one-off" clients. +- **Smithy Clients:** Any clients not generated for the AWS SDK, excluding "adhoc" or "one-off" clients. + +SDK/Smithy Purity +----------------- + +Before proposing any changes, the topic of purity needs to be covered. Request IDs are not +currently a Smithy concept. However, at time of writing, the request ID concept is leaked into +the non-SDK rust runtime crates and generated code via the [generic error] struct and the +`request_id` functions on generated operation errors (e.g., [`GetObjectError` example in S3]). + +This RFC attempts to remove these leaks from Smithy clients. + +Proposed Changes +---------------- + +First, we'll explore making it easier to retrieve a request ID from errors, +and then look at making it possible to retrieve them from successful responses. +To see the customer experience of these changes, see the **Example Interactions** +section below. + +### Make request ID retrieval on errors consistent + +One could argue that customers being able to convert a `SdkError` into an operation error +that has a request ID on it is sufficient. However, there's no way to write a function +that takes an error from any operation and logs a request ID, so it's still not ideal. + +The `aws-http` crate needs to have a `RequestId` trait on it to facilitate generic +request ID retrieval: + +```rust +pub trait RequestId { + /// Returns the request ID if it's available. + fn request_id(&self) -> Option<&str>; +} +``` + +This trait will be implemented for `SdkError` in `aws-http` where it is declared, +complete with logic to pull the request ID header out of the raw HTTP responses +(it will always return `None` for event stream `Message` responses; an additional +trait may need to be added to `aws-smithy-http` to facilitate access to the headers). +This logic will try different request ID header names in order of probability +since AWS services have a couple of header name variations. `x-amzn-requestid` is +the most common, with `x-amzn-request-id` being the second most common. + +`aws-http` will also implement `RequestId` for `aws_smithy_types::error::Error`, +and the `request_id` method will be removed from `aws_smithy_types::error::Error`. +Places that construct `Error` will place the request ID into its `extras` field, +where the `RequestId` trait implementation can retrieve it. + +A codegen decorator will be added to `sdk-codegen` to implement `RequestId` for +operation errors, and the existing `request_id` accessors will be removed from +`CombinedErrorGenerator` in `codegen-core`. + +With these changes, customers can directly access request IDs from `SdkError` and +operations errors by importing the `RequestId` trait. Additionally, the Smithy/SDK +purity is improved since both places where request IDs are leaked to Smithy clients +will be resolved. + +### Implement `RequestId` for outputs + +To make it possible to retrieve request IDs when using the fluent client, the new +`RequestId` trait can be implemented for outputs. + +Some services (e.g., Transcribe Streaming) model the request ID header in their +outputs, while other services (e.g., Directory Service) model a request ID +field on errors. In some cases, services take `RequestId` as a modeled input +(e.g., IoT Event Data). It follows that it is possible, but unlikely, that +a service could have a field named `RequestId` that is not the same concept +in the future. + +Thus, name collisions are going to be a concern for putting a request ID accessor +on output. However, if it is implemented as a trait, then this concern is partially +resolved. In the vast majority of cases, importing `RequestId` will provide the +accessor without any confusion. In cases where it is already modeled and is the +same concept, customers will likely just use it and not even realize they didn't +import the trait. The only concern is future cases where it is modeled as a +separate concept, and as long as customers don't import `RequestId` for something +else in the same file, that confusion can be avoided. + +In order to implement `RequestId` for outputs, either the original response needs +to be stored on the output, or the request ID needs to be extracted earlier and +stored on the output. The latter will lead to a small amount of header lookup +code duplication. + +In either case, the `StructureGenerator` needs to be customized in `sdk-codegen` +(Appendix B outlines an alternative approach to this and why it was dismissed). +This will be done by adding customization hooks to `StructureGenerator` similar +to the ones for `ServiceConfigGenerator` so that a `sdk-codegen` decorator can +conditionally add fields and functions to any generated structs. A hook will +also be needed to additional trait impl blocks. + +Once the hooks are in place, a decorator will be added to store either the original +response or the request ID on outputs, and then the `RequestId` trait will be +implemented for them. The `ParseResponse` trait implementation will be customized +to populate this new field. + +Note: To avoid name collisions of the request ID or response on the output struct, +these fields can be prefixed with an underscore. It shouldn't be possible for SDK +fields to code generate with this prefix given the model validation rules in place. + +### Implement `RequestId` for `Operation` and `operation::Response` + +In the case that a customer wants to ditch the fluent client, it should still +be easy to retrieve a request ID. To do this, `aws-http` will provide `RequestId` +implementations for `Operation` and `operation::Response`. These implementations +will likely make the other `RequestId` implementations easier to implement as well. + +### Implement `RequestId` for `Result` + +The `Result` returned by the SDK should directly implement `RequestId` when both +its `Ok` and `Err` variants implement `RequestId`. This will make it possible +for a customer to feed the return value from `send()` directly to a request ID logger. + +Example Interactions +-------------------- + +### Generic Handling Case + +```rust +// A re-export of the RequestId trait +use aws_sdk_service::primitives::RequestId; + +fn my_request_id_logging_fn(request_id: &dyn RequestId) { + println!("request ID: {:?}", request_id.request_id()); +} + +let result = client.some_operation().send().await?; +my_request_id_logging_fn(&result); +``` + +### Success Case + +```rust +use aws_sdk_service::primitives::RequestId; + +let output = client.some_operation().send().await?; +println!("request ID: {:?}", output.request_id()); +``` + +### Error Case with `SdkError` + +```rust +use aws_sdk_service::primitives::RequestId; + +match client.some_operation().send().await { + Ok(_) => { /* handle OK */ } + Err(err) => { + println!("request ID: {:?}", output.request_id()); + } +} +``` + +### Error Case with operation error + +```rust +use aws_sdk_service::primitives::RequestId; + +match client.some_operation().send().await { + Ok(_) => { /* handle OK */ } + Err(err) => match err.into_service_err() { + err @ SomeOperationError::SomeError(_) => { println!("request ID: {:?}", err.request_id()); } + _ => { /* don't care */ } + } +} +``` + +Changes Checklist +----------------- + +- [x] Create the `RequestId` trait in `aws-http` +- [x] Implement for errors + - [x] Implement `RequestId` for `SdkError` in `aws-http` + - [x] Remove `request_id` from `aws_smithy_types::error::Error`, and store request IDs in its `extras` instead + - [x] Implement `RequestId` for `aws_smithy_types::error::Error` in `aws-http` + - [x] Remove generation of `request_id` accessors from `CombinedErrorGenerator` in `codegen-core` +- [x] Implement for outputs + - [x] Add customization hooks to `StructureGenerator` + - [x] Add customization hook to `ParseResponse` + - [x] Add customization hook to `HttpBoundProtocolGenerator` + - [x] Customize output structure code gen in `sdk-codegen` to add either a request ID or a response field + - [x] Customize `ParseResponse` in `sdk-codegen` to populate the outputs +- [x] Implement `RequestId` for `Operation` and `operation::Response` +- [x] Implement `RequestId` for `Result` where `O` and `E` both implement `RequestId` +- [x] Re-export `RequestId` in generated crates +- [x] Add integration tests for each request ID access point + +Appendix A: Alternate solution for access on successful responses +----------------------------------------------------------------- + +Alternatively, for successful responses, a second `send` method (that is difficult to name)w +be added to the fluent client that has a return value that includes both the output and +the request ID (or entire response). + +This solution was dismissed due to difficulty naming, and the risk of name collision. + +Appendix B: Adding `RequestId` as a string to outputs via model transform +------------------------------------------------------------------------- + +The request ID could be stored on outputs by doing a model transform in `sdk-codegen` to add a +`RequestId` member field. However, this causes problems when an output already has a `RequestId` field, +and requires the addition of a synthetic trait to skip binding the field in the generated +serializers/deserializers. + +[generic error]: https://docs.rs/aws-smithy-types/0.51.0/aws_smithy_types/error/struct.Error.html +[`GetObjectError` example in S3]: https://docs.rs/aws-sdk-s3/0.21.0/aws_sdk_s3/error/struct.GetObjectError.html#method.request_id diff --git a/design/src/rfcs/rfc0034_smithy_orchestrator.md b/design/src/rfcs/rfc0034_smithy_orchestrator.md new file mode 100644 index 0000000000..84d5952253 --- /dev/null +++ b/design/src/rfcs/rfc0034_smithy_orchestrator.md @@ -0,0 +1,528 @@ +# Smithy Orchestrator + +> status: implemented +> applies-to: The smithy client + +This RFC proposes a new process for constructing client requests and handling service responses. This new process is intended to: + +- Improve the user experience by +- Simplifying several aspects of sending a request +- Adding more extension points to the request/response lifecycle +- Improve the maintainer experience by +- Making our SDK more similar in structure to other AWS SDKs +- Simplifying many aspects of the request/response lifecycle +- Making room for future changes + +Additionally, functionality that the SDKs currently provide like retries, logging, and auth with be incorporated into this new process in such a way as to make it more configurable and understandable. + +This RFC references but is not the source of truth on: + +- Interceptors: To be described in depth in a future RFC. +- Runtime Plugins: To be described in depth in a future RFC. + +## TLDR; + +When a smithy client communicates with a smithy service, messages are handled by an "orchestrator." The orchestrator runs in two main phases: +1. Constructing configuration. + - This process is user-configurable with "runtime plugins." + - Configuration is stored in a typemap. +1. Transforming a client request into a server response. + - This process is user-configurable with "interceptors." + - Interceptors are functions that are run by "hooks" in the request/response lifecycle. + +## Terminology + +- **SDK Client**: A high-level abstraction allowing users to make requests to remote services. +- **Remote Service**: A remote API that a user wants to use. Communication with a remote service usually happens over HTTP. The remote service is usually, but not necessarily, an AWS service. +- **Operation**: A high-level abstraction representing an interaction between an *SDK Client and a *remote service*. +- **Input Message**: A modeled request passed into an *SDK client*. For example, S3’s `ListObjectsRequest`. +- **Transport Request Message**: A message that can be transmitted to a *remote service*. For example, an HTTP request. +- **Transport Response Message**: A message that can be received from a *remote service*. For example, an HTTP response. +- **Output Message**: A modeled response or exception returned to an *SDK client* caller. For example, S3’s `ListObjectsResponse` or `NoSuchBucketException`. +- **The request/response lifecycle**: The process by which an *SDK client* makes requests and receives responses from a *remote service*. This process is enacted and managed by the *orchestrator*. +- **Orchestrator**: The code within an *SDK client* that handles the process of making requests and receiving responses from *remote services*. The orchestrator is configurable by modifying the *runtime plugins* it's built from. The orchestrator is responsible for calling *interceptors* at the appropriate times in the *request/response lifecycle*. +- **Interceptor**/**Hook**: A generic extension point within the *orchestrator*. Supports "anything that someone should be able to do", NOT "anything anyone might want to do". These hooks are: + - Either **read-only** or **read/write**. + - Able to read and modify the **Input**, **Transport Request**, **Transport Response**, or **Output** messages. +- **Runtime Plugin**: Runtime plugins are similar to interceptors, but they act on configuration instead of requests and response. Both users and services may define runtime plugins. Smithy also defines several default runtime plugins used by most clients. See the F.A.Q. for a list of plugins with descriptions. +- **ConfigBag**: A `typemap` that's equivalent to [`http::Extensions`](https://docs.rs/http/latest/http/struct.Extensions.html). Used to store configuration for the orchestrator. + +## The user experience if this RFC is implemented + +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 +let sdk_config = aws_config::load_from_env().await; +let client = aws_sdk_s3::Client::new(&sdk_config); +let res = client.get_object() + .bucket("a-bucket") + .key("a-file.txt") + .send() + .await?; + +match res { + Ok(res) => println!("success: {:?}"), + Err(err) => eprintln!("failure: {:?}") +}; +``` + +Users may further configure clients and operations with **runtime plugins**, and they can modify requests and responses with **interceptors**. We'll examine each of these concepts in the following sections. + +### Service clients and operations are configured with runtime plugins + +> The exact implementation of **runtime plugins** is left for another RFC. That other RFC will be linked here once it's written. To get an idea of what they may look like, see the *"Layered configuration, stored in type maps"* section of this RFC. + +Runtime plugins construct and modify client configuration. Plugin initialization is the first step of sending a request, and plugins set in later steps can override the actions of earlier plugins. Plugin ordering is deterministic and non-customizable. + +While AWS services define a default set of plugins, users may define their own plugins, and set them by calling the appropriate methods on a service's config, client, or operation. Plugins are specifically meant for constructing service and operation configuration. If a user wants to define behavior that should occur at specific points in the *request/response lifecycle*, then they should instead consider defining an *interceptor*. + +### Requests and responses are modified by interceptors + +Interceptors are similar to middlewares, in that they are functions that can read and modify request and response state. However, they are more restrictive than middlewares in that they can't modify the "control flow" of the request/response lifecycle. This is intentional. Interceptors can be registered on a client or operation, and the orchestrator is responsible for calling interceptors at the appropriate time. Users MUST NOT perform blocking IO within an interceptor. Interceptors are sync, and are not intended to perform large amounts of work. This makes them easier to reason about and use. Depending on when they are called, interceptors may read and modify *input messages*, *transport request messages*, *transport response messages*, and *output messages*. Additionally, all interceptors may write to a context object that is shared between all interceptors. + +#### Currently supported hooks + +1. **Read Before Execution *(Read-Only)***: Before anything happens. This is the first + thing the SDK calls during operation execution. +1. **Modify Before Serialization *(Read/Write)***: Before the input message given by + the customer is marshalled into a transport request message. Allows modifying the + input message. +1. **Read Before Serialization *(Read-Only)***: The last thing the SDK calls before + marshaling the input message into a transport message. +1. **Read After Serialization *(Read-Only)***: The first thing the SDK calls after marshaling the input message into a transport message. +1. *(Retry Loop)* + 1. **Modify Before Retry Loop *(Read/Write)***: The last thing the SDK calls before entering the retry look. Allows modifying the transport message. + 1. **Read Before Attempt *(Read-Only)***: The first thing the SDK calls “inside” of the retry loop. + 1. **Modify Before Signing *(Read/Write)***: Before the transport request message is signed. Allows modifying the transport message. + 1. **Read Before Signing *(Read-Only)***: The last thing the SDK calls before signing the transport request message. + 1. **Read After Signing (Read-Only)****: The first thing the SDK calls after signing the transport request message. + 1. **Modify Before Transmit *(Read/Write)***: Before the transport request message is sent to the service. Allows modifying the transport message. + 1. **Read Before Transmit *(Read-Only)***: The last thing the SDK calls before sending the transport request message. + 1. **Read After Transmit *(Read-Only)***: The last thing the SDK calls after receiving the transport response message. + 1. **Modify Before Deserialization *(Read/Write)***: Before the transport response message is unmarshaled. Allows modifying the transport response message. + 1. **Read Before Deserialization *(Read-Only)***: The last thing the SDK calls before unmarshalling the transport response message into an output message. + 1. **Read After Deserialization *(Read-Only)***: The last thing the SDK calls after unmarshaling the transport response message into an output message. + 1. **Modify Before Attempt Completion *(Read/Write)***: Before the retry loop ends. Allows modifying the unmarshaled response (output message or error). + 1. **Read After Attempt *(Read-Only)***: The last thing the SDK calls “inside” of the retry loop. +1. **Modify Before Execution Completion *(Read/Write)***: Before the execution ends. Allows modifying the unmarshaled response (output message or error). +1. **Read After Execution *(Read-Only)***: After everything has happened. This is the last thing the SDK calls during operation execution. + +### Interceptor context + +As mentioned above, interceptors may read/write a context object that is shared between all interceptors: + +```rust +pub struct InterceptorContext { + // a.k.a. the input message + modeled_request: ModReq, + // a.k.a. the transport request message + tx_request: Option, + // a.k.a. the output message + modeled_response: Option, + // a.k.a. the transport response message + tx_response: Option, + // A type-keyed map + properties: SharedPropertyBag, +} +``` + +The optional request and response types in the interceptor context can only be accessed by interceptors that are run after specific points in the *request/response lifecycle*. Rather than go into depth in this RFC, I leave that to a future "Interceptors RFC." + +## How to implement this RFC + +### Integrating with the orchestrator + +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 +struct Signer; + +impl Signer { + fn sign(headers: &http::HeaderMap, signing_name: &str, signing_region: &str) -> String { + todo!() + } +} +``` + +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 +pub trait AuthOrchestrator: Send + Sync + Debug { + fn auth_request(&self, req: &mut Req, cfg: &ConfigBag) -> Result<(), BoxError>; +} + +// And it calls that `AuthOrchestrator` like so: +fn invoke() { + // code omitted for brevity + + // Get the request to be signed + let tx_req_mut = ctx.tx_request_mut().expect("tx_request has been set"); + // Fetch the auth orchestrator from the bag + let auth_orchestrator = cfg + .get::>>() + .ok_or("missing auth orchestrator")?; + // Auth the request + auth_orchestrator.auth_request(tx_req_mut, cfg)?; + + // code omitted for brevity +} +``` + +The specific implementation of the `AuthOrchestrator` is what brings these two things together: + +```rust +struct Sigv4AuthOrchestrator; + +impl AuthOrchestrator for Sigv4AuthOrchestrator { + fn auth_request(&self, req: &mut http::Request, cfg: &ConfigBag) -> Result<(), BoxError> { + let signer = Signer; + let signing_name = cfg.get::().ok_or(Error::MissingSigningName)?; + let signing_region = cfg.get::().ok_or(Error::MissingSigningRegion)?; + let headers = req.headers_mut(); + + let signature = signer.sign(headers, signing_name, signing_region); + match cfg.get::() { + Some(SignatureLocation::Query) => req.query.set("sig", signature), + Some(SignatureLocation::Header) => req.headers_mut().insert("sig", signature), + None => return Err(Error::MissingSignatureLocation), + }; + + Ok(()) + } +} +``` + +This intermediate code should be free from as much logic as possible. Whenever possible, we must maintain this encapsulation. Doing so will make the Orchestrator more flexible, maintainable, and understandable. + +### Layered configuration, stored in type maps + +> **Type map**: A data structure where stored values are keyed by their type. Hence, only one value can be stored for a given type. +> +> *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 + let conf: ConfigBag = aws_config::from_env() + // Configuration can be common to all smithy clients + .with(RetryConfig::builder().disable_retries().build()) + // Or, protocol-specific + .with(HttpClient::builder().build()) + // Or, AWS-specific + .with(Region::from("us-east-1")) + // Or, service-specific + .with(S3Config::builder().force_path_style(false).build()) + .await; + +let client = aws_sdk_s3::Client::new(&conf); + +client.list_buckets() + .customize() + // Configuration can be set on operations as well as clients + .with(HttpConfig::builder().conn(some_other_conn).build()) + .send() + .await; +``` + +Setting configuration that will not be used wastes memory and can make debugging more difficult. Therefore, configuration defaults are only set when they're relevant. For example, if a smithy service doesn't support HTTP, then no HTTP client will be set. + +#### What is "layered" configuration? + +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 +let conf: ConfigBag = aws_config::from_env() + .with( + SomeConfig::builder() + .option_a(1) + .option_b(2) + .option_c(3) + ) + .build() + .await; + +let client = aws_sdk_s3::Client::new(&conf); + +client.list_buckets() + .customize() + .with( + SomeConfig::builder() + .option_a(0) + .option_b(Value::Inherit) + .option_c(Value::Unset) + ) + .build() + .send() + .await; +``` + +In the above example, when the `option_a`, `option_b`, `option_c`, values of `SomeConfig` are accessed, they'll return: + +- `option_a`: `0` +- `option_b`: `2` +- `option_c`: No value + +Config values are wrapped in a special enum called `Value` with three variants: + +- `Value::Set`: A set value that will override values from lower layers. +- `Value::Unset`: An explicitly unset value that will override values from lower layers. +- `Value::Inherit`: An explicitly unset value that will inherit a value from a lower layer. + +Builders are defined like this: + +```rust +struct SomeBuilder { + value: Value, +} + +impl struct SomeBuilder { + fn new() -> Self { + // By default, config values inherit from lower-layer configs + Self { value: Value::Inherit } + } + + fn some_field(&mut self, value: impl Into>) -> &mut self { + self.value = value.into(); + self + } +} +``` + +Because of `impl Into>`, users don't need to reference the `Value` enum unless they want to "unset" a value. + +#### Layer separation and precedence + +Codegen defines default sets of interceptors and runtime plugins at various "levels": + +1. AWS-wide defaults set by codegen. +1. Service-wide defaults set by codegen. +1. Operation-specific defaults set by codegen. + +Likewise, users may mount their own interceptors and runtime plugins: + +1. The AWS config level, e.g. `aws_types::Config`. +1. The service config level, e.g. `aws_sdk_s3::Config`. +1. The operation config level, e.g. `aws_sdk_s3::Client::get_object`. + +Configuration is resolved in a fixed manner by reading the "lowest level" of config available, falling back to "higher levels" only when no value has been set. Therefore, at least 3 separate `ConfigBag`s are necessary, and user configuration has precedence over codegen-defined default configuration. With that in mind, resolution of configuration would look like this: + +1. Check user-set operation config. +1. Check codegen-defined operation config. +1. Check user-set service config. +1. Check codegen-defined service config. +1. Check user-set AWS config. +1. Check codegen-defined AWS config. + +### The `aws-smithy-orchestrator` crate + +*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 +/// `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` +/// `Out`: The output message. A `Result` containing either: +/// - The 'success' output message e.g. `ListObjectsResponse` +/// - The 'failure' output message e.g. `NoSuchBucketException` +pub async fn invoke( + input: In, + interceptors: &mut Interceptors>, + runtime_plugins: &RuntimePlugins, + cfg: &mut ConfigBag, +) -> Result + where + // The input must be Clone in case of retries + In: Clone + 'static, + Req: 'static, + Res: 'static, + T: 'static, +{ + let mut ctx: InterceptorContext> = + InterceptorContext::new(input); + + runtime_plugins.apply_client_configuration(cfg)?; + interceptors.client_read_before_execution(&ctx, cfg)?; + + runtime_plugins.apply_operation_configuration(cfg)?; + interceptors.operation_read_before_execution(&ctx, cfg)?; + + interceptors.read_before_serialization(&ctx, cfg)?; + interceptors.modify_before_serialization(&mut ctx, cfg)?; + + let request_serializer = cfg + .get::>>() + .ok_or("missing serializer")?; + let req = request_serializer.serialize_request(ctx.modeled_request_mut(), cfg)?; + ctx.set_tx_request(req); + + interceptors.read_after_serialization(&ctx, cfg)?; + interceptors.modify_before_retry_loop(&mut ctx, cfg)?; + + loop { + make_an_attempt(&mut ctx, cfg, interceptors).await?; + interceptors.read_after_attempt(&ctx, cfg)?; + interceptors.modify_before_attempt_completion(&mut ctx, cfg)?; + + let retry_strategy = cfg + .get::>>>() + .ok_or("missing retry strategy")?; + let mod_res = ctx + .modeled_response() + .expect("it's set during 'make_an_attempt'"); + if retry_strategy.should_retry(mod_res, cfg)? { + continue; + } + + interceptors.modify_before_completion(&mut ctx, cfg)?; + let trace_probe = cfg + .get::>() + .ok_or("missing trace probes")?; + trace_probe.dispatch_events(cfg); + interceptors.read_after_execution(&ctx, cfg)?; + + break; + } + + let (modeled_response, _) = ctx.into_responses()?; + modeled_response +} + +// 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( + ctx: &mut InterceptorContext>, + cfg: &mut ConfigBag, + interceptors: &mut Interceptors>, +) -> Result<(), BoxError> + where + In: Clone + 'static, + Req: 'static, + Res: 'static, + T: 'static, +{ + interceptors.read_before_attempt(ctx, cfg)?; + + let tx_req_mut = ctx.tx_request_mut().expect("tx_request has been set"); + let endpoint_orchestrator = cfg + .get::>>() + .ok_or("missing endpoint orchestrator")?; + endpoint_orchestrator.resolve_and_apply_endpoint(tx_req_mut, cfg)?; + + interceptors.modify_before_signing(ctx, cfg)?; + interceptors.read_before_signing(ctx, cfg)?; + + let tx_req_mut = ctx.tx_request_mut().expect("tx_request has been set"); + let auth_orchestrator = cfg + .get::>>() + .ok_or("missing auth orchestrator")?; + auth_orchestrator.auth_request(tx_req_mut, cfg)?; + + interceptors.read_after_signing(ctx, cfg)?; + interceptors.modify_before_transmit(ctx, cfg)?; + interceptors.read_before_transmit(ctx, cfg)?; + + // The connection consumes the request but we need to keep a copy of it + // within the interceptor context, so we clone it here. + let res = { + let tx_req = ctx.tx_request_mut().expect("tx_request has been set"); + let connection = cfg + .get::>>() + .ok_or("missing connector")?; + connection.call(tx_req, cfg).await? + }; + ctx.set_tx_response(res); + + interceptors.read_after_transmit(ctx, cfg)?; + interceptors.modify_before_deserialization(ctx, cfg)?; + interceptors.read_before_deserialization(ctx, cfg)?; + let tx_res = ctx.tx_response_mut().expect("tx_response has been set"); + let response_deserializer = cfg + .get::>>>() + .ok_or("missing response deserializer")?; + let res = response_deserializer.deserialize_response(tx_res, cfg)?; + ctx.set_modeled_response(res); + + interceptors.read_after_deserialization(ctx, cfg)?; + + Ok(()) +} +``` + +#### Traits + +At various points in the execution of `invoke`, trait objects are fetched from the `ConfigBag`. These are preliminary definitions of those traits: + +```rust +pub trait TraceProbe: Send + Sync + Debug { + fn dispatch_events(&self, cfg: &ConfigBag) -> BoxFallibleFut<()>; +} + +pub trait RequestSerializer: Send + Sync + Debug { + fn serialize_request(&self, req: &mut In, cfg: &ConfigBag) -> Result; +} + +pub trait ResponseDeserializer: Send + Sync + Debug { + fn deserialize_response(&self, res: &mut TxRes, cfg: &ConfigBag) -> Result; +} + +pub trait Connection: Send + Sync + Debug { + fn call(&self, req: &mut TxReq, cfg: &ConfigBag) -> BoxFallibleFut; +} + +pub trait RetryStrategy: Send + Sync + Debug { + fn should_retry(&self, res: &Out, cfg: &ConfigBag) -> Result; +} + +pub trait AuthOrchestrator: Send + Sync + Debug { + fn auth_request(&self, req: &mut Req, cfg: &ConfigBag) -> Result<(), BoxError>; +} + +pub trait EndpointOrchestrator: Send + Sync + Debug { + fn resolve_and_apply_endpoint(&self, req: &mut Req, cfg: &ConfigBag) -> Result<(), BoxError>; + fn resolve_auth_schemes(&self) -> Result, BoxError>; +} +``` + +## F.A.Q. + +- The orchestrator is a large and complex feature, with many moving parts. How can we ensure that multiple people can contribute in parallel? + - By defining the entire orchestrator and agreeing on its structure, we can then move on to working on individual runtime plugins and interceptors. +- What is the precedence of interceptors? + - The precedence of interceptors is as follows: + - Interceptors registered via Smithy default plugins. + - *(AWS Services only)* Interceptors registered via AWS default plugins. + - Interceptors registered via service-customization plugins. + - Interceptors registered via client-level plugins. + - Interceptors registered via client-level configuration. + - Interceptors registered via operation-level plugins. + - Interceptors registered via operation-level configuration. +- What runtime plugins will be defined in `smithy-rs`? + - `RetryStrategy`: Configures how requests are retried. + - `TraceProbes`: Configures locations to which SDK metrics are published. + - `EndpointProviders`: Configures which hostname an SDK will call when making a request. + - `HTTPClients`: Configures how remote services are called. + - `IdentityProviders`: Configures how customers identify themselves to remote services. + - `HTTPAuthSchemes` & `AuthSchemeResolvers`: Configures how customers authenticate themselves to remote services. + - `Checksum Algorithms`: Configures how an SDK calculates request and response checksums. + +## Changes checklist + +- [x] Create a new `aws-smithy-runtime` crate. + - Add orchestrator implementation + - Define the orchestrator/runtime plugin interface traits + - `TraceProbe` + - `RequestSerializer` + - `ResponseDeserializer` + - `Connection` + - `RetryStrategy` + - `AuthOrchestrator` + - `EndpointOrchestrator` +- [x] Create a new `aws-smithy-runtime-api` crate. + - Add `ConfigBag` module + - Add `retries` module + - Add `rate_limiting` sub-module + - Add `interceptors` module + - `Interceptor` trait + - `InterceptorContext` impl + - Add `runtime_plugins` module +- [x] Create a new integration test that ensures the orchestrator works. diff --git a/design/src/server/anatomy.md b/design/src/server/anatomy.md index 794239d3bc..51f7b68f2d 100644 --- a/design/src/server/anatomy.md +++ b/design/src/server/anatomy.md @@ -301,7 +301,7 @@ where When we `GetPokemonService::from_handler` or `GetPokemonService::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 `Layer`, `UpgradeLayer` 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. @@ -648,7 +648,7 @@ stateDiagram-v2 Op1 --> Op2 : Plugin#colon;#colon;map ``` -An example `Plugin` implementation can be found in [aws-smithy-http-server/examples/pokemon-service/src/plugin.rs](https://github.com/awslabs/smithy-rs/blob/main/rust-runtime/aws-smithy-http-server/examples/pokemon-service/src/plugin.rs). +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. diff --git a/design/src/server/code_generation.md b/design/src/server/code_generation.md index 20da6c46c6..b04008cb46 100644 --- a/design/src/server/code_generation.md +++ b/design/src/server/code_generation.md @@ -7,13 +7,15 @@ This document introduces the project and how code is being generated. It is writ The project is divided in: -- `/codegen`: it contains shared code for both client and server, but only generates a client -- `/codegen-server`: server only. This project started with `codegen` to generate a client, but client and server share common code; that code lives in `codegen`, which `codegen-server` depends on +- `/codegen-core`: contains common code to be used for both client and server code generation +- `/codegen-client`: client code generation. Depends on `codegen-core` +- `/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 -`/rust-runtime` crates ("runtime crates") are added to a crate's dependency only when used. If a model uses event streams, it will depend on [`aws-smithy-eventstream`][3]. +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]. ## Generating code diff --git a/design/src/server/instrumentation.md b/design/src/server/instrumentation.md index 31eef1c38b..d946e51b0e 100644 --- a/design/src/server/instrumentation.md +++ b/design/src/server/instrumentation.md @@ -69,7 +69,7 @@ let app = PokemonService::builder_with_plugins(plugins) ### Example -The Pokémon service example, located at `rust-runtime/aws-smithy-http-server/examples/pokemon-service`, sets up a `tracing` `Subscriber` as follows: +The Pokémon service example, located at `/examples/pokemon-service`, sets up a `tracing` `Subscriber` as follows: ```rust /// Setup `tracing::subscriber` to read the log level from RUST_LOG environment variable. diff --git a/rust-runtime/aws-smithy-http-server/examples/.gitignore b/examples/.gitignore similarity index 82% rename from rust-runtime/aws-smithy-http-server/examples/.gitignore rename to examples/.gitignore index 8349ce53e7..aaf1fa215c 100644 --- a/rust-runtime/aws-smithy-http-server/examples/.gitignore +++ b/examples/.gitignore @@ -1,2 +1,3 @@ pokemon-service-client/ pokemon-service-server-sdk/ +Cargo.lock diff --git a/rust-runtime/aws-smithy-http-server/examples/BENCHMARKS.md b/examples/BENCHMARKS.md similarity index 72% rename from rust-runtime/aws-smithy-http-server/examples/BENCHMARKS.md rename to examples/BENCHMARKS.md index a5a3393066..bbc33c7b97 100644 --- a/rust-runtime/aws-smithy-http-server/examples/BENCHMARKS.md +++ b/examples/BENCHMARKS.md @@ -5,11 +5,12 @@ using [wrk](https://github.com/wg/wrk). -* [2022-03-04](#2022-03-04) - * [c6i.8xlarge](#c6i.8xlarge) - * [Full result](#full-result) - * [c6g.8xlarge](#c6g.8xlarge) - * [Full result](#full-result) +- [Smithy Rust Server SDK benchmarks](#smithy-rust-server-sdk-benchmarks) + - [2022-03-04](#2022-03-04) + - [c6i.8xlarge](#c6i8xlarge) + - [Full result](#full-result) + - [c6g.8xlarge](#c6g8xlarge) + - [Full result](#full-result-1) @@ -20,19 +21,19 @@ returning an empty output and can be used to stress test the framework overhead. ### c6i.8xlarge -* 32 cores Intel(R) Xeon(R) Platinum 8375C CPU @ 2.90GHz -* 64 Gb memory -* Benchmark: - - Duration: 10 minutes - - Connections: 1024 - - Threads: 16 -* Result: - - Request/sec: 1_608_742 - * RSS[^1] memory: 72200 bytes +- 32 cores Intel(R) Xeon(R) Platinum 8375C CPU @ 2.90GHz +- 64 Gb memory +- Benchmark: + - Duration: 10 minutes + - Connections: 1024 + - Threads: 16 +- Result: + - Request/sec: 1_608_742 + - RSS[^1] memory: 72200 bytes #### Full result -``` +```text ❯❯❯ wrk -t16 -c1024 -d10m --latency http://localhost:13734/empty-operation Running 10m test @ http://localhost:13734/empty-operation 16 threads and 1024 connections @@ -52,20 +53,20 @@ Transfer/sec: 167.23MB ### c6g.8xlarge -* 32 cores Amazon Graviton 2 @ 2.50GHz -* 64 Gb memory -* Benchmark: - - Duration: 10 minutes - - Connections: 1024 - - Threads: 16 -* Result: - - Request/sec: 1_379_942 - - RSS[^1] memory: 70264 bytes +- 32 cores Amazon Graviton 2 @ 2.50GHz +- 64 Gb memory +- Benchmark: + - Duration: 10 minutes + - Connections: 1024 + - Threads: 16 +- Result: + - Request/sec: 1_379_942 + - RSS[^1] memory: 70264 bytes #### Full result -``` +```text ❯❯❯ wrk -t16 -c1024 -d10m --latency http://localhost:13734/empty-operation Running 10m test @ http://localhost:13734/empty-operation 16 threads and 1024 connections diff --git a/rust-runtime/aws-smithy-http-server/examples/Cargo.toml b/examples/Cargo.toml similarity index 65% rename from rust-runtime/aws-smithy-http-server/examples/Cargo.toml rename to examples/Cargo.toml index e96e074b32..33c374bbcb 100644 --- a/rust-runtime/aws-smithy-http-server/examples/Cargo.toml +++ b/examples/Cargo.toml @@ -1,9 +1,12 @@ # Without this configuration, the workspace will be read from `rust-runtime`, causing the build to fail. [workspace] members = [ + "pokemon-service-common", "pokemon-service", + "pokemon-service-tls", + "pokemon-service-lambda", "pokemon-service-server-sdk", - "pokemon-service-client", + "pokemon-service-client" ] [profile.release] diff --git a/rust-runtime/aws-smithy-http-server/examples/Makefile b/examples/Makefile similarity index 100% rename from rust-runtime/aws-smithy-http-server/examples/Makefile rename to examples/Makefile diff --git a/examples/README.md b/examples/README.md new file mode 100644 index 0000000000..7f08738455 --- /dev/null +++ b/examples/README.md @@ -0,0 +1,49 @@ +# Smithy Rust Server SDK examples + +This folder contains an example services showcasing the 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. + +The `/{binary}/tests` folders are integration tests involving the generated clients. + +## 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` 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. + +## Run + +To run a binary use + +```bash +cargo run -p $BINARY +``` + +CLI arguments can be passed to the servers, use + +```bash +cargo run -p $BINARY -- --help +``` + +for more information. + +## Test + +`cargo test` can be used to spawn a service and run some simple integration +tests against it. Use `-p $BINARY` to filter by package. + +More info can be found in the `tests` folder of each package. + +## Benchmarks + +Please see [BENCHMARKS.md](/examples/BENCHMARKS.md). diff --git a/examples/pokemon-service-common/Cargo.toml b/examples/pokemon-service-common/Cargo.toml new file mode 100644 index 0000000000..704055c813 --- /dev/null +++ b/examples/pokemon-service-common/Cargo.toml @@ -0,0 +1,24 @@ +[package] +name = "pokemon-service-common" +version = "0.1.0" +edition = "2021" +publish = false +authors = ["Smithy-rs Server Team "] +description = "A smithy Rust service to retrieve information about Pokémon." + +[dependencies] +async-stream = "0.3" +http = "0.2.9" +rand = "0.8" +tracing = "0.1" +tracing-subscriber = { version = "0.3.16", features = ["env-filter", "json"] } +tokio = { version = "1", default-features = false, features = ["time"] } + +# Local paths +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" diff --git a/rust-runtime/aws-smithy-http-server/examples/pokemon-service/src/lib.rs b/examples/pokemon-service-common/src/lib.rs similarity index 90% rename from rust-runtime/aws-smithy-http-server/examples/pokemon-service/src/lib.rs rename to examples/pokemon-service-common/src/lib.rs index 0beb61b110..f431270da6 100644 --- a/rust-runtime/aws-smithy-http-server/examples/pokemon-service/src/lib.rs +++ b/examples/pokemon-service-common/src/lib.rs @@ -10,17 +10,19 @@ use std::{ collections::HashMap, convert::TryInto, + process::Child, sync::{atomic::AtomicUsize, Arc}, }; use async_stream::stream; +use aws_smithy_http::operation::Request; use aws_smithy_http_server::Extension; -use pokemon_service_server_sdk::{error, input, model, model::CapturingPayload, output, types::Blob}; +use pokemon_service_server_sdk::{ + error, input, model, model::CapturingPayload, output, types::Blob, +}; use rand::Rng; use tracing_subscriber::{prelude::*, EnvFilter}; -pub mod plugin; - const PIKACHU_ENGLISH_FLAVOR_TEXT: &str = "When several of these Pokémon gather, their electricity could build and cause lightning storms."; const PIKACHU_SPANISH_FLAVOR_TEXT: &str = @@ -30,13 +32,37 @@ 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] +pub struct ChildDrop(pub Child); + +impl Drop for ChildDrop { + fn drop(&mut self) { + self.0.kill().expect("failed to kill process") + } +} + /// Setup `tracing::subscriber` to read the log level from RUST_LOG environment variable. pub fn setup_tracing() { let format = tracing_subscriber::fmt::layer().json(); let filter = EnvFilter::try_from_default_env() .or_else(|_| EnvFilter::try_new("info")) .unwrap(); - tracing_subscriber::registry().with(format).with(filter).init(); + tracing_subscriber::registry() + .with(format) + .with(filter) + .init(); } /// Structure holding the translations for a Pokémon description. @@ -134,7 +160,10 @@ pub async fn get_pokemon_species( input: input::GetPokemonSpeciesInput, state: Extension>, ) -> Result { - state.0.call_count.fetch_add(1, std::sync::atomic::Ordering::SeqCst); + state + .0 + .call_count + .fetch_add(1, std::sync::atomic::Ordering::SeqCst); // We only support retrieving information about Pikachu. let pokemon = state.0.pokemons_translations.get(&input.name); match pokemon.as_ref() { @@ -215,7 +244,9 @@ pub async fn capture_pokemon( ) -> Result { if input.region != "Kanto" { return Err(error::CapturePokemonError::UnsupportedRegionError( - error::UnsupportedRegionError { region: input.region }, + error::UnsupportedRegionError { + region: input.region, + }, )); } let output_stream = stream! { @@ -226,8 +257,8 @@ pub async fn capture_pokemon( Some(event) => { let capturing_event = event.as_event(); if let Ok(attempt) = capturing_event { - let payload = attempt.payload.clone().unwrap_or(CapturingPayload::builder().build()); - let pokeball = payload.pokeball.as_ref().map(|ball| ball.as_str()).unwrap_or(""); + let payload = attempt.payload.clone().unwrap_or_else(|| CapturingPayload::builder().build()); + let pokeball = payload.pokeball().unwrap_or(""); if ! matches!(pokeball, "Master Ball" | "Great Ball" | "Fast Ball") { yield Err( crate::error::CapturePokemonEventsError::InvalidPokeballError( @@ -249,9 +280,7 @@ pub async fn capture_pokemon( if captured { let shiny = rand::thread_rng().gen_range(0..4096) == 0; let pokemon = payload - .name - .as_ref() - .map(|name| name.as_str()) + .name() .unwrap_or("") .to_string(); let pokedex: Vec = (0..255).collect(); @@ -309,7 +338,10 @@ mod tests { .find(|flavor_text| flavor_text.language == model::Language::Spanish) .unwrap(); - assert_eq!(PIKACHU_SPANISH_FLAVOR_TEXT, actual_spanish_flavor_text.flavor_text()); + assert_eq!( + PIKACHU_SPANISH_FLAVOR_TEXT, + actual_spanish_flavor_text.flavor_text() + ); let input = input::GetServerStatisticsInput {}; let stats = get_server_statistics(input, Extension(state.clone())).await; diff --git a/rust-runtime/aws-smithy-http-server/examples/pokemon-service/tests/plugins_execution_order.rs b/examples/pokemon-service-common/tests/plugins_execution_order.rs similarity index 88% rename from rust-runtime/aws-smithy-http-server/examples/pokemon-service/tests/plugins_execution_order.rs rename to examples/pokemon-service-common/tests/plugins_execution_order.rs index cc805b375b..78dccecde1 100644 --- a/rust-runtime/aws-smithy-http-server/examples/pokemon-service/tests/plugins_execution_order.rs +++ b/examples/pokemon-service-common/tests/plugins_execution_order.rs @@ -2,20 +2,25 @@ * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. * SPDX-License-Identifier: Apache-2.0 */ + +use std::{ + ops::Deref, + sync::Arc, + sync::Mutex, + task::{Context, Poll}, +}; + use aws_smithy_http::body::SdkBody; -use aws_smithy_http_server::operation::Operation; -use aws_smithy_http_server::plugin::{Plugin, PluginPipeline}; -use hyper::http; -use pokemon_service::do_nothing; -use pokemon_service_client::input::DoNothingInput; -use pokemon_service_client::Config; -use std::ops::Deref; -use std::sync::Arc; -use std::sync::Mutex; -use std::task::{Context, Poll}; +use aws_smithy_http_server::{ + operation::Operation, + plugin::{Plugin, PluginPipeline}, +}; use tower::layer::util::Stack; use tower::{Layer, Service}; +use pokemon_service_client::{operation::do_nothing::DoNothingInput, Config}; +use pokemon_service_common::do_nothing; + trait OperationExt { /// Convert an SDK operation into an `http::Request`. fn into_http(self) -> http::Request; @@ -59,7 +64,10 @@ struct SentinelPlugin { impl SentinelPlugin { pub fn new(name: &'static str, output: Arc>>) -> Self { - Self { name, output: output } + Self { + name, + output: output, + } } } diff --git a/examples/pokemon-service-lambda/Cargo.toml b/examples/pokemon-service-lambda/Cargo.toml new file mode 100644 index 0000000000..96a2d18230 --- /dev/null +++ b/examples/pokemon-service-lambda/Cargo.toml @@ -0,0 +1,21 @@ +[package] +name = "pokemon-service-lambda" +version = "0.1.0" +edition = "2021" +publish = false +authors = ["Smithy-rs Server Team "] +description = "A smithy Rust service to retrieve information about Pokémon via Lambda." + +[dependencies] +async-stream = "0.3.4" +clap = { version = "4.1.11", features = ["derive"] } +hyper = {version = "0.14.25", features = ["server"] } +tokio = "1.26.0" +tracing = "0.1" + +lambda_http = "0.7.3" + +# Local paths +aws-smithy-http-server = { path = "../../rust-runtime/aws-smithy-http-server", features = ["aws-lambda"] } +pokemon-service-server-sdk = { path = "../pokemon-service-server-sdk/" } +pokemon-service-common = { path = "../pokemon-service-common/" } diff --git a/examples/pokemon-service-lambda/src/lib.rs b/examples/pokemon-service-lambda/src/lib.rs new file mode 100644 index 0000000000..b2034d6566 --- /dev/null +++ b/examples/pokemon-service-lambda/src/lib.rs @@ -0,0 +1,33 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +use std::sync::Arc; + +use aws_smithy_http_server::{request::lambda::Context, Extension}; + +use pokemon_service_common::State; +use pokemon_service_server_sdk::{ + error::{GetStorageError, StorageAccessNotAuthorized}, + input::GetStorageInput, + output::GetStorageOutput, +}; + +/// Retrieves the user's storage and logs the lambda request ID. +pub async fn get_storage_lambda( + input: GetStorageInput, + _state: Extension>, + context: Context, +) -> Result { + tracing::debug!(request_id = %context.request_id, "attempting to authenticate storage user"); + + // We currently only support Ash and he has nothing stored + if !(input.user == "ash" && input.passcode == "pikachu123") { + tracing::debug!("authentication failed"); + return Err(GetStorageError::StorageAccessNotAuthorized( + StorageAccessNotAuthorized {}, + )); + } + Ok(GetStorageOutput { collection: vec![] }) +} diff --git a/examples/pokemon-service-lambda/src/main.rs b/examples/pokemon-service-lambda/src/main.rs new file mode 100644 index 0000000000..2e80cb9802 --- /dev/null +++ b/examples/pokemon-service-lambda/src/main.rs @@ -0,0 +1,39 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +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, +}; +use pokemon_service_lambda::get_storage_lambda; +use pokemon_service_server_sdk::PokemonService; + +#[tokio::main] +pub async fn main() { + 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 + // return the operation's output. + .get_pokemon_species(get_pokemon_species) + .get_storage(get_storage_lambda) + .get_server_statistics(get_server_statistics) + .capture_pokemon(capture_pokemon) + .do_nothing(do_nothing) + .check_health(check_health) + .build() + .expect("failed to build an instance of PokemonService") + // Set up shared state and middlewares. + .layer(&AddExtensionLayer::new(Arc::new(State::default()))); + + let handler = LambdaHandler::new(app); + let lambda = lambda_http::run(handler); + + if let Err(err) = lambda.await { + eprintln!("lambda error: {}", err); + } +} diff --git a/rust-runtime/aws-smithy-http-server/examples/pokemon-service/tests/fixtures/example-apigw-request.json b/examples/pokemon-service-lambda/tests/fixtures/example-apigw-request.json similarity index 100% rename from rust-runtime/aws-smithy-http-server/examples/pokemon-service/tests/fixtures/example-apigw-request.json rename to examples/pokemon-service-lambda/tests/fixtures/example-apigw-request.json diff --git a/examples/pokemon-service-tls/Cargo.toml b/examples/pokemon-service-tls/Cargo.toml new file mode 100644 index 0000000000..ac3b32b7fe --- /dev/null +++ b/examples/pokemon-service-tls/Cargo.toml @@ -0,0 +1,36 @@ +[package] +name = "pokemon-service-tls" +version = "0.1.0" +edition = "2021" +publish = false +authors = ["Smithy-rs Server Team "] +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"] } +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" +rustls-pemfile = "1.0.2" +futures-util = { version = "0.3.27", default-features = false } + +# Local paths +aws-smithy-http-server = { path = "../../rust-runtime/aws-smithy-http-server" } +pokemon-service-server-sdk = { path = "../pokemon-service-server-sdk/" } +pokemon-service-common = { path = "../pokemon-service-common/" } + +[dev-dependencies] +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"] } + +# 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/examples/pokemon-service-tls/src/lib.rs b/examples/pokemon-service-tls/src/lib.rs new file mode 100644 index 0000000000..d5006504e1 --- /dev/null +++ b/examples/pokemon-service-tls/src/lib.rs @@ -0,0 +1,13 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +// Defaults shared between `main.rs` and `/tests`. +pub const DEFAULT_TEST_KEY: &str = + concat!(env!("CARGO_MANIFEST_DIR"), "/tests/testdata/localhost.key"); +pub const DEFAULT_TEST_CERT: &str = + concat!(env!("CARGO_MANIFEST_DIR"), "/tests/testdata/localhost.crt"); +pub const DEFAULT_ADDRESS: &str = "127.0.0.1"; +pub const DEFAULT_PORT: u16 = 13734; +pub const DEFAULT_DOMAIN: &str = "localhost"; diff --git a/rust-runtime/aws-smithy-http-server/examples/pokemon-service/src/bin/pokemon-service-tls.rs b/examples/pokemon-service-tls/src/main.rs similarity index 87% rename from rust-runtime/aws-smithy-http-server/examples/pokemon-service/src/bin/pokemon-service-tls.rs rename to examples/pokemon-service-tls/src/main.rs index 34ccd0c6f1..2a9ef679d2 100644 --- a/rust-runtime/aws-smithy-http-server/examples/pokemon-service/src/bin/pokemon-service-tls.rs +++ b/examples/pokemon-service-tls/src/main.rs @@ -22,39 +22,37 @@ // note that by default created certificates will be unknown and you should use `-k|--insecure` // flag while making requests with cURL or you can run `mkcert -install` to trust certificates created by `mkcert`. -use std::fs::File; -use std::future; -use std::io::BufReader; -use std::net::SocketAddr; -use std::sync::Arc; +use std::{fs::File, future, io::BufReader, net::SocketAddr, sync::Arc}; -use aws_smithy_http_server::{plugin::PluginPipeline, AddExtensionLayer}; +use aws_smithy_http_server::AddExtensionLayer; use clap::Parser; use futures_util::stream::StreamExt; -use pokemon_service::{ - capture_pokemon, check_health, do_nothing, get_pokemon_species, get_server_statistics, get_storage, - plugin::PrintExt, setup_tracing, State, -}; -use pokemon_service_server_sdk::PokemonService; use tokio_rustls::{ rustls::{Certificate, PrivateKey, ServerConfig}, TlsAcceptor, }; +use pokemon_service_common::{ + capture_pokemon, check_health, do_nothing, get_pokemon_species, get_server_statistics, + get_storage, setup_tracing, State, +}; +use pokemon_service_server_sdk::PokemonService; +use pokemon_service_tls::{DEFAULT_ADDRESS, DEFAULT_PORT, DEFAULT_TEST_CERT, DEFAULT_TEST_KEY}; + #[derive(Parser, Debug)] #[clap(author, version, about, long_about = None)] struct Args { /// Hyper server bind address. - #[clap(short, long, action, default_value = "127.0.0.1")] + #[clap(short, long, action, default_value = DEFAULT_ADDRESS)] address: String, /// Hyper server bind port. - #[clap(short, long, action, default_value = "13734")] + #[clap(short, long, action, default_value_t = DEFAULT_PORT)] port: u16, /// Hyper server TLS certificate path. Must be a PEM file. - #[clap(long, default_value = "")] + #[clap(long, default_value = DEFAULT_TEST_CERT)] tls_cert_path: String, /// Hyper server TLS private key path. Must be a PEM file. - #[clap(long, default_value = "")] + #[clap(long, default_value = DEFAULT_TEST_KEY)] tls_key_path: String, } @@ -62,9 +60,8 @@ struct Args { pub async fn main() { let args = Args::parse(); setup_tracing(); - // Apply the `PrintPlugin` defined in `plugin.rs` - let plugins = PluginPipeline::new().print(); - let app = PokemonService::builder_with_plugins(plugins) + + 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 // return the operation's output. @@ -96,7 +93,8 @@ pub async fn main() { future::ready(true) } }); - let server = hyper::Server::builder(hyper::server::accept::from_stream(listener)).serve(app.into_make_service()); + let server = hyper::Server::builder(hyper::server::accept::from_stream(listener)) + .serve(app.into_make_service()); if let Err(err) = server.await { eprintln!("server error: {}", err); } diff --git a/examples/pokemon-service-tls/tests/common/mod.rs b/examples/pokemon-service-tls/tests/common/mod.rs new file mode 100644 index 0000000000..746b7823e2 --- /dev/null +++ b/examples/pokemon-service-tls/tests/common/mod.rs @@ -0,0 +1,58 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +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 tokio::time::sleep; + +use pokemon_service_client::{Builder, Client, Config}; +use pokemon_service_common::{rewrite_base_url, 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(); + + sleep(Duration::from_millis(500)).await; + + ChildDrop(child) +} + +// 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> { + // 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")); + let certs = rustls_pemfile::certs(&mut reader).expect("could not parse certificate"); + let mut roots = tokio_rustls::rustls::RootCertStore::empty(); + roots.add_parsable_certificates(&certs); + + let connector = hyper_rustls::HttpsConnectorBuilder::new() + .with_tls_config( + tokio_rustls::rustls::ClientConfig::builder() + .with_safe_defaults() + .with_root_certificates(roots) + .with_no_client_auth(), + ) + .https_only() + .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) +} diff --git a/examples/pokemon-service-tls/tests/http2.rs b/examples/pokemon-service-tls/tests/http2.rs new file mode 100644 index 0000000000..32c0fba08c --- /dev/null +++ b/examples/pokemon-service-tls/tests/http2.rs @@ -0,0 +1,14 @@ +/* + * 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/rust-runtime/aws-smithy-http-server/examples/pokemon-service/tests/testdata/localhost.crt b/examples/pokemon-service-tls/tests/testdata/localhost.crt similarity index 100% rename from rust-runtime/aws-smithy-http-server/examples/pokemon-service/tests/testdata/localhost.crt rename to examples/pokemon-service-tls/tests/testdata/localhost.crt diff --git a/rust-runtime/aws-smithy-http-server/examples/pokemon-service/tests/testdata/localhost.key b/examples/pokemon-service-tls/tests/testdata/localhost.key similarity index 100% rename from rust-runtime/aws-smithy-http-server/examples/pokemon-service/tests/testdata/localhost.key rename to examples/pokemon-service-tls/tests/testdata/localhost.key diff --git a/examples/pokemon-service/Cargo.toml b/examples/pokemon-service/Cargo.toml new file mode 100644 index 0000000000..30839509e5 --- /dev/null +++ b/examples/pokemon-service/Cargo.toml @@ -0,0 +1,34 @@ +[package] +name = "pokemon-service" +version = "0.1.0" +edition = "2021" +publish = false +authors = ["Smithy-rs Server Team "] +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"] } +tokio = "1.26.0" +tower = "0.4" +tracing = "0.1" + +# Local paths +aws-smithy-http-server = { path = "../../rust-runtime/aws-smithy-http-server", features = ["request-id"] } +pokemon-service-server-sdk = { path = "../pokemon-service-server-sdk/" } +pokemon-service-common = { path = "../pokemon-service-common/" } + +[dev-dependencies] +assert_cmd = "2.0" +async-stream = "0.3" +rand = "0.8.5" +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"] } + +# 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/examples/pokemon-service/src/lib.rs b/examples/pokemon-service/src/lib.rs new file mode 100644 index 0000000000..b6521213a1 --- /dev/null +++ b/examples/pokemon-service/src/lib.rs @@ -0,0 +1,59 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +use std::net::{IpAddr, SocketAddr}; + +use aws_smithy_http_server::request::{connect_info::ConnectInfo, request_id::ServerRequestId}; +use pokemon_service_server_sdk::{ + error::{GetStorageError, StorageAccessNotAuthorized}, + input::{DoNothingInput, GetStorageInput}, + output::{DoNothingOutput, GetStorageOutput}, +}; + +// Defaults shared between `main.rs` and `/tests`. +pub const DEFAULT_ADDRESS: &str = "127.0.0.1"; +pub const DEFAULT_PORT: u16 = 13734; + +/// Logs the request IDs to `DoNothing` operation. +pub async fn do_nothing_but_log_request_ids( + _input: DoNothingInput, + request_id: ServerRequestId, +) -> DoNothingOutput { + tracing::debug!(%request_id, "do nothing"); + DoNothingOutput {} +} + +/// Retrieves the user's storage. No authentication required for locals. +pub async fn get_storage_with_local_approved( + input: GetStorageInput, + connect_info: ConnectInfo, +) -> Result { + tracing::debug!("attempting to authenticate storage user"); + + if !(input.user == "ash" && input.passcode == "pikachu123") { + tracing::debug!("authentication failed"); + return Err(GetStorageError::StorageAccessNotAuthorized( + StorageAccessNotAuthorized {}, + )); + } + + // We support trainers in our local gym + let local = connect_info.0.ip() == "127.0.0.1".parse::().unwrap(); + if local { + tracing::info!("welcome back"); + return Ok(GetStorageOutput { + collection: vec![ + String::from("bulbasaur"), + String::from("charmander"), + String::from("squirtle"), + String::from("pikachu"), + ], + }); + } + + Ok(GetStorageOutput { + collection: vec![String::from("pikachu")], + }) +} diff --git a/rust-runtime/aws-smithy-http-server/examples/pokemon-service/src/bin/pokemon-service.rs b/examples/pokemon-service/src/main.rs similarity index 59% rename from rust-runtime/aws-smithy-http-server/examples/pokemon-service/src/bin/pokemon-service.rs rename to examples/pokemon-service/src/main.rs index 402e57555a..f26e6d14ba 100644 --- a/rust-runtime/aws-smithy-http-server/examples/pokemon-service/src/bin/pokemon-service.rs +++ b/examples/pokemon-service/src/main.rs @@ -3,14 +3,22 @@ * SPDX-License-Identifier: Apache-2.0 */ -// This program is exported as a binary named `pokemon-service`. +mod plugin; + use std::{net::SocketAddr, sync::Arc}; -use aws_smithy_http_server::{extension::OperationExtensionExt, plugin::PluginPipeline, AddExtensionLayer}; +use aws_smithy_http_server::{ + extension::OperationExtensionExt, instrumentation::InstrumentExt, plugin::PluginPipeline, + request::request_id::ServerRequestIdProviderLayer, AddExtensionLayer, +}; use clap::Parser; + +use plugin::PrintExt; use pokemon_service::{ - capture_pokemon, check_health, do_nothing, get_pokemon_species, get_server_statistics, get_storage, - plugin::PrintExt, setup_tracing, State, + 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, }; use pokemon_service_server_sdk::PokemonService; @@ -18,10 +26,10 @@ use pokemon_service_server_sdk::PokemonService; #[clap(author, version, about, long_about = None)] struct Args { /// Hyper server bind address. - #[clap(short, long, action, default_value = "127.0.0.1")] + #[clap(short, long, action, default_value = DEFAULT_ADDRESS)] address: String, /// Hyper server bind port. - #[clap(short, long, action, default_value = "13734")] + #[clap(short, long, action, default_value_t = DEFAULT_PORT)] port: u16, } @@ -29,33 +37,44 @@ struct Args { pub async fn main() { let args = Args::parse(); setup_tracing(); + let plugins = PluginPipeline::new() // Apply the `PrintPlugin` defined in `plugin.rs` .print() // 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(); + .insert_operation_extension() + // Adds `tracing` spans and events to the request lifecycle. + .instrument(); let app = PokemonService::builder_with_plugins(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 // return the operation's output. .get_pokemon_species(get_pokemon_species) - .get_storage(get_storage) + .get_storage(get_storage_with_local_approved) .get_server_statistics(get_server_statistics) .capture_pokemon(capture_pokemon) - .do_nothing(do_nothing) + .do_nothing(do_nothing_but_log_request_ids) .check_health(check_health) .build() - .expect("failed to build an instance of PokemonService") + .expect("failed to build an instance of PokemonService"); + + let app = app // Setup shared state and middlewares. - .layer(&AddExtensionLayer::new(Arc::new(State::default()))); + .layer(&AddExtensionLayer::new(Arc::new(State::default()))) + // Add request IDs + .layer(&ServerRequestIdProviderLayer::new()); + + // Using `into_make_service_with_connect_info`, rather than `into_make_service`, to adjoin the `SocketAddr` + // connection info. + let make_app = app.into_make_service_with_connect_info::(); - // Start the [`hyper::Server`]. + // Bind the application to a socket. let bind: SocketAddr = format!("{}:{}", args.address, args.port) .parse() .expect("unable to parse the server bind address and port"); - let server = hyper::Server::bind(&bind).serve(app.into_make_service()); + let server = hyper::Server::bind(&bind).serve(make_app); // Run forever-ish... if let Err(err) = server.await { diff --git a/rust-runtime/aws-smithy-http-server/examples/pokemon-service/src/plugin.rs b/examples/pokemon-service/src/plugin.rs similarity index 100% rename from rust-runtime/aws-smithy-http-server/examples/pokemon-service/src/plugin.rs rename to examples/pokemon-service/src/plugin.rs diff --git a/examples/pokemon-service/tests/common/mod.rs b/examples/pokemon-service/tests/common/mod.rs new file mode 100644 index 0000000000..b21eb2a78b --- /dev/null +++ b/examples/pokemon-service/tests/common/mod.rs @@ -0,0 +1,35 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +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}; + +pub async fn run_server() -> ChildDrop { + let child = Command::cargo_bin("pokemon-service") + .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) +} diff --git a/rust-runtime/aws-smithy-http-server/examples/pokemon-service/tests/simple_integration_test.rs b/examples/pokemon-service/tests/event_streaming.rs similarity index 54% rename from rust-runtime/aws-smithy-http-server/examples/pokemon-service/tests/simple_integration_test.rs rename to examples/pokemon-service/tests/event_streaming.rs index 5e6efd29da..664827620b 100644 --- a/rust-runtime/aws-smithy-http-server/examples/pokemon-service/tests/simple_integration_test.rs +++ b/examples/pokemon-service/tests/event_streaming.rs @@ -3,26 +3,21 @@ * 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 crate::helpers::{client, client_http2_only, PokemonService}; +pub mod common; use async_stream::stream; -use aws_smithy_types::error::display::DisplayErrorContext; -use pokemon_service_client::{ - error::{ - AttemptCapturingPokemonEventError, AttemptCapturingPokemonEventErrorKind, GetStorageError, GetStorageErrorKind, - MasterBallUnsuccessful, StorageAccessNotAuthorized, - }, - model::{AttemptCapturingPokemonEvent, CapturingEvent, CapturingPayload}, - types::SdkError, -}; use rand::Rng; use serial_test::serial; -mod helpers; +use pokemon_service_client::types::{ + error::{AttemptCapturingPokemonEventError, MasterBallUnsuccessful}, + AttemptCapturingPokemonEvent, CapturingEvent, CapturingPayload, +}; + +fn get_pokemon_to_capture() -> String { + let pokemons = vec!["Charizard", "Pikachu", "Regieleki"]; + pokemons[rand::thread_rng().gen_range(0..pokemons.len())].to_string() +} fn get_pokeball() -> String { let random = rand::thread_rng().gen_range(0..100); @@ -38,85 +33,11 @@ fn get_pokeball() -> String { 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() -} - -#[tokio::test] -#[serial] -async fn test_check_health() { - let _program = PokemonService::run().await; - - let _check_health = client().check_health().send().await.unwrap(); -} - -#[tokio::test] -#[serial] -async fn test_check_health_http2() { - // Make sure our server can serve http2 - let _program = PokemonService::run_https().await; - let _check_health = client_http2_only().check_health().send().await.unwrap(); -} - -#[tokio::test] -#[serial] -async fn simple_integration_test() { - let _program = PokemonService::run().await; - - 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 storage_err = client().get_storage().user("ash").passcode("pikachu321").send().await; - let has_not_authorized_error = if let Err(SdkError::ServiceError(context)) = storage_err { - matches!( - context.err(), - GetStorageError { - kind: GetStorageErrorKind::StorageAccessNotAuthorized(StorageAccessNotAuthorized { .. }), - .. - } - ) - } else { - false - }; - assert!(has_not_authorized_error, "expected NotAuthorized error"); - - let storage_out = client() - .get_storage() - .user("ash") - .passcode("pikachu123") - .send() - .await - .unwrap(); - assert_eq!(Some(vec![]), storage_out.collection); - - 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()); -} - #[tokio::test] #[serial] async fn event_stream_test() { - let _program = PokemonService::run().await; + let _child = common::run_server().await; + let client = common::client(); let mut team = vec![]; let input_stream = stream! { @@ -137,10 +58,7 @@ async fn event_stream_test() { .build()) .build() )); - yield Err(AttemptCapturingPokemonEventError::new( - AttemptCapturingPokemonEventErrorKind::MasterBallUnsuccessful(MasterBallUnsuccessful::builder().build()), - Default::default() - )); + yield Err(AttemptCapturingPokemonEventError::MasterBallUnsuccessful(MasterBallUnsuccessful::builder().build())); // The next event should not happen yield Ok(AttemptCapturingPokemonEvent::Event( CapturingEvent::builder() @@ -153,7 +71,7 @@ async fn event_stream_test() { }; // Throw many! - let mut output = client() + let mut output = common::client() .capture_pokemon() .region("Kanto") .events(input_stream.into()) @@ -164,7 +82,13 @@ async fn event_stream_test() { 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 pokedex = capture + .as_event() + .unwrap() + .pokedex_update + .as_ref() + .unwrap() + .clone(); let shiny = if *capture.as_event().unwrap().shiny.as_ref().unwrap() { "" } else { @@ -198,7 +122,7 @@ async fn event_stream_test() { .build() )) }; - let mut output = client() + let mut output = client .capture_pokemon() .region("Kanto") .events(input_stream.into()) @@ -208,7 +132,13 @@ async fn event_stream_test() { 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 pokedex = capture + .as_event() + .unwrap() + .pokedex_update + .as_ref() + .unwrap() + .clone(); let shiny = if *capture.as_event().unwrap().shiny.as_ref().unwrap() { "" } else { diff --git a/examples/pokemon-service/tests/simple.rs b/examples/pokemon-service/tests/simple.rs new file mode 100644 index 0000000000..1c86d3cf10 --- /dev/null +++ b/examples/pokemon-service/tests/simple.rs @@ -0,0 +1,84 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +use pokemon_service_client::{ + error::{DisplayErrorContext, SdkError}, + operation::get_storage::GetStorageError, + types::error::StorageAccessNotAuthorized, +}; +use serial_test::serial; + +pub mod common; + +#[tokio::test] +#[serial] +async fn simple_integration_test() { + let _child = common::run_server().await; + let client = common::client(); + + 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 storage_err = client + .get_storage() + .user("ash") + .passcode("pikachu321") + .send() + .await; + let has_not_authorized_error = if let Err(SdkError::ServiceError(context)) = storage_err { + matches!( + context.err(), + GetStorageError::StorageAccessNotAuthorized(StorageAccessNotAuthorized { .. }), + ) + } else { + false + }; + assert!(has_not_authorized_error, "expected NotAuthorized error"); + + let storage_out = client + .get_storage() + .user("ash") + .passcode("pikachu123") + .send() + .await + .unwrap(); + assert_eq!( + Some(vec![ + "bulbasaur".to_string(), + "charmander".to_string(), + "squirtle".to_string(), + "pikachu".to_string() + ]), + storage_out.collection + ); + + 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()); +} diff --git a/gradle.properties b/gradle.properties index 9857dcc895..ead4b8d783 100644 --- a/gradle.properties +++ b/gradle.properties @@ -4,8 +4,11 @@ # # Rust MSRV (entered into the generated README) -rust.msrv=1.62.1 +rust.msrv=1.66.1 +# To enable debug, swap out the two lines below. +# When changing this value, be sure to run `./gradlew --stop` to kill the Gradle daemon. +# org.gradle.jvmargs=-Xmx1024M -agentlib:jdwp=transport=dt_socket,server=y,suspend=y,address=localhost:5006 org.gradle.jvmargs=-Xmx1024M # Version number to use for the generated runtime crates @@ -15,13 +18,13 @@ kotlin.code.style=official # codegen smithyGradlePluginVersion=0.6.0 -smithyVersion=1.26.2 +smithyVersion=1.28.1 # kotlin -kotlinVersion=1.6.21 +kotlinVersion=1.7.21 # testing/utility -ktlintVersion=0.46.1 +ktlintVersion=0.48.2 kotestVersion=5.2.3 # Avoid registering dependencies/plugins/tasks that are only used for testing purposes isTestingEnabled=true diff --git a/rust-runtime/Cargo.toml b/rust-runtime/Cargo.toml index 185d7082a0..4f1555345a 100644 --- a/rust-runtime/Cargo.toml +++ b/rust-runtime/Cargo.toml @@ -1,19 +1,23 @@ [workspace] + members = [ "inlineable", "aws-smithy-async", - "aws-smithy-client", "aws-smithy-checksums", + "aws-smithy-client", "aws-smithy-eventstream", "aws-smithy-http", + "aws-smithy-http-auth", + "aws-smithy-http-server", + "aws-smithy-http-server-python", "aws-smithy-http-tower", "aws-smithy-json", "aws-smithy-protocol-test", "aws-smithy-query", + "aws-smithy-runtime", + "aws-smithy-runtime-api", "aws-smithy-types", "aws-smithy-types-convert", "aws-smithy-xml", - "aws-smithy-http-server", - "aws-smithy-http-server-python", ] diff --git a/rust-runtime/aws-smithy-async/Cargo.toml b/rust-runtime/aws-smithy-async/Cargo.toml index 04d600ea62..d77d6097d6 100644 --- a/rust-runtime/aws-smithy-async/Cargo.toml +++ b/rust-runtime/aws-smithy-async/Cargo.toml @@ -12,12 +12,12 @@ rt-tokio = ["tokio/time"] [dependencies] pin-project-lite = "0.2" -tokio = { version = "1.8.4", features = ["sync"] } +tokio = { version = "1.23.1", features = ["sync"] } tokio-stream = "0.1.5" -futures-util = "0.3.16" +futures-util = { version = "0.3.16", default-features = false } [dev-dependencies] -tokio = { version = "1.8.4", features = ["rt", "macros", "test-util"] } +tokio = { version = "1.23.1", features = ["rt", "macros", "test-util"] } tokio-test = "0.4.2" [package.metadata.docs.rs] diff --git a/rust-runtime/aws-smithy-async/src/lib.rs b/rust-runtime/aws-smithy-async/src/lib.rs index 6cd95109e2..7e106da5db 100644 --- a/rust-runtime/aws-smithy-async/src/lib.rs +++ b/rust-runtime/aws-smithy-async/src/lib.rs @@ -3,10 +3,11 @@ * SPDX-License-Identifier: Apache-2.0 */ +#![allow(clippy::derive_partial_eq_without_eq)] #![warn( - missing_debug_implementations, missing_docs, - rustdoc::all, + rustdoc::missing_crate_level_docs, + unreachable_pub, rust_2018_idioms )] diff --git a/rust-runtime/aws-smithy-checksums/Cargo.toml b/rust-runtime/aws-smithy-checksums/Cargo.toml index b210360d55..b02b34cc09 100644 --- a/rust-runtime/aws-smithy-checksums/Cargo.toml +++ b/rust-runtime/aws-smithy-checksums/Cargo.toml @@ -29,8 +29,8 @@ tracing = "0.1" [dev-dependencies] bytes-utils = "0.1.2" -pretty_assertions = "1.2" -tokio = { version = "1.8.4", features = ["macros", "rt"] } +pretty_assertions = "1.3" +tokio = { version = "1.23.1", features = ["macros", "rt"] } tracing-test = "0.2.1" [package.metadata.docs.rs] diff --git a/rust-runtime/aws-smithy-checksums/src/lib.rs b/rust-runtime/aws-smithy-checksums/src/lib.rs index ce422fe5ee..9edda514ec 100644 --- a/rust-runtime/aws-smithy-checksums/src/lib.rs +++ b/rust-runtime/aws-smithy-checksums/src/lib.rs @@ -3,6 +3,14 @@ * SPDX-License-Identifier: Apache-2.0 */ +#![allow(clippy::derive_partial_eq_without_eq)] +#![warn( + // missing_docs, + rustdoc::missing_crate_level_docs, + unreachable_pub, + rust_2018_idioms +)] + //! Checksum calculation and verification callbacks. use crate::error::UnknownChecksumAlgorithmError; @@ -62,11 +70,11 @@ impl ChecksumAlgorithm { /// Return the `HttpChecksum` implementor for this algorithm pub fn into_impl(self) -> Box { match self { - Self::Crc32 => Box::new(Crc32::default()), - Self::Crc32c => Box::new(Crc32c::default()), - Self::Md5 => Box::new(Md5::default()), - Self::Sha1 => Box::new(Sha1::default()), - Self::Sha256 => Box::new(Sha256::default()), + Self::Crc32 => Box::::default(), + Self::Crc32c => Box::::default(), + Self::Md5 => Box::::default(), + Self::Sha1 => Box::::default(), + Self::Sha256 => Box::::default(), } } @@ -379,8 +387,7 @@ mod tests { fn test_checksum_algorithm_returns_error_for_unknown() { let error = "some invalid checksum algorithm" .parse::() - .err() - .expect("it should error"); + .expect_err("it should error"); assert_eq!( "some invalid checksum algorithm", error.checksum_algorithm() diff --git a/rust-runtime/aws-smithy-client/Cargo.toml b/rust-runtime/aws-smithy-client/Cargo.toml index d8f8041bbd..56ab945aa0 100644 --- a/rust-runtime/aws-smithy-client/Cargo.toml +++ b/rust-runtime/aws-smithy-client/Cargo.toml @@ -9,12 +9,13 @@ repository = "https://github.com/awslabs/smithy-rs" [features] rt-tokio = ["aws-smithy-async/rt-tokio"] -test-util = ["aws-smithy-protocol-test", "serde/derive", "rustls"] +test-util = ["aws-smithy-protocol-test", "serde/derive", "rustls", "hyper/server", "hyper/h2", "tokio/full"] native-tls = ["client-hyper", "hyper-tls", "rt-tokio"] -rustls = ["client-hyper", "hyper-rustls", "rt-tokio", "lazy_static"] +rustls = ["client-hyper", "hyper-rustls", "rt-tokio", "lazy_static", "dep:rustls"] client-hyper = ["hyper"] hyper-webpki-doctest-only = ["hyper-rustls/webpki-roots"] + [dependencies] aws-smithy-async = { path = "../aws-smithy-async" } aws-smithy-http = { path = "../aws-smithy-http" } @@ -25,16 +26,17 @@ bytes = "1" fastrand = "1.4.0" http = "0.2.3" http-body = "0.4.4" -hyper = { version = "0.14.12", features = ["client", "http2", "http1", "tcp"], optional = true } +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. # https://github.com/rust-lang/cargo/issues/1596 hyper-rustls = { version = "0.23.0", optional = true, features = ["rustls-native-certs", "http2"] } hyper-tls = { version = "0.5.0", optional = true } +rustls = { version = "0.20", optional = true } lazy_static = { version = "1", optional = true } pin-project-lite = "0.2.7" serde = { version = "1", features = ["derive"], optional = true } -tokio = { version = "1.8.4" } +tokio = { version = "1.13.1" } tower = { version = "0.4.6", features = ["util", "retry"] } tracing = "0.1" @@ -42,8 +44,11 @@ tracing = "0.1" aws-smithy-async = { path = "../aws-smithy-async", features = ["rt-tokio"] } serde = { version = "1", features = ["derive"] } serde_json = "1" -tokio = { version = "1.8.4", features = ["full", "test-util"] } +tokio = { version = "1.23.1", features = ["full", "test-util"] } tower-test = "0.4.0" +tracing-subscriber = "0.3.16" +tracing-test = "0.2.4" + [package.metadata.docs.rs] all-features = true diff --git a/rust-runtime/aws-smithy-client/external-types.toml b/rust-runtime/aws-smithy-client/external-types.toml index fd2d76368f..0bab3e6536 100644 --- a/rust-runtime/aws-smithy-client/external-types.toml +++ b/rust-runtime/aws-smithy-client/external-types.toml @@ -21,10 +21,12 @@ allowed_external_types = [ "tokio::io::async_read::AsyncRead", "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 "bytes::bytes::Bytes", "serde::ser::Serialize", "serde::de::Deserialize", + "hyper::client::connect::dns::Name", # TODO(https://github.com/awslabs/smithy-rs/issues/1193): Decide if we want to continue exposing tower_layer "tower_layer::Layer", diff --git a/rust-runtime/aws-smithy-client/src/bounds.rs b/rust-runtime/aws-smithy-client/src/bounds.rs index 0cddf86096..e0abf9e4df 100644 --- a/rust-runtime/aws-smithy-client/src/bounds.rs +++ b/rust-runtime/aws-smithy-client/src/bounds.rs @@ -7,7 +7,7 @@ //! required for `call` and friends. //! //! The short-hands will one day be true [trait aliases], but for now they are traits with blanket -//! implementations. Also, due to [compiler limitations], the bounds repeat a nubmer of associated +//! implementations. Also, due to [compiler limitations], the bounds repeat a number of associated //! types with bounds so that those bounds [do not need to be repeated] at the call site. It's a //! bit of a mess to define, but _should_ be invisible to callers. //! @@ -17,8 +17,12 @@ use crate::erase::DynConnector; use crate::http_connector::HttpConnector; -use crate::*; -use aws_smithy_http::result::ConnectorError; +use aws_smithy_http::body::SdkBody; +use aws_smithy_http::operation::{self, Operation}; +use aws_smithy_http::response::ParseHttpResponse; +use aws_smithy_http::result::{ConnectorError, SdkError, SdkSuccess}; +use aws_smithy_http::retry::ClassifyRetry; +use tower::{Layer, Service}; /// A service that has parsed a raw Smithy response. pub type Parsed = @@ -75,14 +79,14 @@ where } } -/// A Smithy middleware service that adjusts [`aws_smithy_http::operation::Request`]s. +/// A Smithy middleware service that adjusts [`aws_smithy_http::operation::Request`](operation::Request)s. /// /// This trait has a blanket implementation for all compatible types, and should never be /// implemented. pub trait SmithyMiddlewareService: Service< - aws_smithy_http::operation::Request, - Response = aws_smithy_http::operation::Response, + operation::Request, + Response = operation::Response, Error = aws_smithy_http_tower::SendOperationError, Future = ::Future, > @@ -96,8 +100,8 @@ pub trait SmithyMiddlewareService: impl SmithyMiddlewareService for T where T: Service< - aws_smithy_http::operation::Request, - Response = aws_smithy_http::operation::Response, + operation::Request, + Response = operation::Response, Error = aws_smithy_http_tower::SendOperationError, >, T::Future: Send + 'static, @@ -143,7 +147,7 @@ pub trait SmithyRetryPolicy: /// Forwarding type to `E` for bound inference. /// /// See module-level docs for details. - type E: Error; + type E: std::error::Error; /// Forwarding type to `Retry` for bound inference. /// @@ -155,7 +159,7 @@ impl SmithyRetryPolicy for R where R: tower::retry::Policy, SdkSuccess, SdkError> + Clone, O: ParseHttpResponse> + Send + Sync + Clone + 'static, - E: Error, + E: std::error::Error, Retry: ClassifyRetry, SdkError>, { type O = O; diff --git a/rust-runtime/aws-smithy-client/src/builder.rs b/rust-runtime/aws-smithy-client/src/builder.rs index 1fe4ba12eb..d226dcb1cf 100644 --- a/rust-runtime/aws-smithy-client/src/builder.rs +++ b/rust-runtime/aws-smithy-client/src/builder.rs @@ -7,6 +7,7 @@ use crate::{bounds, erase, retry, Client}; use aws_smithy_async::rt::sleep::{default_async_sleep, AsyncSleep}; 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; @@ -37,6 +38,12 @@ pub struct Builder { retry_policy: MaybeRequiresSleep, operation_timeout_config: Option, sleep_impl: Option>, + reconnect_mode: Option, +} + +/// transitional default: disable this behavior by default +const fn default_reconnect_mode() -> ReconnectMode { + ReconnectMode::ReuseAllConnections } impl Default for Builder @@ -55,6 +62,7 @@ where ), operation_timeout_config: None, sleep_impl: default_async_sleep(), + reconnect_mode: Some(default_reconnect_mode()), } } } @@ -173,6 +181,7 @@ impl Builder<(), M, R> { retry_policy: self.retry_policy, operation_timeout_config: self.operation_timeout_config, sleep_impl: self.sleep_impl, + reconnect_mode: self.reconnect_mode, } } @@ -229,6 +238,7 @@ impl Builder { operation_timeout_config: self.operation_timeout_config, middleware, sleep_impl: self.sleep_impl, + reconnect_mode: self.reconnect_mode, } } @@ -280,6 +290,7 @@ impl Builder { operation_timeout_config: self.operation_timeout_config, middleware: self.middleware, sleep_impl: self.sleep_impl, + reconnect_mode: self.reconnect_mode, } } } @@ -347,6 +358,7 @@ impl Builder { retry_policy: self.retry_policy, operation_timeout_config: self.operation_timeout_config, sleep_impl: self.sleep_impl, + reconnect_mode: self.reconnect_mode, } } @@ -361,9 +373,41 @@ impl Builder { retry_policy: self.retry_policy, operation_timeout_config: self.operation_timeout_config, sleep_impl: self.sleep_impl, + reconnect_mode: self.reconnect_mode, } } + /// Set the [`ReconnectMode`] for the retry strategy + /// + /// By default, no reconnection occurs. + /// + /// When enabled and a transient error is encountered, the connection in use will be poisoned. + /// This prevents reusing a connection to a potentially bad host. + pub fn reconnect_mode(mut self, reconnect_mode: ReconnectMode) -> Self { + self.set_reconnect_mode(Some(reconnect_mode)); + self + } + + /// Set the [`ReconnectMode`] for the retry strategy + /// + /// By default, no reconnection occurs. + /// + /// When enabled and a transient error is encountered, the connection in use will be poisoned. + /// This prevents reusing a connection to a potentially bad host. + pub fn set_reconnect_mode(&mut self, reconnect_mode: Option) -> &mut Self { + self.reconnect_mode = reconnect_mode; + self + } + + /// Enable reconnection on transient errors + /// + /// By default, when a transient error is encountered, the connection in use will be poisoned. + /// This prevents reusing a connection to a potentially bad host but may increase the load on + /// the server. + pub fn reconnect_on_transient_errors(self) -> Self { + self.reconnect_mode(ReconnectMode::ReconnectOnTransientError) + } + /// Build a Smithy service [`Client`]. pub fn build(self) -> Client { let operation_timeout_config = self @@ -392,6 +436,7 @@ impl Builder { middleware: self.middleware, operation_timeout_config, sleep_impl: self.sleep_impl, + reconnect_mode: self.reconnect_mode.unwrap_or(default_reconnect_mode()), } } } diff --git a/rust-runtime/aws-smithy-client/src/conns.rs b/rust-runtime/aws-smithy-client/src/conns.rs new file mode 100644 index 0000000000..6ccf07f462 --- /dev/null +++ b/rust-runtime/aws-smithy-client/src/conns.rs @@ -0,0 +1,150 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +//! Type aliases for standard connection types. + +#[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; + +#[cfg(feature = "rustls")] +use hyper_rustls::ConfigBuilderExt; + +// Creating a `with_native_roots` HTTP client takes 300ms on OS X. Cache this so that we +// don't need to repeatedly incur that cost. +#[cfg(feature = "rustls")] +lazy_static::lazy_static! { + static ref HTTPS_NATIVE_ROOTS: Https = { + hyper_rustls::HttpsConnectorBuilder::new() + .with_tls_config( + rustls::ClientConfig::builder() + .with_cipher_suites(&[ + // TLS1.3 suites + rustls::cipher_suite::TLS13_AES_256_GCM_SHA384, + rustls::cipher_suite::TLS13_AES_128_GCM_SHA256, + // TLS1.2 suites + rustls::cipher_suite::TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384, + rustls::cipher_suite::TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256, + rustls::cipher_suite::TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384, + rustls::cipher_suite::TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256, + rustls::cipher_suite::TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256, + ]) + .with_safe_default_kx_groups() + .with_safe_default_protocol_versions() + .expect("Error with the TLS configuration. Please file a bug report under https://github.com/awslabs/smithy-rs/issues.") + .with_native_roots() + .with_no_client_auth() + ) + .https_or_http() + .enable_http1() + .enable_http2() + .build() + }; +} + +#[cfg(feature = "rustls")] +/// Return a default HTTPS connector backed by the `rustls` crate. +/// +/// It requires a minimum TLS version of 1.2. +/// It allows you to connect to both `http` and `https` URLs. +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")))] +mod 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}; + + async fn send_request_and_assert_success(conn: DynConnector, uri: &Uri) { + let mut svc = ServiceBuilder::new().service(conn); + let req = Request::builder() + .uri(uri) + .method(Method::GET) + .body(SdkBody::empty()) + .unwrap(); + let res = svc.call(req).await.unwrap(); + 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; + use super::*; + + #[tokio::test] + async fn test_rustls_connector_can_make_http_requests() { + let conn = Adapter::builder().build(https()); + 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_rustls_connector_can_make_https_requests() { + let conn = Adapter::builder().build(https()); + let conn = DynConnector::new(conn); + let https_uri: Uri = "https://example.com/".parse().unwrap(); + + send_request_and_assert_success(conn, &https_uri).await; + } + } +} diff --git a/rust-runtime/aws-smithy-client/src/erase.rs b/rust-runtime/aws-smithy-client/src/erase.rs index 2cac5afeaa..648562192c 100644 --- a/rust-runtime/aws-smithy-client/src/erase.rs +++ b/rust-runtime/aws-smithy-client/src/erase.rs @@ -61,6 +61,7 @@ where retry_policy: self.retry_policy, operation_timeout_config: self.operation_timeout_config, sleep_impl: self.sleep_impl, + reconnect_mode: self.reconnect_mode, } } } @@ -101,6 +102,7 @@ where retry_policy: self.retry_policy, operation_timeout_config: self.operation_timeout_config, sleep_impl: self.sleep_impl, + reconnect_mode: self.reconnect_mode, } } diff --git a/rust-runtime/aws-smithy-client/src/erase/boxclone.rs b/rust-runtime/aws-smithy-client/src/erase/boxclone.rs index e0a2d408f6..1604d448b9 100644 --- a/rust-runtime/aws-smithy-client/src/erase/boxclone.rs +++ b/rust-runtime/aws-smithy-client/src/erase/boxclone.rs @@ -29,7 +29,7 @@ impl Clone for ArcCloneLayer { impl ArcCloneLayer { /// Create a new [`BoxLayer`]. - pub fn new(inner_layer: L) -> Self + pub(crate) fn new(inner_layer: L) -> Self where L: Layer + Send + Sync + 'static, L::Service: Service + Clone + Send + Sync + 'static, diff --git a/rust-runtime/aws-smithy-client/src/hyper_ext.rs b/rust-runtime/aws-smithy-client/src/hyper_ext.rs index 11a27c1d53..7032604a78 100644 --- a/rust-runtime/aws-smithy-client/src/hyper_ext.rs +++ b/rust-runtime/aws-smithy-client/src/hyper_ext.rs @@ -92,13 +92,22 @@ 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_http::body::SdkBody; + use aws_smithy_http::result::ConnectorError; use aws_smithy_types::error::display::DisplayErrorContext; use aws_smithy_types::retry::ErrorKind; -use http::Uri; -use hyper::client::connect::{Connected, Connection}; +use http::{Extensions, Uri}; +use hyper::client::connect::{ + capture_connection, CaptureConnection, Connected, Connection, HttpInfo, +}; + 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}; use tower::{BoxError, Service}; @@ -107,13 +116,35 @@ use tower::{BoxError, Service}; /// This adapter also enables TCP `CONNECT` and HTTP `READ` timeouts via [`Adapter::builder`]. For examples /// see [the module documentation](crate::hyper_ext). #[derive(Clone, Debug)] -#[non_exhaustive] -pub struct Adapter(HttpReadTimeout, SdkBody>>); +pub struct Adapter { + client: HttpReadTimeout, SdkBody>>, +} + +/// Extract a smithy connection from a hyper CaptureConnection +fn extract_smithy_connection(capture_conn: &CaptureConnection) -> Option { + let capture_conn = capture_conn.clone(); + if let Some(conn) = capture_conn.clone().connection_metadata().as_ref() { + let mut extensions = Extensions::new(); + conn.get_extras(&mut extensions); + let http_info = extensions.get::(); + let smithy_connection = ConnectionMetadata::new( + conn.is_proxied(), + http_info.map(|info| info.remote_addr()), + move || match capture_conn.connection_metadata().as_ref() { + Some(conn) => conn.poison(), + None => tracing::trace!("no connection existed to poison"), + }, + ); + Some(smithy_connection) + } else { + None + } +} impl Service> for Adapter where C: Clone + Send + Sync + 'static, - C: tower::Service, + C: Service, C::Response: Connection + AsyncRead + AsyncWrite + Send + Unpin + 'static, C::Future: Unpin + Send + 'static, C::Error: Into, @@ -121,20 +152,22 @@ where type Response = http::Response; type Error = ConnectorError; - #[allow(clippy::type_complexity)] - type Future = std::pin::Pin< - Box> + Send + 'static>, - >; + type Future = BoxFuture; fn poll_ready( &mut self, cx: &mut std::task::Context<'_>, ) -> std::task::Poll> { - self.0.poll_ready(cx).map_err(downcast_error) + self.client.poll_ready(cx).map_err(downcast_error) } - fn call(&mut self, req: http::Request) -> Self::Future { - let fut = self.0.call(req); + fn call(&mut self, mut req: http::Request) -> Self::Future { + let capture_connection = capture_connection(&mut req); + if let Some(capture_smithy_connection) = req.extensions().get::() { + capture_smithy_connection + .set_connection_retriever(move || extract_smithy_connection(&capture_connection)); + } + let fut = self.client.call(req); Box::pin(async move { Ok(fut.await.map_err(downcast_error)?.map(SdkBody::from)) }) } } @@ -203,7 +236,7 @@ fn find_source<'a, E: Error + 'static>(err: &'a (dyn Error + 'static)) -> Option /// Builder for [`hyper_ext::Adapter`](Adapter) /// -/// Unlike a Smithy client, the [`tower::Service`] inside a [`hyper_ext::Adapter`](Adapter) is actually a service that +/// 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`. /// @@ -237,7 +270,7 @@ impl Builder { pub fn build(self, connector: C) -> Adapter where C: Clone + Send + Sync + 'static, - C: tower::Service, + C: Service, C::Response: Connection + AsyncRead + AsyncWrite + Send + Unpin + 'static, C::Future: Unpin + Send + 'static, C::Error: Into, @@ -271,13 +304,15 @@ impl Builder { ), None => HttpReadTimeout::no_timeout(base), }; - Adapter(read_timeout) + Adapter { + client: read_timeout, + } } /// Set the async sleep implementation used for timeouts /// /// Calling this is only necessary for testing or to use something other than - /// [`aws_smithy_async::rt::sleep::default_async_sleep`]. + /// [`default_async_sleep`]. pub fn sleep_impl(mut self, sleep_impl: Arc) -> Self { self.sleep_impl = Some(sleep_impl); self @@ -286,7 +321,7 @@ impl Builder { /// Set the async sleep implementation used for timeouts /// /// Calling this is only necessary for testing or to use something other than - /// [`aws_smithy_async::rt::sleep::default_async_sleep`]. + /// [`default_async_sleep`]. pub fn set_sleep_impl( &mut self, sleep_impl: Option>, @@ -343,7 +378,6 @@ mod timeout_middleware { use pin_project_lite::pin_project; use tower::BoxError; - use aws_smithy_async::future; use aws_smithy_async::future::timeout::{TimedOutError, Timeout}; use aws_smithy_async::rt::sleep::AsyncSleep; use aws_smithy_async::rt::sleep::Sleep; @@ -387,14 +421,14 @@ mod timeout_middleware { /// Create a new `ConnectTimeout` around `inner`. /// /// Typically, `I` will implement [`hyper::client::connect::Connect`]. - pub fn new(inner: I, sleep: Arc, timeout: Duration) -> Self { + pub(crate) fn new(inner: I, sleep: Arc, timeout: Duration) -> Self { Self { inner, timeout: Some((sleep, timeout)), } } - pub fn no_timeout(inner: I) -> Self { + pub(crate) fn no_timeout(inner: I) -> Self { Self { inner, timeout: None, @@ -403,7 +437,7 @@ mod timeout_middleware { } #[derive(Clone, Debug)] - pub struct HttpReadTimeout { + pub(crate) struct HttpReadTimeout { inner: I, timeout: Option<(Arc, Duration)>, } @@ -412,14 +446,14 @@ mod timeout_middleware { /// Create a new `HttpReadTimeout` around `inner`. /// /// Typically, `I` will implement [`tower::Service>`]. - pub fn new(inner: I, sleep: Arc, timeout: Duration) -> Self { + pub(crate) fn new(inner: I, sleep: Arc, timeout: Duration) -> Self { Self { inner, timeout: Some((sleep, timeout)), } } - pub fn no_timeout(inner: I) -> Self { + pub(crate) fn no_timeout(inner: I) -> Self { Self { inner, timeout: None, @@ -493,7 +527,7 @@ mod timeout_middleware { Some((sleep, duration)) => { let sleep = sleep.sleep(*duration); MaybeTimeoutFuture::Timeout { - timeout: future::timeout::Timeout::new(self.inner.call(req), sleep), + timeout: Timeout::new(self.inner.call(req), sleep), error_type: "HTTP connect", duration: *duration, } @@ -522,7 +556,7 @@ mod timeout_middleware { Some((sleep, duration)) => { let sleep = sleep.sleep(*duration); MaybeTimeoutFuture::Timeout { - timeout: future::timeout::Timeout::new(self.inner.call(req), sleep), + timeout: Timeout::new(self.inner.call(req), sleep), error_type: "HTTP read", duration: *duration, } @@ -688,7 +722,7 @@ mod test { _cx: &mut Context<'_>, _buf: &mut ReadBuf<'_>, ) -> Poll> { - Poll::Ready(Err(std::io::Error::new( + Poll::Ready(Err(Error::new( ErrorKind::ConnectionReset, "connection reset", ))) @@ -720,7 +754,7 @@ mod test { impl tower::Service for TestConnection where - T: Clone + hyper::client::connect::Connection, + T: Clone + Connection, { type Response = T; type Error = BoxError; diff --git a/rust-runtime/aws-smithy-client/src/lib.rs b/rust-runtime/aws-smithy-client/src/lib.rs index b1e2b1128d..080e5ac9eb 100644 --- a/rust-runtime/aws-smithy-client/src/lib.rs +++ b/rust-runtime/aws-smithy-client/src/lib.rs @@ -14,16 +14,21 @@ //! | `rustls` | Use `rustls` as the HTTP client's TLS implementation | //! | `client-hyper` | Use `hyper` to handle HTTP requests | +#![allow(clippy::derive_partial_eq_without_eq)] #![warn( - missing_debug_implementations, missing_docs, - rustdoc::all, + rustdoc::missing_crate_level_docs, + unreachable_pub, rust_2018_idioms )] pub mod bounds; pub mod erase; +pub mod http_connector; +pub mod never; +mod poison; pub mod retry; +pub mod timeout; // https://github.com/rust-lang/rust/issues/72081 #[allow(rustdoc::private_doc_tests)] @@ -35,8 +40,8 @@ pub mod dvr; #[cfg(feature = "test-util")] pub mod test_connection; -pub mod http_connector; - +#[cfg(feature = "client-hyper")] +pub mod conns; #[cfg(feature = "client-hyper")] pub mod hyper_ext; @@ -46,51 +51,9 @@ pub mod hyper_ext; #[doc(hidden)] pub mod static_tests; -pub mod never; -pub mod timeout; -pub use timeout::TimeoutLayer; - -/// Type aliases for standard connection types. -#[cfg(feature = "client-hyper")] -#[allow(missing_docs)] -pub mod conns { - #[cfg(feature = "rustls")] - pub type Https = hyper_rustls::HttpsConnector; - - // Creating a `with_native_roots` HTTP client takes 300ms on OS X. Cache this so that we - // don't need to repeatedly incur that cost. - #[cfg(feature = "rustls")] - lazy_static::lazy_static! { - static ref HTTPS_NATIVE_ROOTS: Https = { - hyper_rustls::HttpsConnectorBuilder::new() - .with_native_roots() - .https_or_http() - .enable_http1() - .enable_http2() - .build() - }; - } - - #[cfg(feature = "rustls")] - pub fn https() -> Https { - HTTPS_NATIVE_ROOTS.clone() - } - - #[cfg(feature = "native-tls")] - pub fn native_tls() -> NativeTls { - hyper_tls::HttpsConnector::new() - } - - #[cfg(feature = "native-tls")] - pub type NativeTls = hyper_tls::HttpsConnector; - - #[cfg(feature = "rustls")] - pub type Rustls = - crate::hyper_ext::Adapter>; -} - +use crate::poison::PoisonLayer; use aws_smithy_async::rt::sleep::AsyncSleep; -use aws_smithy_http::body::SdkBody; + use aws_smithy_http::operation::Operation; use aws_smithy_http::response::ParseHttpResponse; pub use aws_smithy_http::result::{SdkError, SdkSuccess}; @@ -98,13 +61,13 @@ use aws_smithy_http::retry::ClassifyRetry; use aws_smithy_http_tower::dispatch::DispatchLayer; use aws_smithy_http_tower::parse_response::ParseResponseLayer; use aws_smithy_types::error::display::DisplayErrorContext; -use aws_smithy_types::retry::ProvideErrorKind; +use aws_smithy_types::retry::{ProvideErrorKind, ReconnectMode}; use aws_smithy_types::timeout::OperationTimeoutConfig; -use std::error::Error; use std::sync::Arc; use timeout::ClientTimeoutParams; -use tower::{Layer, Service, ServiceBuilder, ServiceExt}; -use tracing::{debug_span, field, field::display, Instrument}; +pub use timeout::TimeoutLayer; +use tower::{Service, ServiceBuilder, ServiceExt}; +use tracing::{debug_span, field, Instrument}; /// Smithy service client. /// @@ -116,14 +79,14 @@ use tracing::{debug_span, field, field::display, Instrument}; /// such as those used for routing (like the URL), authentication, and authorization. /// /// The middleware takes the form of a [`tower::Layer`] that wraps the actual connection for each -/// request. The [`tower::Service`] that the middleware produces must accept requests of the type +/// request. The [`tower::Service`](Service) that the middleware produces must accept requests of the type /// [`aws_smithy_http::operation::Request`] and return responses of the type /// [`http::Response`], most likely by modifying the provided request in place, passing it /// 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 +/// `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. #[derive(Debug)] pub struct Client< @@ -134,6 +97,7 @@ pub struct Client< connector: Connector, middleware: Middleware, retry_policy: RetryPolicy, + reconnect_mode: ReconnectMode, operation_timeout_config: OperationTimeoutConfig, sleep_impl: Option>, } @@ -150,9 +114,9 @@ impl Client where M: Default, { - /// Create a Smithy client from the given `connector`, a middleware default, the [standard - /// retry policy](crate::retry::Standard), and the [`default_async_sleep`](aws_smithy_async::rt::sleep::default_async_sleep) - /// sleep implementation. + /// Create a Smithy client from the given `connector`, a middleware default, the + /// [standard retry policy](retry::Standard), and the + /// [`default_async_sleep`](aws_smithy_async::rt::sleep::default_async_sleep) sleep implementation. pub fn new(connector: C) -> Self { Builder::new() .connector(connector) @@ -181,6 +145,7 @@ where 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, { @@ -200,6 +165,7 @@ where 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, @@ -220,6 +186,7 @@ where self.retry_policy .new_request_policy(self.sleep_impl.clone()), ) + .layer(PoisonLayer::new(self.reconnect_mode)) .layer(TimeoutLayer::new(timeout_params.operation_attempt_timeout)) .layer(ParseResponseLayer::::new()) // These layers can be considered as occurring in order. That is, first invoke the @@ -239,8 +206,13 @@ where ); let (mut req, parts) = op.into_request_response(); if let Some(metadata) = &parts.metadata { - span.record("operation", &metadata.name()); - span.record("service", &metadata.service()); + // Clippy has a bug related to needless borrows so we need to allow them here + // https://github.com/rust-lang/rust-clippy/issues/9782 + #[allow(clippy::needless_borrow)] + { + span.record("operation", &metadata.name()); + span.record("service", &metadata.service()); + } // This will clone two `Cow::<&'static str>::Borrow`s in the vast majority of cases req.properties_mut().insert(metadata.clone()); } @@ -249,6 +221,7 @@ where let result = async move { check_send_sync(svc).ready().await?.call(op).await } .instrument(span.clone()) .await; + #[allow(clippy::needless_borrow)] match &result { Ok(_) => { span.record("status", &"ok"); @@ -265,7 +238,7 @@ where _ => "error", }, ) - .record("message", &display(DisplayErrorContext(err))); + .record("message", &field::display(DisplayErrorContext(err))); } } result diff --git a/rust-runtime/aws-smithy-client/src/poison.rs b/rust-runtime/aws-smithy-client/src/poison.rs new file mode 100644 index 0000000000..ffbaaf8abc --- /dev/null +++ b/rust-runtime/aws-smithy-client/src/poison.rs @@ -0,0 +1,143 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +//! Connection Poisoning +//! +//! The client supports behavior where on transient errors (e.g. timeouts, 503, etc.) it will ensure +//! that the offending connection is not reused. This happens to ensure that in the case where the +//! connection itself is broken (e.g. connected to a bad host) we don't reuse it for other requests. +//! +//! This relies on a series of mechanisms: +//! 1. [`CaptureSmithyConnection`] is a container which exists in the operation property bag. It is +//! inserted by this layer before the request is sent. +//! 2. The [`DispatchLayer`](aws_smithy_http_tower::dispatch::DispatchLayer) copies the field from operation extensions HTTP request extensions. +//! 3. The HTTP layer (e.g. Hyper) sets [`ConnectionMetadata`](aws_smithy_http::connection::ConnectionMetadata) +//! when it is available. +//! 4. When the response comes back, if indicated, this layer invokes +//! [`ConnectionMetadata::poison`](aws_smithy_http::connection::ConnectionMetadata::poison). +//! +//! ### Why isn't this integrated into `retry.rs`? +//! If the request has a streaming body, we won't attempt to retry because [`Operation::try_clone()`] will +//! return `None`. Therefore, we need to handle this inside of the retry loop. + +use std::future::Future; + +use aws_smithy_http::operation::Operation; +use aws_smithy_http::result::{SdkError, SdkSuccess}; +use aws_smithy_http::retry::ClassifyRetry; + +use aws_smithy_http::connection::CaptureSmithyConnection; +use aws_smithy_types::retry::{ErrorKind, ReconnectMode, RetryKind}; +use pin_project_lite::pin_project; +use std::marker::PhantomData; +use std::pin::Pin; +use std::task::{Context, Poll}; + +/// PoisonLayer that poisons connections depending on the error kind +pub(crate) struct PoisonLayer { + inner: PhantomData, + mode: ReconnectMode, +} + +impl PoisonLayer { + pub(crate) fn new(mode: ReconnectMode) -> Self { + Self { + inner: Default::default(), + mode, + } + } +} + +impl Clone for PoisonLayer { + fn clone(&self) -> Self { + Self { + inner: Default::default(), + mode: self.mode, + } + } +} + +impl tower::Layer for PoisonLayer { + type Service = PoisonService; + + fn layer(&self, inner: S) -> Self::Service { + PoisonService { + inner, + mode: self.mode, + } + } +} + +#[derive(Clone)] +pub(crate) struct PoisonService { + inner: S, + mode: ReconnectMode, +} + +impl tower::Service> for PoisonService +where + R: ClassifyRetry, SdkError>, + S: tower::Service, Response = SdkSuccess, Error = SdkError>, +{ + type Response = S::Response; + type Error = S::Error; + type Future = PoisonServiceFuture; + + fn poll_ready(&mut self, cx: &mut Context<'_>) -> Poll> { + self.inner.poll_ready(cx) + } + + fn call(&mut self, mut req: Operation) -> Self::Future { + let classifier = req.retry_classifier().clone(); + let capture_smithy_connection = CaptureSmithyConnection::new(); + req.properties_mut() + .insert(capture_smithy_connection.clone()); + PoisonServiceFuture { + inner: self.inner.call(req), + conn: capture_smithy_connection, + mode: self.mode, + classifier, + } + } +} + +pin_project! { + pub struct PoisonServiceFuture { + #[pin] + inner: F, + classifier: R, + conn: CaptureSmithyConnection, + mode: ReconnectMode + } +} + +impl Future for PoisonServiceFuture +where + F: Future, SdkError>>, + R: ClassifyRetry, SdkError>, +{ + type Output = F::Output; + + fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { + let this = self.project(); + match this.inner.poll(cx) { + Poll::Ready(resp) => { + let retry_kind = this.classifier.classify_retry(resp.as_ref()); + if this.mode == &ReconnectMode::ReconnectOnTransientError + && retry_kind == RetryKind::Error(ErrorKind::TransientError) + { + if let Some(smithy_conn) = this.conn.get() { + tracing::info!("poisoning connection: {:?}", smithy_conn); + smithy_conn.poison(); + } else { + tracing::trace!("No smithy connection found! The underlying HTTP connection never set a connection."); + } + } + Poll::Ready(resp) + } + Poll::Pending => Poll::Pending, + } + } +} diff --git a/rust-runtime/aws-smithy-client/src/retry.rs b/rust-runtime/aws-smithy-client/src/retry.rs index 7e6ceff10c..2c38807fae 100644 --- a/rust-runtime/aws-smithy-client/src/retry.rs +++ b/rust-runtime/aws-smithy-client/src/retry.rs @@ -17,14 +17,15 @@ use std::pin::Pin; use std::sync::{Arc, Mutex}; use std::time::Duration; -use crate::{SdkError, SdkSuccess}; +use tracing::Instrument; use aws_smithy_async::rt::sleep::AsyncSleep; + use aws_smithy_http::operation::Operation; use aws_smithy_http::retry::ClassifyRetry; use aws_smithy_types::retry::{ErrorKind, RetryKind}; -use tracing::Instrument; +use crate::{SdkError, SdkSuccess}; /// A policy instantiator. /// @@ -197,7 +198,7 @@ impl Default for RequestLocalRetryState { } impl RequestLocalRetryState { - pub fn new() -> Self { + fn new() -> Self { Self::default() } } @@ -218,7 +219,7 @@ struct CrossRequestRetryState { // significantly more complicated for negligible benefit. #[allow(clippy::mutex_atomic)] impl CrossRequestRetryState { - pub fn new(initial_quota: usize) -> Self { + fn new(initial_quota: usize) -> Self { Self { quota_available: Arc::new(Mutex::new(initial_quota)), } @@ -292,9 +293,20 @@ impl RetryHandler { fn should_retry_error(&self, error_kind: &ErrorKind) -> Option<(Self, Duration)> { let quota_used = { if self.local.attempts == self.config.max_attempts { + tracing::trace!( + attempts = self.local.attempts, + max_attempts = self.config.max_attempts, + "not retrying becuase we are out of attempts" + ); return None; } - self.shared.quota_acquire(error_kind, &self.config)? + match self.shared.quota_acquire(error_kind, &self.config) { + Some(quota) => quota, + None => { + tracing::trace!(state = ?self.shared, "not retrying because no quota is available"); + return None; + } + } }; let backoff = calculate_exponential_backoff( // Generate a random base multiplier to create jitter @@ -334,7 +346,9 @@ impl RetryHandler { } fn retry_for(&self, retry_kind: RetryKind) -> Option> { - let (next, dur) = self.should_retry(&retry_kind)?; + let retry = self.should_retry(&retry_kind); + tracing::trace!(retry=?retry, retry_kind = ?retry_kind, "retry action"); + let (next, dur) = retry?; let sleep = match &self.sleep_impl { Some(sleep) => sleep, @@ -377,6 +391,7 @@ where ) -> Option { let classifier = req.retry_classifier(); let retry_kind = classifier.classify_retry(result); + tracing::trace!(retry_kind = ?retry_kind, "retry classification"); self.retry_for(retry_kind) } diff --git a/rust-runtime/aws-smithy-client/src/static_tests.rs b/rust-runtime/aws-smithy-client/src/static_tests.rs index 4c9ef91bad..a8cd503022 100644 --- a/rust-runtime/aws-smithy-client/src/static_tests.rs +++ b/rust-runtime/aws-smithy-client/src/static_tests.rs @@ -5,7 +5,7 @@ //! This module provides types useful for static tests. #![allow(missing_docs, missing_debug_implementations)] -use crate::{Builder, Error, Operation, ParseHttpResponse, ProvideErrorKind}; +use crate::{Builder, Operation, ParseHttpResponse, ProvideErrorKind}; use aws_smithy_http::operation; use aws_smithy_http::retry::DefaultResponseRetryClassifier; @@ -17,7 +17,7 @@ impl std::fmt::Display for TestOperationError { unreachable!("only used for static tests") } } -impl Error for TestOperationError {} +impl std::error::Error for TestOperationError {} impl ProvideErrorKind for TestOperationError { fn retryable_error_kind(&self) -> Option { unreachable!("only used for static tests") diff --git a/rust-runtime/aws-smithy-client/src/test_connection.rs b/rust-runtime/aws-smithy-client/src/test_connection.rs index d7b0b15ece..86466944df 100644 --- a/rust-runtime/aws-smithy-client/src/test_connection.rs +++ b/rust-runtime/aws-smithy-client/src/test_connection.rs @@ -7,6 +7,7 @@ // TODO(docs) #![allow(missing_docs)] +use std::fmt::{Debug, Formatter}; use std::future::Ready; use std::ops::Deref; use std::sync::{Arc, Mutex}; @@ -16,6 +17,7 @@ use http::header::{HeaderName, CONTENT_TYPE}; use http::Request; use tokio::sync::oneshot; +use crate::erase::DynConnector; use aws_smithy_http::body::SdkBody; use aws_smithy_http::result::ConnectorError; use aws_smithy_protocol_test::{assert_ok, validate_body, MediaType}; @@ -90,7 +92,7 @@ impl tower::Service> for CaptureRequestHandler { /// If response is `None`, it will reply with a 200 response with an empty body /// /// Example: -/// ```rust,compile_fail +/// ```compile_fail /// let (server, request) = capture_request(None); /// let conf = aws_sdk_sts::Config::builder() /// .http_connector(server) @@ -271,6 +273,404 @@ where } } +/// Create a DynConnector from `Fn(http:Request) -> http::Response` +/// +/// # Examples +/// +/// ```rust +/// use aws_smithy_client::test_connection::infallible_connection_fn; +/// let connector = infallible_connection_fn(|_req|http::Response::builder().status(200).body("OK!").unwrap()); +/// ``` +pub fn infallible_connection_fn( + f: impl Fn(http::Request) -> http::Response + Send + Sync + 'static, +) -> DynConnector +where + B: Into, +{ + ConnectionFn::infallible(f) +} + +#[derive(Clone)] +struct ConnectionFn { + #[allow(clippy::type_complexity)] + response: Arc< + dyn Fn(http::Request) -> Result, ConnectorError> + + Send + + Sync, + >, +} + +impl Debug for ConnectionFn { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + f.debug_struct("ConnectionFn").finish() + } +} + +impl ConnectionFn { + fn infallible>( + f: impl Fn(http::Request) -> http::Response + Send + Sync + 'static, + ) -> DynConnector { + DynConnector::new(Self { + response: Arc::new(move |request| Ok(f(request).map(|b| b.into()))), + }) + } +} + +impl tower::Service> for ConnectionFn { + type Response = http::Response; + type Error = ConnectorError; + type Future = Ready>; + + fn poll_ready(&mut self, _cx: &mut Context<'_>) -> Poll> { + Poll::Ready(Ok(())) + } + + fn call(&mut self, req: Request) -> Self::Future { + std::future::ready((self.response)(req)) + } +} + +/// [`wire_mock`] contains utilities for mocking at the socket level +/// +/// Other tools in this module actually operate at the `http::Request` / `http::Response` level. This +/// is useful, but it shortcuts the HTTP implementation (e.g. Hyper). [`wire_mock::WireLevelTestConnection`] binds +/// to an actual socket on the host +/// +/// # Examples +/// ``` +/// use tower::layer::util::Identity; +/// use aws_smithy_client::http_connector::ConnectorSettings; +/// use aws_smithy_client::{match_events, ev}; +/// use aws_smithy_client::test_connection::wire_mock::check_matches; +/// # async fn example() { +/// use aws_smithy_client::test_connection::wire_mock::{ReplayedEvent, WireLevelTestConnection}; +/// // This connection binds to a local address +/// let mock = WireLevelTestConnection::spinup(vec![ +/// ReplayedEvent::status(503), +/// ReplayedEvent::status(200) +/// ]).await; +/// let client = aws_smithy_client::Client::builder() +/// .connector(mock.http_connector().connector(&ConnectorSettings::default(), None).unwrap()) +/// .middleware(Identity::new()) +/// .build(); +/// /* do something with */ +/// // assert that you got the events you expected +/// match_events!(ev!(dns), ev!(connect), ev!(http(200)))(&mock.events()); +/// # } +/// ``` +pub mod wire_mock { + use bytes::Bytes; + use http::{Request, Response}; + use hyper::client::connect::dns::Name; + use hyper::server::conn::AddrStream; + use hyper::service::{make_service_fn, service_fn}; + use hyper::{Body, Server}; + use std::collections::HashSet; + use std::convert::Infallible; + use std::error::Error; + + use hyper::client::HttpConnector as HyperHttpConnector; + use std::iter; + use std::iter::Once; + use std::net::{SocketAddr, TcpListener}; + use std::sync::{Arc, Mutex}; + use std::task::{Context, Poll}; + + use tokio::spawn; + use tower::Service; + + /// An event recorded by [`WireLevelTestConnection`] + #[derive(Debug, Clone)] + pub enum RecordedEvent { + DnsLookup(String), + NewConnection, + Response(ReplayedEvent), + } + + type Matcher = ( + Box Result<(), Box>>, + &'static str, + ); + + /// This method should only be used by the macro + #[doc(hidden)] + pub fn check_matches(events: &[RecordedEvent], matchers: &[Matcher]) { + let mut events_iter = events.iter(); + let mut matcher_iter = matchers.iter(); + let mut idx = -1; + loop { + idx += 1; + let bail = |err: Box| panic!("failed on event {}:\n {}", idx, err); + match (events_iter.next(), matcher_iter.next()) { + (Some(event), Some((matcher, _msg))) => matcher(event).unwrap_or_else(bail), + (None, None) => return, + (Some(event), None) => { + bail(format!("got {:?} but no more events were expected", event).into()) + } + (None, Some((_expect, msg))) => { + bail(format!("expected {:?} but no more events were expected", msg).into()) + } + } + } + } + + #[macro_export] + macro_rules! matcher { + ($expect:tt) => { + ( + Box::new( + |event: &::aws_smithy_client::test_connection::wire_mock::RecordedEvent| { + if !matches!(event, $expect) { + return Err(format!( + "expected `{}` but got {:?}", + stringify!($expect), + event + ) + .into()); + } + Ok(()) + }, + ), + stringify!($expect), + ) + }; + } + + /// Helper macro to generate a series of test expectations + #[macro_export] + macro_rules! match_events { + ($( $expect:pat),*) => { + |events| { + check_matches(events, &[$( ::aws_smithy_client::matcher!($expect) ),*]); + } + }; + } + + /// Helper to generate match expressions for events + #[macro_export] + macro_rules! ev { + (http($status:expr)) => { + ::aws_smithy_client::test_connection::wire_mock::RecordedEvent::Response( + ReplayedEvent::HttpResponse { + status: $status, + .. + }, + ) + }; + (dns) => { + ::aws_smithy_client::test_connection::wire_mock::RecordedEvent::DnsLookup(_) + }; + (connect) => { + ::aws_smithy_client::test_connection::wire_mock::RecordedEvent::NewConnection + }; + (timeout) => { + ::aws_smithy_client::test_connection::wire_mock::RecordedEvent::Response( + ReplayedEvent::Timeout, + ) + }; + } + + pub use {ev, match_events, matcher}; + + #[derive(Clone, Debug, PartialEq, Eq)] + pub enum ReplayedEvent { + Timeout, + HttpResponse { status: u16, body: Bytes }, + } + + impl ReplayedEvent { + pub fn ok() -> Self { + Self::HttpResponse { + status: 200, + body: Bytes::new(), + } + } + + pub fn with_body(body: &str) -> Self { + Self::HttpResponse { + status: 200, + body: Bytes::copy_from_slice(body.as_ref()), + } + } + + pub fn status(status: u16) -> Self { + Self::HttpResponse { + status, + body: Bytes::new(), + } + } + } + + use crate::erase::boxclone::BoxFuture; + use crate::http_connector::HttpConnector; + use crate::hyper_ext; + use aws_smithy_async::future::never::Never; + use tokio::sync::oneshot; + + /// Test connection that starts a server bound to 0.0.0.0 + /// + /// See the [module docs](crate::test_connection::wire_mock) for a usage example. + /// + /// Usage: + /// - Call [`WireLevelTestConnection::spinup`] to start the server + /// - Use [`WireLevelTestConnection::http_connector`] or [`dns_resolver`](WireLevelTestConnection::dns_resolver) to configure your client. + /// - Make requests to [`endpoint_url`](WireLevelTestConnection::endpoint_url). + /// - Once the test is complete, retrieve a list of events from [`WireLevelTestConnection::events`] + #[derive(Debug)] + pub struct WireLevelTestConnection { + event_log: Arc>>, + bind_addr: SocketAddr, + // when the sender is dropped, that stops the server + shutdown_hook: oneshot::Sender<()>, + } + + impl WireLevelTestConnection { + pub async fn spinup(mut response_events: Vec) -> Self { + let listener = TcpListener::bind("127.0.0.1:0").unwrap(); + let (tx, rx) = oneshot::channel(); + let listener_addr = listener.local_addr().unwrap(); + response_events.reverse(); + let response_events = Arc::new(Mutex::new(response_events)); + let handler_events = response_events; + let wire_events = Arc::new(Mutex::new(vec![])); + let wire_log_for_service = wire_events.clone(); + let poisoned_conns: Arc>> = Default::default(); + let make_service = make_service_fn(move |connection: &AddrStream| { + let poisoned_conns = poisoned_conns.clone(); + let events = handler_events.clone(); + let wire_log = wire_log_for_service.clone(); + let remote_addr = connection.remote_addr(); + tracing::info!("established connection: {:?}", connection); + wire_log.lock().unwrap().push(RecordedEvent::NewConnection); + async move { + Ok::<_, Infallible>(service_fn(move |_: Request| { + if poisoned_conns.lock().unwrap().contains(&remote_addr) { + tracing::error!("poisoned connection {:?} was reused!", &remote_addr); + panic!("poisoned connection was reused!"); + } + let next_event = events.clone().lock().unwrap().pop(); + let wire_log = wire_log.clone(); + let poisoned_conns = poisoned_conns.clone(); + async move { + let next_event = next_event + .unwrap_or_else(|| panic!("no more events! Log: {:?}", wire_log)); + wire_log + .lock() + .unwrap() + .push(RecordedEvent::Response(next_event.clone())); + if next_event == ReplayedEvent::Timeout { + tracing::info!("{} is poisoned", remote_addr); + poisoned_conns.lock().unwrap().insert(remote_addr); + } + tracing::debug!("replying with {:?}", next_event); + let event = generate_response_event(next_event).await; + dbg!(event) + } + })) + } + }); + let server = Server::from_tcp(listener) + .unwrap() + .serve(make_service) + .with_graceful_shutdown(async { + rx.await.ok(); + tracing::info!("server shutdown!"); + }); + spawn(async move { server.await }); + Self { + event_log: wire_events, + bind_addr: listener_addr, + shutdown_hook: tx, + } + } + + /// Retrieve the events recorded by this connection + pub fn events(&self) -> Vec { + self.event_log.lock().unwrap().clone() + } + + fn bind_addr(&self) -> SocketAddr { + self.bind_addr + } + + pub fn dns_resolver(&self) -> LoggingDnsResolver { + let event_log = self.event_log.clone(); + let bind_addr = self.bind_addr; + LoggingDnsResolver { + log: event_log, + socket_addr: bind_addr, + } + } + + /// Prebuilt HTTP connector with correctly wired DNS resolver + /// + /// **Note**: This must be used in tandem with [`Self::dns_resolver`] + pub fn http_connector(&self) -> HttpConnector { + let http_connector = HyperHttpConnector::new_with_resolver(self.dns_resolver()); + hyper_ext::Adapter::builder().build(http_connector).into() + } + + /// Endpoint to use when connecting + /// + /// This works in tandem with the [`Self::dns_resolver`] to bind to the correct local IP Address + pub fn endpoint_url(&self) -> String { + format!( + "http://this-url-is-converted-to-localhost.com:{}", + self.bind_addr().port() + ) + } + + pub fn shutdown(self) { + let _ = self.shutdown_hook.send(()); + } + } + + async fn generate_response_event(event: ReplayedEvent) -> Result, Infallible> { + let resp = match event { + ReplayedEvent::HttpResponse { status, body } => http::Response::builder() + .status(status) + .body(hyper::Body::from(body)) + .unwrap(), + ReplayedEvent::Timeout => { + Never::new().await; + unreachable!() + } + }; + Ok::<_, Infallible>(resp) + } + + /// DNS resolver that keeps a log of all lookups + /// + /// Regardless of what hostname is requested, it will always return the same socket address. + #[derive(Clone, Debug)] + pub struct LoggingDnsResolver { + log: Arc>>, + socket_addr: SocketAddr, + } + + impl Service for LoggingDnsResolver { + type Response = Once; + type Error = Infallible; + type Future = BoxFuture; + + fn poll_ready(&mut self, _cx: &mut Context<'_>) -> Poll> { + Poll::Ready(Ok(())) + } + + fn call(&mut self, req: Name) -> Self::Future { + let sock_addr = self.socket_addr; + let log = self.log.clone(); + Box::pin(async move { + println!("looking up {:?}, replying with {:?}", req, sock_addr); + log.lock() + .unwrap() + .push(RecordedEvent::DnsLookup(req.to_string())); + Ok(iter::once(sock_addr)) + }) + } + } +} + #[cfg(test)] mod tests { use hyper::service::Service; diff --git a/rust-runtime/aws-smithy-client/src/timeout.rs b/rust-runtime/aws-smithy-client/src/timeout.rs index 4cfc1938f6..640e1c4562 100644 --- a/rust-runtime/aws-smithy-client/src/timeout.rs +++ b/rust-runtime/aws-smithy-client/src/timeout.rs @@ -25,7 +25,7 @@ struct RequestTimeoutError { } impl RequestTimeoutError { - pub fn new(kind: &'static str, duration: Duration) -> Self { + fn new(kind: &'static str, duration: Duration) -> Self { Self { kind, duration } } } @@ -59,7 +59,7 @@ pub(crate) struct ClientTimeoutParams { } impl ClientTimeoutParams { - pub fn new( + pub(crate) fn new( timeout_config: &OperationTimeoutConfig, async_sleep: Option>, ) -> Self { @@ -208,7 +208,7 @@ where InnerService: tower::Service, Error = SdkError>, { type Response = InnerService::Response; - type Error = aws_smithy_http::result::SdkError; + type Error = SdkError; type Future = TimeoutServiceFuture; fn poll_ready(&mut self, cx: &mut Context<'_>) -> Poll> { diff --git a/rust-runtime/aws-smithy-client/tests/e2e_test.rs b/rust-runtime/aws-smithy-client/tests/e2e_test.rs index 99b689d430..0a8594d6b3 100644 --- a/rust-runtime/aws-smithy-client/tests/e2e_test.rs +++ b/rust-runtime/aws-smithy-client/tests/e2e_test.rs @@ -3,6 +3,7 @@ * SPDX-License-Identifier: Apache-2.0 */ +mod test_operation; use crate::test_operation::{TestOperationParser, TestRetryClassifier}; use aws_smithy_async::rt::sleep::TokioSleep; use aws_smithy_client::test_connection::TestConnection; @@ -15,78 +16,6 @@ use std::sync::Arc; use std::time::Duration; use tower::layer::util::Identity; -mod test_operation { - use aws_smithy_http::operation; - use aws_smithy_http::response::ParseHttpResponse; - use aws_smithy_http::result::SdkError; - use aws_smithy_http::retry::ClassifyRetry; - use aws_smithy_types::retry::{ErrorKind, ProvideErrorKind, RetryKind}; - use bytes::Bytes; - use std::error::Error; - use std::fmt::{self, Debug, Display, Formatter}; - - #[derive(Clone)] - pub(super) struct TestOperationParser; - - #[derive(Debug)] - pub(super) struct OperationError; - - impl Display for OperationError { - fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { - write!(f, "{:?}", self) - } - } - - impl Error for OperationError {} - - impl ProvideErrorKind for OperationError { - fn retryable_error_kind(&self) -> Option { - Some(ErrorKind::ThrottlingError) - } - - fn code(&self) -> Option<&str> { - None - } - } - - impl ParseHttpResponse for TestOperationParser { - type Output = Result; - - fn parse_unloaded(&self, response: &mut operation::Response) -> Option { - if response.http().status().is_success() { - Some(Ok("Hello!".to_string())) - } else { - Some(Err(OperationError)) - } - } - - fn parse_loaded(&self, _response: &http::Response) -> Self::Output { - Ok("Hello!".to_string()) - } - } - - #[derive(Clone)] - pub(super) struct TestRetryClassifier; - - impl ClassifyRetry> for TestRetryClassifier - where - E: ProvideErrorKind + Debug, - T: Debug, - { - fn classify_retry(&self, err: Result<&T, &SdkError>) -> RetryKind { - let kind = match err { - Err(SdkError::ServiceError(context)) => context.err().retryable_error_kind(), - Ok(_) => return RetryKind::Unnecessary, - _ => panic!("test handler only handles modeled errors got: {:?}", err), - }; - match kind { - Some(kind) => RetryKind::Error(kind), - None => RetryKind::UnretryableFailure, - } - } - } -} - fn test_operation() -> Operation { let req = operation::Request::new( http::Request::builder() @@ -108,14 +37,14 @@ async fn end_to_end_retry_test() { fn ok() -> http::Response<&'static str> { http::Response::builder() .status(200) - .body("response body") + .body("Hello!") .unwrap() } fn err() -> http::Response<&'static str> { http::Response::builder() .status(500) - .body("response body") + .body("This was an error") .unwrap() } // 1 failing response followed by 1 successful response 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 new file mode 100644 index 0000000000..475c076b12 --- /dev/null +++ b/rust-runtime/aws-smithy-client/tests/reconnect_on_transient_error.rs @@ -0,0 +1,230 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +#![cfg(feature = "test-util")] + +mod test_operation; + +use aws_smithy_async::rt::sleep::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}; +use aws_smithy_client::{match_events, Client}; +use aws_smithy_http::body::SdkBody; +use aws_smithy_http::operation; +use aws_smithy_http::operation::Operation; +use aws_smithy_types::retry::ReconnectMode; +use aws_smithy_types::timeout::{OperationTimeoutConfig, TimeoutConfig}; +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; +use wire_mock::ev; + +fn end_of_test() -> &'static str { + "end_of_test" +} + +fn test_operation( + uri: Uri, + retryable: bool, +) -> Operation { + let mut req = operation::Request::new( + http::Request::builder() + .uri(uri) + .body(SdkBody::from("request body")) + .unwrap(), + ); + if !retryable { + req = req + .augment(|req, _conf| { + Ok::<_, Infallible>( + req.map(|_| SdkBody::from_dyn(BoxBody::new(SdkBody::from("body")))), + ) + }) + .unwrap(); + } + Operation::new(req, TestOperationParser).with_retry_classifier(TestRetryClassifier) +} + +async fn h1_and_h2(events: Vec, match_clause: impl Fn(&[RecordedEvent])) { + wire_level_test(events.clone(), |_b| {}, |b| b, &match_clause).await; + wire_level_test( + events, + |b| { + b.http2_only(true); + }, + |b| b, + match_clause, + ) + .await; + println!("h2 ok!"); +} + +/// Repeatedly send test operation until `end_of_test` is received +/// +/// When the test is over, match_clause is evaluated +async fn wire_level_test( + events: Vec, + hyper_builder_settings: impl Fn(&mut HyperBuilder), + client_builder_settings: impl Fn(Builder) -> Builder, + match_clause: impl Fn(&[RecordedEvent]), +) { + let connection = wire_mock::WireLevelTestConnection::spinup(events).await; + + let http_connector = HttpConnector::new_with_resolver(connection.dns_resolver()); + let mut hyper_builder = hyper::Client::builder(); + hyper_builder_settings(&mut hyper_builder); + let hyper_adapter = hyper_ext::Adapter::builder() + .hyper_builder(hyper_builder) + .build(http_connector); + let client = client_builder_settings( + Client::builder().reconnect_mode(ReconnectMode::ReconnectOnTransientError), + ) + .connector(hyper_adapter) + .middleware(Identity::new()) + .operation_timeout_config(OperationTimeoutConfig::from( + &TimeoutConfig::builder() + .operation_attempt_timeout(Duration::from_millis(100)) + .build(), + )) + .sleep_impl(Arc::new(TokioSleep::new())) + .build(); + loop { + match client + .call(test_operation( + connection.endpoint_url().parse().unwrap(), + false, + )) + .await + { + Ok(resp) => { + tracing::info!("response: {:?}", resp); + if resp == end_of_test() { + break; + } + } + Err(e) => tracing::info!("error: {:?}", e), + } + } + let events = connection.events(); + match_clause(&events); +} + +#[tokio::test] +async fn non_transient_errors_no_reconect() { + h1_and_h2( + vec![ + ReplayedEvent::status(400), + ReplayedEvent::with_body(end_of_test()), + ], + match_events!(ev!(dns), ev!(connect), ev!(http(400)), ev!(http(200))), + ) + .await +} + +#[tokio::test] +async fn reestablish_dns_on_503() { + h1_and_h2( + vec![ + ReplayedEvent::status(503), + ReplayedEvent::status(503), + ReplayedEvent::status(503), + ReplayedEvent::with_body(end_of_test()), + ], + match_events!( + // first request + ev!(dns), + ev!(connect), + ev!(http(503)), + // second request + ev!(dns), + ev!(connect), + ev!(http(503)), + // third request + ev!(dns), + ev!(connect), + ev!(http(503)), + // all good + ev!(dns), + ev!(connect), + ev!(http(200)) + ), + ) + .await; +} + +#[tokio::test] +async fn connection_shared_on_success() { + h1_and_h2( + vec![ + ReplayedEvent::ok(), + ReplayedEvent::ok(), + ReplayedEvent::status(503), + ReplayedEvent::with_body(end_of_test()), + ], + match_events!( + ev!(dns), + ev!(connect), + ev!(http(200)), + ev!(http(200)), + ev!(http(503)), + ev!(dns), + ev!(connect), + ev!(http(200)) + ), + ) + .await; +} + +#[tokio::test] +async fn no_reconnect_when_disabled() { + use wire_mock::ev; + wire_level_test( + vec![ + ReplayedEvent::status(503), + ReplayedEvent::with_body(end_of_test()), + ], + |_b| {}, + |b| b.reconnect_mode(ReconnectMode::ReuseAllConnections), + match_events!(ev!(dns), ev!(connect), ev!(http(503)), ev!(http(200))), + ) + .await; +} + +#[tokio::test] +async fn connection_reestablished_after_timeout() { + use wire_mock::ev; + h1_and_h2( + vec![ + ReplayedEvent::ok(), + ReplayedEvent::Timeout, + ReplayedEvent::ok(), + ReplayedEvent::Timeout, + ReplayedEvent::with_body(end_of_test()), + ], + match_events!( + // first connection + ev!(dns), + ev!(connect), + ev!(http(200)), + // reuse but got a timeout + ev!(timeout), + // so we reconnect + ev!(dns), + ev!(connect), + ev!(http(200)), + ev!(timeout), + ev!(dns), + ev!(connect), + ev!(http(200)) + ), + ) + .await; +} diff --git a/rust-runtime/aws-smithy-client/tests/test_operation/mod.rs b/rust-runtime/aws-smithy-client/tests/test_operation/mod.rs new file mode 100644 index 0000000000..db193e4bd9 --- /dev/null +++ b/rust-runtime/aws-smithy-client/tests/test_operation/mod.rs @@ -0,0 +1,84 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +use aws_smithy_http::operation; +use aws_smithy_http::response::ParseHttpResponse; +use aws_smithy_http::result::SdkError; +use aws_smithy_http::retry::ClassifyRetry; +use aws_smithy_types::retry::{ErrorKind, ProvideErrorKind, RetryKind}; +use bytes::Bytes; +use std::error::Error; +use std::fmt::{self, Debug, Display, Formatter}; +use std::str; + +#[derive(Clone)] +pub(super) struct TestOperationParser; + +#[derive(Debug)] +pub(super) struct OperationError(ErrorKind); + +impl Display for OperationError { + fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { + write!(f, "{:?}", self) + } +} + +impl Error for OperationError {} + +impl ProvideErrorKind for OperationError { + fn retryable_error_kind(&self) -> Option { + Some(self.0) + } + + fn code(&self) -> Option<&str> { + None + } +} + +impl ParseHttpResponse for TestOperationParser { + type Output = Result; + + fn parse_unloaded(&self, response: &mut operation::Response) -> Option { + tracing::debug!("got response: {:?}", response); + match response.http().status() { + s if s.is_success() => None, + s if s.is_client_error() => Some(Err(OperationError(ErrorKind::ServerError))), + s if s.is_server_error() => Some(Err(OperationError(ErrorKind::TransientError))), + _ => panic!("unexpected status: {}", response.http().status()), + } + } + + fn parse_loaded(&self, response: &http::Response) -> Self::Output { + Ok(str::from_utf8(response.body().as_ref()) + .unwrap() + .to_string()) + } +} + +#[derive(Clone)] +pub(super) struct TestRetryClassifier; + +impl ClassifyRetry> for TestRetryClassifier +where + E: ProvideErrorKind + Debug, + T: Debug, +{ + fn classify_retry(&self, err: Result<&T, &SdkError>) -> RetryKind { + tracing::info!("got response: {:?}", err); + let kind = match err { + Err(SdkError::ServiceError(context)) => context.err().retryable_error_kind(), + Err(SdkError::DispatchFailure(err)) if err.is_timeout() => { + Some(ErrorKind::TransientError) + } + Err(SdkError::TimeoutError(_)) => Some(ErrorKind::TransientError), + Ok(_) => return RetryKind::Unnecessary, + _ => panic!("test handler only handles modeled errors got: {:?}", err), + }; + match kind { + Some(kind) => RetryKind::Error(kind), + None => RetryKind::UnretryableFailure, + } + } +} diff --git a/rust-runtime/aws-smithy-eventstream/src/buf.rs b/rust-runtime/aws-smithy-eventstream/src/buf.rs index 9e7c11ae72..31cbf40f18 100644 --- a/rust-runtime/aws-smithy-eventstream/src/buf.rs +++ b/rust-runtime/aws-smithy-eventstream/src/buf.rs @@ -3,5 +3,5 @@ * SPDX-License-Identifier: Apache-2.0 */ -pub mod count; -pub mod crc; +pub(crate) mod count; +pub(crate) mod crc; diff --git a/rust-runtime/aws-smithy-eventstream/src/buf/count.rs b/rust-runtime/aws-smithy-eventstream/src/buf/count.rs index 9fe6695473..ea9505d48d 100644 --- a/rust-runtime/aws-smithy-eventstream/src/buf/count.rs +++ b/rust-runtime/aws-smithy-eventstream/src/buf/count.rs @@ -8,7 +8,7 @@ use bytes::Buf; /// A [`Buf`] implementation that counts bytes read. -pub struct CountBuf<'a, B> +pub(crate) struct CountBuf<'a, B> where B: Buf, { @@ -21,12 +21,12 @@ where B: Buf, { /// Creates a new `CountBuf` by wrapping the given `buffer`. - pub fn new(buffer: &'a mut B) -> Self { + pub(crate) fn new(buffer: &'a mut B) -> Self { CountBuf { buffer, count: 0 } } /// Consumes the `CountBuf` and returns the number of bytes read. - pub fn into_count(self) -> usize { + pub(crate) fn into_count(self) -> usize { self.count } } diff --git a/rust-runtime/aws-smithy-eventstream/src/buf/crc.rs b/rust-runtime/aws-smithy-eventstream/src/buf/crc.rs index f9ec9f0ad5..f94f20f1ac 100644 --- a/rust-runtime/aws-smithy-eventstream/src/buf/crc.rs +++ b/rust-runtime/aws-smithy-eventstream/src/buf/crc.rs @@ -3,7 +3,7 @@ * SPDX-License-Identifier: Apache-2.0 */ -//! Utilites for calculating CRC-32 while reading from a [`Buf`] or writing to a [`BufMut`]. +//! Utilities for calculating CRC-32 while reading from a [`Buf`] or writing to a [`BufMut`]. use bytes::buf::UninitSlice; use bytes::{Buf, BufMut}; @@ -11,7 +11,7 @@ use crc32fast::Hasher; /// Implementation of [`Buf`] that calculates a CRC-32 checksum of the data /// being read from an underlying `Buf` instance. -pub struct CrcBuf<'a, B> +pub(crate) struct CrcBuf<'a, B> where B: Buf, { @@ -24,7 +24,7 @@ where B: Buf, { /// Creates a new `CrcBuf` by wrapping the given `buffer`. - pub fn new(buffer: &'a mut B) -> Self { + pub(crate) fn new(buffer: &'a mut B) -> Self { CrcBuf { buffer, crc: Hasher::new(), @@ -32,7 +32,7 @@ where } /// Consumes the `CrcBuf` and returns the calculated checksum. - pub fn into_crc(self) -> u32 { + pub(crate) fn into_crc(self) -> u32 { self.crc.finalize() } } @@ -98,14 +98,14 @@ mod crc_buf_tests { /// Implementation of [`BufMut`] that calculates a CRC-32 checksum of the data /// being written to an underlying `Buf` instance, with a function to then write /// the calculated CRC-32 to the buffer. -pub struct CrcBufMut<'a> { +pub(crate) struct CrcBufMut<'a> { buffer: &'a mut dyn BufMut, crc: Hasher, } impl<'a> CrcBufMut<'a> { /// Creates a new `CrcBufMut` by wrapping the given `buffer`. - pub fn new(buffer: &'a mut dyn BufMut) -> Self { + pub(crate) fn new(buffer: &'a mut dyn BufMut) -> Self { CrcBufMut { buffer, crc: Hasher::new(), @@ -115,7 +115,7 @@ impl<'a> CrcBufMut<'a> { /// Puts the calculated CRC-32 to the buffer as a Big Endian 32-bit integer. /// This can be called multiple times, and each successive call will include /// the previously written checksum in its new checksum. - pub fn put_crc(&mut self) { + pub(crate) fn put_crc(&mut self) { self.put_u32(self.crc.clone().finalize()); } } diff --git a/rust-runtime/aws-smithy-eventstream/src/lib.rs b/rust-runtime/aws-smithy-eventstream/src/lib.rs index 07cb538857..0d060b914e 100644 --- a/rust-runtime/aws-smithy-eventstream/src/lib.rs +++ b/rust-runtime/aws-smithy-eventstream/src/lib.rs @@ -3,10 +3,11 @@ * SPDX-License-Identifier: Apache-2.0 */ +#![allow(clippy::derive_partial_eq_without_eq)] #![warn( - missing_debug_implementations, - /*missing_docs, - rustdoc::all,*/ + // missing_docs, + rustdoc::missing_crate_level_docs, + unreachable_pub, rust_2018_idioms )] diff --git a/rust-runtime/aws-smithy-http-auth/Cargo.toml b/rust-runtime/aws-smithy-http-auth/Cargo.toml new file mode 100644 index 0000000000..0d70b25eec --- /dev/null +++ b/rust-runtime/aws-smithy-http-auth/Cargo.toml @@ -0,0 +1,20 @@ +[package] +name = "aws-smithy-http-auth" +version = "0.0.0-smithy-rs-head" +authors = [ + "AWS Rust SDK Team ", + "Eduardo Rodrigues <16357187+eduardomourar@users.noreply.github.com>", +] +description = "Smithy HTTP logic for smithy-rs." +edition = "2021" +license = "Apache-2.0" +repository = "https://github.com/awslabs/smithy-rs" + +[dependencies] +zeroize = "1" + +[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-auth/LICENSE b/rust-runtime/aws-smithy-http-auth/LICENSE new file mode 100644 index 0000000000..67db858821 --- /dev/null +++ b/rust-runtime/aws-smithy-http-auth/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/rust-runtime/aws-smithy-http-auth/README.md b/rust-runtime/aws-smithy-http-auth/README.md new file mode 100644 index 0000000000..1d963cafce --- /dev/null +++ b/rust-runtime/aws-smithy-http-auth/README.md @@ -0,0 +1,7 @@ +# aws-smithy-http-auth + +HTTP Auth implementation for service clients generated by [smithy-rs](https://github.com/awslabs/smithy-rs). + + +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-auth/external-types.toml b/rust-runtime/aws-smithy-http-auth/external-types.toml new file mode 100644 index 0000000000..ff30ccf5ad --- /dev/null +++ b/rust-runtime/aws-smithy-http-auth/external-types.toml @@ -0,0 +1,2 @@ +allowed_external_types = [ +] diff --git a/rust-runtime/aws-smithy-http-auth/src/api_key.rs b/rust-runtime/aws-smithy-http-auth/src/api_key.rs new file mode 100644 index 0000000000..bb2ab65b3c --- /dev/null +++ b/rust-runtime/aws-smithy-http-auth/src/api_key.rs @@ -0,0 +1,74 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +//! HTTP Auth API Key + +use std::cmp::PartialEq; +use std::fmt::Debug; +use std::sync::Arc; +use zeroize::Zeroizing; + +/// Authentication configuration to connect to a Smithy Service +#[derive(Clone, Eq, PartialEq)] +pub struct AuthApiKey(Arc); + +#[derive(Clone, Eq, PartialEq)] +struct Inner { + api_key: Zeroizing, +} + +impl Debug for AuthApiKey { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + let mut auth_api_key = f.debug_struct("AuthApiKey"); + auth_api_key.field("api_key", &"** redacted **").finish() + } +} + +impl AuthApiKey { + /// Constructs a new API key. + pub fn new(api_key: impl Into) -> Self { + Self(Arc::new(Inner { + api_key: Zeroizing::new(api_key.into()), + })) + } + + /// Returns the underlying api key. + pub fn api_key(&self) -> &str { + &self.0.api_key + } +} + +impl From<&str> for AuthApiKey { + fn from(api_key: &str) -> Self { + Self::from(api_key.to_owned()) + } +} + +impl From for AuthApiKey { + fn from(api_key: String) -> Self { + Self(Arc::new(Inner { + api_key: Zeroizing::new(api_key), + })) + } +} + +#[cfg(test)] +mod tests { + use super::AuthApiKey; + + #[test] + fn api_key_is_equal() { + let api_key_a: AuthApiKey = "some-api-key".into(); + let api_key_b = AuthApiKey::new("some-api-key"); + assert_eq!(api_key_a, api_key_b); + } + + #[test] + fn api_key_is_different() { + let api_key_a = AuthApiKey::new("some-api-key"); + let api_key_b: AuthApiKey = String::from("another-api-key").into(); + assert_ne!(api_key_a, api_key_b); + } +} diff --git a/rust-runtime/aws-smithy-http-auth/src/definition.rs b/rust-runtime/aws-smithy-http-auth/src/definition.rs new file mode 100644 index 0000000000..918f6aae8f --- /dev/null +++ b/rust-runtime/aws-smithy-http-auth/src/definition.rs @@ -0,0 +1,251 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +//! HTTP Auth Definition + +use crate::location::HttpAuthLocation; +use std::cmp::PartialEq; +use std::fmt::Debug; + +/// A HTTP-specific authentication scheme that sends an arbitrary +/// auth value in a header or query string parameter. +// As described in the Smithy documentation: +// https://github.com/awslabs/smithy/blob/main/smithy-model/src/main/resources/software/amazon/smithy/model/loader/prelude.smithy +#[derive(Clone, Debug, Default, PartialEq)] +pub struct HttpAuthDefinition { + /// Defines the location of where the Auth is serialized. + location: HttpAuthLocation, + + /// Defines the name of the HTTP header or query string parameter + /// that contains the Auth. + name: String, + + /// Defines the security scheme to use on the `Authorization` header value. + /// This can only be set if the "location" property is set to [`HttpAuthLocation::Header`]. + scheme: Option, +} + +impl HttpAuthDefinition { + /// Returns a builder for `HttpAuthDefinition`. + pub fn builder() -> http_auth_definition::Builder { + http_auth_definition::Builder::default() + } + + /// Constructs a new HTTP auth definition in header. + pub fn header(header_name: N, scheme: S) -> Self + where + N: Into, + S: Into>, + { + let mut builder = Self::builder() + .location(HttpAuthLocation::Header) + .name(header_name); + let scheme: Option = scheme.into(); + if scheme.is_some() { + builder.set_scheme(scheme); + } + builder.build() + } + + /// Constructs a new HTTP auth definition following the RFC 2617 for Basic Auth. + pub fn basic_auth() -> Self { + Self::builder() + .location(HttpAuthLocation::Header) + .name("Authorization".to_owned()) + .scheme("Basic".to_owned()) + .build() + } + + /// Constructs a new HTTP auth definition following the RFC 2617 for Digest Auth. + pub fn digest_auth() -> Self { + Self::builder() + .location(HttpAuthLocation::Header) + .name("Authorization".to_owned()) + .scheme("Digest".to_owned()) + .build() + } + + /// Constructs a new HTTP auth definition following the RFC 6750 for Bearer Auth. + pub fn bearer_auth() -> Self { + Self::builder() + .location(HttpAuthLocation::Header) + .name("Authorization".to_owned()) + .scheme("Bearer".to_owned()) + .build() + } + + /// Constructs a new HTTP auth definition in query string. + pub fn query(name: impl Into) -> Self { + Self::builder() + .location(HttpAuthLocation::Query) + .name(name.into()) + .build() + } + + /// Returns the HTTP auth location. + pub fn location(&self) -> HttpAuthLocation { + self.location + } + + /// Returns the HTTP auth name. + pub fn name(&self) -> &str { + &self.name + } + + /// Returns the HTTP auth scheme. + pub fn scheme(&self) -> Option<&str> { + self.scheme.as_deref() + } +} + +/// Types associated with [`HttpAuthDefinition`]. +pub mod http_auth_definition { + use super::HttpAuthDefinition; + use crate::{ + definition::HttpAuthLocation, + error::{AuthError, AuthErrorKind}, + }; + + /// A builder for [`HttpAuthDefinition`]. + #[derive(Debug, Default)] + pub struct Builder { + location: Option, + name: Option, + scheme: Option, + } + + impl Builder { + /// Sets the HTTP auth location. + pub fn location(mut self, location: HttpAuthLocation) -> Self { + self.location = Some(location); + self + } + + /// Sets the HTTP auth location. + pub fn set_location(&mut self, location: Option) -> &mut Self { + self.location = location; + self + } + + /// Sets the the HTTP auth name. + pub fn name(mut self, name: impl Into) -> Self { + self.name = Some(name.into()); + self + } + + /// Sets the the HTTP auth name. + pub fn set_name(&mut self, name: Option) -> &mut Self { + self.name = name; + self + } + + /// Sets the HTTP auth scheme. + pub fn scheme(mut self, scheme: impl Into) -> Self { + self.scheme = Some(scheme.into()); + self + } + + /// Sets the HTTP auth scheme. + pub fn set_scheme(&mut self, scheme: Option) -> &mut Self { + self.scheme = scheme; + self + } + + /// Constructs a [`HttpAuthDefinition`] from the builder. + pub fn build(self) -> HttpAuthDefinition { + if self.scheme.is_some() + && self + .name + .as_deref() + .map_or("".to_string(), |s| s.to_ascii_lowercase()) + != "authorization" + { + // Stop execution because the Smithy model should not contain such combination. + // Otherwise, this would cause unexpected behavior in the SDK. + panic!("{}", AuthError::from(AuthErrorKind::SchemeNotAllowed)); + } + HttpAuthDefinition { + location: self.location.unwrap_or_else(|| { + panic!( + "{}", + AuthError::from(AuthErrorKind::MissingRequiredField("location")) + ) + }), + name: self.name.unwrap_or_else(|| { + panic!( + "{}", + AuthError::from(AuthErrorKind::MissingRequiredField("name")) + ) + }), + scheme: self.scheme, + } + } + } +} + +#[cfg(test)] +mod tests { + use super::HttpAuthDefinition; + use crate::location::HttpAuthLocation; + + #[test] + fn definition_for_header_without_scheme() { + let definition = HttpAuthDefinition::header("Header", None); + assert_eq!(definition.location, HttpAuthLocation::Header); + assert_eq!(definition.name, "Header"); + assert_eq!(definition.scheme, None); + } + + #[test] + fn definition_for_authorization_header_with_scheme() { + let definition = HttpAuthDefinition::header("authorization", "Scheme".to_owned()); + assert_eq!(definition.location(), HttpAuthLocation::Header); + assert_eq!(definition.name(), "authorization"); + assert_eq!(definition.scheme(), Some("Scheme")); + } + + #[test] + #[should_panic] + fn definition_fails_with_scheme_not_allowed() { + let _ = HttpAuthDefinition::header("Invalid".to_owned(), "Scheme".to_owned()); + } + + #[test] + fn definition_for_basic() { + let definition = HttpAuthDefinition::basic_auth(); + assert_eq!( + definition, + HttpAuthDefinition { + location: HttpAuthLocation::Header, + name: "Authorization".to_owned(), + scheme: Some("Basic".to_owned()), + } + ); + } + + #[test] + fn definition_for_digest() { + let definition = HttpAuthDefinition::digest_auth(); + assert_eq!(definition.location(), HttpAuthLocation::Header); + assert_eq!(definition.name(), "Authorization"); + assert_eq!(definition.scheme(), Some("Digest")); + } + + #[test] + fn definition_for_bearer_token() { + let definition = HttpAuthDefinition::bearer_auth(); + assert_eq!(definition.location(), HttpAuthLocation::Header); + assert_eq!(definition.name(), "Authorization"); + assert_eq!(definition.scheme(), Some("Bearer")); + } + + #[test] + fn definition_for_query() { + let definition = HttpAuthDefinition::query("query_key"); + assert_eq!(definition.location(), HttpAuthLocation::Query); + assert_eq!(definition.name(), "query_key"); + assert_eq!(definition.scheme(), None); + } +} diff --git a/rust-runtime/aws-smithy-http-auth/src/error.rs b/rust-runtime/aws-smithy-http-auth/src/error.rs new file mode 100644 index 0000000000..227dbe1cf2 --- /dev/null +++ b/rust-runtime/aws-smithy-http-auth/src/error.rs @@ -0,0 +1,42 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +//! HTTP Auth Error + +use std::cmp::PartialEq; +use std::fmt::Debug; + +#[derive(Debug, Eq, PartialEq)] +pub(crate) enum AuthErrorKind { + InvalidLocation, + MissingRequiredField(&'static str), + SchemeNotAllowed, +} + +/// Error for Smithy authentication +#[derive(Debug, Eq, PartialEq)] +pub struct AuthError { + kind: AuthErrorKind, +} + +impl std::fmt::Display for AuthError { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + use AuthErrorKind::*; + match &self.kind { + InvalidLocation => write!(f, "invalid location: expected `header` or `query`"), + MissingRequiredField(field) => write!(f, "missing required field: {}", field), + SchemeNotAllowed => write!( + f, + "scheme only allowed when it is set into the `Authorization` header" + ), + } + } +} + +impl From for AuthError { + fn from(kind: AuthErrorKind) -> Self { + Self { kind } + } +} diff --git a/rust-runtime/aws-smithy-http-auth/src/lib.rs b/rust-runtime/aws-smithy-http-auth/src/lib.rs new file mode 100644 index 0000000000..b245385253 --- /dev/null +++ b/rust-runtime/aws-smithy-http-auth/src/lib.rs @@ -0,0 +1,19 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +#![allow(clippy::derive_partial_eq_without_eq)] +#![warn( + missing_docs, + rustdoc::missing_crate_level_docs, + unreachable_pub, + rust_2018_idioms +)] + +//! Smithy HTTP Auth Types + +pub mod api_key; +pub mod definition; +pub mod error; +pub mod location; diff --git a/rust-runtime/aws-smithy-http-auth/src/location.rs b/rust-runtime/aws-smithy-http-auth/src/location.rs new file mode 100644 index 0000000000..5b044cf904 --- /dev/null +++ b/rust-runtime/aws-smithy-http-auth/src/location.rs @@ -0,0 +1,73 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +//! HTTP Auth Location + +use std::cmp::PartialEq; +use std::fmt::Debug; + +use crate::error::{AuthError, AuthErrorKind}; + +/// Enum for describing where the HTTP Auth can be placed. +#[derive(Copy, Clone, Debug, Default, Eq, PartialEq)] +pub enum HttpAuthLocation { + /// In the HTTP header. + #[default] + Header, + /// In the query string of the URL. + Query, +} + +impl HttpAuthLocation { + fn as_str(&self) -> &'static str { + match self { + Self::Header => "header", + Self::Query => "query", + } + } +} + +impl TryFrom<&str> for HttpAuthLocation { + type Error = AuthError; + fn try_from(value: &str) -> Result { + match value { + "header" => Ok(Self::Header), + "query" => Ok(Self::Query), + _ => Err(AuthError::from(AuthErrorKind::InvalidLocation)), + } + } +} + +impl TryFrom for HttpAuthLocation { + type Error = AuthError; + fn try_from(value: String) -> Result { + Self::try_from(value.as_str()) + } +} + +impl AsRef for HttpAuthLocation { + fn as_ref(&self) -> &str { + self.as_str() + } +} + +impl std::fmt::Display for HttpAuthLocation { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + std::fmt::Display::fmt(&self.as_str(), f) + } +} + +#[cfg(test)] +mod tests { + use super::HttpAuthLocation; + use crate::error::{AuthError, AuthErrorKind}; + + #[test] + fn fails_if_location_is_invalid() { + let actual = HttpAuthLocation::try_from("invalid").unwrap_err(); + let expected = AuthError::from(AuthErrorKind::InvalidLocation); + assert_eq!(actual, expected); + } +} diff --git a/rust-runtime/aws-smithy-http-server-python/Cargo.toml b/rust-runtime/aws-smithy-http-server-python/Cargo.toml index 78bcac926b..69e9577160 100644 --- a/rust-runtime/aws-smithy-http-server-python/Cargo.toml +++ b/rust-runtime/aws-smithy-http-server-python/Cargo.toml @@ -48,17 +48,25 @@ tracing-appender = { version = "0.2.2"} [dev-dependencies] pretty_assertions = "1" -futures-util = "0.3" +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"] } +pyo3-asyncio = { version = "0.17.0", features = ["testing", "attributes", "tokio-runtime", "unstable-streams"] } rcgen = "0.10.0" hyper-rustls = { version = "0.23.1", 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` +# for the async Python tests. For more detail see: +# https://docs.rs/pyo3-asyncio/0.18.0/pyo3_asyncio/testing/index.html#pyo3-asyncio-testing-utilities [[test]] name = "middleware_tests" path = "src/middleware/pytests/harness.rs" harness = false +[[test]] +name = "python_tests" +path = "src/pytests/harness.rs" +harness = false [package.metadata.docs.rs] all-features = true diff --git a/rust-runtime/aws-smithy-http-server-python/examples/.gitignore b/rust-runtime/aws-smithy-http-server-python/examples/.gitignore index 32bb8ef00e..e2773ce005 100644 --- a/rust-runtime/aws-smithy-http-server-python/examples/.gitignore +++ b/rust-runtime/aws-smithy-http-server-python/examples/.gitignore @@ -1,3 +1,4 @@ pokemon-service-client/ pokemon-service-server-sdk/ wheels/ +__pycache__ diff --git a/rust-runtime/aws-smithy-http-server-python/examples/Makefile b/rust-runtime/aws-smithy-http-server-python/examples/Makefile index 22be94a19e..3b73f08ca6 100644 --- a/rust-runtime/aws-smithy-http-server-python/examples/Makefile +++ b/rust-runtime/aws-smithy-http-server-python/examples/Makefile @@ -25,29 +25,35 @@ endif # Note on `--compatibility linux`: Maturin by default uses `manylinux_x_y` but it is not supported # by our current CI version (3.7.10), we can drop `--compatibility linux` when we switch to higher Python version. # For more detail: https://github.com/pypa/manylinux -build-wheel: ensure-maturin codegen - maturin build --manifest-path $(SERVER_SDK_DST)/Cargo.toml --out $(WHEELS) --compatibility linux +build-wheel: ensure-maturin + cd $(SERVER_SDK_DST) && maturin build --out $(WHEELS) --compatibility linux -build-wheel-release: ensure-maturin codegen - maturin build --manifest-path $(SERVER_SDK_DST)/Cargo.toml --out $(WHEELS) --compatibility linux --release +build-wheel-release: ensure-maturin + cd $(SERVER_SDK_DST) && maturin build --out $(WHEELS) --compatibility linux --release install-wheel: find $(WHEELS) -type f -name '*.whl' | xargs python3 -m pip install --user --force-reinstall -build: build-wheel install-wheel +generate-stubs: + python3 $(CUR_DIR)/stubgen.py pokemon_service_server_sdk $(SERVER_SDK_DST)/python/pokemon_service_server_sdk -release: build-wheel-release install-wheel +build: codegen + $(MAKE) build-wheel + $(MAKE) install-wheel + $(MAKE) generate-stubs + $(MAKE) build-wheel-release + $(MAKE) install-wheel run: build python3 $(CUR_DIR)/pokemon_service.py -run-release: release - python3 $(CUR_DIR)/pokemon_service.py - py-check: build - mypy pokemon_service.py + python3 -m mypy pokemon_service.py + +py-test: + python3 stubgen_test.py -test: build +test: build py-check py-test cargo test clippy: codegen @@ -60,6 +66,6 @@ clean: cargo clean || echo "Unable to run cargo clean" distclean: clean - rm -rf $(SERVER_SDK_DST) $(CLIENT_SDK_DST) $(WHEELS) $(CUR_DIR)/Cargo.lock + rm -rf $(SERVER_SDK_DST) $(SERVER_SDK_SRC) $(CLIENT_SDK_DST) $(CLIENT_SDK_SRC) $(WHEELS) $(CUR_DIR)/Cargo.lock .PHONY: all diff --git a/rust-runtime/aws-smithy-http-server-python/examples/pokemon_service.py b/rust-runtime/aws-smithy-http-server-python/examples/pokemon_service.py index 1f45c5fe13..a3c7bf1c93 100644 --- a/rust-runtime/aws-smithy-http-server-python/examples/pokemon_service.py +++ b/rust-runtime/aws-smithy-http-server-python/examples/pokemon_service.py @@ -8,34 +8,34 @@ import random from threading import Lock from dataclasses import dataclass -from typing import List, Optional, Callable, Awaitable +from typing import Dict, Any, List, Optional, Callable, Awaitable from pokemon_service_server_sdk import App -from pokemon_service_server_sdk.tls import TlsConfig # type: ignore -from pokemon_service_server_sdk.aws_lambda import LambdaContext # type: ignore -from pokemon_service_server_sdk.error import ResourceNotFoundException # type: ignore -from pokemon_service_server_sdk.input import ( # type: ignore +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.input import ( DoNothingInput, GetPokemonSpeciesInput, GetServerStatisticsInput, CheckHealthInput, StreamPokemonRadioInput, ) -from pokemon_service_server_sdk.logging import TracingHandler # type: ignore -from pokemon_service_server_sdk.middleware import ( # type: ignore +from pokemon_service_server_sdk.logging import TracingHandler +from pokemon_service_server_sdk.middleware import ( MiddlewareException, Response, Request, ) -from pokemon_service_server_sdk.model import FlavorText, Language # type: ignore -from pokemon_service_server_sdk.output import ( # type: ignore +from pokemon_service_server_sdk.model import FlavorText, Language +from pokemon_service_server_sdk.output import ( DoNothingOutput, GetPokemonSpeciesOutput, GetServerStatisticsOutput, CheckHealthOutput, StreamPokemonRadioOutput, ) -from pokemon_service_server_sdk.types import ByteStream # type: ignore +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. @@ -131,7 +131,7 @@ def get_random_radio_stream(self) -> str: # Entrypoint ########################################################### # Get an App instance. -app = App() +app: "App[Context]" = App() # Register the context. app.context(Context()) @@ -249,7 +249,7 @@ def check_health(_: CheckHealthInput) -> CheckHealthOutput: async def stream_pokemon_radio( _: StreamPokemonRadioInput, context: Context ) -> StreamPokemonRadioOutput: - import aiohttp + import aiohttp # type: ignore radio_url = context.get_random_radio_stream() logging.info("Random radio URL for this stream is %s", radio_url) @@ -270,7 +270,7 @@ def main() -> None: parser.add_argument("--tls-cert-path") args = parser.parse_args() - config = dict(workers=1) + config: Dict[str, Any] = dict(workers=1) if args.enable_tls: config["tls"] = TlsConfig( key_path=args.tls_key_path, diff --git a/rust-runtime/aws-smithy-http-server-python/examples/pokemon_service_server_sdk.pyi b/rust-runtime/aws-smithy-http-server-python/examples/pokemon_service_server_sdk.pyi deleted file mode 100644 index ccbd5edb2e..0000000000 --- a/rust-runtime/aws-smithy-http-server-python/examples/pokemon_service_server_sdk.pyi +++ /dev/null @@ -1,20 +0,0 @@ -# Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. -# SPDX-License-Identifier: Apache-2.0 - -# NOTE: This is manually created to surpass some mypy errors and it is incomplete, -# in future we will autogenerate correct stubs. - -from typing import Any, TypeVar, Callable - -F = TypeVar("F", bound=Callable[..., Any]) - -class App: - context: Any - run: Any - - def middleware(self, func: F) -> F: ... - def do_nothing(self, func: F) -> F: ... - def get_pokemon_species(self, func: F) -> F: ... - def get_server_statistics(self, func: F) -> F: ... - def check_health(self, func: F) -> F: ... - def stream_pokemon_radio(self, func: F) -> F: ... diff --git a/rust-runtime/aws-smithy-http-server-python/examples/stubgen.py b/rust-runtime/aws-smithy-http-server-python/examples/stubgen.py new file mode 100644 index 0000000000..30348838d2 --- /dev/null +++ b/rust-runtime/aws-smithy-http-server-python/examples/stubgen.py @@ -0,0 +1,422 @@ +# Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +# SPDX-License-Identifier: Apache-2.0 + +from __future__ import annotations +import re +import inspect +import textwrap +from pathlib import Path +from typing import Any, Set, Dict, List, Tuple, Optional + +ROOT_MODULE_NAME_PLACEHOLDER = "__root_module_name__" + + +class Writer: + """ + Writer provides utilities for writing Python stubs. + """ + + root_module_name: str + path: Path + subwriters: List[Writer] + imports: Set[str] + defs: List[str] + generics: Set[str] + + def __init__(self, path: Path, root_module_name: str) -> None: + self.path = path + self.root_module_name = root_module_name + self.subwriters = [] + self.imports = set([]) + self.defs = [] + self.generics = set([]) + + 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("\\]", "]") + ) + + def submodule(self, path: Path) -> Writer: + w = Writer(path, self.root_module_name) + self.subwriters.append(w) + return w + + def include(self, path: str) -> str: + # `path` might be nested like: typing.Optional[typing.List[pokemon_service_server_sdk.model.GetPokemonSpecies]] + # we need to process every subpath in a nested path + paths = filter(lambda p: p, re.split("\\[|\\]|,| ", path)) + for subpath in paths: + parts = subpath.rsplit(".", maxsplit=1) + # add `typing` to imports for a path like `typing.List` + # but skip if the path doesn't have any namespace like `str` or `bool` + if len(parts) == 2: + self.imports.add(parts[0]) + + return path + + def fix_and_include(self, path: str) -> str: + return self.include(self.fix_path(path)) + + def define(self, code: str) -> None: + self.defs.append(code) + + def generic(self, name: str) -> None: + self.generics.add(name) + + def dump(self) -> None: + for w in self.subwriters: + w.dump() + + generics = "" + for g in sorted(self.generics): + generics += f"{g} = {self.include('typing.TypeVar')}('{g}')\n" + + self.path.parent.mkdir(parents=True, exist_ok=True) + contents = join([f"import {p}" for p in sorted(self.imports)]) + contents += "\n\n" + if generics: + contents += generics + "\n" + contents += join(self.defs) + self.path.write_text(contents) + + +class DocstringParserResult: + def __init__(self) -> None: + self.types: List[str] = [] + self.params: List[Tuple[str, str]] = [] + self.rtypes: List[str] = [] + self.generics: List[str] = [] + self.extends: List[str] = [] + + +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" + ) + 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" + ) + 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" + ) + name = parts[1] + ty = parts[2].rstrip(":") + res.params.append((name, ty)) + + +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" + ) + 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" + ) + res.extends.append(parts[1].rstrip(":")) + + +DocstringParserDirectives = { + "type": parse_type_directive, + "param": parse_param_directive, + "rtype": parse_rtype_directive, + "generic": parse_generic_directive, + "extends": parse_extends_directive, +} + + +class DocstringParser: + """ + DocstringParser provides utilities for parsing type information from docstring. + """ + + @staticmethod + def parse(obj: Any) -> Optional[DocstringParserResult]: + doc = inspect.getdoc(obj) + if not doc: + return None + + res = DocstringParserResult() + for line in doc.splitlines(): + line = line.strip() + for d, p in DocstringParserDirectives.items(): + if line.startswith(f":{d} ") and line.endswith(":"): + p(line, res) + return res + + @staticmethod + def parse_type(obj: Any) -> str: + result = DocstringParser.parse(obj) + if not result or len(result.types) == 0: + return "typing.Any" + return result.types[0] + + @staticmethod + def parse_function(obj: Any) -> Optional[Tuple[List[Tuple[str, str]], str]]: + result = DocstringParser.parse(obj) + if not result: + return None + + return ( + result.params, + "None" if len(result.rtypes) == 0 else result.rtypes[0], + ) + + @staticmethod + def parse_class(obj: Any) -> Tuple[List[str], List[str]]: + result = DocstringParser.parse(obj) + if not result: + return ([], []) + return (result.generics, result.extends) + + @staticmethod + def clean_doc(obj: Any) -> str: + doc = inspect.getdoc(obj) + if not doc: + return "" + + def predicate(l: str) -> bool: + for k in DocstringParserDirectives.keys(): + if l.startswith(f":{k} ") and l.endswith(":"): + return False + return True + + return "\n".join([l for l in doc.splitlines() if predicate(l)]).strip() + + +def indent(code: str, level: int = 4) -> str: + return textwrap.indent(code, level * " ") + + +def is_fn_like(obj: Any) -> bool: + return ( + inspect.isbuiltin(obj) + or inspect.ismethod(obj) + or inspect.isfunction(obj) + or inspect.ismethoddescriptor(obj) + or inspect.iscoroutine(obj) + or inspect.iscoroutinefunction(obj) + ) + + +def join(args: List[str], delim: str = "\n") -> str: + return delim.join(filter(lambda x: x, args)) + + +def make_doc(obj: Any) -> str: + doc = DocstringParser.clean_doc(obj) + doc = textwrap.dedent(doc) + if not doc: + return "" + + return join(['"""', doc, '"""']) + + +def make_field(writer: Writer, name: str, field: Any) -> str: + return f"{name}: {writer.fix_and_include(DocstringParser.parse_type(field))}" + + +def make_function( + writer: Writer, + name: str, + obj: Any, + include_docs: bool = True, + parent: Optional[Any] = None, +) -> str: + is_static_method = False + if parent and isinstance(obj, staticmethod): + # Get real method instance from `parent` if `obj` is a `staticmethod` + is_static_method = True + obj = getattr(parent, name) + + res = DocstringParser.parse_function(obj) + if not res: + # Make it `Any` if we can't parse the docstring + return f"{name}: {writer.include('typing.Any')}" + + params, rtype = res + # We're using signature for getting default values only, currently type hints are not supported + # in signatures. We can leverage signatures more if it supports type hints in future. + sig: Optional[inspect.Signature] = None + try: + sig = inspect.signature(obj) + except: + pass + + def has_default(param: str, ty: str) -> bool: + # PyO3 allows omitting `Option` params while calling a Rust function from Python, + # we should always mark `typing.Optional[T]` values as they have default values to allow same + # flexibiliy as runtime dynamics in type-stubs. + if ty.startswith("typing.Optional["): + return True + + if sig is None: + return False + + sig_param = sig.parameters.get(param) + return sig_param is not None and sig_param.default is not sig_param.empty + + receivers: List[str] = [] + attrs: List[str] = [] + if parent: + if is_static_method: + attrs.append("@staticmethod") + else: + receivers.append("self") + + def make_param(name: str, ty: str) -> str: + fixed_ty = writer.fix_and_include(ty) + param = f"{name}: {fixed_ty}" + if has_default(name, fixed_ty): + param += " = ..." + return param + + params = join(receivers + [make_param(n, t) for n, t in params], delim=", ") + attrs_str = join(attrs) + rtype = writer.fix_and_include(rtype) + body = "..." + if include_docs: + body = join([make_doc(obj), body]) + + return f""" +{attrs_str} +def {name}({params}) -> {rtype}: +{indent(body)} +""".lstrip() + + +def make_class(writer: Writer, name: str, klass: Any) -> str: + 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 + bases.extend(map(writer.fix_and_include, extends)) + for g in generics: + writer.generic(g) + + members: List[str] = [] + + class_vars: Dict[str, Any] = vars(klass) + for member_name, member in sorted(class_vars.items(), key=lambda k: k[0]): + if member_name.startswith("__"): + continue + + if inspect.isdatadescriptor(member): + members.append( + join( + [ + make_field(writer, member_name, member), + make_doc(member), + ] + ) + ) + elif is_fn_like(member): + members.append( + make_function(writer, member_name, member, parent=klass), + ) + elif isinstance(member, klass): + # Enum variant + members.append( + join( + [ + f"{member_name}: {name}", + make_doc(member), + ] + ) + ) + else: + print(f"Unknown member type: {member}") + + if inspect.getdoc(klass) is not None: + constructor_sig = DocstringParser.parse(klass) + if constructor_sig is not None and ( + # Make sure to only generate `__init__` if the class has a constructor defined + len(constructor_sig.rtypes) > 0 + or len(constructor_sig.params) > 0 + ): + members.append( + make_function( + writer, + "__init__", + klass, + include_docs=False, + parent=klass, + ) + ) + + bases_str = "" if len(bases) == 0 else f"({join(bases, delim=', ')})" + doc = make_doc(klass) + if doc: + doc += "\n" + body = join([doc, join(members, delim="\n\n") or "..."]) + return f"""\ +class {name}{bases_str}: +{indent(body)} +""" + + +def walk_module(writer: Writer, mod: Any): + exported = mod.__all__ + + for (name, member) in inspect.getmembers(mod): + if name not in exported: + continue + + if inspect.ismodule(member): + subpath = writer.path.parent / name / "__init__.pyi" + walk_module(writer.submodule(subpath), member) + elif inspect.isclass(member): + writer.define(make_class(writer, name, member)) + elif is_fn_like(member): + writer.define(make_function(writer, name, member)) + else: + print(f"Unknown type: {member}") + + +if __name__ == "__main__": + import argparse + import importlib + + parser = argparse.ArgumentParser() + parser.add_argument("module") + 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() diff --git a/rust-runtime/aws-smithy-http-server-python/examples/stubgen_test.py b/rust-runtime/aws-smithy-http-server-python/examples/stubgen_test.py new file mode 100644 index 0000000000..f1d2aa840a --- /dev/null +++ b/rust-runtime/aws-smithy-http-server-python/examples/stubgen_test.py @@ -0,0 +1,407 @@ +# Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +# SPDX-License-Identifier: Apache-2.0 + +import sys +import unittest +from types import ModuleType +from textwrap import dedent +from pathlib import Path +from tempfile import TemporaryDirectory + +from stubgen import Writer, walk_module + + +def create_module(name: str, code: str) -> ModuleType: + mod = ModuleType(name) + exec(dedent(code), mod.__dict__) + if not hasattr(mod, "__all__"): + # Manually populate `__all__` with all the members that doesn't start with `__` + mod.__all__ = [k for k in mod.__dict__.keys() if not k.startswith("__")] # type: ignore + sys.modules[name] = mod + return mod + + +class TestStubgen(unittest.TestCase): + def test_function_without_docstring(self): + self.single_mod( + """ + def foo(): + pass + """, + """ + import typing + + foo: typing.Any + """, + ) + + def test_regular_function(self): + self.single_mod( + """ + def foo(bar): + ''' + :param bar str: + :rtype bool: + ''' + pass + """, + """ + def foo(bar: str) -> bool: + ... + """, + ) + + def test_function_with_default_value(self): + self.single_mod( + """ + def foo(bar, qux=None): + ''' + :param bar int: + :param qux typing.Optional[str]: + :rtype None: + ''' + pass + """, + """ + import typing + + def foo(bar: int, qux: typing.Optional[str] = ...) -> None: + ... + """, + ) + + def test_empty_class(self): + self.single_mod( + """ + class Foo: + pass + """, + """ + class Foo: + ... + """, + ) + + def test_class(self): + self.single_mod( + """ + class Foo: + @property + def bar(self): + ''' + :type typing.List[bool]: + ''' + pass + + def qux(self, a, b, c): + ''' + :param a typing.Dict[typing.List[int]]: + :param b str: + :param c float: + :rtype typing.Union[int, str, bool]: + ''' + pass + """, + """ + import typing + + class Foo: + bar: typing.List[bool] + + def qux(self, a: typing.Dict[typing.List[int]], b: str, c: float) -> typing.Union[int, str, bool]: + ... + """, + ) + + def test_class_with_constructor_signature(self): + self.single_mod( + """ + class Foo: + ''' + :param bar str: + :rtype None: + ''' + """, + """ + class Foo: + def __init__(self, bar: str) -> None: + ... + """, + ) + + def test_class_with_static_method(self): + self.single_mod( + """ + class Foo: + @staticmethod + def bar(name): + ''' + :param name str: + :rtype typing.List[bool]: + ''' + pass + """, + """ + import typing + + class Foo: + @staticmethod + def bar(name: str) -> typing.List[bool]: + ... + """, + ) + + def test_class_with_an_undocumented_descriptor(self): + self.single_mod( + """ + class Foo: + @property + def bar(self): + pass + """, + """ + import typing + + class Foo: + bar: typing.Any + """, + ) + + def test_enum(self): + self.single_mod( + """ + class Foo: + def __init__(self, name): + pass + + Foo.Bar = Foo("Bar") + Foo.Baz = Foo("Baz") + Foo.Qux = Foo("Qux") + """, + """ + class Foo: + Bar: Foo + + Baz: Foo + + Qux: Foo + """, + ) + + def test_generic(self): + self.single_mod( + """ + class Foo: + ''' + :generic T: + :generic U: + :extends typing.Generic[T]: + :extends typing.Generic[U]: + ''' + + @property + def bar(self): + ''' + :type typing.Tuple[T, U]: + ''' + pass + + def baz(self, a): + ''' + :param a U: + :rtype T: + ''' + pass + """, + """ + import typing + + T = typing.TypeVar('T') + U = typing.TypeVar('U') + + class Foo(typing.Generic[T], typing.Generic[U]): + bar: typing.Tuple[T, U] + + def baz(self, a: U) -> T: + ... + """, + ) + + def test_items_with_docstrings(self): + self.single_mod( + """ + class Foo: + ''' + This is the docstring of Foo. + + And it has multiple lines. + + :generic T: + :extends typing.Generic[T]: + :param member T: + ''' + + @property + def bar(self): + ''' + This is the docstring of property `bar`. + + :type typing.Optional[T]: + ''' + pass + + def baz(self, t): + ''' + This is the docstring of method `baz`. + :param t T: + :rtype T: + ''' + pass + """, + ''' + import typing + + T = typing.TypeVar('T') + + class Foo(typing.Generic[T]): + """ + This is the docstring of Foo. + + And it has multiple lines. + """ + + bar: typing.Optional[T] + """ + This is the docstring of property `bar`. + """ + + def baz(self, t: T) -> T: + """ + This is the docstring of method `baz`. + """ + ... + + + def __init__(self, member: T) -> None: + ... + ''', + ) + + def test_adds_default_to_optional_types(self): + # Since PyO3 provides `impl FromPyObject for Option` and maps Python `None` to Rust `None`, + # you don't have to pass `None` explicitly. Type-stubs also shoudln't require `None`s + # to be passed explicitly (meaning they should have a default value). + + self.single_mod( + """ + def foo(bar, qux): + ''' + :param bar typing.Optional[int]: + :param qux typing.List[typing.Optional[int]]: + :rtype int: + ''' + pass + """, + """ + import typing + + def foo(bar: typing.Optional[int] = ..., qux: typing.List[typing.Optional[int]]) -> int: + ... + """, + ) + + def test_multiple_mods(self): + create_module( + "foo.bar", + """ + class Bar: + ''' + :param qux str: + :rtype None: + ''' + pass + """, + ) + + foo = create_module( + "foo", + """ + import sys + + bar = sys.modules["foo.bar"] + + class Foo: + ''' + :param a __root_module_name__.bar.Bar: + :param b typing.Optional[__root_module_name__.bar.Bar]: + :rtype None: + ''' + + @property + def a(self): + ''' + :type __root_module_name__.bar.Bar: + ''' + pass + + @property + def b(self): + ''' + :type typing.Optional[__root_module_name__.bar.Bar]: + ''' + pass + + __all__ = ["bar", "Foo"] + """, + ) + + with TemporaryDirectory() as temp_dir: + foo_path = Path(temp_dir) / "foo.pyi" + bar_path = Path(temp_dir) / "bar" / "__init__.pyi" + + writer = Writer(foo_path, "foo") + walk_module(writer, foo) + writer.dump() + + self.assert_stub( + foo_path, + """ + import foo.bar + import typing + + class Foo: + a: foo.bar.Bar + + b: typing.Optional[foo.bar.Bar] + + def __init__(self, a: foo.bar.Bar, b: typing.Optional[foo.bar.Bar] = ...) -> None: + ... + """, + ) + + self.assert_stub( + bar_path, + """ + class Bar: + def __init__(self, qux: str) -> None: + ... + """, + ) + + def single_mod(self, mod_code: str, expected_stub: str) -> None: + with TemporaryDirectory() as temp_dir: + mod = create_module("test", mod_code) + path = Path(temp_dir) / "test.pyi" + + writer = Writer(path, "test") + walk_module(writer, mod) + writer.dump() + + self.assert_stub(path, expected_stub) + + def assert_stub(self, path: Path, expected: str) -> None: + self.assertEqual(path.read_text().strip(), dedent(expected).strip()) + + +if __name__ == "__main__": + unittest.main() diff --git a/rust-runtime/aws-smithy-http-server-python/src/context.rs b/rust-runtime/aws-smithy-http-server-python/src/context.rs index 04f370a876..dbbd49f733 100644 --- a/rust-runtime/aws-smithy-http-server-python/src/context.rs +++ b/rust-runtime/aws-smithy-http-server-python/src/context.rs @@ -16,7 +16,6 @@ mod testing; /// PyContext is a wrapper for context object provided by the user. /// It injects some values (currently only [super::lambda::PyLambdaContext]) that is type-hinted by the user. /// -/// /// PyContext is initialised during the startup, it inspects the provided context object for fields /// that are type-hinted to inject some values provided by the framework (see [PyContext::new()]). /// 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 06e20e9b52..01596be507 100644 --- a/rust-runtime/aws-smithy-http-server-python/src/error.rs +++ b/rust-runtime/aws-smithy-http-server-python/src/error.rs @@ -39,12 +39,19 @@ impl From for PyErr { /// /// It allows to specify a message and HTTP status code and implementing protocol specific capabilities /// to build a [aws_smithy_http_server::response::Response] from it. +/// +/// :param message str: +/// :param status_code typing.Optional\[int\]: +/// :rtype None: #[pyclass(name = "MiddlewareException", extends = BasePyException)] -#[pyo3(text_signature = "(message, status_code)")] +#[pyo3(text_signature = "($self, message, status_code=None)")] #[derive(Debug, Clone)] pub struct PyMiddlewareException { + /// :type str: #[pyo3(get, set)] message: String, + + /// :type int: #[pyo3(get, set)] status_code: u16, } diff --git a/rust-runtime/aws-smithy-http-server-python/src/lambda.rs b/rust-runtime/aws-smithy-http-server-python/src/lambda.rs index 15bb9bf1b0..3823e8d93f 100644 --- a/rust-runtime/aws-smithy-http-server-python/src/lambda.rs +++ b/rust-runtime/aws-smithy-http-server-python/src/lambda.rs @@ -11,70 +11,111 @@ use lambda_http::Context; use pyo3::pyclass; /// AWS Mobile SDK client fields. -#[pyclass] +#[pyclass(name = "ClientApplication")] #[derive(Clone)] -struct PyClientApplication { +pub struct PyClientApplication { /// The mobile app installation id + /// + /// :type str: #[pyo3(get)] installation_id: String, + /// The app title for the mobile app as registered with AWS' mobile services. + /// + /// :type str: #[pyo3(get)] app_title: String, + /// The version name of the application as registered with AWS' mobile services. + /// + /// :type str: #[pyo3(get)] app_version_name: String, + /// The app version code. + /// + /// :type str: #[pyo3(get)] app_version_code: String, + /// The package name for the mobile application invoking the function + /// + /// :type str: #[pyo3(get)] app_package_name: String, } /// Client context sent by the AWS Mobile SDK. -#[pyclass] +#[pyclass(name = "ClientContext")] #[derive(Clone)] -struct PyClientContext { +pub struct PyClientContext { /// Information about the mobile application invoking the function. + /// + /// :type ClientApplication: #[pyo3(get)] client: PyClientApplication, + /// Custom properties attached to the mobile event context. + /// + /// :type typing.Dict[str, str]: #[pyo3(get)] custom: HashMap, + /// Environment settings from the mobile client. + /// + /// :type typing.Dict[str, str]: #[pyo3(get)] environment: HashMap, } /// Cognito identity information sent with the event -#[pyclass] +#[pyclass(name = "CognitoIdentity")] #[derive(Clone)] -struct PyCognitoIdentity { +pub struct PyCognitoIdentity { /// The unique identity id for the Cognito credentials invoking the function. + /// + /// :type str: #[pyo3(get)] identity_id: String, + /// The identity pool id the caller is "registered" with. + /// + /// :type str: #[pyo3(get)] identity_pool_id: String, } /// Configuration derived from environment variables. -#[pyclass] +#[pyclass(name = "Config")] #[derive(Clone)] -struct PyConfig { +pub struct PyConfig { /// The name of the function. + /// + /// :type str: #[pyo3(get)] function_name: String, + /// The amount of memory available to the function in MB. + /// + /// :type int: #[pyo3(get)] memory: i32, + /// The version of the function being executed. + /// + /// :type str: #[pyo3(get)] version: String, + /// The name of the Amazon CloudWatch Logs stream for the function. + /// + /// :type str: #[pyo3(get)] log_stream: String, + /// The name of the Amazon CloudWatch Logs group for the function. + /// + /// :type str: #[pyo3(get)] log_group: String, } @@ -86,29 +127,49 @@ struct PyConfig { #[pyclass(name = "LambdaContext")] pub struct PyLambdaContext { /// The AWS request ID generated by the Lambda service. + /// + /// :type str: #[pyo3(get)] request_id: String, + /// The execution deadline for the current invocation in milliseconds. + /// + /// :type int: #[pyo3(get)] deadline: u64, + /// The ARN of the Lambda function being invoked. + /// + /// :type str: #[pyo3(get)] invoked_function_arn: String, + /// The X-Ray trace ID for the current invocation. + /// + /// :type typing.Optional\[str\]: #[pyo3(get)] xray_trace_id: Option, + /// The client context object sent by the AWS mobile SDK. This field is /// empty unless the function is invoked using an AWS mobile SDK. + /// + /// :type typing.Optional\[ClientContext\]: #[pyo3(get)] client_context: Option, + /// The Cognito identity that invoked the function. This field is empty /// unless the invocation request to the Lambda APIs was made using AWS /// credentials issues by Amazon Cognito Identity Pools. + /// + /// :type typing.Optional\[CognitoIdentity\]: #[pyo3(get)] identity: Option, + /// Lambda function configuration from the local environment variables. /// Includes information such as the function name, memory allocation, /// version, and log streams. + /// + /// :type Config: #[pyo3(get)] env_config: PyConfig, } diff --git a/rust-runtime/aws-smithy-http-server-python/src/lib.rs b/rust-runtime/aws-smithy-http-server-python/src/lib.rs index b8efefe03f..17b7a96a03 100644 --- a/rust-runtime/aws-smithy-http-server-python/src/lib.rs +++ b/rust-runtime/aws-smithy-http-server-python/src/lib.rs @@ -3,6 +3,7 @@ * SPDX-License-Identifier: Apache-2.0 */ +#![allow(clippy::derive_partial_eq_without_eq)] #![cfg_attr(docsrs, feature(doc_cfg))] //! Rust/Python bindings, runtime and utilities. 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 2492096269..80d4966ac0 100644 --- a/rust-runtime/aws-smithy-http-server-python/src/logging.rs +++ b/rust-runtime/aws-smithy-http-server-python/src/logging.rs @@ -86,7 +86,12 @@ fn setup_tracing_subscriber( /// - A new builtin function `logging.py_tracing_event` transcodes `logging.LogRecord`s to `tracing::Event`s. This function /// is not exported in `logging.__all__`, as it is not intended to be called directly. /// - A new class `logging.TracingHandler` provides a `logging.Handler` that delivers all records to `python_tracing`. +/// +/// :param level typing.Optional\[int\]: +/// :param logfile typing.Optional\[pathlib.Path\]: +/// :rtype None: #[pyclass(name = "TracingHandler")] +#[pyo3(text_signature = "($self, level=None, logfile=None)")] #[derive(Debug)] pub struct PyTracingHandler { _guard: Option, @@ -104,6 +109,7 @@ impl PyTracingHandler { Ok(Self { _guard }) } + /// :rtype typing.Any: fn handler(&self, py: Python) -> PyResult> { let logging = py.import("logging")?; logging.setattr( 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 118b4e7cf8..506e5a6c20 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 @@ -253,6 +253,7 @@ fn simple_request(body: &'static str) -> Request { .expect("could not create request") } +#[allow(clippy::type_complexity)] fn spawn_service( layer: L, ) -> ( @@ -306,7 +307,7 @@ fn py_handler(code: &str) -> PyMiddlewareHandler { .get_item("middleware") .expect("your handler must be named `middleware`") .into(); - Ok::<_, PyErr>(PyMiddlewareHandler::new(py, handler)?) + PyMiddlewareHandler::new(py, handler) }) .unwrap() } diff --git a/rust-runtime/aws-smithy-http-server-python/src/middleware/request.rs b/rust-runtime/aws-smithy-http-server-python/src/middleware/request.rs index 16fc9d6d7f..d1eff86e00 100644 --- a/rust-runtime/aws-smithy-http-server-python/src/middleware/request.rs +++ b/rust-runtime/aws-smithy-http-server-python/src/middleware/request.rs @@ -17,7 +17,6 @@ use super::{PyHeaderMap, PyMiddlewareError}; /// Python-compatible [Request] object. #[pyclass(name = "Request")] -#[pyo3(text_signature = "(request)")] #[derive(Debug)] pub struct PyRequest { parts: Option, @@ -56,6 +55,8 @@ impl PyRequest { #[pymethods] impl PyRequest { /// Return the HTTP method of this request. + /// + /// :type str: #[getter] fn method(&self) -> PyResult { self.parts @@ -65,6 +66,8 @@ impl PyRequest { } /// Return the URI of this request. + /// + /// :type str: #[getter] fn uri(&self) -> PyResult { self.parts @@ -74,6 +77,8 @@ impl PyRequest { } /// Return the HTTP version of this request. + /// + /// :type str: #[getter] fn version(&self) -> PyResult { self.parts @@ -83,6 +88,8 @@ impl PyRequest { } /// Return the HTTP headers of this request. + /// + /// :type typing.MutableMapping[str, str]: #[getter] fn headers(&self) -> PyHeaderMap { self.headers.clone() @@ -90,6 +97,8 @@ impl PyRequest { /// Return the HTTP body of this request. /// Note that this is a costly operation because the whole request body is cloned. + /// + /// :type typing.Awaitable[bytes]: #[getter] fn body<'p>(&self, py: Python<'p>) -> PyResult<&'p PyAny> { let body = self.body.clone(); diff --git a/rust-runtime/aws-smithy-http-server-python/src/middleware/response.rs b/rust-runtime/aws-smithy-http-server-python/src/middleware/response.rs index 5e3619cb11..2e97af5e49 100644 --- a/rust-runtime/aws-smithy-http-server-python/src/middleware/response.rs +++ b/rust-runtime/aws-smithy-http-server-python/src/middleware/response.rs @@ -17,8 +17,13 @@ use tokio::sync::Mutex; use super::{PyHeaderMap, PyMiddlewareError}; /// Python-compatible [Response] object. +/// +/// :param status int: +/// :param headers typing.Optional[typing.Dict[str, str]]: +/// :param body typing.Optional[bytes]: +/// :rtype None: #[pyclass(name = "Response")] -#[pyo3(text_signature = "(status, headers, body)")] +#[pyo3(text_signature = "($self, status, headers=None, body=None)")] pub struct PyResponse { parts: Option, headers: PyHeaderMap, @@ -78,6 +83,8 @@ impl PyResponse { } /// Return the HTTP status of this response. + /// + /// :type int: #[getter] fn status(&self) -> PyResult { self.parts @@ -87,6 +94,8 @@ impl PyResponse { } /// Return the HTTP version of this response. + /// + /// :type str: #[getter] fn version(&self) -> PyResult { self.parts @@ -96,6 +105,8 @@ impl PyResponse { } /// Return the HTTP headers of this response. + /// + /// :type typing.MutableMapping[str, str]: #[getter] fn headers(&self) -> PyHeaderMap { self.headers.clone() @@ -103,6 +114,8 @@ impl PyResponse { /// Return the HTTP body of this response. /// Note that this is a costly operation because the whole response body is cloned. + /// + /// :type typing.Awaitable[bytes]: #[getter] fn body<'p>(&self, py: Python<'p>) -> PyResult<&'p PyAny> { let body = self.body.clone(); diff --git a/rust-runtime/aws-smithy-http-server-python/src/pytests/bytestream.rs b/rust-runtime/aws-smithy-http-server-python/src/pytests/bytestream.rs new file mode 100644 index 0000000000..30cc997e76 --- /dev/null +++ b/rust-runtime/aws-smithy-http-server-python/src/pytests/bytestream.rs @@ -0,0 +1,151 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +use std::io; + +use futures::StreamExt; +use futures_util::stream; +use hyper::Body; +use pyo3::{prelude::*, py_run}; + +use aws_smithy_http::body::SdkBody; +use aws_smithy_http_server_python::types::ByteStream; + +#[pyo3_asyncio::tokio::test] +fn consuming_stream_on_python_synchronously() -> PyResult<()> { + let bytestream = streaming_bytestream_from_vec(vec!["hello", " ", "world"]); + Python::with_gil(|py| { + let bytestream = bytestream.into_py(py); + py_run!( + py, + bytestream, + r#" +assert next(bytestream) == b"hello" +assert next(bytestream) == b" " +assert next(bytestream) == b"world" + +try: + next(bytestream) + assert False, "iteration should stop by now" +except StopIteration: + pass +"# + ); + Ok(()) + }) +} + +#[pyo3_asyncio::tokio::test] +fn consuming_stream_on_python_synchronously_with_loop() -> PyResult<()> { + let bytestream = streaming_bytestream_from_vec(vec!["hello", " ", "world"]); + Python::with_gil(|py| { + let bytestream = bytestream.into_py(py); + py_run!( + py, + bytestream, + r#" +total = [] +for chunk in bytestream: + total.append(chunk) + +assert total == [b"hello", b" ", b"world"] +"# + ); + Ok(()) + }) +} + +#[pyo3_asyncio::tokio::test] +fn consuming_stream_on_python_asynchronously() -> PyResult<()> { + let bytestream = streaming_bytestream_from_vec(vec!["hello", " ", "world"]); + Python::with_gil(|py| { + let bytestream = bytestream.into_py(py); + py_run!( + py, + bytestream, + r#" +import asyncio + +async def main(bytestream): + assert await bytestream.__anext__() == b"hello" + assert await bytestream.__anext__() == b" " + assert await bytestream.__anext__() == b"world" + + try: + await bytestream.__anext__() + assert False, "iteration should stop by now" + except StopAsyncIteration: + pass + +asyncio.run(main(bytestream)) +"# + ); + Ok(()) + }) +} + +#[pyo3_asyncio::tokio::test] +fn consuming_stream_on_python_asynchronously_with_loop() -> PyResult<()> { + let bytestream = streaming_bytestream_from_vec(vec!["hello", " ", "world"]); + Python::with_gil(|py| { + let bytestream = bytestream.into_py(py); + py_run!( + py, + bytestream, + r#" +import asyncio + +async def main(bytestream): + total = [] + async for chunk in bytestream: + total.append(chunk) + assert total == [b"hello", b" ", b"world"] + +asyncio.run(main(bytestream)) +"# + ); + Ok(()) + }) +} + +#[pyo3_asyncio::tokio::test] +async fn streaming_back_to_rust_from_python() -> PyResult<()> { + let bytestream = streaming_bytestream_from_vec(vec!["hello", " ", "world"]); + let py_stream = Python::with_gil(|py| { + let module = PyModule::from_code( + py, + r#" +async def handler(bytestream): + async for chunk in bytestream: + yield "🐍 " + chunk.decode("utf-8") + yield "Hello from Python!" +"#, + "", + "", + )?; + let handler = module.getattr("handler")?; + let output = handler.call1((bytestream,))?; + Ok::<_, PyErr>(pyo3_asyncio::tokio::into_stream_v2(output)) + })??; + + let mut py_stream = py_stream.map(|v| Python::with_gil(|py| v.extract::(py).unwrap())); + + assert_eq!(py_stream.next().await, Some("🐍 hello".to_string())); + assert_eq!(py_stream.next().await, Some("🐍 ".to_string())); + assert_eq!(py_stream.next().await, Some("🐍 world".to_string())); + assert_eq!( + py_stream.next().await, + Some("Hello from Python!".to_string()) + ); + assert_eq!(py_stream.next().await, None); + + Ok(()) +} + +fn streaming_bytestream_from_vec(chunks: Vec<&'static str>) -> ByteStream { + let stream = stream::iter(chunks.into_iter().map(Ok::<_, io::Error>)); + let body = Body::wrap_stream(stream); + ByteStream::new(SdkBody::from(body)) +} diff --git a/rust-runtime/aws-smithy-http-server-python/src/pytests/harness.rs b/rust-runtime/aws-smithy-http-server-python/src/pytests/harness.rs new file mode 100644 index 0000000000..069e4e987f --- /dev/null +++ b/rust-runtime/aws-smithy-http-server-python/src/pytests/harness.rs @@ -0,0 +1,11 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +#[pyo3_asyncio::tokio::main] +async fn main() -> pyo3::PyResult<()> { + pyo3_asyncio::testing::main().await +} + +mod bytestream; diff --git a/rust-runtime/aws-smithy-http-server-python/src/socket.rs b/rust-runtime/aws-smithy-http-server-python/src/socket.rs index 8243aa28c2..13900ff8c8 100644 --- a/rust-runtime/aws-smithy-http-server-python/src/socket.rs +++ b/rust-runtime/aws-smithy-http-server-python/src/socket.rs @@ -20,7 +20,12 @@ use std::net::SocketAddr; /// computing capacity of the host. /// /// [GIL]: https://wiki.python.org/moin/GlobalInterpreterLock -#[pyclass] +/// +/// :param address str: +/// :param port int: +/// :param backlog typing.Optional\[int\]: +/// :rtype None: +#[pyclass(text_signature = "($self, address, port, backlog=None)")] #[derive(Debug)] pub struct PySocket { pub(crate) inner: Socket, @@ -49,7 +54,8 @@ impl PySocket { /// Clone the inner socket allowing it to be shared between multiple /// Python processes. - #[pyo3(text_signature = "($self, socket, worker_number)")] + /// + /// :rtype PySocket: pub fn try_clone(&self) -> PyResult { let copied = self.inner.try_clone()?; Ok(PySocket { inner: copied }) 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 0c09a224e8..538508fcec 100644 --- a/rust-runtime/aws-smithy-http-server-python/src/tls.rs +++ b/rust-runtime/aws-smithy-http-server-python/src/tls.rs @@ -20,19 +20,30 @@ use tokio_rustls::rustls::{Certificate, Error as RustTlsError, PrivateKey, Serve pub mod listener; /// PyTlsConfig represents TLS configuration created from Python. +/// +/// :param key_path pathlib.Path: +/// :param cert_path pathlib.Path: +/// :param reload_secs int: +/// :rtype None: #[pyclass( name = "TlsConfig", - text_signature = "(*, key_path, cert_path, reload)" + text_signature = "($self, *, key_path, cert_path, reload_secs=86400)" )] #[derive(Clone)] pub struct PyTlsConfig { /// Absolute path of the RSA or PKCS private key. + /// + /// :type pathlib.Path: key_path: PathBuf, /// Absolute path of the x509 certificate. + /// + /// :type pathlib.Path: cert_path: PathBuf, /// Duration to reloading certificates. + /// + /// :type int: reload_secs: u64, } 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 345e0e24ab..fb4f759f89 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 @@ -214,7 +214,7 @@ mod tests { fn client_config_with_cert(cert: &rcgen::Certificate) -> ClientConfig { let mut roots = RootCertStore::empty(); - roots.add_parsable_certificates(&vec![cert.serialize_der().unwrap()]); + roots.add_parsable_certificates(&[cert.serialize_der().unwrap()]); ClientConfig::builder() .with_safe_defaults() .with_root_certificates(roots) @@ -223,7 +223,7 @@ mod tests { fn cert_with_invalid_date() -> rcgen::Certificate { let mut params = rcgen::CertificateParams::new(vec!["localhost".to_string()]); - params.not_after = rcgen::date_time_ymd(1970, 01, 01); + params.not_after = rcgen::date_time_ymd(1970, 1, 1); rcgen::Certificate::from_params(params).unwrap() } diff --git a/rust-runtime/aws-smithy-http-server-python/src/types.rs b/rust-runtime/aws-smithy-http-server-python/src/types.rs index b42ced51a5..a2fa308512 100644 --- a/rust-runtime/aws-smithy-http-server-python/src/types.rs +++ b/rust-runtime/aws-smithy-http-server-python/src/types.rs @@ -4,6 +4,16 @@ */ //! Python wrapped types from aws-smithy-types and aws-smithy-http. +//! +//! ## `Deref` hacks for Json serializer +//! [aws_smithy_json::serialize::JsonValueWriter] expects references to the types +//! from [aws_smithy_types] (for example [aws_smithy_json::serialize::JsonValueWriter::document()] +//! expects `&aws_smithy_types::Document`). In order to make +//! [aws_smithy_json::serialize::JsonValueWriter] happy, we implement `Deref` traits for +//! Python types to their Rust counterparts (for example +//! `impl Deref for Document` and that allows `&Document` to +//! get coerced to `&aws_smithy_types::Document`). This is a hack, we should ideally handle this +//! in `JsonSerializerGenerator.kt` but it's not easy to do it with our current Kotlin structure. use std::{ collections::HashMap, @@ -16,17 +26,19 @@ use std::{ use bytes::Bytes; use pyo3::{ - exceptions::{PyRuntimeError, PyStopIteration, PyTypeError}, + exceptions::{PyRuntimeError, PyStopAsyncIteration, PyTypeError}, iter::IterNextOutput, prelude::*, - pyclass::IterANextOutput, }; -use tokio::sync::Mutex; +use tokio::{runtime::Handle, sync::Mutex}; use tokio_stream::StreamExt; use crate::PyError; /// Python Wrapper for [aws_smithy_types::Blob]. +/// +/// :param input bytes: +/// :rtype None: #[pyclass] #[derive(Debug, Clone, PartialEq, Eq, Hash)] pub struct Blob(aws_smithy_types::Blob); @@ -58,6 +70,8 @@ impl Blob { } /// Python getter for the `Blob` byte array. + /// + /// :type bytes: #[getter(data)] pub fn get_data(&self) -> &[u8] { self.as_ref() @@ -134,6 +148,9 @@ impl DateTime { #[pymethods] impl DateTime { /// Creates a `DateTime` from a number of seconds since the Unix epoch. + /// + /// :param epoch_seconds int: + /// :rtype DateTime: #[staticmethod] pub fn from_secs(epoch_seconds: i64) -> Self { Self(aws_smithy_types::date_time::DateTime::from_secs( @@ -142,6 +159,9 @@ impl DateTime { } /// Creates a `DateTime` from a number of milliseconds since the Unix epoch. + /// + /// :param epoch_millis int: + /// :rtype DateTime: #[staticmethod] pub fn from_millis(epoch_millis: i64) -> Self { Self(aws_smithy_types::date_time::DateTime::from_secs( @@ -150,6 +170,9 @@ impl DateTime { } /// Creates a `DateTime` from a number of nanoseconds since the Unix epoch. + /// + /// :param epoch_nanos int: + /// :rtype DateTime: #[staticmethod] pub fn from_nanos(epoch_nanos: i128) -> PyResult { Ok(Self( @@ -159,6 +182,12 @@ impl DateTime { } /// Read 1 date of `format` from `s`, expecting either `delim` or EOF. + /// + /// TODO(PythonTyping): How do we represent `char` in Python? + /// + /// :param format Format: + /// :param delim str: + /// :rtype typing.Tuple[DateTime, str]: #[staticmethod] pub fn read(s: &str, format: Format, delim: char) -> PyResult<(Self, &str)> { let (self_, next) = aws_smithy_types::date_time::DateTime::read(s, format.into(), delim) @@ -167,6 +196,10 @@ impl DateTime { } /// Creates a `DateTime` from a number of seconds and a fractional second since the Unix epoch. + /// + /// :param epoch_seconds int: + /// :param fraction float: + /// :rtype DateTime: #[staticmethod] pub fn from_fractional_secs(epoch_seconds: i64, fraction: f64) -> Self { Self(aws_smithy_types::date_time::DateTime::from_fractional_secs( @@ -176,6 +209,10 @@ impl DateTime { } /// Creates a `DateTime` from a number of seconds and sub-second nanos since the Unix epoch. + /// + /// :param seconds int: + /// :param subsecond_nanos int: + /// :rtype DateTime: #[staticmethod] pub fn from_secs_and_nanos(seconds: i64, subsecond_nanos: u32) -> Self { Self(aws_smithy_types::date_time::DateTime::from_secs_and_nanos( @@ -185,6 +222,9 @@ impl DateTime { } /// Creates a `DateTime` from an `f64` representing the number of seconds since the Unix epoch. + /// + /// :param epoch_seconds float: + /// :rtype DateTime: #[staticmethod] pub fn from_secs_f64(epoch_seconds: f64) -> Self { Self(aws_smithy_types::date_time::DateTime::from_secs_f64( @@ -193,6 +233,10 @@ impl DateTime { } /// Parses a `DateTime` from a string using the given `format`. + /// + /// :param s str: + /// :param format Format: + /// :rtype DateTime: #[staticmethod] pub fn from_str(s: &str, format: Format) -> PyResult { Ok(Self( @@ -202,31 +246,43 @@ impl DateTime { } /// Returns the number of nanoseconds since the Unix epoch that this `DateTime` represents. + /// + /// :rtype int: pub fn as_nanos(&self) -> i128 { self.0.as_nanos() } /// Returns the `DateTime` value as an `f64` representing the seconds since the Unix epoch. + /// + /// :rtype float: pub fn as_secs_f64(&self) -> f64 { self.0.as_secs_f64() } /// Returns true if sub-second nanos is greater than zero. + /// + /// :rtype bool: pub fn has_subsec_nanos(&self) -> bool { self.0.has_subsec_nanos() } /// Returns the epoch seconds component of the `DateTime`. + /// + /// :rtype int: pub fn secs(&self) -> i64 { self.0.secs() } /// Returns the sub-second nanos component of the `DateTime`. + /// + /// :rtype int: pub fn subsec_nanos(&self) -> u32 { self.0.subsec_nanos() } /// Converts the `DateTime` to the number of milliseconds since the Unix epoch. + /// + /// :rtype int: pub fn to_millis(&self) -> PyResult { Ok(self.0.to_millis().map_err(PyError::DateTimeConversion)?) } @@ -238,15 +294,11 @@ impl From for DateTime { } } -impl From for aws_smithy_types::DateTime { - fn from(other: DateTime) -> aws_smithy_types::DateTime { - other.0 - } -} +impl Deref for DateTime { + type Target = aws_smithy_types::DateTime; -impl<'date> From<&'date DateTime> for &'date aws_smithy_types::DateTime { - fn from(other: &'date DateTime) -> &'date aws_smithy_types::DateTime { - &other.0 + fn deref(&self) -> &Self::Target { + &self.0 } } @@ -283,6 +335,9 @@ impl<'date> From<&'date DateTime> for &'date aws_smithy_types::DateTime { /// /// The original Rust [ByteStream](aws_smithy_http::byte_stream::ByteStream) is wrapped inside a `Arc` to allow the type to be /// [Clone] (required by PyO3) and to allow internal mutability, required to fetch the next chunk of data. +/// +/// :param input bytes: +/// :rtype None: #[pyclass] #[derive(Debug, Clone)] pub struct ByteStream(Arc>); @@ -303,12 +358,13 @@ impl futures::stream::Stream for ByteStream { /// Return a new data chunk from the stream. async fn yield_data_chunk( body: Arc>, -) -> PyResult { +) -> PyResult> { let mut stream = body.lock().await; - match stream.next().await { - Some(bytes) => bytes.map_err(|e| PyRuntimeError::new_err(e.to_string())), - None => Err(PyStopIteration::new_err("stream exhausted")), - } + stream + .next() + .await + .transpose() + .map_err(|e| PyRuntimeError::new_err(e.to_string())) } impl ByteStream { @@ -330,7 +386,6 @@ impl Default for ByteStream { } } -/// ByteStream Abstractions. #[pymethods] impl ByteStream { /// Create a new [ByteStream](aws_smithy_http::byte_stream::ByteStream) from a slice of bytes. @@ -347,9 +402,12 @@ impl ByteStream { /// requiring Python to await this method. /// /// **NOTE:** This method will block the Rust event loop when it is running. + /// + /// :param path str: + /// :rtype ByteStream: #[staticmethod] pub fn from_path_blocking(py: Python, path: String) -> PyResult> { - let byte_stream = futures::executor::block_on(async { + let byte_stream = Handle::current().block_on(async { aws_smithy_http::byte_stream::ByteStream::from_path(path) .await .map_err(|e| PyRuntimeError::new_err(e.to_string())) @@ -360,6 +418,9 @@ impl ByteStream { /// Create a new [ByteStream](aws_smithy_http::byte_stream::ByteStream) from a path, forcing /// Python to await this coroutine. + /// + /// :param path str: + /// :rtype typing.Awaitable[ByteStream]: #[staticmethod] pub fn from_path(py: Python, path: String) -> PyResult<&PyAny> { pyo3_asyncio::tokio::future_into_py(py, async move { @@ -388,14 +449,9 @@ impl ByteStream { let body = slf.0.clone(); let data_chunk = futures::executor::block_on(yield_data_chunk(body)); match data_chunk { - Ok(data_chunk) => Ok(IterNextOutput::Yield(data_chunk.into_py(slf.py()))), - Err(e) => { - if e.is_instance_of::(slf.py()) { - Ok(IterNextOutput::Return(slf.py().None())) - } else { - Err(e) - } - } + Ok(Some(data_chunk)) => Ok(IterNextOutput::Yield(data_chunk.into_py(slf.py()))), + Ok(None) => Ok(IterNextOutput::Return(slf.py().None())), + Err(e) => Err(e), } } @@ -407,29 +463,30 @@ impl ByteStream { } /// Return an awaitable resulting in a next value of the iterator or raise a StopAsyncIteration - /// exception when the iteration is over. PyO3 allows to raise the correct exception using the enum - /// [IterANextOutput](pyo3::pyclass::IterANextOutput). + /// exception when the iteration is over. /// /// To get the next value of the iterator, the `Arc` inner stream is cloned and the Rust call /// to `next()` is converted into an awaitable Python coroutine. /// /// More info: `` - pub fn __anext__(slf: PyRefMut) -> PyResult, PyObject>> { + /// + /// About the return type, we cannot use `IterANextOutput` because we don't know if we + /// have a next value or not until we call the `next` on the underlying stream which is + /// an async operation and it's awaited on the Python side. So we're returning + /// `StopAsyncIteration` inside the returned future lazily. + /// The reason for the extra `Option` wrapper is that PyO3 expects `__anext__` to return + /// either `Option` or `IterANextOutput` and fails to compile otherwise, so we're + /// using extra `Option` just to make PyO3 happy. + pub fn __anext__(slf: PyRefMut) -> PyResult> { let body = slf.0.clone(); - let data_chunk = pyo3_asyncio::tokio::local_future_into_py(slf.py(), async move { + let fut = pyo3_asyncio::tokio::future_into_py(slf.py(), async move { let data = yield_data_chunk(body).await?; - Ok(Python::with_gil(|py| data.into_py(py))) - }); - match data_chunk { - Ok(data_chunk) => Ok(IterANextOutput::Yield(data_chunk.into_py(slf.py()))), - Err(e) => { - if e.is_instance_of::(slf.py()) { - Ok(IterANextOutput::Return(slf.py().None())) - } else { - Err(e) - } + match data { + Some(data) => Ok(Python::with_gil(|py| data.into_py(py))), + None => Err(PyStopAsyncIteration::new_err("stream exhausted")), } - } + })?; + Ok(Some(fut.into())) } } @@ -494,10 +551,6 @@ impl FromPyObject<'_> for Document { } } -// TODO(PythonSerialization): Get rid of this hack. -// `JsonValueWriter::document` expects `&aws_smithy_types::Document` -// and this impl allows `&Document` to get coerced to `&aws_smithy_types::Document`. -// We should ideally handle this in `JsonSerializerGenerator.kt` but I'm not sure how hard it is. impl Deref for Document { type Target = aws_smithy_types::Document; @@ -654,10 +707,10 @@ mod tests { .into(), ), "{ - 't': True, - 'foo': 'foo', - 'f42': 42.0, - 'i42': 42, + 't': True, + 'foo': 'foo', + 'f42': 42.0, + 'i42': 42, 'f': False, 'vec': [ 'inner', diff --git a/rust-runtime/aws-smithy-http-server-python/src/util.rs b/rust-runtime/aws-smithy-http-server-python/src/util.rs index b420c26fd4..4cac7f06d2 100644 --- a/rust-runtime/aws-smithy-http-server-python/src/util.rs +++ b/rust-runtime/aws-smithy-http-server-python/src/util.rs @@ -43,7 +43,6 @@ fn is_coroutine(py: Python, func: &PyObject) -> PyResult { } // Checks whether given Python type is `Optional[T]`. -#[allow(unused)] pub fn is_optional_of(py: Python, ty: &PyAny) -> PyResult { // for reference: https://stackoverflow.com/a/56833826 @@ -131,6 +130,7 @@ async def async_func(): }) } + #[allow(clippy::bool_assert_comparison)] #[test] fn check_if_is_optional_of() -> PyResult<()> { pyo3::prepare_freethreaded_python(); diff --git a/rust-runtime/aws-smithy-http-server/Cargo.toml b/rust-runtime/aws-smithy-http-server/Cargo.toml index b58e75bd89..e5226e910a 100644 --- a/rust-runtime/aws-smithy-http-server/Cargo.toml +++ b/rust-runtime/aws-smithy-http-server/Cargo.toml @@ -24,7 +24,7 @@ aws-smithy-json = { path = "../aws-smithy-json" } aws-smithy-xml = { path = "../aws-smithy-xml" } async-trait = "0.1" bytes = "1.1" -futures-util = { version = "0.3", default-features = false } +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"] } @@ -38,7 +38,7 @@ serde_urlencoded = "0.7" strum_macros = "0.24" thiserror = "1.0.0" tracing = "0.1.35" -tokio = { version = "1.8.4", features = ["full"] } +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"] } uuid = { version = "1", features = ["v4", "fast-rng"], optional = true } diff --git a/rust-runtime/aws-smithy-http-server/examples/README.md b/rust-runtime/aws-smithy-http-server/examples/README.md deleted file mode 100644 index f3dd3e4b5f..0000000000 --- a/rust-runtime/aws-smithy-http-server/examples/README.md +++ /dev/null @@ -1,32 +0,0 @@ -# Smithy Rust Server SDK example - -This folder contains an example service called Pokémon Service used to showcase -the service framework capabilities and to run benchmarks. - -## 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` 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. - -## 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 service and run some simple integration -tests against it. - -More info can be found in the `tests` folder of `pokemon-service` package. - -## Benchmarks - -Please see [BENCHMARKS.md](/rust-runtime/aws-smithy-http-server/examples/BENCHMARKS.md). diff --git a/rust-runtime/aws-smithy-http-server/examples/pokemon-service/Cargo.toml b/rust-runtime/aws-smithy-http-server/examples/pokemon-service/Cargo.toml deleted file mode 100644 index da4cb284c0..0000000000 --- a/rust-runtime/aws-smithy-http-server/examples/pokemon-service/Cargo.toml +++ /dev/null @@ -1,63 +0,0 @@ -[package] -name = "pokemon-service" -version = "0.1.0" -edition = "2021" -publish = false -authors = ["Smithy-rs Server Team "] -description = "A smithy Rust service to retrieve information about Pokémon." -default-run = "pokemon-service" - -[[bin]] -name = "pokemon-service" -path = "src/bin/pokemon-service.rs" - -[[bin]] -name = "pokemon-service-tls" -path = "src/bin/pokemon-service-tls.rs" - -[[bin]] -name = "pokemon-service-lambda" -path = "src/bin/pokemon-service-lambda.rs" - -[[bin]] -name = "pokemon-service-connect-info" -path = "src/bin/pokemon-service-connect-info.rs" - -[dependencies] -async-stream = "0.3" -clap = { version = "~3.2.1", features = ["derive"] } -hyper = {version = "0.14.12", features = ["server"] } -rand = "0.8" -tokio = "1.20.1" -tower = "0.4" -tower-http = { version = "0.3", features = ["trace"] } -tracing = "0.1" -tracing-subscriber = { version = "0.3.15", features = ["env-filter", "json"] } - -# These dependencies are only required for the `pokemon-service-tls` program. -tls-listener = { version = "0.5.1", features = ["rustls", "hyper-h2"] } -tokio-rustls = "0.23.4" -rustls-pemfile = "1.0.1" -futures-util = "0.3" - -# This dependency is only required for the `pokemon-service-lambda` program. -lambda_http = "0.7.1" - -# Local paths -aws-smithy-http-server = { path = "../../", features = ["aws-lambda", "request-id"] } -pokemon-service-server-sdk = { path = "../pokemon-service-server-sdk/" } - -[dev-dependencies] -assert_cmd = "2.0" -home = "0.5" -serial_test = "0.7.0" -wrk-api-bench = "0.0.8" - -# This dependency is only required for testing the `pokemon-service-tls` program. -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/examples/pokemon-service/src/bin/pokemon-service-connect-info.rs b/rust-runtime/aws-smithy-http-server/examples/pokemon-service/src/bin/pokemon-service-connect-info.rs deleted file mode 100644 index 545b09a307..0000000000 --- a/rust-runtime/aws-smithy-http-server/examples/pokemon-service/src/bin/pokemon-service-connect-info.rs +++ /dev/null @@ -1,95 +0,0 @@ -/* - * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. - * SPDX-License-Identifier: Apache-2.0 - */ - -use std::net::{IpAddr, SocketAddr}; - -use aws_smithy_http_server::{ - request::connect_info::ConnectInfo, request::request_id::ServerRequestId, - request::request_id::ServerRequestIdProviderLayer, -}; -use clap::Parser; -use pokemon_service::{capture_pokemon, check_health, get_pokemon_species, get_server_statistics, setup_tracing}; -use pokemon_service_server_sdk::{ - error::{GetStorageError, StorageAccessNotAuthorized}, - input::{DoNothingInput, GetStorageInput}, - output::{DoNothingOutput, GetStorageOutput}, - PokemonService, -}; - -#[derive(Parser, Debug)] -#[clap(author, version, about, long_about = None)] -struct Args { - /// Hyper server bind address. - #[clap(short, long, action, default_value = "127.0.0.1")] - address: String, - /// Hyper server bind port. - #[clap(short, long, action, default_value = "13734")] - port: u16, -} - -/// Retrieves the user's storage. No authentication required for locals. -pub async fn get_storage_with_local_approved( - input: GetStorageInput, - connect_info: ConnectInfo, -) -> Result { - tracing::debug!("attempting to authenticate storage user"); - let local = connect_info.0.ip() == "127.0.0.1".parse::().unwrap(); - - // We currently support Ash: he has nothing stored - if input.user == "ash" && input.passcode == "pikachu123" { - return Ok(GetStorageOutput { collection: vec![] }); - } - // We support trainers in our gym - if local { - tracing::info!("welcome back"); - return Ok(GetStorageOutput { - collection: vec![ - String::from("bulbasaur"), - String::from("charmander"), - String::from("squirtle"), - ], - }); - } - tracing::debug!("authentication failed"); - Err(GetStorageError::StorageAccessNotAuthorized( - StorageAccessNotAuthorized {}, - )) -} - -pub async fn do_nothing_but_log_request_ids( - _input: DoNothingInput, - server_request_id: ServerRequestId, -) -> DoNothingOutput { - tracing::debug!("This request has this server ID: {}", server_request_id); - DoNothingOutput {} -} - -#[tokio::main] -async fn main() { - let args = Args::parse(); - setup_tracing(); - let app = PokemonService::builder_without_plugins() - .get_pokemon_species(get_pokemon_species) - .get_storage(get_storage_with_local_approved) - .get_server_statistics(get_server_statistics) - .capture_pokemon(capture_pokemon) - .do_nothing(do_nothing_but_log_request_ids) - .check_health(check_health) - .build() - .expect("failed to build an instance of PokemonService"); - - let app = app.layer(&ServerRequestIdProviderLayer::new()); - - // Start the [`hyper::Server`]. - let bind: SocketAddr = format!("{}:{}", args.address, args.port) - .parse() - .expect("unable to parse the server bind address and port"); - let server = hyper::Server::bind(&bind).serve(app.into_make_service_with_connect_info::()); - - // Run forever-ish... - if let Err(err) = server.await { - eprintln!("server error: {}", err); - } -} diff --git a/rust-runtime/aws-smithy-http-server/examples/pokemon-service/src/bin/pokemon-service-lambda.rs b/rust-runtime/aws-smithy-http-server/examples/pokemon-service/src/bin/pokemon-service-lambda.rs deleted file mode 100644 index 78b1bff621..0000000000 --- a/rust-runtime/aws-smithy-http-server/examples/pokemon-service/src/bin/pokemon-service-lambda.rs +++ /dev/null @@ -1,62 +0,0 @@ -/* - * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. - * SPDX-License-Identifier: Apache-2.0 - */ - -// This program is exported as a binary named `pokemon-service-lambda`. -use std::sync::Arc; - -use aws_smithy_http_server::{ - plugin::PluginPipeline, request::lambda::Context, routing::LambdaHandler, AddExtensionLayer, Extension, -}; -use pokemon_service::{ - capture_pokemon, check_health, do_nothing, get_pokemon_species, get_server_statistics, plugin::PrintExt, - setup_tracing, State, -}; -use pokemon_service_server_sdk::{error, input, output, PokemonService}; - -/// Retrieves the user's storage and records the . -pub async fn get_storage_lambda( - input: input::GetStorageInput, - _state: Extension>, - context: Context, -) -> Result { - tracing::debug!(request_id = %context.request_id, "attempting to authenticate storage user"); - - // We currently only support Ash and he has nothing stored - if !(input.user == "ash" && input.passcode == "pikachu123") { - tracing::debug!("authentication failed"); - return Err(error::GetStorageError::StorageAccessNotAuthorized( - error::StorageAccessNotAuthorized {}, - )); - } - Ok(output::GetStorageOutput { collection: vec![] }) -} - -#[tokio::main] -pub async fn main() { - setup_tracing(); - // Apply the `PrintPlugin` defined in `plugin.rs` - let plugins = PluginPipeline::new().print(); - let app = PokemonService::builder_with_plugins(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 - // return the operation's output. - .get_pokemon_species(get_pokemon_species) - .get_storage(get_storage_lambda) - .get_server_statistics(get_server_statistics) - .capture_pokemon(capture_pokemon) - .do_nothing(do_nothing) - .check_health(check_health) - .build() - .expect("failed to build an instance of PokemonService") - // Set up shared state and middlewares. - .layer(&AddExtensionLayer::new(Arc::new(State::default()))); - - let handler = LambdaHandler::new(app); - let lambda = lambda_http::run(handler); - - if let Err(err) = lambda.await { - eprintln!("lambda error: {}", err); - } -} diff --git a/rust-runtime/aws-smithy-http-server/examples/pokemon-service/tests/benchmark.rs b/rust-runtime/aws-smithy-http-server/examples/pokemon-service/tests/benchmark.rs deleted file mode 100644 index 4499854d79..0000000000 --- a/rust-runtime/aws-smithy-http-server/examples/pokemon-service/tests/benchmark.rs +++ /dev/null @@ -1,58 +0,0 @@ -/* - * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. - * SPDX-License-Identifier: Apache-2.0 - */ - -use std::{env, fs::OpenOptions, io::Write, path::Path, time::Duration}; - -use tokio::time; -use wrk_api_bench::{BenchmarkBuilder, HistoryPeriod, WrkBuilder}; - -use crate::helpers::PokemonService; - -mod helpers; - -#[tokio::test] -async fn benchmark() -> Result<(), Box> { - // Benchmarks are expensive, so they run only if the environment - // variable `RUN_BENCHMARKS` is present. - if env::var_os("RUN_BENCHMARKS").is_some() { - let _program = PokemonService::run(); - // Give PokémonService some time to start up. - time::sleep(Duration::from_millis(50)).await; - - // The history directory is cached inside GitHub actions under - // the running use home directory to allow us to recover historical - // data between runs. - let history_dir = if env::var_os("GITHUB_ACTIONS").is_some() { - home::home_dir().unwrap().join(".wrk-api-bench") - } else { - Path::new(".").join(".wrk-api-bench") - }; - - let mut wrk = WrkBuilder::default() - .url(String::from("http://localhost:13734/empty-operation")) - .history_dir(history_dir) - .build()?; - - // Run a single benchmark with 8 threads and 64 connections for 60 seconds. - let benches = vec![BenchmarkBuilder::default() - .duration(Duration::from_secs(90)) - .threads(2) - .connections(32) - .build()?]; - wrk.bench(&benches)?; - - // Calculate deviation from last run and write it to disk. - if let Ok(deviation) = wrk.deviation(HistoryPeriod::Last) { - let mut deviation_file = OpenOptions::new() - .create(true) - .write(true) - .truncate(true) - .open("/tmp/smithy_rs_benchmark_deviation.txt") - .unwrap(); - deviation_file.write_all(deviation.to_github_markdown().as_bytes())?; - } - } - Ok(()) -} diff --git a/rust-runtime/aws-smithy-http-server/examples/pokemon-service/tests/helpers.rs b/rust-runtime/aws-smithy-http-server/examples/pokemon-service/tests/helpers.rs deleted file mode 100644 index 1d632a27ed..0000000000 --- a/rust-runtime/aws-smithy-http-server/examples/pokemon-service/tests/helpers.rs +++ /dev/null @@ -1,132 +0,0 @@ -/* - * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. - * SPDX-License-Identifier: Apache-2.0 - */ - -use std::fs::File; -use std::io::BufReader; -use std::process::{Child, Command}; -use std::time::Duration; - -use assert_cmd::prelude::*; -use aws_smithy_client::{erase::DynConnector, hyper_ext::Adapter}; -use aws_smithy_http::operation::Request; -use pokemon_service_client::{Builder, 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"); - -enum PokemonServiceVariant { - Http, - Https, -} - -impl PokemonServiceVariant { - async fn run_process(&self) -> Child { - let process = match self { - PokemonServiceVariant::Http => Command::cargo_bin("pokemon-service").unwrap().spawn().unwrap(), - PokemonServiceVariant::Https => Command::cargo_bin("pokemon-service-tls") - .unwrap() - .args(["--tls-cert-path", TEST_CERT, "--tls-key-path", TEST_KEY]) - .spawn() - .unwrap(), - }; - - // Give PokémonService some time to start up. - time::sleep(Duration::from_millis(500)).await; - - process - } - - fn base_url(&self) -> String { - match self { - PokemonServiceVariant::Http => "http://localhost:13734".to_string(), - PokemonServiceVariant::Https => "https://localhost:13734".to_string(), - } - } -} - -pub(crate) struct PokemonService { - child_process: Child, -} - -impl PokemonService { - #[allow(dead_code)] - pub(crate) async fn run() -> Self { - Self { - child_process: PokemonServiceVariant::Http.run_process().await, - } - } - - #[allow(dead_code)] - pub(crate) async fn run_https() -> Self { - Self { - child_process: PokemonServiceVariant::Https.run_process().await, - } - } -} - -impl Drop for PokemonService { - fn drop(&mut self) { - self.child_process - .kill() - .expect("failed to kill Pokémon Service program") - } -} - -#[allow(dead_code)] -pub fn client() -> Client< - aws_smithy_client::erase::DynConnector, - aws_smithy_client::erase::DynMiddleware, -> { - 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) -} - -// Returns a client that only talks through https and http2 connections. -// It is useful in testing whether our server can talk to http2. -#[allow(dead_code)] -pub fn client_http2_only() -> Client< - aws_smithy_client::erase::DynConnector, - aws_smithy_client::erase::DynMiddleware, -> { - // Create custom cert store and add our test certificate to prevent unknown cert issues. - let mut reader = BufReader::new(File::open(TEST_CERT).expect("could not open certificate")); - let certs = rustls_pemfile::certs(&mut reader).expect("could not parse certificate"); - let mut roots = tokio_rustls::rustls::RootCertStore::empty(); - roots.add_parsable_certificates(&certs); - - let connector = hyper_rustls::HttpsConnectorBuilder::new() - .with_tls_config( - tokio_rustls::rustls::ClientConfig::builder() - .with_safe_defaults() - .with_root_certificates(roots) - .with_no_client_auth(), - ) - .https_only() - .enable_http2() - .build(); - - let base_url = PokemonServiceVariant::Https.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: 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 - } -} 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 32fabd47cb..1047167ffb 100644 --- a/rust-runtime/aws-smithy-http-server/src/instrumentation/service.rs +++ b/rust-runtime/aws-smithy-http-server/src/instrumentation/service.rs @@ -76,9 +76,9 @@ where } /// A middleware [`Service`](tower::Service) responsible for: -/// - Opening a [`tracing::debug_span`] for the lifetime of the request, which includes the operation name, the +/// - Opening a [`tracing::debug_span`] for the lifetime of the request, which includes the operation name, the /// [`Uri`](http::Uri), and the request headers. -/// - A [`tracing::debug`] during response, which includes the response status code and headers. +/// - A [`tracing::debug`] during response, which includes the response status code and headers. /// /// The [`Display`](std::fmt::Display) and [`Debug`] of the request and response components can be modified using /// [`request_fmt`](InstrumentOperation::request_fmt) and [`response_fmt`](InstrumentOperation::response_fmt). diff --git a/rust-runtime/aws-smithy-http-server/src/lib.rs b/rust-runtime/aws-smithy-http-server/src/lib.rs index 804b00d8a3..6031c53e2a 100644 --- a/rust-runtime/aws-smithy-http-server/src/lib.rs +++ b/rust-runtime/aws-smithy-http-server/src/lib.rs @@ -3,6 +3,7 @@ * SPDX-License-Identifier: Apache-2.0 */ +#![allow(clippy::derive_partial_eq_without_eq)] #![cfg_attr(docsrs, feature(doc_cfg))] //! HTTP server runtime and utilities, loosely based on [axum]. 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 cc38466c8c..42c4a5b26b 100644 --- a/rust-runtime/aws-smithy-http-server/src/operation/upgrade.rs +++ b/rust-runtime/aws-smithy-http-server/src/operation/upgrade.rs @@ -32,39 +32,36 @@ use super::{Operation, OperationError, OperationShape}; /// /// See [`Upgrade`]. #[derive(Debug, Clone)] -pub struct UpgradeLayer { +pub struct UpgradeLayer { _protocol: PhantomData, _operation: PhantomData, _exts: PhantomData, - _body: PhantomData, } -impl Default for UpgradeLayer { +impl Default for UpgradeLayer { fn default() -> Self { Self { _protocol: PhantomData, _operation: PhantomData, _exts: PhantomData, - _body: PhantomData, } } } -impl UpgradeLayer { +impl UpgradeLayer { /// Creates a new [`UpgradeLayer`]. pub fn new() -> Self { Self::default() } } -impl Layer for UpgradeLayer { - type Service = Upgrade; +impl Layer for UpgradeLayer { + type Service = Upgrade; fn layer(&self, inner: S) -> Self::Service { Upgrade { _protocol: PhantomData, _operation: PhantomData, - _body: PhantomData, _exts: PhantomData, inner, } @@ -73,15 +70,14 @@ 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, - _body: PhantomData, inner: S, } -impl Clone for Upgrade +impl Clone for Upgrade where S: Clone, { @@ -89,7 +85,6 @@ where Self { _protocol: PhantomData, _operation: PhantomData, - _body: PhantomData, _exts: PhantomData, inner: self.inner.clone(), } @@ -177,7 +172,7 @@ where } } -impl Service> for Upgrade +impl Service> for Upgrade where // `Op` is used to specify the operation shape Op: OperationShape, @@ -225,9 +220,8 @@ pub trait Upgradable { fn upgrade(self, plugin: &Plugin) -> Route; } -type UpgradedService = <>::Layer as Layer< - Upgrade>::Service>, ->>::Service; +type UpgradedService = + <>::Layer as Layer>::Service>>>::Service; impl Upgradable for Operation where @@ -245,22 +239,19 @@ where Exts: FromParts

, // The signature of the inner service is correct - S: Service<(Op::Input, Exts), Response = Op::Output, Error = OperationError> + Clone, + 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>, - - // The signature of the output is correct - >>::Service: - Service, Response = http::Response>, + // The modified Layer applies correctly to `Upgrade` + Pl::Layer: Layer>, // For `Route::new` for the resulting service - >>::Service: Service, Error = Infallible>, - UpgradedService: Clone + Send + 'static, - as Service>>::Future: Send + 'static, + 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`. diff --git a/rust-runtime/aws-smithy-http-server/src/plugin/either.rs b/rust-runtime/aws-smithy-http-server/src/plugin/either.rs new file mode 100644 index 0000000000..bf33546ca1 --- /dev/null +++ b/rust-runtime/aws-smithy-http-server/src/plugin/either.rs @@ -0,0 +1,132 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +//! Contains the [`Either`] enum. + +use pin_project_lite::pin_project; +use std::{ + future::Future, + pin::Pin, + task::{Context, Poll}, +}; +use tower::{Layer, Service}; + +use crate::operation::Operation; + +use super::Plugin; + +pin_project! { + /// Combine two different [`Future`]/[`Service`]/[`Layer`]/[`Plugin`] types into a single type. + /// + /// # Notes on [`Future`] + /// + /// The [`Future::Output`] must be identical. + /// + /// # Notes on [`Service`] + /// + /// The [`Service::Response`] and [`Service::Error`] must be identical. + #[derive(Clone, Debug)] + #[project = EitherProj] + pub enum Either { + /// One type of backing [`Service`]. + Left { #[pin] value: L }, + /// The other type of backing [`Service`]. + Right { #[pin] value: R }, + } +} + +impl Future for Either +where + L: Future, + R: Future, +{ + type Output = L::Output; + + fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { + match self.project() { + EitherProj::Left { value } => value.poll(cx), + EitherProj::Right { value } => value.poll(cx), + } + } +} + +impl Service for Either +where + L: Service, + R: Service, +{ + type Response = L::Response; + type Error = L::Error; + type Future = Either; + + fn poll_ready(&mut self, cx: &mut Context<'_>) -> Poll> { + use self::Either::*; + + match self { + Left { value } => value.poll_ready(cx), + Right { value } => value.poll_ready(cx), + } + } + + fn call(&mut self, request: Request) -> Self::Future { + use self::Either::*; + + match self { + Left { value } => Either::Left { + value: value.call(request), + }, + Right { value } => Either::Right { + value: value.call(request), + }, + } + } +} + +impl Layer for Either +where + L: Layer, + R: Layer, +{ + type Service = Either; + + fn layer(&self, inner: S) -> Self::Service { + match self { + Either::Left { value } => Either::Left { + value: value.layer(inner), + }, + Either::Right { value } => Either::Right { + value: value.layer(inner), + }, + } + } +} + +impl Plugin for Either +where + Le: Plugin, + Ri: Plugin, +{ + type Service = Either; + type Layer = Either; + + fn map(&self, input: Operation) -> Operation { + 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 }, + } + } + } + } +} 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 a9804169b8..814398b089 100644 --- a/rust-runtime/aws-smithy-http-server/src/plugin/filter.rs +++ b/rust-runtime/aws-smithy-http-server/src/plugin/filter.rs @@ -3,7 +3,7 @@ * SPDX-License-Identifier: Apache-2.0 */ -use tower::util::Either; +use super::{either::Either, IdentityPlugin}; use crate::operation::{Operation, OperationShape}; @@ -60,17 +60,11 @@ where type Layer = Either; fn map(&self, input: Operation) -> Operation { - if (self.predicate)(Op::NAME) { - let Operation { inner, layer } = self.inner.map(input); - Operation { - inner: Either::A(inner), - layer: Either::A(layer), - } + let either_plugin = if (self.predicate)(Op::NAME) { + Either::Left { value: &self.inner } } else { - Operation { - inner: Either::B(input.inner), - layer: Either::B(input.layer), - } - } + Either::Right { value: IdentityPlugin } + }; + either_plugin.map(input) } } 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 6e43d4b493..72db50cbb4 100644 --- a/rust-runtime/aws-smithy-http-server/src/plugin/mod.rs +++ b/rust-runtime/aws-smithy-http-server/src/plugin/mod.rs @@ -119,6 +119,7 @@ //! mod closure; +mod either; mod filter; mod identity; mod layer; @@ -128,6 +129,7 @@ mod stack; use crate::operation::Operation; pub use closure::{plugin_from_operation_name_fn, OperationNameFn}; +pub use either::Either; pub use filter::{filter_by_operation_name, FilterByOperationName}; pub use identity::IdentityPlugin; pub use layer::HttpLayer; @@ -149,3 +151,15 @@ pub trait Plugin { /// Maps an [`Operation`] to another. fn map(&self, input: Operation) -> Operation; } + +impl<'a, P, Op, S, L, Pl> Plugin for &'a Pl +where + Pl: Plugin, +{ + type Service = Pl::Service; + type Layer = Pl::Layer; + + fn map(&self, input: Operation) -> Operation { + >::map(*self, input) + } +} diff --git a/rust-runtime/aws-smithy-http-server/src/protocols.rs b/rust-runtime/aws-smithy-http-server/src/protocols.rs index 2267c0384c..98d0223971 100644 --- a/rust-runtime/aws-smithy-http-server/src/protocols.rs +++ b/rust-runtime/aws-smithy-http-server/src/protocols.rs @@ -9,6 +9,7 @@ use http::HeaderMap; /// 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)] pub fn content_type_header_empty_body_no_modeled_input(headers: &HeaderMap) -> Result<(), MissingContentTypeReason> { if headers.contains_key(http::header::CONTENT_TYPE) { let found_mime = parse_content_type(headers)?; @@ -21,6 +22,7 @@ pub fn content_type_header_empty_body_no_modeled_input(headers: &HeaderMap) -> R } } +#[allow(clippy::result_large_err)] fn parse_content_type(headers: &HeaderMap) -> Result { headers .get(http::header::CONTENT_TYPE) @@ -32,7 +34,7 @@ fn parse_content_type(headers: &HeaderMap) -> Result, diff --git a/rust-runtime/aws-smithy-http-server/src/request/connect_info.rs b/rust-runtime/aws-smithy-http-server/src/request/connect_info.rs index 9dd1788f82..7a48e70421 100644 --- a/rust-runtime/aws-smithy-http-server/src/request/connect_info.rs +++ b/rust-runtime/aws-smithy-http-server/src/request/connect_info.rs @@ -7,8 +7,8 @@ //! [`IntoMakeServiceWithConnectInfo`](crate::routing::IntoMakeServiceWithConnectInfo) is used. [`ConnectInfo`]'s //! [`FromParts`] implementation allows it to be extracted from the [`http::Request`]. //! -//! The [`pokemon-service-connect-info.rs`](https://github.com/awslabs/smithy-rs/blob/main/rust-runtime/aws-smithy-http-server/examples/pokemon-service/src/bin/pokemon-service-connect-info.rs) -//! example illustrates the use of [`IntoMakeServiceWithConnectInfo`](crate::routing::IntoMakeServiceWithConnectInfo) +//! The [`example service`](https://github.com/awslabs/smithy-rs/blob/main/examples/pokemon-service/src/main.rs) +//! illustrates the use of [`IntoMakeServiceWithConnectInfo`](crate::routing::IntoMakeServiceWithConnectInfo) //! and [`ConnectInfo`] with a service builder. use http::request::Parts; diff --git a/rust-runtime/aws-smithy-http-server/src/request/request_id.rs b/rust-runtime/aws-smithy-http-server/src/request/request_id.rs index 7894d9806e..7d6ee70ab7 100644 --- a/rust-runtime/aws-smithy-http-server/src/request/request_id.rs +++ b/rust-runtime/aws-smithy-http-server/src/request/request_id.rs @@ -12,8 +12,10 @@ //! A [`ServerRequestId`] is an opaque random identifier generated by the server every time it receives a request. //! It uniquely identifies the request within that service instance. It can be used to collate all logs, events and //! data related to a single operation. +//! Use [`ServerRequestIdProviderLayer::new`] to use [`ServerRequestId`] in your handler. //! //! The [`ServerRequestId`] can be returned to the caller, who can in turn share the [`ServerRequestId`] to help the service owner in troubleshooting issues related to their usage of the service. +//! Use [`ServerRequestIdProviderLayer::new_with_response_header`] to use [`ServerRequestId`] in your handler and add it to the response headers. //! //! The [`ServerRequestId`] is not meant to be propagated to downstream dependencies of the service. You should rely on a distributed tracing implementation for correlation purposes (e.g. OpenTelemetry). //! @@ -34,7 +36,8 @@ //! .operation(handler) //! .build().unwrap(); //! -//! let app = app.layer(&ServerRequestIdProviderLayer::new()); /* Generate a server request ID */ +//! let app = app +//! .layer(&ServerRequestIdProviderLayer::new_with_response_header(HeaderName::from_static("x-request-id"))); /* Generate a server request ID and add it to the response header */ //! //! let bind: std::net::SocketAddr = format!("{}:{}", args.address, args.port) //! .parse() @@ -42,12 +45,15 @@ //! let server = hyper::Server::bind(&bind).serve(app.into_make_service()); //! ``` +use std::future::Future; use std::{ fmt::Display, task::{Context, Poll}, }; +use futures_util::TryFuture; use http::request::Parts; +use http::{header::HeaderName, HeaderValue, Response}; use thiserror::Error; use tower::{Layer, Service}; use uuid::Uuid; @@ -74,6 +80,10 @@ impl ServerRequestId { pub fn new() -> Self { Self { id: Uuid::new_v4() } } + + pub(crate) fn to_header(&self) -> HeaderValue { + HeaderValue::from_str(&self.id.to_string()).expect("This string contains only valid ASCII") + } } impl Display for ServerRequestId { @@ -99,17 +109,28 @@ impl Default for ServerRequestId { #[derive(Clone)] pub struct ServerRequestIdProvider { inner: S, + header_key: Option, } /// A layer that provides services with a unique request ID instance #[derive(Debug)] #[non_exhaustive] -pub struct ServerRequestIdProviderLayer; +pub struct ServerRequestIdProviderLayer { + header_key: Option, +} impl ServerRequestIdProviderLayer { - /// Generate a new unique request ID + /// Generate a new unique request ID and do not add it as a response header + /// Use [`ServerRequestIdProviderLayer::new_with_response_header`] to also add it as a response header pub fn new() -> Self { - Self {} + Self { header_key: None } + } + + /// Generate a new unique request ID and add it as a response header + pub fn new_with_response_header(header_key: HeaderName) -> Self { + Self { + header_key: Some(header_key), + } } } @@ -123,25 +144,47 @@ impl Layer for ServerRequestIdProviderLayer { type Service = ServerRequestIdProvider; fn layer(&self, inner: S) -> Self::Service { - ServerRequestIdProvider { inner } + ServerRequestIdProvider { + inner, + header_key: self.header_key.clone(), + } } } impl Service> for ServerRequestIdProvider where - S: Service>, + S: Service, Response = Response>, + S::Future: std::marker::Send + 'static, { type Response = S::Response; type Error = S::Error; - type Future = S::Future; + type Future = ServerRequestIdResponseFuture; fn poll_ready(&mut self, cx: &mut Context<'_>) -> Poll> { self.inner.poll_ready(cx) } fn call(&mut self, mut req: http::Request) -> Self::Future { - req.extensions_mut().insert(ServerRequestId::new()); - self.inner.call(req) + let request_id = ServerRequestId::new(); + match &self.header_key { + Some(header_key) => { + req.extensions_mut().insert(request_id.clone()); + ServerRequestIdResponseFuture { + response_package: Some(ResponsePackage { + request_id, + header_key: header_key.clone(), + }), + fut: self.inner.call(req), + } + } + None => { + req.extensions_mut().insert(request_id); + ServerRequestIdResponseFuture { + response_package: None, + fut: self.inner.call(req), + } + } + } } } @@ -150,3 +193,84 @@ impl IntoResponse for MissingServerRequestId { internal_server_error() } } + +struct ResponsePackage { + request_id: ServerRequestId, + header_key: HeaderName, +} + +pin_project_lite::pin_project! { + pub struct ServerRequestIdResponseFuture { + response_package: Option, + #[pin] + fut: Fut, + } +} + +impl Future for ServerRequestIdResponseFuture +where + Fut: TryFuture>, +{ + type Output = Result; + + fn poll(self: std::pin::Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { + let this = self.project(); + let fut = this.fut; + let response_package = this.response_package; + fut.try_poll(cx).map_ok(|mut res| { + if let Some(response_package) = response_package.take() { + res.headers_mut() + .insert(response_package.header_key, response_package.request_id.to_header()); + } + res + }) + } +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::body::{Body, BoxBody}; + use crate::request::Request; + use http::HeaderValue; + use std::convert::Infallible; + use tower::{service_fn, ServiceBuilder, ServiceExt}; + + #[test] + fn test_request_id_parsed_by_header_value_infallible() { + ServerRequestId::new().to_header(); + } + + #[tokio::test] + async fn test_request_id_in_response_header() { + let svc = ServiceBuilder::new() + .layer(&ServerRequestIdProviderLayer::new_with_response_header( + HeaderName::from_static("x-request-id"), + )) + .service(service_fn(|_req: Request| async move { + Ok::<_, Infallible>(Response::new(BoxBody::default())) + })); + + let req = Request::new(Body::empty()); + + let res = svc.oneshot(req).await.unwrap(); + let request_id = res.headers().get("x-request-id").unwrap().to_str().unwrap(); + + assert!(HeaderValue::from_str(request_id).is_ok()); + } + + #[tokio::test] + async fn test_request_id_not_in_response_header() { + let svc = ServiceBuilder::new() + .layer(&ServerRequestIdProviderLayer::new()) + .service(service_fn(|_req: Request| async move { + Ok::<_, Infallible>(Response::new(BoxBody::default())) + })); + + let req = Request::new(Body::empty()); + + let res = svc.oneshot(req).await.unwrap(); + + assert!(res.headers().is_empty()); + } +} diff --git a/rust-runtime/aws-smithy-http-tower/Cargo.toml b/rust-runtime/aws-smithy-http-tower/Cargo.toml index e74934119b..048a96f922 100644 --- a/rust-runtime/aws-smithy-http-tower/Cargo.toml +++ b/rust-runtime/aws-smithy-http-tower/Cargo.toml @@ -19,7 +19,7 @@ tracing = "0.1" [dev-dependencies] tower = { version = "0.4.4", features = ["util"] } -tokio = { version = "1.8.4", features = ["full"]} +tokio = { version = "1.23.1", features = ["full"]} [package.metadata.docs.rs] all-features = true diff --git a/rust-runtime/aws-smithy-http-tower/src/dispatch.rs b/rust-runtime/aws-smithy-http-tower/src/dispatch.rs index 8a1119d61b..a10693a62b 100644 --- a/rust-runtime/aws-smithy-http-tower/src/dispatch.rs +++ b/rust-runtime/aws-smithy-http-tower/src/dispatch.rs @@ -5,6 +5,7 @@ use crate::SendOperationError; use aws_smithy_http::body::SdkBody; +use aws_smithy_http::connection::CaptureSmithyConnection; use aws_smithy_http::operation; use aws_smithy_http::result::ConnectorError; use std::future::Future; @@ -41,7 +42,13 @@ where } fn call(&mut self, req: operation::Request) -> Self::Future { - let (req, property_bag) = req.into_parts(); + let (mut req, property_bag) = req.into_parts(); + // copy the smithy connection + if let Some(smithy_conn) = property_bag.acquire().get::() { + req.extensions_mut().insert(smithy_conn.clone()); + } else { + println!("nothing to copy!"); + } let mut inner = self.inner.clone(); let future = async move { trace!(request = ?req, "dispatching request"); diff --git a/rust-runtime/aws-smithy-http-tower/src/lib.rs b/rust-runtime/aws-smithy-http-tower/src/lib.rs index d2a1c54603..8a41796114 100644 --- a/rust-runtime/aws-smithy-http-tower/src/lib.rs +++ b/rust-runtime/aws-smithy-http-tower/src/lib.rs @@ -3,6 +3,14 @@ * SPDX-License-Identifier: Apache-2.0 */ +#![allow(clippy::derive_partial_eq_without_eq)] +#![warn( + // missing_docs, + // rustdoc::missing_crate_level_docs, + unreachable_pub, + rust_2018_idioms +)] + pub mod dispatch; pub mod map_request; pub mod parse_response; diff --git a/rust-runtime/aws-smithy-http/Cargo.toml b/rust-runtime/aws-smithy-http/Cargo.toml index 23bb28437d..cef87ef295 100644 --- a/rust-runtime/aws-smithy-http/Cargo.toml +++ b/rust-runtime/aws-smithy-http/Cargo.toml @@ -28,20 +28,20 @@ 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.12" +hyper = "0.14.25" # ByteStream internals futures-core = "0.3.14" -tokio = { version = "1.8.4", optional = true } +tokio = { version = "1.23.1", optional = true } tokio-util = { version = "0.7", optional = true } [dev-dependencies] async-stream = "0.3" -futures-util = "0.3" -hyper = { version = "0.14.12", features = ["stream"] } -pretty_assertions = "1.2" +futures-util = { version = "0.3.16", default-features = false } +hyper = { version = "0.14.25", features = ["stream"] } +pretty_assertions = "1.3" proptest = "1" -tokio = { version = "1.8.4", features = [ +tokio = { version = "1.23.1", features = [ "macros", "rt", "rt-multi-thread", diff --git a/rust-runtime/aws-smithy-http/external-types.toml b/rust-runtime/aws-smithy-http/external-types.toml index a3dbbce3c5..b06231e92f 100644 --- a/rust-runtime/aws-smithy-http/external-types.toml +++ b/rust-runtime/aws-smithy-http/external-types.toml @@ -26,9 +26,6 @@ allowed_external_types = [ # TODO(https://github.com/awslabs/smithy-rs/issues/1193): Feature gate references to Tokio `File` "tokio::fs::file::File", - # TODO(https://github.com/awslabs/smithy-rs/issues/1193): Decide if `InvalidUri` should be exposed - "http::uri::InvalidUri", - # TODO(https://github.com/awslabs/smithy-rs/issues/1193): Don't expose `once_cell` in public API "once_cell::sync::Lazy", diff --git a/rust-runtime/aws-smithy-http/src/body.rs b/rust-runtime/aws-smithy-http/src/body.rs index 5e9b4fbf2d..32ad205ed2 100644 --- a/rust-runtime/aws-smithy-http/src/body.rs +++ b/rust-runtime/aws-smithy-http/src/body.rs @@ -3,6 +3,8 @@ * SPDX-License-Identifier: Apache-2.0 */ +//! Types for representing the body of an HTTP request or response + use bytes::Bytes; use http::{HeaderMap, HeaderValue}; use http_body::{Body, SizeHint}; @@ -13,6 +15,7 @@ use std::pin::Pin; use std::sync::Arc; use std::task::{Context, Poll}; +/// A generic, boxed error that's `Send` and `Sync` pub type Error = Box; pin_project! { @@ -44,6 +47,7 @@ impl Debug for SdkBody { } } +/// A boxed generic HTTP body that, when consumed, will result in [`Bytes`] or an [`Error`]. pub type BoxBody = http_body::combinators::BoxBody; pin_project! { @@ -109,6 +113,8 @@ impl SdkBody { } } + /// When an SdkBody is read, the inner data must be consumed. In order to do this, the SdkBody + /// is swapped with a "taken" body. This "taken" body cannot be read but aids in debugging. pub fn taken() -> Self { Self { inner: Inner::Taken, @@ -116,6 +122,7 @@ impl SdkBody { } } + /// Create an empty SdkBody for requests and responses that don't transfer any data in the body. pub fn empty() -> Self { Self { inner: Inner::Once { inner: None }, @@ -157,6 +164,8 @@ impl SdkBody { } } + /// Attempt to clone this SdkBody. This will fail if the inner data is not cloneable, such as when + /// it is a single-use stream that can't be recreated. pub fn try_clone(&self) -> Option { self.rebuild.as_ref().map(|rebuild| { let next = rebuild(); @@ -167,10 +176,14 @@ impl SdkBody { }) } + /// Return the length, in bytes, of this SdkBody. If this returns `None`, then the body does not + /// have a known length. pub fn content_length(&self) -> Option { http_body::Body::size_hint(self).exact() } + /// Given a function to modify an `SdkBody`, run that function against this `SdkBody` before + /// returning the result. pub fn map(self, f: impl Fn(SdkBody) -> SdkBody + Sync + Send + 'static) -> SdkBody { if self.rebuild.is_some() { SdkBody::retryable(move || f(self.try_clone().unwrap())) @@ -277,6 +290,7 @@ mod test { assert_eq!(SdkBody::from("").size_hint().exact(), Some(0)); } + #[allow(clippy::bool_assert_comparison)] #[test] fn valid_eos() { assert_eq!(SdkBody::from("hello").is_end_stream(), false); diff --git a/rust-runtime/aws-smithy-http/src/byte_stream.rs b/rust-runtime/aws-smithy-http/src/byte_stream.rs index e19ed3ac19..3b99c0a68d 100644 --- a/rust-runtime/aws-smithy-http/src/byte_stream.rs +++ b/rust-runtime/aws-smithy-http/src/byte_stream.rs @@ -262,12 +262,14 @@ pin_project! { } impl ByteStream { + /// Create a new `ByteStream` from an [`SdkBody`]. pub fn new(body: SdkBody) -> Self { Self { inner: Inner::new(body), } } + /// Create a new `ByteStream` from a static byte slice. pub fn from_static(bytes: &'static [u8]) -> Self { Self { inner: Inner::new(SdkBody::from(Bytes::from_static(bytes))), @@ -394,6 +396,8 @@ impl ByteStream { tokio_util::io::StreamReader::new(self) } + /// Given a function to modify an [`SdkBody`], run it on the `SdkBody` inside this `Bytestream`. + /// returning a new `Bytestream`. pub fn map(self, f: impl Fn(SdkBody) -> SdkBody + Send + Sync + 'static) -> ByteStream { ByteStream::new(self.into_inner().map(f)) } diff --git a/rust-runtime/aws-smithy-http/src/byte_stream/bytestream_util.rs b/rust-runtime/aws-smithy-http/src/byte_stream/bytestream_util.rs index 28937b2aac..3768a57509 100644 --- a/rust-runtime/aws-smithy-http/src/byte_stream/bytestream_util.rs +++ b/rust-runtime/aws-smithy-http/src/byte_stream/bytestream_util.rs @@ -58,7 +58,7 @@ impl PathBody { } } -/// Builder for creating [`ByteStreams`](crate::byte_stream::ByteStream) from a file/path, with full control over advanced options. +/// Builder for creating [`ByteStreams`](ByteStream) from a file/path, with full control over advanced options. /// /// Example usage: /// ```no_run @@ -84,8 +84,9 @@ impl PathBody { /// } /// # } /// ``` +#[allow(missing_debug_implementations)] pub struct FsBuilder { - file: Option, + file: Option, path: Option, length: Option, buffer_size: usize, @@ -99,6 +100,7 @@ impl Default for FsBuilder { } /// The length (in bytes) to read. Determines whether or not a short read counts as an error. +#[allow(missing_debug_implementations)] pub enum Length { /// Read this number of bytes exactly. Returns an error if the file is smaller than expected. Exact(u64), @@ -135,7 +137,7 @@ impl FsBuilder { /// /// NOTE: The resulting ByteStream (after calling [build](FsBuilder::build)) will not be a retryable ByteStream. /// For a ByteStream that can be retried in the case of upstream failures, use [`FsBuilder::path`](FsBuilder::path). - pub fn file(mut self, file: tokio::fs::File) -> Self { + pub fn file(mut self, file: File) -> Self { self.file = Some(file); self } @@ -167,7 +169,7 @@ impl FsBuilder { self } - /// Returns a [`ByteStream`](crate::byte_stream::ByteStream) from this builder. + /// Returns a [`ByteStream`](ByteStream) from this builder. pub async fn build(self) -> Result { if self.path.is_some() && self.file.is_some() { panic!("The 'file' and 'path' options on an FsBuilder are mutually exclusive but both were set. Please set only one") @@ -209,7 +211,7 @@ impl FsBuilder { } else if let Some(mut file) = self.file { // When starting from a `File`, we need to do our own seeking if offset != 0 { - let _s = file.seek(std::io::SeekFrom::Start(offset)).await?; + let _s = file.seek(io::SeekFrom::Start(offset)).await?; } let body = SdkBody::from_dyn(http_body::combinators::BoxBody::new( @@ -235,7 +237,7 @@ impl FsBuilder { enum State { Unloaded(PathBuf), Loading(Pin> + Send + Sync + 'static>>), - Loaded(tokio_util::io::ReaderStream>), + Loaded(ReaderStream>), } impl Body for PathBody { @@ -252,10 +254,10 @@ impl Body for PathBody { State::Unloaded(ref path_buf) => { let buf = path_buf.clone(); self.state = State::Loading(Box::pin(async move { - let mut file = tokio::fs::File::open(&buf).await?; + let mut file = File::open(&buf).await?; if offset != 0 { - let _s = file.seek(std::io::SeekFrom::Start(offset)).await?; + let _s = file.seek(io::SeekFrom::Start(offset)).await?; } Ok(file) @@ -574,7 +576,7 @@ mod test { for i in 0..chunks { let length = if i == chunks - 1 { // If we're on the last chunk, the length to read might be less than a whole chunk. - // We substract the size of all previous chunks from the total file size to get the + // We subtract the size of all previous chunks from the total file size to get the // size of the final chunk. file_size - (i * chunk_size) } else { diff --git a/rust-runtime/aws-smithy-http/src/byte_stream/error.rs b/rust-runtime/aws-smithy-http/src/byte_stream/error.rs index 8ea46acb3e..b0e29af6e6 100644 --- a/rust-runtime/aws-smithy-http/src/byte_stream/error.rs +++ b/rust-runtime/aws-smithy-http/src/byte_stream/error.rs @@ -3,6 +3,8 @@ * SPDX-License-Identifier: Apache-2.0 */ +//! Errors related to bytestreams. + use std::error::Error as StdError; use std::fmt; use std::io::{Error as IoError, ErrorKind as IoErrorKind}; diff --git a/rust-runtime/aws-smithy-http/src/connection.rs b/rust-runtime/aws-smithy-http/src/connection.rs new file mode 100644 index 0000000000..eb81f63687 --- /dev/null +++ b/rust-runtime/aws-smithy-http/src/connection.rs @@ -0,0 +1,106 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +//! Types related to connection monitoring and management. + +use std::fmt::{Debug, Formatter}; +use std::net::SocketAddr; +use std::sync::{Arc, Mutex}; + +/// Metadata that tracks the state of an active connection. +#[derive(Clone)] +pub struct ConnectionMetadata { + is_proxied: bool, + remote_addr: Option, + poison_fn: Arc, +} + +impl ConnectionMetadata { + /// Poison this connection, ensuring that it won't be reused. + pub fn poison(&self) { + tracing::info!("smithy connection was poisoned"); + (self.poison_fn)() + } + + /// Create a new [`ConnectionMetadata`]. + pub fn new( + is_proxied: bool, + remote_addr: Option, + poison: impl Fn() + Send + Sync + 'static, + ) -> Self { + Self { + is_proxied, + remote_addr, + poison_fn: Arc::new(poison), + } + } + + /// Get the remote address for this connection, if one is set. + pub fn remote_addr(&self) -> Option { + self.remote_addr + } +} + +impl Debug for ConnectionMetadata { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + f.debug_struct("SmithyConnection") + .field("is_proxied", &self.is_proxied) + .field("remote_addr", &self.remote_addr) + .finish() + } +} + +type LoaderFn = dyn Fn() -> Option + Send + Sync; + +/// State for a middleware that will monitor and manage connections. +#[allow(missing_debug_implementations)] +#[derive(Clone, Default)] +pub struct CaptureSmithyConnection { + loader: Arc>>>, +} + +impl CaptureSmithyConnection { + /// Create a new connection monitor. + pub fn new() -> Self { + Self { + loader: Default::default(), + } + } + + /// Set the retriever that will capture the `hyper` connection. + pub fn set_connection_retriever(&self, f: F) + where + F: Fn() -> Option + Send + Sync + 'static, + { + *self.loader.lock().unwrap() = Some(Box::new(f)); + } + + /// Get the associated connection metadata. + pub fn get(&self) -> Option { + match self.loader.lock().unwrap().as_ref() { + Some(loader) => loader(), + None => { + println!("no loader was set :-/"); + None + } + } + } +} + +#[cfg(test)] +mod test { + use crate::connection::{CaptureSmithyConnection, ConnectionMetadata}; + + #[test] + fn retrieve_connection_metadata() { + let retriever = CaptureSmithyConnection::new(); + let retriever_clone = retriever.clone(); + assert!(retriever.get().is_none()); + retriever.set_connection_retriever(|| Some(ConnectionMetadata::new(true, None, || {}))); + + assert!(retriever.get().is_some()); + assert!(retriever_clone.get().is_some()); + } +} diff --git a/rust-runtime/aws-smithy-http/src/endpoint.rs b/rust-runtime/aws-smithy-http/src/endpoint.rs index a0fcb0ef3a..e73cd05131 100644 --- a/rust-runtime/aws-smithy-http/src/endpoint.rs +++ b/rust-runtime/aws-smithy-http/src/endpoint.rs @@ -3,6 +3,8 @@ * SPDX-License-Identifier: Apache-2.0 */ +//! Code for resolving an endpoint (URI) that a request should be sent to + use crate::endpoint::error::InvalidEndpointError; use crate::operation::error::BuildError; use http::uri::{Authority, Uri}; @@ -15,9 +17,13 @@ pub mod middleware; pub use error::ResolveEndpointError; +/// An endpoint-resolution-specific Result. Contains either an [`Endpoint`](aws_smithy_types::endpoint::Endpoint) or a [`ResolveEndpointError`]. pub type Result = std::result::Result; +/// Implementors of this trait can resolve an endpoint that will be applied to a request. pub trait ResolveEndpoint: Send + Sync { + /// Given some endpoint parameters, resolve an endpoint or return an error when resolution is + /// impossible. fn resolve_endpoint(&self, params: &Params) -> Result; } @@ -52,9 +58,12 @@ impl ResolveEndpoint for Endpoint { } } +/// A special type that adds support for services that have special URL-prefixing rules. #[derive(Clone, Debug, Eq, PartialEq)] pub struct EndpointPrefix(String); impl EndpointPrefix { + /// Create a new endpoint prefix from an `impl Into`. If the prefix argument is invalid, + /// a [`BuildError`] will be returned. pub fn new(prefix: impl Into) -> StdResult { let prefix = prefix.into(); match Authority::from_str(&prefix) { @@ -67,6 +76,7 @@ impl EndpointPrefix { } } + /// Get the `str` representation of this `EndpointPrefix`. pub fn as_str(&self) -> &str { &self.0 } diff --git a/rust-runtime/aws-smithy-http/src/endpoint/error.rs b/rust-runtime/aws-smithy-http/src/endpoint/error.rs index 47570fa4f8..bab0f93ac6 100644 --- a/rust-runtime/aws-smithy-http/src/endpoint/error.rs +++ b/rust-runtime/aws-smithy-http/src/endpoint/error.rs @@ -3,6 +3,8 @@ * SPDX-License-Identifier: Apache-2.0 */ +//! Errors related to endpoint resolution and validation + use std::error::Error; use std::fmt; @@ -59,6 +61,8 @@ pub(super) enum InvalidEndpointErrorKind { }, } +/// An error that occurs when an endpoint is found to be invalid. This usually occurs due to an +/// incomplete URI. #[derive(Debug)] pub struct InvalidEndpointError { pub(super) kind: InvalidEndpointErrorKind, diff --git a/rust-runtime/aws-smithy-http/src/endpoint/middleware.rs b/rust-runtime/aws-smithy-http/src/endpoint/middleware.rs index 9f6c8e1a1e..1665df9bce 100644 --- a/rust-runtime/aws-smithy-http/src/endpoint/middleware.rs +++ b/rust-runtime/aws-smithy-http/src/endpoint/middleware.rs @@ -3,6 +3,8 @@ * SPDX-License-Identifier: Apache-2.0 */ +//! [`MapRequest`]-based middleware for resolving and applying a request's endpoint. + use crate::endpoint; use crate::endpoint::{apply_endpoint, EndpointPrefix, ResolveEndpointError}; use crate::middleware::MapRequest; @@ -19,6 +21,7 @@ use std::str::FromStr; #[derive(Default, Debug, Clone)] pub struct SmithyEndpointStage; impl SmithyEndpointStage { + /// Create a new `SmithyEndpointStage`. pub fn new() -> Self { Self::default() } diff --git a/rust-runtime/aws-smithy-http/src/event_stream.rs b/rust-runtime/aws-smithy-http/src/event_stream.rs index 8b09a5bb90..0b95bb2d1e 100644 --- a/rust-runtime/aws-smithy-http/src/event_stream.rs +++ b/rust-runtime/aws-smithy-http/src/event_stream.rs @@ -10,6 +10,7 @@ use std::error::Error as StdError; mod receiver; mod sender; +/// A generic, boxed error that's `Send`, `Sync`, and `'static`. pub type BoxError = Box; #[doc(inline)] 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 04640fdc74..aa7f914223 100644 --- a/rust-runtime/aws-smithy-http/src/event_stream/receiver.rs +++ b/rust-runtime/aws-smithy-http/src/event_stream/receiver.rs @@ -109,6 +109,7 @@ enum ReceiverErrorKind { UnexpectedEndOfStream, } +/// An error that occurs within an event stream receiver. #[derive(Debug)] pub struct ReceiverError { kind: ReceiverErrorKind, @@ -127,7 +128,7 @@ impl StdError for ReceiverError {} /// Receives Smithy-modeled messages out of an Event Stream. #[derive(Debug)] pub struct Receiver { - unmarshaller: Box + Send>, + unmarshaller: Box + Send + Sync>, decoder: MessageFrameDecoder, buffer: RecvBuf, body: SdkBody, @@ -142,7 +143,7 @@ pub struct Receiver { impl Receiver { /// Creates a new `Receiver` with the given message unmarshaller and SDK body. pub fn new( - unmarshaller: impl UnmarshallMessage + Send + 'static, + unmarshaller: impl UnmarshallMessage + Send + Sync + 'static, body: SdkBody, ) -> Self { Receiver { @@ -547,10 +548,10 @@ mod tests { ); } - fn assert_send() {} + fn assert_send_and_sync() {} #[tokio::test] - async fn receiver_is_send() { - assert_send::>(); + async fn receiver_is_send_and_sync() { + assert_send_and_sync::>(); } } 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 8ecc1b7fd4..a4faa3f236 100644 --- a/rust-runtime/aws-smithy-http/src/event_stream/sender.rs +++ b/rust-runtime/aws-smithy-http/src/event_stream/sender.rs @@ -49,6 +49,7 @@ where } } +/// An error that occurs within a message stream. #[derive(Debug)] pub struct MessageStreamError { kind: MessageStreamErrorKind, @@ -105,6 +106,7 @@ impl fmt::Display for MessageStreamError { /// /// This will yield an `Err(SdkError::ConstructionFailure)` if a message can't be /// marshalled into an Event Stream frame, (e.g., if the message payload was too large). +#[allow(missing_debug_implementations)] pub struct MessageStreamAdapter { marshaller: Box + Send + Sync>, error_marshaller: Box + Send + Sync>, @@ -117,6 +119,7 @@ pub struct MessageStreamAdapter { impl Unpin for MessageStreamAdapter {} impl MessageStreamAdapter { + /// Create a new `MessageStreamAdapter`. pub fn new( marshaller: impl MarshallMessage + Send + Sync + 'static, error_marshaller: impl MarshallMessage + Send + Sync + 'static, @@ -228,9 +231,7 @@ mod tests { type Input = TestServiceError; fn marshall(&self, _input: Self::Input) -> Result { - Err(Message::read_from(&b""[..]) - .err() - .expect("this should always fail")) + Err(Message::read_from(&b""[..]).expect_err("this should always fail")) } } diff --git a/rust-runtime/aws-smithy-http/src/header.rs b/rust-runtime/aws-smithy-http/src/header.rs index 99695af4d5..b77edbdce0 100644 --- a/rust-runtime/aws-smithy-http/src/header.rs +++ b/rust-runtime/aws-smithy-http/src/header.rs @@ -15,6 +15,7 @@ use std::error::Error; use std::fmt; use std::str::FromStr; +/// An error was encountered while parsing a header #[derive(Debug)] pub struct ParseError { message: Cow<'static, str>, @@ -55,7 +56,7 @@ impl Error for ParseError { /// This is separate from `read_many` below because we need to invoke `DateTime::read` to take advantage /// of comma-aware parsing pub fn many_dates( - values: ValueIter, + values: ValueIter<'_, HeaderValue>, format: Format, ) -> Result, ParseError> { let mut out = vec![]; @@ -87,7 +88,10 @@ pub fn headers_for_prefix<'a>( .map(move |h| (&h.as_str()[key.len()..], h)) } -pub fn read_many_from_str(values: ValueIter) -> Result, ParseError> +/// Convert a `HeaderValue` into a `Vec` where `T: FromStr` +pub fn read_many_from_str( + values: ValueIter<'_, HeaderValue>, +) -> Result, ParseError> where T::Err: Error + Send + Sync + 'static, { @@ -98,7 +102,10 @@ where }) } -pub fn read_many_primitive(values: ValueIter) -> Result, ParseError> { +/// Convert a `HeaderValue` into a `Vec` where `T: Parse` +pub fn read_many_primitive( + values: ValueIter<'_, HeaderValue>, +) -> Result, ParseError> { read_many(values, |v: &str| { T::parse_smithy_primitive(v) .map_err(|err| ParseError::new("failed reading a list of primitives").with_source(err)) @@ -107,7 +114,7 @@ pub fn read_many_primitive(values: ValueIter) -> Result( - values: ValueIter, + values: ValueIter<'_, HeaderValue>, f: impl Fn(&str) -> Result, ) -> Result, ParseError> { let mut out = vec![]; @@ -125,7 +132,9 @@ fn read_many( /// Read exactly one or none from a headers iterator /// /// This function does not perform comma splitting like `read_many` -pub fn one_or_none(mut values: ValueIter) -> Result, ParseError> +pub fn one_or_none( + mut values: ValueIter<'_, HeaderValue>, +) -> Result, ParseError> where T::Err: Error + Send + Sync + 'static, { @@ -145,6 +154,7 @@ where } } +/// Given an HTTP request, set a request header if that header was not already set. pub fn set_request_header_if_absent( request: http::request::Builder, key: HeaderName, @@ -165,6 +175,7 @@ where } } +/// Given an HTTP response, set a response header if that header was not already set. pub fn set_response_header_if_absent( response: http::response::Builder, key: HeaderName, diff --git a/rust-runtime/aws-smithy-http/src/http.rs b/rust-runtime/aws-smithy-http/src/http.rs new file mode 100644 index 0000000000..ad77e951c3 --- /dev/null +++ b/rust-runtime/aws-smithy-http/src/http.rs @@ -0,0 +1,39 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +//! Types for abstracting over HTTP requests and responses. + +use http::{HeaderMap, HeaderValue}; + +/// Trait for accessing HTTP headers. +/// +/// Useful for generic impls so that they can access headers via trait bounds. +pub trait HttpHeaders { + /// Returns a reference to the associated header map. + fn http_headers(&self) -> &HeaderMap; + + /// Returns a mutable reference to the associated header map. + fn http_headers_mut(&mut self) -> &mut HeaderMap; +} + +impl HttpHeaders for http::Response { + fn http_headers(&self) -> &HeaderMap { + self.headers() + } + + fn http_headers_mut(&mut self) -> &mut HeaderMap { + self.headers_mut() + } +} + +impl HttpHeaders for crate::operation::Response { + fn http_headers(&self) -> &HeaderMap { + self.http().http_headers() + } + + fn http_headers_mut(&mut self) -> &mut HeaderMap { + self.http_mut().http_headers_mut() + } +} diff --git a/rust-runtime/aws-smithy-http/src/label.rs b/rust-runtime/aws-smithy-http/src/label.rs index a4742aa0c6..e2f083a383 100644 --- a/rust-runtime/aws-smithy-http/src/label.rs +++ b/rust-runtime/aws-smithy-http/src/label.rs @@ -13,13 +13,17 @@ use percent_encoding::AsciiSet; const GREEDY: &AsciiSet = &BASE_SET.remove(b'/'); +/// The encoding strategy used when parsing an `httpLabel`. #[non_exhaustive] #[derive(Clone, Debug, Eq, PartialEq)] pub enum EncodingStrategy { + /// The default strategy when parsing an `httpLabel`. Only one path segment will be matched. Default, + /// When parsing an `httpLabel`, this strategy will attempt to parse as many path segments as possible. Greedy, } +/// Format a given `httpLabel` as a string according to an [`EncodingStrategy`] pub fn fmt_string>(t: T, strategy: EncodingStrategy) -> String { let uri_set = if strategy == EncodingStrategy::Greedy { GREEDY @@ -29,6 +33,7 @@ pub fn fmt_string>(t: T, strategy: EncodingStrategy) -> String { percent_encoding::utf8_percent_encode(t.as_ref(), uri_set).to_string() } +/// Format a given [`DateTime`] as a string according to an [`EncodingStrategy`] pub fn fmt_timestamp(t: &DateTime, format: Format) -> Result { Ok(fmt_string(t.fmt(format)?, EncodingStrategy::Default)) } diff --git a/rust-runtime/aws-smithy-http/src/lib.rs b/rust-runtime/aws-smithy-http/src/lib.rs index 54449bf38e..ad53e37f9c 100644 --- a/rust-runtime/aws-smithy-http/src/lib.rs +++ b/rust-runtime/aws-smithy-http/src/lib.rs @@ -3,6 +3,13 @@ * SPDX-License-Identifier: Apache-2.0 */ +#![warn( + missing_docs, + rustdoc::missing_crate_level_docs, + unreachable_pub, + rust_2018_idioms +)] + //! Core HTTP primitives for service clients generated by [smithy-rs](https://github.com/awslabs/smithy-rs) including: //! - HTTP Body implementation //! - Endpoint support @@ -15,17 +22,21 @@ //! | `rt-tokio` | Provides features that are dependent on `tokio` including the `ByteStream::from_path` util | //! | `event-stream` | Provides Sender/Receiver implementations for Event Stream codegen. | +#![allow(clippy::derive_partial_eq_without_eq)] #![cfg_attr(docsrs, feature(doc_cfg))] pub mod body; pub mod endpoint; pub mod header; +pub mod http; pub mod http_versions; pub mod label; pub mod middleware; pub mod operation; pub mod property_bag; pub mod query; +#[doc(hidden)] +pub mod query_writer; pub mod response; pub mod result; pub mod retry; @@ -35,4 +46,5 @@ pub mod event_stream; pub mod byte_stream; +pub mod connection; mod urlencode; diff --git a/rust-runtime/aws-smithy-http/src/middleware.rs b/rust-runtime/aws-smithy-http/src/middleware.rs index 091c7caa71..b77c65b935 100644 --- a/rust-runtime/aws-smithy-http/src/middleware.rs +++ b/rust-runtime/aws-smithy-http/src/middleware.rs @@ -30,12 +30,15 @@ type BoxError = Box; /// including signing & endpoint resolution. `AsyncMapRequest` is used for async credential /// retrieval (e.g., from AWS STS's AssumeRole operation). pub trait AsyncMapRequest { + /// The type returned when this [`AsyncMapRequest`] encounters an error. type Error: Into + 'static; + /// The type returned when [`AsyncMapRequest::apply`] is called. type Future: Future> + Send + 'static; /// Returns the name of this map request operation for inclusion in a tracing span. fn name(&self) -> &'static str; + /// Call this middleware, returning a future that resolves to a request or an error. fn apply(&self, request: operation::Request) -> Self::Future; } diff --git a/rust-runtime/aws-smithy-http/src/operation.rs b/rust-runtime/aws-smithy-http/src/operation.rs index 0ca8b848f6..a4d9657cfb 100644 --- a/rust-runtime/aws-smithy-http/src/operation.rs +++ b/rust-runtime/aws-smithy-http/src/operation.rs @@ -3,6 +3,9 @@ * SPDX-License-Identifier: Apache-2.0 */ +//! Types for representing the interaction between a service an a client, referred to as an "operation" in smithy. +//! Clients "send" operations to services, which are composed of 1 or more HTTP requests. + use crate::body::SdkBody; use crate::property_bag::{PropertyBag, SharedPropertyBag}; use crate::retry::DefaultResponseRetryClassifier; @@ -11,6 +14,7 @@ use std::ops::{Deref, DerefMut}; pub mod error; +/// Metadata attached to an [`Operation`] that identifies the API being called. #[derive(Clone, Debug)] pub struct Metadata { operation: Cow<'static, str>, @@ -18,14 +22,17 @@ pub struct Metadata { } impl Metadata { + /// Returns the operation name. pub fn name(&self) -> &str { &self.operation } + /// Returns the service name. pub fn service(&self) -> &str { &self.service } + /// Creates [`Metadata`]. pub fn new( operation: impl Into>, service: impl Into>, @@ -37,17 +44,28 @@ impl Metadata { } } +/// Non-request parts of an [`Operation`]. +/// +/// Generics: +/// - `H`: Response handler +/// - `R`: Implementation of `ClassifyRetry` #[non_exhaustive] #[derive(Clone, Debug)] pub struct Parts { + /// The response deserializer that will convert the connector's response into an `operation::Response` pub response_handler: H, + /// The classifier that will determine if an HTTP response indicates that a request failed for a retryable reason. pub retry_classifier: R, + /// Metadata describing this operation and the service it relates to. pub metadata: Option, } -// Generics: -// - H: Response handler -// - R: Implementation of `ClassifyRetry` +/// An [`Operation`] is a request paired with a response handler, retry classifier, +/// and metadata that identifies the API being called. +/// +/// Generics: +/// - `H`: Response handler +/// - `R`: Implementation of `ClassifyRetry` #[derive(Debug)] pub struct Operation { request: Request, @@ -55,17 +73,22 @@ pub struct Operation { } impl Operation { + /// Converts this operation into its parts. pub fn into_request_response(self) -> (Request, Parts) { (self.request, self.parts) } + + /// Constructs an [`Operation`] from a request and [`Parts`] pub fn from_parts(request: Request, parts: Parts) -> Self { Self { request, parts } } + /// Returns a mutable reference to the request's property bag. pub fn properties_mut(&mut self) -> impl DerefMut + '_ { self.request.properties_mut() } + /// Returns an immutable reference to the request's property bag. pub fn properties(&self) -> impl Deref + '_ { self.request.properties() } @@ -80,11 +103,13 @@ impl Operation { self.request.http() } + /// Attaches metadata to the operation. pub fn with_metadata(mut self, metadata: Metadata) -> Self { self.parts.metadata = Some(metadata); self } + /// Replaces the retry classifier on the operation. pub fn with_retry_classifier(self, retry_classifier: R2) -> Operation { Operation { request: self.request, @@ -96,10 +121,14 @@ impl Operation { } } + /// Returns the retry classifier for this operation. pub fn retry_classifier(&self) -> &R { &self.parts.retry_classifier } + /// Attempts to clone the operation. + /// + /// Will return `None` if the request body is already consumed and can't be replayed. pub fn try_clone(&self) -> Option where H: Clone, @@ -114,6 +143,7 @@ impl Operation { } impl Operation { + /// Creates a new [`Operation`]. pub fn new( request: Request, response_handler: H, diff --git a/rust-runtime/aws-smithy-http/src/operation/error.rs b/rust-runtime/aws-smithy-http/src/operation/error.rs index e6c8236ecb..5b2024396f 100644 --- a/rust-runtime/aws-smithy-http/src/operation/error.rs +++ b/rust-runtime/aws-smithy-http/src/operation/error.rs @@ -17,12 +17,14 @@ enum SerializationErrorKind { DateTimeFormatError { cause: DateTimeFormatError }, } +/// An error that occurs when serialization of an operation fails. #[derive(Debug)] pub struct SerializationError { kind: SerializationErrorKind, } impl SerializationError { + /// An error that occurs when serialization of an operation fails for an unknown reason. pub fn unknown_variant(union: &'static str) -> Self { Self { kind: SerializationErrorKind::CannotSerializeUnknownVariant { union }, diff --git a/rust-runtime/aws-smithy-http/src/property_bag.rs b/rust-runtime/aws-smithy-http/src/property_bag.rs index f47c4a1891..4964b7f09b 100644 --- a/rust-runtime/aws-smithy-http/src/property_bag.rs +++ b/rust-runtime/aws-smithy-http/src/property_bag.rs @@ -3,14 +3,17 @@ * SPDX-License-Identifier: Apache-2.0 */ -// This code is functionally equivalent to `Extensions` in the `http` crate. Examples -// have been updated to be more relevant for smithy use, the interface has been made public, -// and the doc comments have been updated to reflect how the property bag is used in the SDK. -// Additionally, optimizations around the HTTP use case have been removed in favor or simpler code. +//! A typemap used to store configuration for smithy operations. +//! +//! This code is functionally equivalent to `Extensions` in the `http` crate. Examples +//! have been updated to be more relevant for smithy use, the interface has been made public, +//! and the doc comments have been updated to reflect how the property bag is used in the SDK. +//! Additionally, optimizations around the HTTP use case have been removed in favor or simpler code. use std::any::{Any, TypeId}; use std::collections::HashMap; use std::fmt; +use std::fmt::Debug; use std::hash::{BuildHasherDefault, Hasher}; use std::ops::{Deref, DerefMut}; use std::sync::{Arc, Mutex}; diff --git a/rust-runtime/aws-smithy-http/src/query.rs b/rust-runtime/aws-smithy-http/src/query.rs index a744259642..81ac8b344a 100644 --- a/rust-runtime/aws-smithy-http/src/query.rs +++ b/rust-runtime/aws-smithy-http/src/query.rs @@ -13,15 +13,17 @@ use aws_smithy_types::date_time::{DateTimeFormatError, Format}; use aws_smithy_types::DateTime; use percent_encoding::utf8_percent_encode; +/// Format a given string as a query string. pub fn fmt_string>(t: T) -> String { utf8_percent_encode(t.as_ref(), BASE_SET).to_string() } +/// Format a given [`DateTime`] as a query string. pub fn fmt_timestamp(t: &DateTime, format: Format) -> Result { Ok(fmt_string(t.fmt(format)?)) } -/// Simple abstraction to enable appending params to a string as query params +/// Simple abstraction to enable appending params to a string as query params. /// /// ```rust /// use aws_smithy_http::query::Writer; @@ -31,16 +33,19 @@ pub fn fmt_timestamp(t: &DateTime, format: Format) -> Result { out: &'a mut String, prefix: char, } impl<'a> Writer<'a> { + /// Create a new query string writer. pub fn new(out: &'a mut String) -> Self { Writer { out, prefix: '?' } } + /// Add a new key and value pair to this writer. pub fn push_kv(&mut self, k: &str, v: &str) { self.out.push(self.prefix); self.out.push_str(k); @@ -49,6 +54,7 @@ impl<'a> Writer<'a> { self.prefix = '&'; } + /// Add a new value (which is its own key) to this writer. pub fn push_v(&mut self, v: &str) { self.out.push(self.prefix); self.out.push_str(v); @@ -85,7 +91,7 @@ mod test { proptest! { #[test] fn test_encode_request(s: String) { - let _: Uri = format!("http://host.example.com/?{}", fmt_string(&s)).parse().expect("all strings should be encoded properly"); + let _: Uri = format!("http://host.example.com/?{}", fmt_string(s)).parse().expect("all strings should be encoded properly"); } } } diff --git a/aws/rust-runtime/aws-sigv4/src/http_request/query_writer.rs b/rust-runtime/aws-smithy-http/src/query_writer.rs similarity index 92% rename from aws/rust-runtime/aws-sigv4/src/http_request/query_writer.rs rename to rust-runtime/aws-smithy-http/src/query_writer.rs index 40a98d9aba..d3251143f4 100644 --- a/aws/rust-runtime/aws-sigv4/src/http_request/query_writer.rs +++ b/rust-runtime/aws-smithy-http/src/query_writer.rs @@ -3,11 +3,12 @@ * SPDX-License-Identifier: Apache-2.0 */ -use crate::http_request::url_escape::percent_encode_query; +use crate::query::fmt_string as percent_encode_query; use http::Uri; /// Utility for updating the query string in a [`Uri`]. -pub(super) struct QueryWriter { +#[allow(missing_debug_implementations)] +pub struct QueryWriter { base_uri: Uri, new_path_and_query: String, prefix: Option, @@ -15,7 +16,7 @@ pub(super) struct QueryWriter { impl QueryWriter { /// Creates a new `QueryWriter` based off the given `uri`. - pub(super) fn new(uri: &Uri) -> Self { + pub fn new(uri: &Uri) -> Self { let new_path_and_query = uri .path_and_query() .map(|pq| pq.to_string()) @@ -35,7 +36,7 @@ impl QueryWriter { } /// Clears all query parameters. - pub(super) fn clear_params(&mut self) { + pub fn clear_params(&mut self) { if let Some(index) = self.new_path_and_query.find('?') { self.new_path_and_query.truncate(index); self.prefix = Some('?'); @@ -44,7 +45,7 @@ impl QueryWriter { /// Inserts a new query parameter. The key and value are percent encoded /// by `QueryWriter`. Passing in percent encoded values will result in double encoding. - pub(super) fn insert(&mut self, k: &str, v: &str) { + pub fn insert(&mut self, k: &str, v: &str) { if let Some(prefix) = self.prefix { self.new_path_and_query.push(prefix); } @@ -56,12 +57,12 @@ impl QueryWriter { } /// Returns just the built query string. - pub(super) fn build_query(self) -> String { + pub fn build_query(self) -> String { self.build_uri().query().unwrap_or_default().to_string() } /// Returns a full [`Uri`] with the query string updated. - pub(super) fn build_uri(self) -> Uri { + pub fn build_uri(self) -> Uri { let mut parts = self.base_uri.into_parts(); parts.path_and_query = Some( self.new_path_and_query @@ -142,7 +143,7 @@ mod test { let mut query_writer = QueryWriter::new(&uri); query_writer.insert("key", value); - if let Err(_) = std::panic::catch_unwind(|| query_writer.build_uri()) { + if std::panic::catch_unwind(|| query_writer.build_uri()).is_err() { problematic_chars.push(char::from(byte)); }; } diff --git a/rust-runtime/aws-smithy-http/src/response.rs b/rust-runtime/aws-smithy-http/src/response.rs index dbc2a694f6..9102fcbab4 100644 --- a/rust-runtime/aws-smithy-http/src/response.rs +++ b/rust-runtime/aws-smithy-http/src/response.rs @@ -3,6 +3,8 @@ * SPDX-License-Identifier: Apache-2.0 */ +//! Types for response parsing. + use crate::operation; use bytes::Bytes; @@ -65,7 +67,10 @@ pub trait ParseHttpResponse { /// `ParseStrictResponse` enables operations that _never_ need to stream the body incrementally to /// have cleaner implementations. There is a blanket implementation pub trait ParseStrictResponse { + /// The type returned by this parser. type Output; + + /// Parse an [`http::Response`] into `Self::Output`. fn parse(&self, response: &http::Response) -> Self::Output; } @@ -91,8 +96,8 @@ mod test { #[test] fn supports_streaming_body() { - pub struct S3GetObject { - pub body: SdkBody, + struct S3GetObject { + _body: SdkBody, } struct S3GetObjectParser; @@ -103,7 +108,7 @@ mod test { fn parse_unloaded(&self, response: &mut operation::Response) -> Option { // For responses that pass on the body, use mem::take to leave behind an empty body let body = mem::replace(response.http_mut().body_mut(), SdkBody::taken()); - Some(S3GetObject { body }) + Some(S3GetObject { _body: body }) } fn parse_loaded(&self, _response: &http::Response) -> Self::Output { diff --git a/rust-runtime/aws-smithy-http/src/result.rs b/rust-runtime/aws-smithy-http/src/result.rs index 6cf2fc5c78..0ce42c7cbd 100644 --- a/rust-runtime/aws-smithy-http/src/result.rs +++ b/rust-runtime/aws-smithy-http/src/result.rs @@ -3,16 +3,12 @@ * SPDX-License-Identifier: Apache-2.0 */ -#![warn( - missing_debug_implementations, - missing_docs, - rustdoc::all, - unreachable_pub -)] - //! `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; @@ -30,18 +26,183 @@ pub struct SdkSuccess { pub parsed: O, } +/// Builders for `SdkError` variant context. +pub mod builders { + use super::*; + + macro_rules! source_only_error_builder { + ($errorName:ident, $builderName:ident, $sourceType:ident) => { + #[doc = concat!("Builder for [`", stringify!($errorName), "`](super::", stringify!($errorName), ").")] + #[derive(Debug, Default)] + pub struct $builderName { + source: Option<$sourceType>, + } + + impl $builderName { + #[doc = "Creates a new builder."] + pub fn new() -> Self { Default::default() } + + #[doc = "Sets the error source."] + pub fn source(mut self, source: impl Into<$sourceType>) -> Self { + self.source = Some(source.into()); + self + } + + #[doc = "Sets the error source."] + pub fn set_source(&mut self, source: Option<$sourceType>) -> &mut Self { + self.source = source; + self + } + + #[doc = "Builds the error context."] + pub fn build(self) -> $errorName { + $errorName { source: self.source.expect("source is required") } + } + } + }; + } + + source_only_error_builder!(ConstructionFailure, ConstructionFailureBuilder, BoxError); + source_only_error_builder!(TimeoutError, TimeoutErrorBuilder, BoxError); + source_only_error_builder!(DispatchFailure, DispatchFailureBuilder, ConnectorError); + + /// Builder for [`ResponseError`](super::ResponseError). + #[derive(Debug)] + pub struct ResponseErrorBuilder { + source: Option, + raw: Option, + } + + impl Default for ResponseErrorBuilder { + fn default() -> Self { + Self { + source: None, + raw: None, + } + } + } + + impl ResponseErrorBuilder { + /// Creates a new builder. + pub fn new() -> Self { + Default::default() + } + + /// Sets the error source. + pub fn source(mut self, source: impl Into) -> Self { + self.source = Some(source.into()); + self + } + + /// Sets the error source. + pub fn set_source(&mut self, source: Option) -> &mut Self { + self.source = source; + self + } + + /// Sets the raw response. + pub fn raw(mut self, raw: R) -> Self { + self.raw = Some(raw); + self + } + + /// Sets the raw response. + pub fn set_raw(&mut self, raw: Option) -> &mut Self { + self.raw = raw; + self + } + + /// Builds the error context. + pub fn build(self) -> ResponseError { + ResponseError { + source: self.source.expect("source is required"), + raw: self.raw.expect("a raw response is required"), + } + } + } + + /// Builder for [`ServiceError`](super::ServiceError). + #[derive(Debug)] + pub struct ServiceErrorBuilder { + source: Option, + raw: Option, + } + + impl Default for ServiceErrorBuilder { + fn default() -> Self { + Self { + source: None, + raw: None, + } + } + } + + impl ServiceErrorBuilder { + /// Creates a new builder. + pub fn new() -> Self { + Default::default() + } + + /// Sets the error source. + pub fn source(mut self, source: impl Into) -> Self { + self.source = Some(source.into()); + self + } + + /// Sets the error source. + pub fn set_source(&mut self, source: Option) -> &mut Self { + self.source = source; + self + } + + /// Sets the raw response. + pub fn raw(mut self, raw: R) -> Self { + self.raw = Some(raw); + self + } + + /// Sets the raw response. + pub fn set_raw(&mut self, raw: Option) -> &mut Self { + self.raw = raw; + self + } + + /// Builds the error context. + pub fn build(self) -> ServiceError { + ServiceError { + source: self.source.expect("source is required"), + raw: self.raw.expect("a raw response is required"), + } + } + } +} + /// Error context for [`SdkError::ConstructionFailure`] #[derive(Debug)] pub struct ConstructionFailure { source: BoxError, } +impl ConstructionFailure { + /// Creates a builder for this error context type. + pub fn builder() -> builders::ConstructionFailureBuilder { + builders::ConstructionFailureBuilder::new() + } +} + /// Error context for [`SdkError::TimeoutError`] #[derive(Debug)] pub struct TimeoutError { source: BoxError, } +impl TimeoutError { + /// Creates a builder for this error context type. + pub fn builder() -> builders::TimeoutErrorBuilder { + builders::TimeoutErrorBuilder::new() + } +} + /// Error context for [`SdkError::DispatchFailure`] #[derive(Debug)] pub struct DispatchFailure { @@ -49,6 +210,11 @@ pub struct DispatchFailure { } impl DispatchFailure { + /// Creates a builder for this error context type. + pub fn builder() -> builders::DispatchFailureBuilder { + builders::DispatchFailureBuilder::new() + } + /// Returns true if the error is an IO error pub fn is_io(&self) -> bool { self.source.is_io() @@ -68,6 +234,11 @@ impl DispatchFailure { pub fn is_other(&self) -> Option { self.source.is_other() } + + /// Returns the inner error if it is a connector error + pub fn as_connector_error(&self) -> Option<&ConnectorError> { + Some(&self.source) + } } /// Error context for [`SdkError::ResponseError`] @@ -80,6 +251,11 @@ pub struct ResponseError { } impl ResponseError { + /// Creates a builder for this error context type. + pub fn builder() -> builders::ResponseErrorBuilder { + builders::ResponseErrorBuilder::new() + } + /// Returns a reference to the raw response pub fn raw(&self) -> &R { &self.raw @@ -101,6 +277,11 @@ pub struct ServiceError { } impl ServiceError { + /// Creates a builder for this error context type. + pub fn builder() -> builders::ServiceErrorBuilder { + builders::ServiceErrorBuilder::new() + } + /// Returns the underlying error of type `E` pub fn err(&self) -> &E { &self.source @@ -126,8 +307,11 @@ impl ServiceError { /// /// This trait exists so that [`SdkError::into_service_error`] can be infallible. pub trait CreateUnhandledError { - /// Creates an unhandled error variant with the given `source`. - fn create_unhandled_error(source: Box) -> Self; + /// Creates an unhandled error variant with the given `source` and error metadata. + fn create_unhandled_error( + source: Box, + meta: Option, + ) -> Self; } /// Failed SDK Result @@ -150,7 +334,7 @@ pub enum SdkError { DispatchFailure(DispatchFailure), /// A response was received but it was not parseable according the the protocol (for example - /// the server hung up while the body was being read) + /// the server hung up without sending a complete response) ResponseError(ResponseError), /// An error response was received from the service @@ -200,19 +384,21 @@ impl SdkError { /// /// ```no_run /// # use aws_smithy_http::result::{SdkError, CreateUnhandledError}; - /// # #[derive(Debug)] enum GetObjectErrorKind { NoSuchKey(()), Other(()) } - /// # #[derive(Debug)] struct GetObjectError { kind: GetObjectErrorKind } + /// # #[derive(Debug)] enum GetObjectError { NoSuchKey(()), Other(()) } /// # impl std::fmt::Display for GetObjectError { /// # fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { unimplemented!() } /// # } /// # impl std::error::Error for GetObjectError {} /// # impl CreateUnhandledError for GetObjectError { - /// # fn create_unhandled_error(_: Box) -> Self { unimplemented!() } + /// # fn create_unhandled_error( + /// # _: Box, + /// # _: Option, + /// # ) -> Self { unimplemented!() } /// # } /// # fn example() -> Result<(), GetObjectError> { - /// # let sdk_err = SdkError::service_error(GetObjectError { kind: GetObjectErrorKind::NoSuchKey(()) }, ()); + /// # let sdk_err = SdkError::service_error(GetObjectError::NoSuchKey(()), ()); /// match sdk_err.into_service_error() { - /// GetObjectError { kind: GetObjectErrorKind::NoSuchKey(_) } => { + /// GetObjectError::NoSuchKey(_) => { /// // handle NoSuchKey /// } /// err @ _ => return Err(err), @@ -227,7 +413,7 @@ impl SdkError { { match self { Self::ServiceError(context) => context.source, - _ => E::create_unhandled_error(self.into()), + _ => E::create_unhandled_error(self.into(), None), } } @@ -247,6 +433,21 @@ impl SdkError { ServiceError(context) => Ok(context.source.into()), } } + + /// Maps the service error type in `SdkError::ServiceError` + #[doc(hidden)] + pub fn map_service_error(self, map: impl FnOnce(E) -> E2) -> SdkError { + match self { + Self::ServiceError(context) => SdkError::::ServiceError(ServiceError { + source: map(context.source), + raw: context.raw, + }), + Self::ConstructionFailure(context) => SdkError::::ConstructionFailure(context), + Self::DispatchFailure(context) => SdkError::::DispatchFailure(context), + Self::ResponseError(context) => SdkError::::ResponseError(context), + Self::TimeoutError(context) => SdkError::::TimeoutError(context), + } + } } impl Display for SdkError { @@ -278,6 +479,21 @@ where } } +impl ProvideErrorMetadata for SdkError +where + E: ProvideErrorMetadata, +{ + fn meta(&self) -> &aws_smithy_types::Error { + match self { + Self::ConstructionFailure(_) => &EMPTY_ERROR_METADATA, + Self::TimeoutError(_) => &EMPTY_ERROR_METADATA, + Self::DispatchFailure(_) => &EMPTY_ERROR_METADATA, + Self::ResponseError(_) => &EMPTY_ERROR_METADATA, + Self::ServiceError(err) => err.source.meta(), + } + } +} + #[derive(Debug)] enum ConnectorErrorKind { /// A timeout occurred while processing the request @@ -303,6 +519,22 @@ enum ConnectorErrorKind { pub struct ConnectorError { kind: ConnectorErrorKind, source: BoxError, + connection: ConnectionStatus, +} + +#[non_exhaustive] +#[derive(Debug)] +pub(crate) enum ConnectionStatus { + /// This request was never connected to the remote + /// + /// This indicates the failure was during connection establishment + NeverConnected, + + /// It is unknown whether a connection was established + Unknown, + + /// The request connected to the remote prior to failure + Connected(ConnectionMetadata), } impl Display for ConnectorError { @@ -330,14 +562,28 @@ impl ConnectorError { Self { kind: ConnectorErrorKind::Timeout, source, + connection: ConnectionStatus::Unknown, } } + /// Include connection information along with this error + pub fn with_connection(mut self, info: ConnectionMetadata) -> Self { + self.connection = ConnectionStatus::Connected(info); + self + } + + /// Set the connection status on this error to report that a connection was never established + pub fn never_connected(mut self) -> Self { + self.connection = ConnectionStatus::NeverConnected; + self + } + /// Construct a [`ConnectorError`] from an error caused by the user (e.g. invalid HTTP request) pub fn user(source: BoxError) -> Self { Self { kind: ConnectorErrorKind::User, source, + connection: ConnectionStatus::Unknown, } } @@ -346,6 +592,7 @@ impl ConnectorError { Self { kind: ConnectorErrorKind::Io, source, + connection: ConnectionStatus::Unknown, } } @@ -356,6 +603,7 @@ impl ConnectorError { Self { source, kind: ConnectorErrorKind::Other(kind), + connection: ConnectionStatus::Unknown, } } @@ -381,4 +629,16 @@ impl ConnectorError { _ => None, } } + + /// Returns metadata about the connection + /// + /// If a connection was established and provided by the internal connector, a connection will + /// be returned. + pub fn connection_metadata(&self) -> Option<&ConnectionMetadata> { + match &self.connection { + ConnectionStatus::NeverConnected => None, + ConnectionStatus::Unknown => None, + ConnectionStatus::Connected(conn) => Some(conn), + } + } } diff --git a/rust-runtime/aws-smithy-http/src/retry.rs b/rust-runtime/aws-smithy-http/src/retry.rs index d7075b04e5..30c8654926 100644 --- a/rust-runtime/aws-smithy-http/src/retry.rs +++ b/rust-runtime/aws-smithy-http/src/retry.rs @@ -13,6 +13,7 @@ use aws_smithy_types::retry::{ErrorKind, ProvideErrorKind, RetryKind}; /// Classifies what kind of retry is needed for a given `response`. pub trait ClassifyRetry: Clone { + /// Run this classifier against a response to determine if it should be retried. fn classify_retry(&self, response: Result<&T, &E>) -> RetryKind; } diff --git a/rust-runtime/aws-smithy-json/src/deserialize.rs b/rust-runtime/aws-smithy-json/src/deserialize.rs index 64e30cb404..fd8e25cc26 100644 --- a/rust-runtime/aws-smithy-json/src/deserialize.rs +++ b/rust-runtime/aws-smithy-json/src/deserialize.rs @@ -25,7 +25,7 @@ pub use token::{EscapeError, EscapedStr, Offset, Token}; /// The parser *will* accept multiple valid JSON values. For example, `b"null true"` will /// yield `ValueNull` and `ValueTrue`. It is the responsibility of the caller to handle this for /// their use-case. -pub fn json_token_iter(input: &[u8]) -> JsonTokenIterator { +pub fn json_token_iter(input: &[u8]) -> JsonTokenIterator<'_> { JsonTokenIterator { input, index: 0, @@ -513,7 +513,10 @@ mod tests { use proptest::prelude::*; #[track_caller] - fn expect_token(expected: Option>, actual: Option>) { + fn expect_token( + expected: Option, Error>>, + actual: Option, Error>>, + ) { let (expected, actual) = ( expected.transpose().expect("err in expected"), actual.transpose().expect("err in actual"), @@ -670,7 +673,7 @@ mod tests { fn invalid_numbers() { macro_rules! unexpected_token { ($input:expr, $token:pat, $offset:expr, $msg:pat) => { - let tokens: Vec> = json_token_iter($input).collect(); + let tokens: Vec, Error>> = json_token_iter($input).collect(); assert_eq!(1, tokens.len()); expect_err!( ErrorKind::UnexpectedToken($token, $msg), @@ -681,7 +684,7 @@ mod tests { } let invalid_number = |input, offset| { - let tokens: Vec> = json_token_iter(input).collect(); + let tokens: Vec, Error>> = json_token_iter(input).collect(); assert_eq!(1, tokens.len()); expect_err!( ErrorKind::InvalidNumber, diff --git a/rust-runtime/aws-smithy-json/src/deserialize/token.rs b/rust-runtime/aws-smithy-json/src/deserialize/token.rs index 0bed404d8d..fcce8365f2 100644 --- a/rust-runtime/aws-smithy-json/src/deserialize/token.rs +++ b/rust-runtime/aws-smithy-json/src/deserialize/token.rs @@ -135,7 +135,7 @@ expect_fn!( macro_rules! expect_value_or_null_fn { ($name:ident, $token:ident, $typ:ident, $doc:tt) => { #[doc=$doc] - pub fn $name(token: Option>) -> Result, Error> { + pub fn $name(token: Option, Error>>) -> Result, Error> { match token.transpose()? { Some(Token::ValueNull { .. }) => Ok(None), Some(Token::$token { value, .. }) => Ok(Some(value)), @@ -216,10 +216,12 @@ pub fn expect_timestamp_or_null( } }) .transpose()?, - Format::DateTime | Format::HttpDate => expect_string_or_null(token)? - .map(|v| DateTime::from_str(v.as_escaped_str(), timestamp_format)) - .transpose() - .map_err(|err| Error::custom_source("failed to parse timestamp", err))?, + Format::DateTime | Format::HttpDate | Format::DateTimeWithOffset => { + expect_string_or_null(token)? + .map(|v| DateTime::from_str(v.as_escaped_str(), timestamp_format)) + .transpose() + .map_err(|err| Error::custom_source("failed to parse timestamp", err))? + } }) } @@ -362,7 +364,7 @@ pub mod test { })) } - pub fn object_key(offset: usize, key: &str) -> Option> { + pub fn object_key(offset: usize, key: &str) -> Option, Error>> { Some(Ok(Token::ObjectKey { offset: Offset(offset), key: EscapedStr::new(key), @@ -389,7 +391,7 @@ pub mod test { })) } - pub fn value_string(offset: usize, string: &str) -> Option> { + pub fn value_string(offset: usize, string: &str) -> Option, Error>> { Some(Ok(Token::ValueString { offset: Offset(offset), value: EscapedStr::new(string), diff --git a/rust-runtime/aws-smithy-json/src/escape.rs b/rust-runtime/aws-smithy-json/src/escape.rs index 4643880a66..e09402abd9 100644 --- a/rust-runtime/aws-smithy-json/src/escape.rs +++ b/rust-runtime/aws-smithy-json/src/escape.rs @@ -53,7 +53,7 @@ impl From for EscapeError { } /// Escapes a string for embedding in a JSON string value. -pub fn escape_string(value: &str) -> Cow { +pub(crate) fn escape_string(value: &str) -> Cow<'_, str> { let bytes = value.as_bytes(); for (index, byte) in bytes.iter().enumerate() { match byte { @@ -94,7 +94,7 @@ fn escape_string_inner(start: &[u8], rest: &[u8]) -> String { /// Unescapes a JSON-escaped string. /// If there are no escape sequences, it directly returns the reference. -pub fn unescape_string(value: &str) -> Result, EscapeError> { +pub(crate) fn unescape_string(value: &str) -> Result, EscapeError> { let bytes = value.as_bytes(); for (index, byte) in bytes.iter().enumerate() { if *byte == b'\\' { diff --git a/rust-runtime/aws-smithy-json/src/lib.rs b/rust-runtime/aws-smithy-json/src/lib.rs index 58da71b621..08c324772e 100644 --- a/rust-runtime/aws-smithy-json/src/lib.rs +++ b/rust-runtime/aws-smithy-json/src/lib.rs @@ -3,6 +3,15 @@ * SPDX-License-Identifier: Apache-2.0 */ +#![allow(clippy::derive_partial_eq_without_eq)] +#![warn( + // missing_docs, + rustdoc::missing_crate_level_docs, + unreachable_pub, + // Enabling this requires fixing a macro but I don't understand how to do that. + // rust_2018_idioms +)] + //! JSON Abstractions for Smithy pub mod deserialize; diff --git a/rust-runtime/aws-smithy-json/src/serialize.rs b/rust-runtime/aws-smithy-json/src/serialize.rs index 128fb52914..744028efe4 100644 --- a/rust-runtime/aws-smithy-json/src/serialize.rs +++ b/rust-runtime/aws-smithy-json/src/serialize.rs @@ -134,7 +134,7 @@ impl<'a> JsonObjectWriter<'a> { } /// Starts a value with the given `key`. - pub fn key(&mut self, key: &str) -> JsonValueWriter { + pub fn key(&mut self, key: &str) -> JsonValueWriter<'_> { if self.started { self.json.push(','); } @@ -168,7 +168,7 @@ impl<'a> JsonArrayWriter<'a> { } /// Starts a new value in the array. - pub fn value(&mut self) -> JsonValueWriter { + pub fn value(&mut self) -> JsonValueWriter<'_> { self.comma_delimit(); JsonValueWriter::new(self.json) } diff --git a/rust-runtime/aws-smithy-protocol-test/Cargo.toml b/rust-runtime/aws-smithy-protocol-test/Cargo.toml index b4d5bb516b..e18aea12b4 100644 --- a/rust-runtime/aws-smithy-protocol-test/Cargo.toml +++ b/rust-runtime/aws-smithy-protocol-test/Cargo.toml @@ -15,7 +15,7 @@ regex = "1.5" # Not perfect for our needs, but good for now assert-json-diff = "1.1" -pretty_assertions = "1.0" +pretty_assertions = "1.3" roxmltree = "0.14.1" diff --git a/rust-runtime/aws-smithy-protocol-test/src/lib.rs b/rust-runtime/aws-smithy-protocol-test/src/lib.rs index 735685ac53..da9c213d5d 100644 --- a/rust-runtime/aws-smithy-protocol-test/src/lib.rs +++ b/rust-runtime/aws-smithy-protocol-test/src/lib.rs @@ -3,6 +3,13 @@ * SPDX-License-Identifier: Apache-2.0 */ +#![warn( + // missing_docs, + // rustdoc::missing_crate_level_docs, + unreachable_pub, + rust_2018_idioms +)] + mod urlencoded; mod xml; @@ -156,7 +163,7 @@ pub fn forbid_query_params( request: &Request, forbid_params: &[&str], ) -> Result<(), ProtocolTestFailure> { - let actual_params: HashSet = extract_params(request.uri()) + let actual_params: HashSet> = extract_params(request.uri()) .iter() .map(|param| QueryParam::parse(param)) .collect(); @@ -488,11 +495,11 @@ mod tests { fn test_validate_json_body() { let expected = r#"{"abc": 5 }"#; let actual = r#" {"abc": 5 }"#; - validate_body(&actual, expected, MediaType::Json).expect("inputs matched as JSON"); + validate_body(actual, expected, MediaType::Json).expect("inputs matched as JSON"); let expected = r#"{"abc": 5 }"#; let actual = r#" {"abc": 6 }"#; - validate_body(&actual, expected, MediaType::Json).expect_err("bodies do not match"); + validate_body(actual, expected, MediaType::Json).expect_err("bodies do not match"); } #[test] @@ -501,22 +508,22 @@ mod tests { hello123 "#; let actual = "hello123"; - validate_body(&actual, expected, MediaType::Xml).expect("inputs match as XML"); + validate_body(actual, expected, MediaType::Xml).expect("inputs match as XML"); let expected = r#" hello123 "#; let actual = "hello124"; - validate_body(&actual, expected, MediaType::Xml).expect_err("inputs are different"); + validate_body(actual, expected, MediaType::Xml).expect_err("inputs are different"); } #[test] fn test_validate_non_json_body() { let expected = r#"asdf"#; let actual = r#"asdf "#; - validate_body(&actual, expected, MediaType::from("something/else")) + validate_body(actual, expected, MediaType::from("something/else")) .expect_err("bodies do not match"); - validate_body(&expected, expected, MediaType::from("something/else")) + validate_body(expected, expected, MediaType::from("something/else")) .expect("inputs matched exactly") } diff --git a/rust-runtime/aws-smithy-protocol-test/src/urlencoded.rs b/rust-runtime/aws-smithy-protocol-test/src/urlencoded.rs index fce91e4c9d..83c64e777b 100644 --- a/rust-runtime/aws-smithy-protocol-test/src/urlencoded.rs +++ b/rust-runtime/aws-smithy-protocol-test/src/urlencoded.rs @@ -40,7 +40,7 @@ fn rewrite_url_encoded_body(input: &str) -> String { entries.join("\n&") } -pub fn try_url_encoded_form_equivalent( +pub(crate) fn try_url_encoded_form_equivalent( actual: &str, expected: &str, ) -> Result<(), ProtocolTestFailure> { diff --git a/rust-runtime/aws-smithy-protocol-test/src/xml.rs b/rust-runtime/aws-smithy-protocol-test/src/xml.rs index 76a3ac9e11..c92882c5ee 100644 --- a/rust-runtime/aws-smithy-protocol-test/src/xml.rs +++ b/rust-runtime/aws-smithy-protocol-test/src/xml.rs @@ -11,7 +11,7 @@ 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 fn try_xml_equivalent(actual: &str, expected: &str) -> Result<(), ProtocolTestFailure> { +pub(crate) fn try_xml_equivalent(actual: &str, expected: &str) -> Result<(), ProtocolTestFailure> { 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), @@ -34,20 +34,20 @@ pub fn try_xml_equivalent(actual: &str, expected: &str) -> Result<(), ProtocolTe /// /// 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 fn normalize_xml(s: &str) -> Result { +pub(crate) fn normalize_xml(s: &str) -> Result { let rotree = roxmltree::Document::parse(s)?; let root = rotree.root().first_child().unwrap(); Ok(unparse_tag(root, 1)) } -/// Unparse a "tag" (a subtree) of an XML document +/// Un-parse a "tag" (a subtree) of an XML document /// /// This function will first convert each of the tag's children into a normalized string /// then, assuming the node does not represent a list, it will simply lexicographically sort the fully /// rendered nodes themselves (avoiding the need to sort on keys then values then attributes, etc.). /// /// This is not a fast algorithm ;-), but the test data it's running on is not large. -fn unparse_tag(tag: Node, depth: usize) -> String { +fn unparse_tag(tag: Node<'_, '_>, depth: usize) -> String { let mut out = String::new(); out.push_str(&unparse_start_element(tag)); let mut child_nodes = tag @@ -78,7 +78,7 @@ fn unparse_tag(tag: Node, depth: usize) -> String { /// If the node is a start element, it will recursively convert all of its children /// If the node is text, it will return the text, stripped of whitespace /// If the node is neither, it is ignored -fn unparse_node(n: Node, depth: usize) -> Option { +fn unparse_node(n: Node<'_, '_>, depth: usize) -> Option { match n.node_type() { NodeType::Element => Some(unparse_tag(n, depth)), NodeType::Text => { @@ -96,7 +96,7 @@ fn unparse_node(n: Node, depth: usize) -> Option { /// Convert a node back into a string. Attributes are sorted by key, value, and namespace /// /// Produces output like: `` -fn unparse_start_element(n: Node) -> String { +fn unparse_start_element(n: Node<'_, '_>) -> String { let mut out = String::new(); out.push('<'); out.push_str(n.tag_name().name()); @@ -121,7 +121,7 @@ fn unparse_start_element(n: Node) -> String { out } -fn is_list(node: Node) -> bool { +fn is_list(node: Node<'_, '_>) -> bool { // a flat list looks like: // // example1 diff --git a/rust-runtime/aws-smithy-query/src/lib.rs b/rust-runtime/aws-smithy-query/src/lib.rs index 3b9d57a9d0..9ce7799718 100644 --- a/rust-runtime/aws-smithy-query/src/lib.rs +++ b/rust-runtime/aws-smithy-query/src/lib.rs @@ -3,6 +3,14 @@ * SPDX-License-Identifier: Apache-2.0 */ +#![allow(clippy::derive_partial_eq_without_eq)] +#![warn( + // missing_docs, + rustdoc::missing_crate_level_docs, + unreachable_pub, + rust_2018_idioms +)] + //! Abstractions for the Smithy AWS Query protocol use aws_smithy_types::date_time::{DateTimeFormatError, Format}; @@ -25,7 +33,7 @@ impl<'a> QueryWriter<'a> { QueryWriter { output } } - pub fn prefix(&mut self, prefix: &'a str) -> QueryValueWriter { + pub fn prefix(&mut self, prefix: &'a str) -> QueryValueWriter<'_> { QueryValueWriter::new(self.output, Cow::Borrowed(prefix)) } @@ -62,7 +70,7 @@ impl<'a> QueryMapWriter<'a> { } } - pub fn entry(&mut self, key: &str) -> QueryValueWriter { + pub fn entry(&mut self, key: &str) -> QueryValueWriter<'_> { let entry = if self.flatten { "" } else { ".entry" }; write!( &mut self.output, @@ -115,7 +123,7 @@ impl<'a> QueryListWriter<'a> { } } - pub fn entry(&mut self) -> QueryValueWriter { + pub fn entry(&mut self) -> QueryValueWriter<'_> { let value_name = if self.flatten { format!("{}.{}", self.prefix, self.next_index) } else if self.member_override.is_some() { @@ -154,7 +162,7 @@ impl<'a> QueryValueWriter<'a> { } /// Starts a new prefix. - pub fn prefix(&mut self, prefix: &'a str) -> QueryValueWriter { + pub fn prefix(&mut self, prefix: &'a str) -> QueryValueWriter<'_> { QueryValueWriter::new( self.output, Cow::Owned(format!("{}.{}", self.prefix, prefix)), diff --git a/rust-runtime/aws-smithy-runtime-api/Cargo.toml b/rust-runtime/aws-smithy-runtime-api/Cargo.toml new file mode 100644 index 0000000000..9c6d96f52f --- /dev/null +++ b/rust-runtime/aws-smithy-runtime-api/Cargo.toml @@ -0,0 +1,23 @@ +[package] +name = "aws-smithy-runtime-api" +version = "0.0.0-smithy-rs-head" +authors = ["AWS Rust SDK Team ", "Zelda Hessler "] +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 + +[dependencies] +aws-smithy-types = { path = "../aws-smithy-types" } +aws-smithy-http = { path = "../aws-smithy-http" } +tokio = { version = "1.25", features = ["sync"] } +http = "0.2.3" + +[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-runtime-api/LICENSE b/rust-runtime/aws-smithy-runtime-api/LICENSE new file mode 100644 index 0000000000..67db858821 --- /dev/null +++ b/rust-runtime/aws-smithy-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/rust-runtime/aws-smithy-runtime-api/README.md b/rust-runtime/aws-smithy-runtime-api/README.md new file mode 100644 index 0000000000..0e33661021 --- /dev/null +++ b/rust-runtime/aws-smithy-runtime-api/README.md @@ -0,0 +1,9 @@ +# aws-smithy-retries + +**This crate is UNSTABLE! All internal and external interfaces are subject to change without notice.** + +Smithy runtime types. + + +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/external-types.toml b/rust-runtime/aws-smithy-runtime-api/external-types.toml new file mode 100644 index 0000000000..4c9c93b25d --- /dev/null +++ b/rust-runtime/aws-smithy-runtime-api/external-types.toml @@ -0,0 +1,7 @@ +allowed_external_types = [ + "aws_smithy_types::*", + "aws_smithy_http::*", + + "http::request::Request", + "http::response::Response", +] diff --git a/rust-runtime/aws-smithy-runtime-api/src/client.rs b/rust-runtime/aws-smithy-runtime-api/src/client.rs new file mode 100644 index 0000000000..3b01dca567 --- /dev/null +++ b/rust-runtime/aws-smithy-runtime-api/src/client.rs @@ -0,0 +1,22 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +/// 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; diff --git a/rust-runtime/aws-smithy-runtime-api/src/client/identity.rs b/rust-runtime/aws-smithy-runtime-api/src/client/identity.rs new file mode 100644 index 0000000000..f958fab918 --- /dev/null +++ b/rust-runtime/aws-smithy-runtime-api/src/client/identity.rs @@ -0,0 +1,67 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +use aws_smithy_types::DateTime; +use std::any::Any; +use std::fmt::Debug; +use std::sync::Arc; + +#[derive(Clone, Debug)] +pub struct Identity { + data: Arc, + expiration: Option, +} + +impl Identity { + pub fn new(data: impl Any + Send + Sync, expiration: Option) -> Self { + Self { + data: Arc::new(data), + expiration, + } + } + + pub fn data(&self) -> Option<&T> { + self.data.downcast_ref() + } + + pub fn expiration(&self) -> Option<&DateTime> { + self.expiration.as_ref() + } +} + +#[cfg(test)] +mod tests { + use super::*; + use aws_smithy_types::date_time::Format; + + #[test] + fn check_send_sync() { + fn is_send_sync(_: T) {} + is_send_sync(Identity::new("foo", None)); + } + + #[test] + fn create_retrieve_identity() { + #[derive(Debug)] + struct MyIdentityData { + first: String, + last: String, + } + + let expiration = + DateTime::from_str("2023-03-15T00:00:00.000Z", Format::DateTimeWithOffset).unwrap(); + let identity = Identity::new( + MyIdentityData { + first: "foo".into(), + last: "bar".into(), + }, + Some(expiration.clone()), + ); + + assert_eq!("foo", identity.data::().unwrap().first); + assert_eq!("bar", identity.data::().unwrap().last); + assert_eq!(Some(&expiration), identity.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 new file mode 100644 index 0000000000..f584c3752c --- /dev/null +++ b/rust-runtime/aws-smithy-runtime-api/src/client/interceptors.rs @@ -0,0 +1,622 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +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) => { + #[doc = $docs] + fn $name( + &mut self, + context: &InterceptorContext, + cfg: &mut ConfigBag, + ) -> Result<(), InterceptorError> { + let _ctx = context; + let _cfg = cfg; + Ok(()) + } + }; +} + +/// An interceptor allows injecting code into the SDK ’s request execution pipeline. +/// +/// ## Terminology: +/// - An execution is one end-to-end invocation against an SDK client. +/// - An attempt is an attempt at performing an execution. By default executions are retried multiple +/// times based on the client ’s retry strategy. +/// - A hook is a single method on the interceptor, allowing injection of code into a specific part +/// 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. + " + ); + + interceptor_trait_fn!( + modify_before_serialization, + " + A hook called before the input message is marshalled into a + transport message. + This method has the ability to modify and return a new + request message of the same type. + + **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 + **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()]. + + **Return Constraints:** The input message returned by this hook + MUST be the same type of input message passed into this hook. + If not, an error will immediately be raised. + " + ); + + interceptor_trait_fn!( + read_before_serialization, + " + A hook called before 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 `after_serialization` is + very close to the amount of time spent marshalling the request. + + **Available Information:** The [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()]. + " + ); + + interceptor_trait_fn!( + read_after_serialization, + " + /// 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()]. + " + ); + + interceptor_trait_fn!( + modify_before_retry_loop, + " + 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. + + **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()]. + + **Return Constraints:** The transport request message returned by this + hook MUST be the same type of request message passed into this hook + If not, an error will immediately be raised. + " + ); + + interceptor_trait_fn!( + read_before_attempt, + " + A hook called before each attempt at sending the transmission + request message to the service. + + **When:** This will **ALWAYS** be called once per attempt, except when a + 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, + 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 `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 + `before_attempt` methods raise errors, the latest will be used + and earlier ones will be logged and dropped. + " + ); + + interceptor_trait_fn!( + modify_before_signing, + " + A hook called before the transport request message is signed. + 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_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). + + **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()]. + + **Return Constraints:** The transport request message returned by this + hook MUST be the same type of request message passed into this hook + + If not, an error will immediately be raised. + " + ); + + interceptor_trait_fn!( + read_before_signing, + " + A hook called before the transport request message is signed. + + **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. The duration between + 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. + 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()]. + " + ); + + interceptor_trait_fn!( + read_after_signing, + " + A hook called after the transport request message is signed. + + **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. The duration between + 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. + 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()]. + " + ); + + interceptor_trait_fn!( + modify_before_transmit, + " + /// 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 + 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()]. + + **Return Constraints:** The transport request message returned by this + hook MUST be the same type of request message passed into this hook + + If not, an error will immediately be raised. + " + ); + + interceptor_trait_fn!( + read_before_transmit, + " + A hook called before the transport request message is sent to the + service. + + **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. The duration between + invocation of this hook and `after_transmit` is very close to + the amount of time spent communicating with the service. + 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, + 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()]. + " + ); + + interceptor_trait_fn!( + read_after_transmit, + " + A hook called after the transport request message is sent to the + service and a transport response message is received. + + **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. The duration between + invocation of this hook and `before_transmit` is very close to + the amount of time spent communicating with the service. + 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, + 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()]. + " + ); + + interceptor_trait_fn!( + modify_before_deserialization, + " + A hook called before the transport response message is unmarshalled. + This method has the ability to modify and return a new transport + response 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()], + [InterceptorContext::request()] and + [InterceptorContext::response()] are **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 + 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()]. + + **Return Constraints:** The transport response message returned by this + hook MUST be the same type of response message passed into + this hook. If not, an error will immediately be raised. + " + ); + + interceptor_trait_fn!( + read_before_deserialization, + " + A hook called before the transport response message is unmarshalled + + **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. The duration between + invocation of this hook and `after_deserialization` is very close + to the amount of time spent unmarshalling the service response. + 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, + 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()]. + " + ); + + interceptor_trait_fn!( + read_after_deserialization, + " + A hook called after the transport response message is unmarshalled. + + **When:** This will **ALWAYS** be called once per attempt, except when a + failure occurs earlier in the request pipeline. The duration + between invocation of this hook and `before_deserialization` is + very close to the amount of time spent unmarshalling the + service response. Depending on the protocol and operation, + 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 + 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()]. + " + ); + + 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. + " + ); + + interceptor_trait_fn!( + read_after_execution, + " + 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()] + 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:** 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. + " + ); +} + +pub struct Interceptors { + client_interceptors: Vec>>, + operation_interceptors: Vec>>, +} + +impl Default for Interceptors { + fn default() -> Self { + Self { + client_interceptors: Vec::new(), + operation_interceptors: Vec::new(), + } + } +} + +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(()) + } + }; +} + +impl Interceptors { + pub fn new() -> Self { + Self::default() + } + + pub fn with_client_interceptor( + &mut self, + interceptor: impl Interceptor + 'static, + ) -> &mut Self { + self.client_interceptors.push(Box::new(interceptor)); + self + } + + pub fn with_operation_interceptor( + &mut self, + interceptor: impl Interceptor + 'static, + ) -> &mut Self { + self.operation_interceptors.push(Box::new(interceptor)); + self + } + + 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); +} 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 new file mode 100644 index 0000000000..fc1065e05c --- /dev/null +++ b/rust-runtime/aws-smithy-runtime-api/src/client/interceptors/context.rs @@ -0,0 +1,140 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +use super::InterceptorError; +use crate::type_erasure::TypeErasedBox; + +pub type Input = TypeErasedBox; +pub type Output = TypeErasedBox; +pub type Error = TypeErasedBox; +pub type OutputOrError = Result; + +/// A container for the data currently available to an interceptor. +pub struct InterceptorContext { + input: Input, + output_or_error: Option, + request: Option, + response: 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 { + input, + output_or_error: None, + request: None, + response: None, + } + } + + /// Retrieve the modeled request for the operation being invoked. + pub fn input(&self) -> &Input { + &self.input + } + + /// Retrieve the modeled request for the operation being invoked. + pub fn input_mut(&mut self) -> &mut Input { + &mut self.input + } + + /// 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) + } + + /// 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) + } + + /// 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) + } + + /// 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) + } + + /// 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> { + self.output_or_error + .as_ref() + .ok_or_else(InterceptorError::invalid_modeled_response_access) + .map(|res| res.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_mut(&mut self) -> Result<&mut Result, InterceptorError> { + self.output_or_error + .as_mut() + .ok_or_else(InterceptorError::invalid_modeled_response_access) + } + + // There is no set_modeled_request method because that can only be set once, during context construction + + 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."); + } + + self.request = Some(request); + } + + 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."); + } + + self.response = Some(response); + } + + 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." + ); + } + + self.output_or_error = Some(output); + } + + #[doc(hidden)] + pub fn into_parts( + self, + ) -> ( + Input, + Option, + Option, + Option, + ) { + ( + self.input, + self.output_or_error, + self.request, + self.response, + ) + } +} 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 new file mode 100644 index 0000000000..9fba28092a --- /dev/null +++ b/rust-runtime/aws-smithy-runtime-api/src/client/interceptors/error.rs @@ -0,0 +1,342 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +//! Errors related to smithy interceptors + +use std::fmt; + +/// A generic error that behaves itself in async contexts +pub type BoxError = Box; + +/// An error related to smithy interceptors. +#[derive(Debug)] +pub struct InterceptorError { + kind: ErrorKind, + 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 tx_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 tx_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 modeled_response out of turn + pub fn invalid_modeled_response_access() -> Self { + Self { + kind: ErrorKind::InvalidModeledResponseAccess, + source: None, + } + } +} + +#[derive(Debug)] +enum ErrorKind { + /// An error occurred within the read_before_execution interceptor + ReadBeforeExecution, + /// An error occurred within the modify_before_serialization interceptor + ModifyBeforeSerialization, + /// An error occurred within the read_before_serialization interceptor + ReadBeforeSerialization, + /// An error occurred within the read_after_serialization interceptor + ReadAfterSerialization, + /// An error occurred within the modify_before_retry_loop interceptor + ModifyBeforeRetryLoop, + /// An error occurred within the read_before_attempt interceptor + ReadBeforeAttempt, + /// An error occurred within the modify_before_signing interceptor + ModifyBeforeSigning, + /// An error occurred within the read_before_signing interceptor + ReadBeforeSigning, + /// An error occurred within the read_after_signing interceptor + ReadAfterSigning, + /// An error occurred within the modify_before_transmit interceptor + ModifyBeforeTransmit, + /// An error occurred within the read_before_transmit interceptor + ReadBeforeTransmit, + /// An error occurred within the read_after_transmit interceptor + ReadAfterTransmit, + /// An error occurred within the modify_before_deserialization interceptor + ModifyBeforeDeserialization, + /// An error occurred within the read_before_deserialization interceptor + ReadBeforeDeserialization, + /// An error occurred within the read_after_deserialization interceptor + ReadAfterDeserialization, + /// An error occurred within the modify_before_attempt_completion interceptor + ModifyBeforeAttemptCompletion, + /// An error occurred within the read_after_attempt interceptor + ReadAfterAttempt, + /// An error occurred within the modify_before_completion interceptor + 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 + InvalidResponseAccess, + /// An interceptor tried to access the modeled_response out of turn + InvalidModeledResponseAccess, +} + +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") + } + InvalidModeledResponseAccess => write!( + f, + "tried to access modeled_response before response deserialization" + ), + } + } +} + +impl std::error::Error for InterceptorError { + 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 new file mode 100644 index 0000000000..c70bcb8c19 --- /dev/null +++ b/rust-runtime/aws-smithy-runtime-api/src/client/orchestrator.rs @@ -0,0 +1,374 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * 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}; +use aws_smithy_http::body::SdkBody; +use aws_smithy_http::property_bag::PropertyBag; +use std::any::Any; +use std::fmt::Debug; +use std::future::Future; +use std::pin::Pin; +use std::sync::Arc; + +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, cfg: &ConfigBag) -> BoxFallibleFut<()>; +} + +pub trait RequestSerializer: Send + Sync + Debug { + fn serialize_input(&self, input: &Input, cfg: &ConfigBag) -> 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>; +} + +#[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)>, +} + +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) + } +} + +#[derive(Debug)] +struct HttpAuthSchemesInner { + schemes: Vec<(&'static str, Box)>, +} +#[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 trait EndpointResolver: Send + Sync + Debug { + fn resolve_and_apply_endpoint( + &self, + request: &mut HttpRequest, + cfg: &ConfigBag, + ) -> 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") + } + + fn set_retry_strategy(&mut self, retry_strategy: impl RetryStrategy + 'static) { + self.put::>(Box::new(retry_strategy)); + } + + fn endpoint_resolver(&self) -> &dyn EndpointResolver { + &**self + .get::>() + .expect("an endpoint resolver must be set") + } + + fn set_endpoint_resolver(&mut self, endpoint_resolver: impl EndpointResolver + 'static) { + self.put::>(Box::new(endpoint_resolver)); + } + + fn identity_resolvers(&self) -> &IdentityResolvers { + self.get::() + .expect("identity resolvers must be configured") + } + + fn set_identity_resolvers(&mut self, identity_resolvers: IdentityResolvers) { + self.put::(identity_resolvers); + } + + fn connection(&self) -> &dyn Connection { + &**self + .get::>() + .expect("missing connector") + } + + fn set_connection(&mut self, connection: impl Connection + 'static) { + self.put::>(Box::new(connection)); + } + + fn request_serializer(&self) -> &dyn RequestSerializer { + &**self + .get::>() + .expect("missing request serializer") + } + + fn set_request_serializer(&mut self, request_serializer: impl RequestSerializer + 'static) { + self.put::>(Box::new(request_serializer)); + } + + fn response_deserializer(&self) -> &dyn ResponseDeserializer { + &**self + .get::>() + .expect("missing response deserializer") + } + + fn set_response_deserializer( + &mut self, + response_deserializer: impl ResponseDeserializer + 'static, + ) { + self.put::>(Box::new(response_deserializer)); + } + + fn trace_probe(&self) -> &dyn TraceProbe { + &**self + .get::>() + .expect("missing trace probe") + } + + fn set_trace_probe(&mut self, trace_probe: impl TraceProbe + 'static) { + self.put::>(Box::new(trace_probe)); + } +} + +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, + } + } + } + + #[derive(Debug, Default)] + pub struct HttpAuthSchemesBuilder { + schemes: Vec<(&'static str, Box)>, + } + + 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, + }), + } + } + } +} diff --git a/rust-runtime/aws-smithy-runtime-api/src/client/retries.rs b/rust-runtime/aws-smithy-runtime-api/src/client/retries.rs new file mode 100644 index 0000000000..91fd85e69b --- /dev/null +++ b/rust-runtime/aws-smithy-runtime-api/src/client/retries.rs @@ -0,0 +1,6 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +pub mod rate_limiting; 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 new file mode 100644 index 0000000000..fcb6085fbd --- /dev/null +++ b/rust-runtime/aws-smithy-runtime-api/src/client/retries/rate_limiting.rs @@ -0,0 +1,13 @@ +/* + * 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 new file mode 100644 index 0000000000..b4f4d9821f --- /dev/null +++ b/rust-runtime/aws-smithy-runtime-api/src/client/retries/rate_limiting/error.rs @@ -0,0 +1,50 @@ +/* + * 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 new file mode 100644 index 0000000000..70d620e790 --- /dev/null +++ b/rust-runtime/aws-smithy-runtime-api/src/client/retries/rate_limiting/token.rs @@ -0,0 +1,65 @@ +/* + * 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 new file mode 100644 index 0000000000..500ee723fd --- /dev/null +++ b/rust-runtime/aws-smithy-runtime-api/src/client/retries/rate_limiting/token_bucket.rs @@ -0,0 +1,235 @@ +/* + * 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_plugin.rs b/rust-runtime/aws-smithy-runtime-api/src/client/runtime_plugin.rs new file mode 100644 index 0000000000..f29de78e0b --- /dev/null +++ b/rust-runtime/aws-smithy-runtime-api/src/client/runtime_plugin.rs @@ -0,0 +1,85 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +use crate::config_bag::ConfigBag; + +type BoxError = Box; + +pub trait RuntimePlugin { + fn configure(&self, cfg: &mut ConfigBag) -> Result<(), BoxError>; +} + +impl From for Box +where + T: RuntimePlugin + 'static, +{ + fn from(t: T) -> Self { + Box::new(t) as _ + } +} + +#[derive(Default)] +pub struct RuntimePlugins { + client_plugins: Vec>, + operation_plugins: Vec>, +} + +impl RuntimePlugins { + pub fn new() -> Self { + Default::default() + } + + pub fn with_client_plugin( + &mut self, + plugin: impl Into>, + ) -> &mut Self { + self.client_plugins.push(plugin.into()); + self + } + + pub fn with_operation_plugin( + &mut self, + plugin: impl Into>, + ) -> &mut Self { + self.operation_plugins.push(plugin.into()); + self + } + + pub fn apply_client_configuration(&self, cfg: &mut ConfigBag) -> Result<(), BoxError> { + for plugin in self.client_plugins.iter() { + plugin.configure(cfg)?; + } + + Ok(()) + } + + pub fn apply_operation_configuration(&self, cfg: &mut ConfigBag) -> Result<(), BoxError> { + for plugin in self.operation_plugins.iter() { + plugin.configure(cfg)?; + } + + Ok(()) + } +} + +#[cfg(test)] +mod tests { + use super::{BoxError, RuntimePlugin, RuntimePlugins}; + use crate::config_bag::ConfigBag; + + struct SomeStruct; + + impl RuntimePlugin for SomeStruct { + fn configure(&self, _cfg: &mut ConfigBag) -> Result<(), BoxError> { + todo!() + } + } + + #[test] + fn can_add_runtime_plugin_implementors_to_runtime_plugins() { + let mut rps = RuntimePlugins::new(); + rps.with_client_plugin(SomeStruct); + } +} diff --git a/rust-runtime/aws-smithy-runtime-api/src/config_bag.rs b/rust-runtime/aws-smithy-runtime-api/src/config_bag.rs new file mode 100644 index 0000000000..78e7b2502e --- /dev/null +++ b/rust-runtime/aws-smithy-runtime-api/src/config_bag.rs @@ -0,0 +1,329 @@ +/* + * 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 new file mode 100644 index 0000000000..275f311756 --- /dev/null +++ b/rust-runtime/aws-smithy-runtime-api/src/lib.rs @@ -0,0 +1,22 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +#![warn( + // missing_docs, + rustdoc::missing_crate_level_docs, + unreachable_pub, + rust_2018_idioms +)] + +//! Basic types for the new smithy client orchestrator. + +/// Smithy runtime for client orchestration. +pub mod client; + +/// A typemap for storing configuration. +pub mod config_bag; + +/// Utilities for type erasure. +pub mod type_erasure; diff --git a/rust-runtime/aws-smithy-runtime-api/src/type_erasure.rs b/rust-runtime/aws-smithy-runtime-api/src/type_erasure.rs new file mode 100644 index 0000000000..72de2ccbbf --- /dev/null +++ b/rust-runtime/aws-smithy-runtime-api/src/type_erasure.rs @@ -0,0 +1,149 @@ +/* + * 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") + } +} + +/// 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 new file mode 100644 index 0000000000..4c035d154c --- /dev/null +++ b/rust-runtime/aws-smithy-runtime/Cargo.toml @@ -0,0 +1,27 @@ +[package] +name = "aws-smithy-runtime" +version = "0.0.0-smithy-rs-head" +authors = ["AWS Rust SDK Team ", "Zelda Hessler "] +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 + +[dependencies] +aws-smithy-http = { path = "../aws-smithy-http" } +aws-smithy-runtime-api = { path = "../aws-smithy-runtime-api" } +aws-smithy-types = { path = "../aws-smithy-types" } +bytes = "1" +http = "0.2.8" +http-body = "0.4.5" +pin-utils = "0.1.0" +tracing = "0.1" + +[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-runtime/LICENSE b/rust-runtime/aws-smithy-runtime/LICENSE new file mode 100644 index 0000000000..67db858821 --- /dev/null +++ b/rust-runtime/aws-smithy-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/rust-runtime/aws-smithy-runtime/README.md b/rust-runtime/aws-smithy-runtime/README.md new file mode 100644 index 0000000000..ba6dbc29ae --- /dev/null +++ b/rust-runtime/aws-smithy-runtime/README.md @@ -0,0 +1,9 @@ +# aws-smithy-orchestrator + +**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. + + +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/external-types.toml b/rust-runtime/aws-smithy-runtime/external-types.toml new file mode 100644 index 0000000000..206c9a3098 --- /dev/null +++ b/rust-runtime/aws-smithy-runtime/external-types.toml @@ -0,0 +1,7 @@ +allowed_external_types = [ + "aws_smithy_runtime_api::*", + "aws_smithy_http::*", + # TODO(audit-external-type-usage) We should newtype these or otherwise avoid exposing them + "http::request::Request", + "http::response::Response", +] diff --git a/rust-runtime/aws-smithy-runtime/src/client.rs b/rust-runtime/aws-smithy-runtime/src/client.rs new file mode 100644 index 0000000000..5870bd2af2 --- /dev/null +++ b/rust-runtime/aws-smithy-runtime/src/client.rs @@ -0,0 +1,6 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +pub mod orchestrator; diff --git a/rust-runtime/aws-smithy-runtime/src/client/orchestrator.rs b/rust-runtime/aws-smithy-runtime/src/client/orchestrator.rs new file mode 100644 index 0000000000..cdecbcc154 --- /dev/null +++ b/rust-runtime/aws-smithy-runtime/src/client/orchestrator.rs @@ -0,0 +1,157 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +use self::auth::orchestrate_auth; +use crate::client::orchestrator::http::read_body; +use crate::client::orchestrator::phase::Phase; +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::client::orchestrator::{ + BoxError, ConfigBagAccessors, HttpRequest, HttpResponse, +}; +use aws_smithy_runtime_api::client::runtime_plugin::RuntimePlugins; +use aws_smithy_runtime_api::config_bag::ConfigBag; +use tracing::{debug_span, Instrument}; + +mod auth; +mod http; +pub(self) mod phase; + +pub async fn invoke( + input: Input, + interceptors: &mut Interceptors, + runtime_plugins: &RuntimePlugins, + cfg: &mut ConfigBag, +) -> Result> { + 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.input(), cfg)?; + 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(); + + { + 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) { + // Yes, let's retry the request + Ok(true) => 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)); + } + } + + 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(cfg); + + break handling_phase.include(|ctx| interceptors.read_after_execution(ctx, cfg))?; + }; + + handling_phase.finalize() +} + +// 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, cfg) + })? + .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(); + + // 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 + }; + + 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), + None => read_body(response) + .instrument(debug_span!("read_body")) + .await + .map(|_| response_deserializer.deserialize_nonstreaming(response)), + } + }; + + 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)) +} diff --git a/rust-runtime/aws-smithy-runtime/src/client/orchestrator/auth.rs b/rust-runtime/aws-smithy-runtime/src/client/orchestrator/auth.rs new file mode 100644 index 0000000000..8f15051a9b --- /dev/null +++ b/rust-runtime/aws-smithy-runtime/src/client/orchestrator/auth.rs @@ -0,0 +1,36 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * 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; + +pub(super) async fn orchestrate_auth( + dispatch_phase: Phase, + 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(()); + } + } + + Err("No auth scheme matched auth options. This is a bug. Please file an issue.".into()) + }) +} diff --git a/rust-runtime/aws-smithy-runtime/src/client/orchestrator/http.rs b/rust-runtime/aws-smithy-runtime/src/client/orchestrator/http.rs new file mode 100644 index 0000000000..247464a7bd --- /dev/null +++ b/rust-runtime/aws-smithy-runtime/src/client/orchestrator/http.rs @@ -0,0 +1,35 @@ +/* + * 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::client::orchestrator::HttpResponse; +use bytes::{Buf, Bytes}; +use http_body::Body; +use pin_utils::pin_mut; + +async fn body_to_bytes(body: SdkBody) -> Result::Error> { + let mut output = Vec::new(); + pin_mut!(body); + while let Some(buf) = body.data().await { + let mut buf = buf?; + while buf.has_remaining() { + output.extend_from_slice(buf.chunk()); + buf.advance(buf.chunk().len()) + } + } + + Ok(Bytes::from(output)) +} + +pub(crate) async fn read_body(response: &mut HttpResponse) -> Result<(), ::Error> { + let mut body = SdkBody::taken(); + std::mem::swap(&mut body, response.body_mut()); + + let bytes = body_to_bytes(body).await?; + let mut body = SdkBody::from(bytes); + std::mem::swap(&mut body, response.body_mut()); + + 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 new file mode 100644 index 0000000000..d163b5c743 --- /dev/null +++ b/rust-runtime/aws-smithy-runtime/src/client/orchestrator/phase.rs @@ -0,0 +1,119 @@ +/* + * 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/lib.rs b/rust-runtime/aws-smithy-runtime/src/lib.rs new file mode 100644 index 0000000000..195fde2d06 --- /dev/null +++ b/rust-runtime/aws-smithy-runtime/src/lib.rs @@ -0,0 +1,13 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +#![warn( + // missing_docs, + // rustdoc::missing_crate_level_docs, + unreachable_pub, + rust_2018_idioms +)] + +pub mod client; diff --git a/rust-runtime/aws-smithy-types-convert/src/lib.rs b/rust-runtime/aws-smithy-types-convert/src/lib.rs index 4487db52b6..e44f5c1c53 100644 --- a/rust-runtime/aws-smithy-types-convert/src/lib.rs +++ b/rust-runtime/aws-smithy-types-convert/src/lib.rs @@ -5,12 +5,12 @@ //! Conversions between `aws-smithy-types` and the types of frequently used Rust libraries. +#![allow(clippy::derive_partial_eq_without_eq)] #![warn( missing_docs, rustdoc::missing_crate_level_docs, - missing_debug_implementations, - rust_2018_idioms, - unreachable_pub + unreachable_pub, + rust_2018_idioms )] #[cfg(any(feature = "convert-time", feature = "convert-chrono"))] diff --git a/rust-runtime/aws-smithy-types/Cargo.toml b/rust-runtime/aws-smithy-types/Cargo.toml index 7b17b55722..9a6fa835f4 100644 --- a/rust-runtime/aws-smithy-types/Cargo.toml +++ b/rust-runtime/aws-smithy-types/Cargo.toml @@ -12,7 +12,7 @@ itoa = "1.0.0" num-integer = "0.1.44" ryu = "1.0.5" time = { version = "0.3.4", features = ["parsing"] } -base64-simd = "0.7" +base64-simd = "0.8" [target.'cfg(aws_sdk_unstable)'.dependencies.serde] version = "1" diff --git a/rust-runtime/aws-smithy-types/benches/base64.rs b/rust-runtime/aws-smithy-types/benches/base64.rs index d29654b059..0539c190ff 100644 --- a/rust-runtime/aws-smithy-types/benches/base64.rs +++ b/rust-runtime/aws-smithy-types/benches/base64.rs @@ -4,7 +4,6 @@ */ use criterion::{criterion_group, criterion_main, BenchmarkId, Criterion}; -use rand; use rand::distributions::{Alphanumeric, DistString}; /// Generates a random string of a given length @@ -155,6 +154,7 @@ mod handrolled_base64 { } /// Failure to decode a base64 value. + #[allow(clippy::enum_variant_names)] #[derive(Debug, Clone, Eq, PartialEq)] #[non_exhaustive] pub enum DecodeError { diff --git a/rust-runtime/aws-smithy-types/src/base64.rs b/rust-runtime/aws-smithy-types/src/base64.rs index 460a07e33d..76a7943ccd 100644 --- a/rust-runtime/aws-smithy-types/src/base64.rs +++ b/rust-runtime/aws-smithy-types/src/base64.rs @@ -3,9 +3,9 @@ * SPDX-License-Identifier: Apache-2.0 */ -//! A thin wrapper over `base64-simd` +//! A thin wrapper over [`base64-simd`](https://docs.rs/base64-simd/) -use base64_simd::Base64; +use base64_simd::STANDARD; use std::error::Error; /// Failure to decode a base64 value. @@ -28,20 +28,15 @@ impl std::fmt::Display for DecodeError { /// /// If input is not a valid base64 encoded string, this function will return `DecodeError`. pub fn decode(input: impl AsRef) -> Result, DecodeError> { - Base64::STANDARD - .decode_to_boxed_bytes(input.as_ref().as_bytes()) - .map(|bytes| bytes.into_vec()) - .map_err(DecodeError) + STANDARD.decode_to_vec(input.as_ref()).map_err(DecodeError) } /// Encode `input` into base64 using the standard base64 alphabet pub fn encode(input: impl AsRef<[u8]>) -> String { - Base64::STANDARD - .encode_to_boxed_str(input.as_ref()) - .into_string() + STANDARD.encode_to_string(input.as_ref()) } /// Returns the base64 representation's length for the given `length` of data pub fn encoded_length(length: usize) -> usize { - Base64::STANDARD.encoded_length(length) + STANDARD.encoded_length(length) } diff --git a/rust-runtime/aws-smithy-types/src/date_time/format.rs b/rust-runtime/aws-smithy-types/src/date_time/format.rs index 6108c7e28d..7253dec1ac 100644 --- a/rust-runtime/aws-smithy-types/src/date_time/format.rs +++ b/rust-runtime/aws-smithy-types/src/date_time/format.rs @@ -193,7 +193,7 @@ pub(crate) mod http_date { let mut out = String::with_capacity(32); fn push_digit(out: &mut String, digit: u8) { debug_assert!(digit < 10); - out.push((b'0' + digit as u8) as char); + out.push((b'0' + digit) as char); } out.push_str(weekday); @@ -399,13 +399,23 @@ pub(crate) mod rfc3339 { use time::format_description::well_known::Rfc3339; use time::OffsetDateTime; + #[derive(Debug, PartialEq)] + pub(crate) enum AllowOffsets { + OffsetsAllowed, + OffsetsForbidden, + } + // OK: 1985-04-12T23:20:50.52Z // OK: 1985-04-12T23:20:50Z // // Timezones not supported: // Not OK: 1985-04-12T23:20:50-02:00 - pub(crate) fn parse(s: &str) -> Result { - if !matches!(s.chars().last(), Some('Z')) { + pub(crate) fn parse( + s: &str, + allow_offsets: AllowOffsets, + ) -> Result { + if allow_offsets == AllowOffsets::OffsetsForbidden && !matches!(s.chars().last(), Some('Z')) + { return Err(DateTimeParseErrorKind::Invalid( "Smithy does not support timezone offsets in RFC-3339 date times".into(), ) @@ -419,10 +429,13 @@ pub(crate) mod rfc3339 { } /// Read 1 RFC-3339 date from &str and return the remaining str - pub(crate) fn read(s: &str) -> Result<(DateTime, &str), DateTimeParseError> { + pub(crate) fn read( + s: &str, + allow_offests: AllowOffsets, + ) -> Result<(DateTime, &str), DateTimeParseError> { let delim = s.find('Z').map(|idx| idx + 1).unwrap_or_else(|| s.len()); let (head, rest) = s.split_at(delim); - Ok((parse(head)?, rest)) + Ok((parse(head, allow_offests)?, rest)) } /// Format a [DateTime] in the RFC-3339 date format @@ -491,6 +504,7 @@ pub(crate) mod rfc3339 { #[cfg(test)] mod tests { use super::*; + use crate::date_time::format::rfc3339::AllowOffsets; use crate::DateTime; use lazy_static::lazy_static; use proptest::prelude::*; @@ -634,7 +648,9 @@ mod tests { #[test] fn parse_date_time() { - parse_test(&TEST_CASES.parse_date_time, rfc3339::parse); + parse_test(&TEST_CASES.parse_date_time, |date| { + rfc3339::parse(date, AllowOffsets::OffsetsForbidden) + }); } #[test] @@ -655,8 +671,10 @@ mod tests { #[test] fn read_rfc3339_date_comma_split() { let date = "1985-04-12T23:20:50Z,1985-04-12T23:20:51Z"; - let (e1, date) = rfc3339::read(date).expect("should succeed"); - let (e2, date2) = rfc3339::read(&date[1..]).expect("should succeed"); + let (e1, date) = + rfc3339::read(date, AllowOffsets::OffsetsForbidden).expect("should succeed"); + let (e2, date2) = + rfc3339::read(&date[1..], AllowOffsets::OffsetsForbidden).expect("should succeed"); assert_eq!(date2, ""); assert_eq!(date, ",1985-04-12T23:20:51Z"); let expected = DateTime::from_secs_and_nanos(482196050, 0); @@ -665,9 +683,15 @@ mod tests { assert_eq!(e2, expected); } + #[test] + fn parse_rfc3339_with_timezone() { + let dt = rfc3339::parse("1985-04-12T21:20:51-02:00", AllowOffsets::OffsetsAllowed); + assert_eq!(dt.unwrap(), DateTime::from_secs_and_nanos(482196051, 0)); + } + #[test] fn parse_rfc3339_timezone_forbidden() { - let dt = rfc3339::parse("1985-04-12T23:20:50-02:00"); + let dt = rfc3339::parse("1985-04-12T23:20:50-02:00", AllowOffsets::OffsetsForbidden); assert!(matches!( dt.unwrap_err(), DateTimeParseError { 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 3bc8e708c2..8a24c4d544 100644 --- a/rust-runtime/aws-smithy-types/src/date_time/mod.rs +++ b/rust-runtime/aws-smithy-types/src/date_time/mod.rs @@ -5,6 +5,7 @@ //! DateTime type for representing Smithy timestamps. +use crate::date_time::format::rfc3339::AllowOffsets; use crate::date_time::format::DateTimeParseErrorKind; use num_integer::div_mod_floor; use num_integer::Integer; @@ -160,7 +161,8 @@ impl DateTime { /// Parses a `DateTime` from a string using the given `format`. pub fn from_str(s: &str, format: Format) -> Result { match format { - Format::DateTime => format::rfc3339::parse(s), + Format::DateTime => format::rfc3339::parse(s, AllowOffsets::OffsetsForbidden), + Format::DateTimeWithOffset => format::rfc3339::parse(s, AllowOffsets::OffsetsAllowed), Format::HttpDate => format::http_date::parse(s), Format::EpochSeconds => format::epoch_seconds::parse(s), } @@ -212,7 +214,8 @@ impl DateTime { /// Enable parsing multiple dates from the same string pub fn read(s: &str, format: Format, delim: char) -> Result<(Self, &str), DateTimeParseError> { let (inst, next) = match format { - Format::DateTime => format::rfc3339::read(s)?, + Format::DateTime => format::rfc3339::read(s, AllowOffsets::OffsetsForbidden)?, + Format::DateTimeWithOffset => format::rfc3339::read(s, AllowOffsets::OffsetsAllowed)?, Format::HttpDate => format::http_date::read(s)?, Format::EpochSeconds => { let split_point = s.find(delim).unwrap_or(s.len()); @@ -234,7 +237,7 @@ impl DateTime { /// Returns an error if the given `DateTime` cannot be represented by the desired format. pub fn fmt(&self, format: Format) -> Result { match format { - Format::DateTime => format::rfc3339::format(self), + Format::DateTime | Format::DateTimeWithOffset => format::rfc3339::format(self), Format::EpochSeconds => Ok(format::epoch_seconds::format(self)), Format::HttpDate => format::http_date::format(self), } @@ -319,10 +322,15 @@ 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. + /// RFC-3339 Date Time. If the date time has an offset, an error will be returned DateTime, + + /// RFC-3339 Date Time. Offsets are supported + DateTimeWithOffset, + /// Date format used by the HTTP `Date` header, specified in RFC-7231. HttpDate, + /// Number of seconds since the Unix epoch formatted as a floating point. EpochSeconds, } diff --git a/rust-runtime/aws-smithy-types/src/error.rs b/rust-runtime/aws-smithy-types/src/error.rs index dc41a67d83..b83d6ffe07 100644 --- a/rust-runtime/aws-smithy-types/src/error.rs +++ b/rust-runtime/aws-smithy-types/src/error.rs @@ -3,147 +3,16 @@ * SPDX-License-Identifier: Apache-2.0 */ -//! Generic errors for Smithy codegen +//! Errors for Smithy codegen -use crate::retry::{ErrorKind, ProvideErrorKind}; -use std::collections::HashMap; use std::fmt; pub mod display; +pub mod metadata; +mod unhandled; -/// Generic Error type -/// -/// For many services, Errors are modeled. However, many services only partially model errors or don't -/// model errors at all. In these cases, the SDK will return this generic error type to expose the -/// `code`, `message` and `request_id`. -#[derive(Debug, Eq, PartialEq, Default, Clone)] -pub struct Error { - code: Option, - message: Option, - request_id: Option, - extras: HashMap<&'static str, String>, -} - -/// Builder for [`Error`]. -#[derive(Debug, Default)] -pub struct Builder { - inner: Error, -} - -impl Builder { - /// Sets the error message. - pub fn message(&mut self, message: impl Into) -> &mut Self { - self.inner.message = Some(message.into()); - self - } - - /// Sets the error code. - pub fn code(&mut self, code: impl Into) -> &mut Self { - self.inner.code = Some(code.into()); - self - } - - /// Sets the request ID the error happened for. - pub fn request_id(&mut self, request_id: impl Into) -> &mut Self { - self.inner.request_id = Some(request_id.into()); - self - } - - /// Set a custom field on the error metadata - /// - /// Typically, these will be accessed with an extension trait: - /// ```rust - /// use aws_smithy_types::Error; - /// const HOST_ID: &str = "host_id"; - /// trait S3ErrorExt { - /// fn extended_request_id(&self) -> Option<&str>; - /// } - /// - /// impl S3ErrorExt for Error { - /// fn extended_request_id(&self) -> Option<&str> { - /// self.extra(HOST_ID) - /// } - /// } - /// - /// fn main() { - /// // Extension trait must be brought into scope - /// use S3ErrorExt; - /// let sdk_response: Result<(), Error> = Err(Error::builder().custom(HOST_ID, "x-1234").build()); - /// if let Err(err) = sdk_response { - /// println!("request id: {:?}, extended request id: {:?}", err.request_id(), err.extended_request_id()); - /// } - /// } - /// ``` - pub fn custom(&mut self, key: &'static str, value: impl Into) -> &mut Self { - self.inner.extras.insert(key, value.into()); - self - } - - /// Creates the error. - pub fn build(&mut self) -> Error { - std::mem::take(&mut self.inner) - } -} - -impl Error { - /// Returns the error code. - pub fn code(&self) -> Option<&str> { - self.code.as_deref() - } - /// Returns the error message. - pub fn message(&self) -> Option<&str> { - self.message.as_deref() - } - /// Returns the request ID the error occurred for, if it's available. - pub fn request_id(&self) -> Option<&str> { - self.request_id.as_deref() - } - /// Returns additional information about the error if it's present. - pub fn extra(&self, key: &'static str) -> Option<&str> { - self.extras.get(key).map(|k| k.as_str()) - } - - /// Creates an `Error` builder. - pub fn builder() -> Builder { - Builder::default() - } - - /// Converts an `Error` into a builder. - pub fn into_builder(self) -> Builder { - Builder { inner: self } - } -} - -impl ProvideErrorKind for Error { - fn retryable_error_kind(&self) -> Option { - None - } - - fn code(&self) -> Option<&str> { - Error::code(self) - } -} - -impl fmt::Display for Error { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - let mut fmt = f.debug_struct("Error"); - if let Some(code) = &self.code { - fmt.field("code", code); - } - if let Some(message) = &self.message { - fmt.field("message", message); - } - if let Some(req_id) = &self.request_id { - fmt.field("request_id", req_id); - } - for (k, v) in &self.extras { - fmt.field(k, &v); - } - fmt.finish() - } -} - -impl std::error::Error for Error {} +pub use metadata::ErrorMetadata; +pub use unhandled::Unhandled; #[derive(Debug)] pub(super) enum TryFromNumberErrorKind { diff --git a/rust-runtime/aws-smithy-types/src/error/metadata.rs b/rust-runtime/aws-smithy-types/src/error/metadata.rs new file mode 100644 index 0000000000..06925e13f9 --- /dev/null +++ b/rust-runtime/aws-smithy-types/src/error/metadata.rs @@ -0,0 +1,166 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +//! Error metadata + +use crate::retry::{ErrorKind, ProvideErrorKind}; +use std::collections::HashMap; +use std::fmt; + +/// Trait to retrieve error metadata from a result +pub trait ProvideErrorMetadata { + /// Returns error metadata, which includes the error code, message, + /// request ID, and potentially additional information. + fn meta(&self) -> &ErrorMetadata; + + /// Returns the error code if it's available. + fn code(&self) -> Option<&str> { + self.meta().code() + } + + /// Returns the error message, if there is one. + fn message(&self) -> Option<&str> { + self.meta().message() + } +} + +/// Empty error metadata +#[doc(hidden)] +pub const EMPTY_ERROR_METADATA: ErrorMetadata = ErrorMetadata { + code: None, + message: None, + extras: None, +}; + +/// Generic Error type +/// +/// For many services, Errors are modeled. However, many services only partially model errors or don't +/// model errors at all. In these cases, the SDK will return this generic error type to expose the +/// `code`, `message` and `request_id`. +#[derive(Debug, Eq, PartialEq, Default, Clone)] +pub struct ErrorMetadata { + code: Option, + message: Option, + extras: Option>, +} + +/// Builder for [`ErrorMetadata`]. +#[derive(Debug, Default)] +pub struct Builder { + inner: ErrorMetadata, +} + +impl Builder { + /// Sets the error message. + pub fn message(mut self, message: impl Into) -> Self { + self.inner.message = Some(message.into()); + self + } + + /// Sets the error code. + pub fn code(mut self, code: impl Into) -> Self { + self.inner.code = Some(code.into()); + self + } + + /// Set a custom field on the error metadata + /// + /// Typically, these will be accessed with an extension trait: + /// ```rust + /// use aws_smithy_types::Error; + /// const HOST_ID: &str = "host_id"; + /// trait S3ErrorExt { + /// fn extended_request_id(&self) -> Option<&str>; + /// } + /// + /// impl S3ErrorExt for Error { + /// fn extended_request_id(&self) -> Option<&str> { + /// self.extra(HOST_ID) + /// } + /// } + /// + /// fn main() { + /// // Extension trait must be brought into scope + /// use S3ErrorExt; + /// let sdk_response: Result<(), Error> = Err(Error::builder().custom(HOST_ID, "x-1234").build()); + /// if let Err(err) = sdk_response { + /// println!("extended request id: {:?}", err.extended_request_id()); + /// } + /// } + /// ``` + pub fn custom(mut self, key: &'static str, value: impl Into) -> Self { + if self.inner.extras.is_none() { + self.inner.extras = Some(HashMap::new()); + } + self.inner + .extras + .as_mut() + .unwrap() + .insert(key, value.into()); + self + } + + /// Creates the error. + pub fn build(self) -> ErrorMetadata { + self.inner + } +} + +impl ErrorMetadata { + /// Returns the error code. + pub fn code(&self) -> Option<&str> { + self.code.as_deref() + } + /// Returns the error message. + pub fn message(&self) -> Option<&str> { + self.message.as_deref() + } + /// Returns additional information about the error if it's present. + pub fn extra(&self, key: &'static str) -> Option<&str> { + self.extras + .as_ref() + .and_then(|extras| extras.get(key).map(|k| k.as_str())) + } + + /// Creates an `Error` builder. + pub fn builder() -> Builder { + Builder::default() + } + + /// Converts an `Error` into a builder. + pub fn into_builder(self) -> Builder { + Builder { inner: self } + } +} + +impl ProvideErrorKind for ErrorMetadata { + fn retryable_error_kind(&self) -> Option { + None + } + + fn code(&self) -> Option<&str> { + ErrorMetadata::code(self) + } +} + +impl fmt::Display for ErrorMetadata { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + let mut fmt = f.debug_struct("Error"); + if let Some(code) = &self.code { + fmt.field("code", code); + } + if let Some(message) = &self.message { + fmt.field("message", message); + } + if let Some(extras) = &self.extras { + for (k, v) in extras { + fmt.field(k, &v); + } + } + fmt.finish() + } +} + +impl std::error::Error for ErrorMetadata {} diff --git a/rust-runtime/aws-smithy-types/src/error/unhandled.rs b/rust-runtime/aws-smithy-types/src/error/unhandled.rs new file mode 100644 index 0000000000..2397d700ff --- /dev/null +++ b/rust-runtime/aws-smithy-types/src/error/unhandled.rs @@ -0,0 +1,90 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +//! Unhandled error type. + +use crate::error::{metadata::ProvideErrorMetadata, ErrorMetadata}; +use std::error::Error as StdError; + +/// Builder for [`Unhandled`] +#[derive(Default, Debug)] +pub struct Builder { + source: Option>, + meta: Option, +} + +impl Builder { + /// Sets the error source + pub fn source(mut self, source: impl Into>) -> Self { + self.source = Some(source.into()); + self + } + + /// Sets the error source + pub fn set_source( + &mut self, + source: Option>, + ) -> &mut Self { + self.source = source; + self + } + + /// Sets the error metadata + pub fn meta(mut self, meta: ErrorMetadata) -> Self { + self.meta = Some(meta); + self + } + + /// Sets the error metadata + pub fn set_meta(&mut self, meta: Option) -> &mut Self { + self.meta = meta; + self + } + + /// Builds the unhandled error + pub fn build(self) -> Unhandled { + Unhandled { + source: self.source.expect("unhandled errors must have a source"), + meta: self.meta.unwrap_or_default(), + } + } +} + +/// An unexpected error occurred (e.g., invalid JSON returned by the service or an unknown error code). +/// +/// When logging an error from the SDK, it is recommended that you either wrap the error in +/// [`DisplayErrorContext`](crate::error::display::DisplayErrorContext), use another +/// error reporter library that visits the error's cause/source chain, or call +/// [`Error::source`](std::error::Error::source) for more details about the underlying cause. +#[derive(Debug)] +pub struct Unhandled { + source: Box, + meta: ErrorMetadata, +} + +impl Unhandled { + /// Returns a builder to construct an unhandled error. + pub fn builder() -> Builder { + Default::default() + } +} + +impl std::fmt::Display for Unhandled { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> Result<(), std::fmt::Error> { + write!(f, "unhandled error") + } +} + +impl StdError for Unhandled { + fn source(&self) -> Option<&(dyn StdError + 'static)> { + Some(self.source.as_ref() as _) + } +} + +impl ProvideErrorMetadata for Unhandled { + fn meta(&self) -> &ErrorMetadata { + &self.meta + } +} diff --git a/rust-runtime/aws-smithy-types/src/lib.rs b/rust-runtime/aws-smithy-types/src/lib.rs index f6542733c6..92fe6a75be 100644 --- a/rust-runtime/aws-smithy-types/src/lib.rs +++ b/rust-runtime/aws-smithy-types/src/lib.rs @@ -5,6 +5,7 @@ //! Protocol-agnostic types for smithy-rs. +#![allow(clippy::derive_partial_eq_without_eq)] #![warn( missing_docs, rustdoc::missing_crate_level_docs, diff --git a/rust-runtime/aws-smithy-types/src/retry.rs b/rust-runtime/aws-smithy-types/src/retry.rs index 43be79cae4..b96ababf06 100644 --- a/rust-runtime/aws-smithy-types/src/retry.rs +++ b/rust-runtime/aws-smithy-types/src/retry.rs @@ -143,6 +143,7 @@ pub struct RetryConfigBuilder { mode: Option, max_attempts: Option, initial_backoff: Option, + reconnect_mode: Option, } impl RetryConfigBuilder { @@ -163,6 +164,30 @@ impl RetryConfigBuilder { self } + /// Set the [`ReconnectMode`] for the retry strategy + /// + /// By default, when a transient error is encountered, the connection in use will be poisoned. + /// This prevents reusing a connection to a potentially bad host but may increase the load on + /// the server. + /// + /// This behavior can be disabled by setting [`ReconnectMode::ReuseAllConnections`] instead. + pub fn reconnect_mode(mut self, reconnect_mode: ReconnectMode) -> Self { + self.set_reconnect_mode(Some(reconnect_mode)); + self + } + + /// Set the [`ReconnectMode`] for the retry strategy + /// + /// By default, when a transient error is encountered, the connection in use will be poisoned. + /// This prevents reusing a connection to a potentially bad host but may increase the load on + /// the server. + /// + /// This behavior can be disabled by setting [`ReconnectMode::ReuseAllConnections`] instead. + pub fn set_reconnect_mode(&mut self, reconnect_mode: Option) -> &mut Self { + self.reconnect_mode = reconnect_mode; + self + } + /// Sets the max attempts. This value must be greater than zero. pub fn set_max_attempts(&mut self, max_attempts: Option) -> &mut Self { self.max_attempts = max_attempts; @@ -208,6 +233,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), + reconnect_mode: self.reconnect_mode.or(other.reconnect_mode), } } @@ -219,6 +245,9 @@ impl RetryConfigBuilder { initial_backoff: self .initial_backoff .unwrap_or_else(|| Duration::from_secs(1)), + reconnect_mode: self + .reconnect_mode + .unwrap_or(ReconnectMode::ReconnectOnTransientError), } } } @@ -230,6 +259,23 @@ pub struct RetryConfig { mode: RetryMode, max_attempts: u32, initial_backoff: Duration, + reconnect_mode: ReconnectMode, +} + +/// Mode for connection re-establishment +/// +/// By default, when a transient error is encountered, the connection in use will be poisoned. This +/// behavior can be disabled by setting [`ReconnectMode::ReuseAllConnections`] instead. +#[derive(Debug, Clone, PartialEq, Copy)] +pub enum ReconnectMode { + /// Reconnect on [`ErrorKind::TransientError`] + ReconnectOnTransientError, + + /// Disable reconnect on error + /// + /// When this setting is applied, 503s, timeouts, and other transient errors will _not_ + /// lead to a new connection being established unless the connection is closed by the remote. + ReuseAllConnections, } impl RetryConfig { @@ -239,6 +285,7 @@ impl RetryConfig { mode: RetryMode::Standard, max_attempts: 3, initial_backoff: Duration::from_secs(1), + reconnect_mode: ReconnectMode::ReconnectOnTransientError, } } @@ -260,6 +307,18 @@ impl RetryConfig { self } + /// Set the [`ReconnectMode`] for the retry strategy + /// + /// By default, when a transient error is encountered, the connection in use will be poisoned. + /// This prevents reusing a connection to a potentially bad host but may increase the load on + /// the server. + /// + /// This behavior can be disabled by setting [`ReconnectMode::ReuseAllConnections`] instead. + pub fn with_reconnect_mode(mut self, reconnect_mode: ReconnectMode) -> Self { + self.reconnect_mode = reconnect_mode; + self + } + /// Set the multiplier used when calculating backoff times as part of an /// [exponential backoff with jitter](https://aws.amazon.com/blogs/architecture/exponential-backoff-and-jitter/) /// strategy. Most services should work fine with the default duration of 1 second, but if you @@ -287,6 +346,11 @@ impl RetryConfig { self.mode } + /// Returns the [`ReconnectMode`] + pub fn reconnect_mode(&self) -> ReconnectMode { + self.reconnect_mode + } + /// Returns the max attempts. pub fn max_attempts(&self) -> u32 { self.max_attempts diff --git a/rust-runtime/aws-smithy-xml/src/decode.rs b/rust-runtime/aws-smithy-xml/src/decode.rs index c567fbd9d9..522c5b12e3 100644 --- a/rust-runtime/aws-smithy-xml/src/decode.rs +++ b/rust-runtime/aws-smithy-xml/src/decode.rs @@ -170,7 +170,7 @@ impl<'a> StartEl<'a> { } /// Returns true of `el` at `depth` is a match for this `start_el` - fn end_el(&self, el: ElementEnd, depth: Depth) -> bool { + fn end_el(&self, el: ElementEnd<'_>, depth: Depth) -> bool { if depth != self.depth { return false; } @@ -428,7 +428,7 @@ fn next_start_element<'a, 'inp>( /// Returns the data element at the current position /// -/// If the current position is not a data element (and is instead a ) an error +/// 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>>, diff --git a/rust-runtime/aws-smithy-xml/src/escape.rs b/rust-runtime/aws-smithy-xml/src/escape.rs index eed9f0ff10..35130f57a1 100644 --- a/rust-runtime/aws-smithy-xml/src/escape.rs +++ b/rust-runtime/aws-smithy-xml/src/escape.rs @@ -10,7 +10,7 @@ const ESCAPES: &[char] = &[ '&', '\'', '\"', '<', '>', '\u{00D}', '\u{00A}', '\u{0085}', '\u{2028}', ]; -pub fn escape(s: &str) -> Cow { +pub(crate) fn escape(s: &str) -> Cow<'_, str> { let mut remaining = s; if !s.contains(ESCAPES) { return Cow::Borrowed(s); diff --git a/rust-runtime/aws-smithy-xml/src/lib.rs b/rust-runtime/aws-smithy-xml/src/lib.rs index f9d4e990ce..a179c0993c 100644 --- a/rust-runtime/aws-smithy-xml/src/lib.rs +++ b/rust-runtime/aws-smithy-xml/src/lib.rs @@ -3,8 +3,17 @@ * SPDX-License-Identifier: Apache-2.0 */ +#![allow(clippy::derive_partial_eq_without_eq)] +#![warn( + // missing_docs, + rustdoc::missing_crate_level_docs, + unreachable_pub, + rust_2018_idioms +)] + //! Abstractions for Smithy //! [XML Binding Traits](https://awslabs.github.io/smithy/1.0/spec/core/xml-traits.html) + pub mod decode; pub mod encode; mod escape; diff --git a/rust-runtime/aws-smithy-xml/src/unescape.rs b/rust-runtime/aws-smithy-xml/src/unescape.rs index 4195164946..5d9f71383f 100644 --- a/rust-runtime/aws-smithy-xml/src/unescape.rs +++ b/rust-runtime/aws-smithy-xml/src/unescape.rs @@ -15,7 +15,7 @@ use std::borrow::Cow; /// /// If no escape sequences are present, Cow<&'str> will be returned, avoiding the need /// to copy the String. -pub fn unescape(s: &str) -> Result, XmlDecodeError> { +pub(crate) fn unescape(s: &str) -> Result, XmlDecodeError> { // no &, no need to escape anything if !s.contains('&') { return Ok(Cow::Borrowed(s)); diff --git a/rust-runtime/aws-smithy-xml/tests/handwritten_parsers.rs b/rust-runtime/aws-smithy-xml/tests/handwritten_parsers.rs index 8db9e202c0..3d0ef718ab 100644 --- a/rust-runtime/aws-smithy-xml/tests/handwritten_parsers.rs +++ b/rust-runtime/aws-smithy-xml/tests/handwritten_parsers.rs @@ -44,7 +44,7 @@ fn deserialize_xml_attribute(inp: &str) -> Result let mut doc = Document::new(inp); let mut root = doc.root_element()?; #[allow(unused_assignments)] - #[allow(clippy::blacklisted_name)] + #[allow(clippy::disallowed_names)] let mut foo: Option = None; let mut bar: Option = None; foo = root.start_el().attr("foo").map(|attr| attr.to_string()); diff --git a/rust-runtime/inlineable/src/aws_query_compatible_errors.rs b/rust-runtime/inlineable/src/aws_query_compatible_errors.rs new file mode 100644 index 0000000000..7a94064d71 --- /dev/null +++ b/rust-runtime/inlineable/src/aws_query_compatible_errors.rs @@ -0,0 +1,103 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +use http::header::ToStrError; +use http::{HeaderMap, HeaderValue}; + +const X_AMZN_QUERY_ERROR: &str = "x-amzn-query-error"; +const QUERY_COMPATIBLE_ERRORCODE_DELIMITER: char = ';'; + +fn aws_query_compatible_error_from_header( + headers: &HeaderMap, +) -> Result, ToStrError> { + headers + .get(X_AMZN_QUERY_ERROR) + .map(|v| v.to_str()) + .transpose() +} + +/// Obtains custom error code and error type from the given `headers`. +/// +/// Looks up a value for the `X_AMZN_QUERY_ERROR` header and if found, the value should be in the +/// form of `;`. The function then splits it into two parts and returns +/// a (error code, error type) as a tuple. +/// +/// Any execution path besides the above happy path will yield a `None`. +pub fn parse_aws_query_compatible_error(headers: &HeaderMap) -> Option<(&str, &str)> { + let header_value = match aws_query_compatible_error_from_header(headers) { + Ok(error) => error?, + _ => return None, + }; + + header_value + .find(QUERY_COMPATIBLE_ERRORCODE_DELIMITER) + .map(|idx| (&header_value[..idx], &header_value[idx + 1..])) +} + +#[cfg(test)] +mod test { + use crate::aws_query_compatible_errors::{ + aws_query_compatible_error_from_header, parse_aws_query_compatible_error, + X_AMZN_QUERY_ERROR, + }; + + #[test] + fn aws_query_compatible_error_from_header_should_provide_value_for_custom_header() { + let mut response: http::Response<()> = http::Response::default(); + response.headers_mut().insert( + X_AMZN_QUERY_ERROR, + http::HeaderValue::from_static("AWS.SimpleQueueService.NonExistentQueue;Sender"), + ); + + let actual = aws_query_compatible_error_from_header(response.headers()).unwrap(); + + assert_eq!( + Some("AWS.SimpleQueueService.NonExistentQueue;Sender"), + actual, + ); + } + + #[test] + fn parse_aws_query_compatible_error_should_parse_code_and_type_fields() { + let mut response: http::Response<()> = http::Response::default(); + response.headers_mut().insert( + X_AMZN_QUERY_ERROR, + http::HeaderValue::from_static("AWS.SimpleQueueService.NonExistentQueue;Sender"), + ); + + let actual = parse_aws_query_compatible_error(response.headers()); + + assert_eq!( + Some(("AWS.SimpleQueueService.NonExistentQueue", "Sender")), + actual, + ); + } + + #[test] + fn parse_aws_query_compatible_error_should_return_none_when_header_value_has_no_delimiter() { + let mut response: http::Response<()> = http::Response::default(); + response.headers_mut().insert( + X_AMZN_QUERY_ERROR, + http::HeaderValue::from_static("AWS.SimpleQueueService.NonExistentQueue"), + ); + + let actual = parse_aws_query_compatible_error(response.headers()); + + assert_eq!(None, actual); + } + + #[test] + fn parse_aws_query_compatible_error_should_return_none_when_there_is_no_target_header() { + let mut response: http::Response<()> = http::Response::default(); + response.headers_mut().insert( + "x-amzn-requestid", + http::HeaderValue::from_static("a918fbf2-457a-4fe1-99ba-5685ce220fc1"), + ); + + let actual = parse_aws_query_compatible_error(response.headers()); + + assert_eq!(None, actual); + } +} diff --git a/rust-runtime/inlineable/src/ec2_query_errors.rs b/rust-runtime/inlineable/src/ec2_query_errors.rs index a7fc1b1163..3355dbe004 100644 --- a/rust-runtime/inlineable/src/ec2_query_errors.rs +++ b/rust-runtime/inlineable/src/ec2_query_errors.rs @@ -3,6 +3,7 @@ * SPDX-License-Identifier: Apache-2.0 */ +use aws_smithy_types::error::metadata::{Builder as ErrorMetadataBuilder, ErrorMetadata}; use aws_smithy_xml::decode::{try_data, Document, ScopedDecoder, XmlDecodeError}; use std::convert::TryFrom; @@ -13,36 +14,30 @@ pub fn body_is_error(body: &[u8]) -> Result { Ok(scoped.start_el().matches("Response")) } -pub fn parse_generic_error(body: &[u8]) -> Result { +pub fn parse_error_metadata(body: &[u8]) -> Result { let mut doc = Document::try_from(body)?; let mut root = doc.root_element()?; - let mut err_builder = aws_smithy_types::Error::builder(); + let mut err_builder = ErrorMetadata::builder(); while let Some(mut tag) = root.next_tag() { - match tag.start_el().local() { - "Errors" => { - while let Some(mut error_tag) = tag.next_tag() { - if let "Error" = error_tag.start_el().local() { - while let Some(mut error_field) = error_tag.next_tag() { - match error_field.start_el().local() { - "Code" => { - err_builder.code(try_data(&mut error_field)?); - } - "Message" => { - err_builder.message(try_data(&mut error_field)?); - } - _ => {} + if tag.start_el().local() == "Errors" { + while let Some(mut error_tag) = tag.next_tag() { + if let "Error" = error_tag.start_el().local() { + while let Some(mut error_field) = error_tag.next_tag() { + match error_field.start_el().local() { + "Code" => { + err_builder = err_builder.code(try_data(&mut error_field)?); } + "Message" => { + err_builder = err_builder.message(try_data(&mut error_field)?); + } + _ => {} } } } } - "RequestId" => { - err_builder.request_id(try_data(&mut tag)?); - } - _ => {} } } - Ok(err_builder.build()) + Ok(err_builder) } #[allow(unused)] @@ -71,7 +66,7 @@ pub fn error_scope<'a, 'b>( #[cfg(test)] mod test { - use super::{body_is_error, parse_generic_error}; + use super::{body_is_error, parse_error_metadata}; use crate::ec2_query_errors::error_scope; use aws_smithy_xml::decode::Document; use std::convert::TryFrom; @@ -92,8 +87,7 @@ mod test { "#; assert!(body_is_error(xml).unwrap()); - let parsed = parse_generic_error(xml).expect("valid xml"); - assert_eq!(parsed.request_id(), Some("foo-id")); + let parsed = parse_error_metadata(xml).expect("valid xml").build(); assert_eq!(parsed.message(), Some("Hi")); assert_eq!(parsed.code(), Some("InvalidGreeting")); } diff --git a/rust-runtime/inlineable/src/endpoint_lib/host.rs b/rust-runtime/inlineable/src/endpoint_lib/host.rs index 41dc029122..4c4168437b 100644 --- a/rust-runtime/inlineable/src/endpoint_lib/host.rs +++ b/rust-runtime/inlineable/src/endpoint_lib/host.rs @@ -40,6 +40,7 @@ mod test { super::is_valid_host_label(label, allow_dots, &mut DiagnosticCollector::new()) } + #[allow(clippy::bool_assert_comparison)] #[test] fn basic_cases() { assert_eq!(is_valid_host_label("", false), false); @@ -57,6 +58,7 @@ mod test { ); } + #[allow(clippy::bool_assert_comparison)] #[test] fn start_bounds() { assert_eq!(is_valid_host_label("-foo", false), false); diff --git a/rust-runtime/inlineable/src/endpoint_lib/parse_url.rs b/rust-runtime/inlineable/src/endpoint_lib/parse_url.rs index bd7862aa94..5e437c9bfe 100644 --- a/rust-runtime/inlineable/src/endpoint_lib/parse_url.rs +++ b/rust-runtime/inlineable/src/endpoint_lib/parse_url.rs @@ -71,6 +71,7 @@ mod test { use super::*; use crate::endpoint_lib::diagnostic::DiagnosticCollector; + #[allow(clippy::bool_assert_comparison)] #[test] fn parse_simple_url() { let url = "https://control.vpce-1a2b3c4d-5e6f.s3.us-west-2.vpce.amazonaws.com"; @@ -92,6 +93,7 @@ mod test { assert_eq!(url.scheme(), "https"); } + #[allow(clippy::bool_assert_comparison)] #[test] fn parse_url_with_port() { let url = "http://localhost:8000/path"; diff --git a/rust-runtime/inlineable/src/endpoint_lib/partition.rs b/rust-runtime/inlineable/src/endpoint_lib/partition.rs index 25bec52d95..55b97a21d2 100644 --- a/rust-runtime/inlineable/src/endpoint_lib/partition.rs +++ b/rust-runtime/inlineable/src/endpoint_lib/partition.rs @@ -574,8 +574,10 @@ mod test { #[test] fn resolve_partitions() { let mut resolver = PartitionResolver::empty(); - let mut new_suffix = PartitionOutputOverride::default(); - new_suffix.dns_suffix = Some("mars.aws".into()); + let new_suffix = PartitionOutputOverride { + dns_suffix: Some("mars.aws".into()), + ..Default::default() + }; resolver.add_partition(PartitionMetadata { id: "aws".into(), region_regex: Regex::new("^(us|eu|ap|sa|ca|me|af)-\\w+-\\d+$").unwrap(), diff --git a/rust-runtime/inlineable/src/json_errors.rs b/rust-runtime/inlineable/src/json_errors.rs index ea13da3ba8..c2669a795d 100644 --- a/rust-runtime/inlineable/src/json_errors.rs +++ b/rust-runtime/inlineable/src/json_errors.rs @@ -5,8 +5,7 @@ use aws_smithy_json::deserialize::token::skip_value; use aws_smithy_json::deserialize::{error::DeserializeError, json_token_iter, Token}; -use aws_smithy_types::Error as SmithyError; -use bytes::Bytes; +use aws_smithy_types::error::metadata::{Builder as ErrorMetadataBuilder, ErrorMetadata}; use http::header::ToStrError; use http::{HeaderMap, HeaderValue}; use std::borrow::Cow; @@ -82,56 +81,47 @@ fn error_type_from_header(headers: &HeaderMap) -> Result) -> Option<&str> { - headers - .get("X-Amzn-Requestid") - .and_then(|v| v.to_str().ok()) -} - -pub fn parse_generic_error( - payload: &Bytes, +pub fn parse_error_metadata( + payload: &[u8], headers: &HeaderMap, -) -> Result { - let ErrorBody { code, message } = parse_error_body(payload.as_ref())?; +) -> Result { + let ErrorBody { code, message } = parse_error_body(payload)?; - let mut err_builder = SmithyError::builder(); + let mut err_builder = ErrorMetadata::builder(); if let Some(code) = error_type_from_header(headers) .map_err(|_| DeserializeError::custom("X-Amzn-Errortype header was not valid UTF-8"))? .or(code.as_deref()) .map(sanitize_error_code) { - err_builder.code(code); + err_builder = err_builder.code(code); } if let Some(message) = message { - err_builder.message(message); + err_builder = err_builder.message(message); } - if let Some(request_id) = request_id(headers) { - err_builder.request_id(request_id); - } - Ok(err_builder.build()) + Ok(err_builder) } #[cfg(test)] mod test { - use crate::json_errors::{parse_error_body, parse_generic_error, sanitize_error_code}; + use crate::json_errors::{parse_error_body, parse_error_metadata, sanitize_error_code}; use aws_smithy_types::Error; use bytes::Bytes; use std::borrow::Cow; #[test] - fn generic_error() { + fn error_metadata() { let response = http::Response::builder() - .header("X-Amzn-Requestid", "1234") .body(Bytes::from_static( br#"{ "__type": "FooError", "message": "Go to foo" }"#, )) .unwrap(); assert_eq!( - parse_generic_error(response.body(), response.headers()).unwrap(), + parse_error_metadata(response.body(), response.headers()) + .unwrap() + .build(), Error::builder() .code("FooError") .message("Go to foo") - .request_id("1234") .build() ) } @@ -209,7 +199,9 @@ mod test { )) .unwrap(); assert_eq!( - parse_generic_error(response.body(), response.headers()).unwrap(), + parse_error_metadata(response.body(), response.headers()) + .unwrap() + .build(), Error::builder() .code("ResourceNotFoundException") .message("Functions from 'us-west-2' are not reachable from us-east-1") diff --git a/rust-runtime/inlineable/src/lib.rs b/rust-runtime/inlineable/src/lib.rs index 41af358919..e53b81db7d 100644 --- a/rust-runtime/inlineable/src/lib.rs +++ b/rust-runtime/inlineable/src/lib.rs @@ -3,6 +3,8 @@ * SPDX-License-Identifier: Apache-2.0 */ +#[allow(dead_code)] +mod aws_query_compatible_errors; #[allow(unused)] mod constrained; #[allow(dead_code)] diff --git a/rust-runtime/inlineable/src/rest_xml_unwrapped_errors.rs b/rust-runtime/inlineable/src/rest_xml_unwrapped_errors.rs index df0f22ef4e..def901cf7f 100644 --- a/rust-runtime/inlineable/src/rest_xml_unwrapped_errors.rs +++ b/rust-runtime/inlineable/src/rest_xml_unwrapped_errors.rs @@ -6,6 +6,7 @@ //! Error abstractions for `noErrorWrapping`. Code generators should either inline this file //! or its companion `rest_xml_wrapped_errors.rs` for code generation +use aws_smithy_types::error::metadata::{Builder as ErrorMetadataBuilder, ErrorMetadata}; use aws_smithy_xml::decode::{try_data, Document, ScopedDecoder, XmlDecodeError}; use std::convert::TryFrom; @@ -26,30 +27,27 @@ pub fn error_scope<'a, 'b>( Ok(scoped) } -pub fn parse_generic_error(body: &[u8]) -> Result { +pub fn parse_error_metadata(body: &[u8]) -> Result { let mut doc = Document::try_from(body)?; let mut root = doc.root_element()?; - let mut err = aws_smithy_types::Error::builder(); + let mut builder = ErrorMetadata::builder(); while let Some(mut tag) = root.next_tag() { match tag.start_el().local() { "Code" => { - err.code(try_data(&mut tag)?); + builder = builder.code(try_data(&mut tag)?); } "Message" => { - err.message(try_data(&mut tag)?); - } - "RequestId" => { - err.request_id(try_data(&mut tag)?); + builder = builder.message(try_data(&mut tag)?); } _ => {} } } - Ok(err.build()) + Ok(builder) } #[cfg(test)] mod test { - use super::{body_is_error, parse_generic_error}; + use super::{body_is_error, parse_error_metadata}; #[test] fn parse_unwrapped_error() { @@ -61,8 +59,7 @@ mod test { foo-id "#; assert!(body_is_error(xml).unwrap()); - let parsed = parse_generic_error(xml).expect("valid xml"); - assert_eq!(parsed.request_id(), Some("foo-id")); + let parsed = parse_error_metadata(xml).expect("valid xml").build(); assert_eq!(parsed.message(), Some("Hi")); assert_eq!(parsed.code(), Some("InvalidGreeting")); } diff --git a/rust-runtime/inlineable/src/rest_xml_wrapped_errors.rs b/rust-runtime/inlineable/src/rest_xml_wrapped_errors.rs index c90301bf39..b735b77249 100644 --- a/rust-runtime/inlineable/src/rest_xml_wrapped_errors.rs +++ b/rust-runtime/inlineable/src/rest_xml_wrapped_errors.rs @@ -3,6 +3,7 @@ * SPDX-License-Identifier: Apache-2.0 */ +use aws_smithy_types::error::metadata::{Builder as ErrorMetadataBuilder, ErrorMetadata}; use aws_smithy_xml::decode::{try_data, Document, ScopedDecoder, XmlDecodeError}; use std::convert::TryFrom; @@ -13,32 +14,27 @@ pub fn body_is_error(body: &[u8]) -> Result { Ok(scoped.start_el().matches("ErrorResponse")) } -pub fn parse_generic_error(body: &[u8]) -> Result { +#[allow(dead_code)] +pub fn parse_error_metadata(body: &[u8]) -> Result { let mut doc = Document::try_from(body)?; let mut root = doc.root_element()?; - let mut err_builder = aws_smithy_types::Error::builder(); + let mut err_builder = ErrorMetadata::builder(); while let Some(mut tag) = root.next_tag() { - match tag.start_el().local() { - "Error" => { - while let Some(mut error_field) = tag.next_tag() { - match error_field.start_el().local() { - "Code" => { - err_builder.code(try_data(&mut error_field)?); - } - "Message" => { - err_builder.message(try_data(&mut error_field)?); - } - _ => {} + if tag.start_el().local() == "Error" { + while let Some(mut error_field) = tag.next_tag() { + match error_field.start_el().local() { + "Code" => { + err_builder = err_builder.code(try_data(&mut error_field)?); } + "Message" => { + err_builder = err_builder.message(try_data(&mut error_field)?); + } + _ => {} } } - "RequestId" => { - err_builder.request_id(try_data(&mut tag)?); - } - _ => {} } } - Ok(err_builder.build()) + Ok(err_builder) } #[allow(unused)] @@ -65,7 +61,7 @@ pub fn error_scope<'a, 'b>( #[cfg(test)] mod test { - use super::{body_is_error, parse_generic_error}; + use super::{body_is_error, parse_error_metadata}; use crate::rest_xml_wrapped_errors::error_scope; use aws_smithy_xml::decode::Document; use std::convert::TryFrom; @@ -83,8 +79,7 @@ mod test { foo-id "#; assert!(body_is_error(xml).unwrap()); - let parsed = parse_generic_error(xml).expect("valid xml"); - assert_eq!(parsed.request_id(), Some("foo-id")); + let parsed = parse_error_metadata(xml).expect("valid xml").build(); assert_eq!(parsed.message(), Some("Hi")); assert_eq!(parsed.code(), Some("InvalidGreeting")); } diff --git a/rust-toolchain.toml b/rust-toolchain.toml index 77c704ff6b..9043e3d537 100644 --- a/rust-toolchain.toml +++ b/rust-toolchain.toml @@ -1,2 +1,2 @@ [toolchain] -channel = "1.62.1" +channel = "1.66.1" \ No newline at end of file diff --git a/settings.gradle.kts b/settings.gradle.kts index 48190cc2a1..88f55a4e96 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -13,10 +13,11 @@ include(":codegen-server:python") include(":codegen-server-test") include(":codegen-server-test:python") include(":rust-runtime") -include(":aws:sdk-codegen") -include(":aws:sdk-adhoc-test") -include(":aws:sdk") 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/ci-build/Dockerfile b/tools/ci-build/Dockerfile index 1b5074ffea..441a004696 100644 --- a/tools/ci-build/Dockerfile +++ b/tools/ci-build/Dockerfile @@ -6,12 +6,19 @@ # This is the base Docker build image used by CI ARG base_image=public.ecr.aws/amazonlinux/amazonlinux:2 -ARG rust_stable_version=1.62.1 +ARG rust_stable_version=1.66.1 ARG rust_nightly_version=nightly-2022-11-16 FROM ${base_image} AS bare_base_image RUN yum -y updateinfo +FROM bare_base_image as musl_toolchain +RUN yum -y install tar gzip gcc make +RUN curl https://musl.libc.org/releases/musl-1.2.3.tar.gz -o musl-1.2.3.tar.gz \ + && ls \ + && tar xvzf musl-1.2.3.tar.gz \ + && (cd musl-1.2.3 && ./configure && make install) + # # Rust & Tools Installation Stage # @@ -51,6 +58,8 @@ RUN set -eux; \ rustup component add rustfmt; \ rustup component add clippy; \ rustup toolchain install ${rust_nightly_version} --component clippy; \ + rustup target add x86_64-unknown-linux-musl; \ + rustup target add wasm32-unknown-unknown; \ cargo --version; \ cargo +${rust_nightly_version} --version; @@ -106,6 +115,8 @@ ARG maturin_version=0.14.1 ARG rust_nightly_version RUN cargo +${rust_nightly_version} -Z sparse-registry install maturin --locked --version ${maturin_version} + + # # Final image # @@ -138,6 +149,8 @@ COPY --chown=build:build --from=cargo_minimal_versions /opt/cargo/bin/cargo-mini COPY --chown=build:build --from=cargo_check_external_types /opt/cargo/bin/cargo-check-external-types /opt/cargo/bin/cargo-check-external-types COPY --chown=build:build --from=maturin /opt/cargo/bin/maturin /opt/cargo/bin/maturin COPY --chown=build:build --from=install_rust /opt/rustup /opt/rustup +COPY --chown=build:build --from=musl_toolchain /usr/local/musl/ /usr/local/musl/ +ENV PATH=$PATH:/usr/local/musl/bin/ ENV PATH=/opt/cargo/bin:$PATH \ CARGO_HOME=/opt/cargo \ RUSTUP_HOME=/opt/rustup \ @@ -154,6 +167,7 @@ ENV PATH=/opt/cargo/bin:$PATH \ # This is used primarily by the `build.gradle.kts` files in choosing how to execute build tools. If inside the image, # they will assume the tools are on the PATH, but if outside of the image, they will `cargo run` the tools. 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 RUN /home/build/sanity-test diff --git a/tools/ci-build/changelogger/Cargo.toml b/tools/ci-build/changelogger/Cargo.toml index fd4d4e5c8c..8dd0936365 100644 --- a/tools/ci-build/changelogger/Cargo.toml +++ b/tools/ci-build/changelogger/Cargo.toml @@ -26,5 +26,5 @@ time = { version = "0.3.9", features = ["local-offset"]} toml = "0.5.8" [dev-dependencies] -pretty_assertions = "1.2.1" +pretty_assertions = "1.3" tempfile = "3.3.0" diff --git a/tools/ci-build/changelogger/smithy-rs-maintainers.txt b/tools/ci-build/changelogger/smithy-rs-maintainers.txt index 6d302b3199..4d980016b9 100644 --- a/tools/ci-build/changelogger/smithy-rs-maintainers.txt +++ b/tools/ci-build/changelogger/smithy-rs-maintainers.txt @@ -7,6 +7,7 @@ hlbarber jdisanti jjant LukeMathWalker +pose rcoh unexge velfi diff --git a/tools/ci-build/changelogger/src/main.rs b/tools/ci-build/changelogger/src/main.rs index db36023ff3..5e29945e34 100644 --- a/tools/ci-build/changelogger/src/main.rs +++ b/tools/ci-build/changelogger/src/main.rs @@ -65,6 +65,7 @@ mod tests { source_to_truncate: PathBuf::from("fromplace"), changelog_output: PathBuf::from("some-changelog"), release_manifest_output: Some(PathBuf::from("some-manifest")), + current_release_versions_manifest: None, previous_release_versions_manifest: None, date_override: None, smithy_rs_location: None, @@ -97,6 +98,7 @@ mod tests { source_to_truncate: PathBuf::from("fromplace"), changelog_output: PathBuf::from("some-changelog"), release_manifest_output: None, + current_release_versions_manifest: None, previous_release_versions_manifest: None, date_override: None, smithy_rs_location: None, @@ -127,6 +129,7 @@ mod tests { source_to_truncate: PathBuf::from("fromplace"), changelog_output: PathBuf::from("some-changelog"), release_manifest_output: None, + current_release_versions_manifest: None, previous_release_versions_manifest: Some(PathBuf::from("path/to/versions.toml")), date_override: None, smithy_rs_location: None, @@ -148,5 +151,42 @@ mod tests { ]) .unwrap() ); + + assert_eq!( + Args::Render(RenderArgs { + change_set: ChangeSet::AwsSdk, + independent_versioning: true, + source: vec![PathBuf::from("fromplace")], + source_to_truncate: PathBuf::from("fromplace"), + changelog_output: PathBuf::from("some-changelog"), + release_manifest_output: None, + current_release_versions_manifest: Some(PathBuf::from( + "path/to/current/versions.toml" + )), + previous_release_versions_manifest: Some(PathBuf::from( + "path/to/previous/versions.toml" + )), + date_override: None, + smithy_rs_location: None, + }), + Args::try_parse_from([ + "./changelogger", + "render", + "--change-set", + "aws-sdk", + "--independent-versioning", + "--source", + "fromplace", + "--source-to-truncate", + "fromplace", + "--changelog-output", + "some-changelog", + "--current-release-versions-manifest", + "path/to/current/versions.toml", + "--previous-release-versions-manifest", + "path/to/previous/versions.toml" + ]) + .unwrap() + ); } } diff --git a/tools/ci-build/changelogger/src/render.rs b/tools/ci-build/changelogger/src/render.rs index 90c26b63d5..4dd8e22c1e 100644 --- a/tools/ci-build/changelogger/src/render.rs +++ b/tools/ci-build/changelogger/src/render.rs @@ -13,9 +13,10 @@ use smithy_rs_tool_common::changelog::{ Changelog, HandAuthoredEntry, Reference, SdkModelChangeKind, SdkModelEntry, }; use smithy_rs_tool_common::git::{find_git_repository_root, Git, GitCLI}; +use smithy_rs_tool_common::versions_manifest::{CrateVersionMetadataMap, VersionsManifest}; use std::env; use std::fmt::Write; -use std::path::PathBuf; +use std::path::{Path, PathBuf}; use time::OffsetDateTime; pub const EXAMPLE_ENTRY: &str = r#" @@ -67,6 +68,10 @@ pub struct RenderArgs { /// Optional path to output a release manifest file to #[clap(long, action)] pub release_manifest_output: Option, + /// Optional path to the SDK's versions.toml file for the current release. + /// This is used to generate a markdown table showing crate versions. + #[clap(long, action)] + pub current_release_versions_manifest: Option, /// Optional path to the SDK's versions.toml file for the previous release. /// This is used to filter out changelog entries that have `since_commit` information. #[clap(long, action)] @@ -217,6 +222,16 @@ fn indented_message(message: &str) -> String { out } +fn render_table_row(columns: [&str; 2], out: &mut String) { + let mut row = "|".to_owned(); + for column in columns { + row.push_str(column); + row.push('|'); + } + write!(out, "{row}").unwrap(); + out.push('\n'); +} + fn load_changelogs(args: &RenderArgs) -> Result { let mut combined = Changelog::new(); for source in &args.source { @@ -233,6 +248,19 @@ fn load_changelogs(args: &RenderArgs) -> Result { Ok(combined) } +fn load_current_crate_version_metadata_map( + current_release_versions_manifest: Option<&Path>, +) -> CrateVersionMetadataMap { + current_release_versions_manifest + .and_then( + |manifest_path| match VersionsManifest::from_file(manifest_path) { + Ok(manifest) => Some(manifest.crates), + Err(_) => None, + }, + ) + .unwrap_or_default() +} + fn update_changelogs( args: &RenderArgs, smithy_rs: &dyn Git, @@ -250,7 +278,13 @@ fn update_changelogs( args.change_set, args.previous_release_versions_manifest.as_deref(), )?; - let (release_header, release_notes) = render(&entries, &release_metadata.title); + let current_crate_version_metadata_map = + load_current_crate_version_metadata_map(args.current_release_versions_manifest.as_deref()); + let (release_header, release_notes) = render( + &entries, + current_crate_version_metadata_map, + &release_metadata.title, + ); if let Some(output_path) = &args.release_manifest_output { let release_manifest = ReleaseManifest { tag_name: release_metadata.tag.clone(), @@ -329,9 +363,94 @@ fn render_sdk_model_entries<'a>( } } -/// Convert a list of changelog entries into markdown. +fn render_external_contributors(entries: &[ChangelogEntry], out: &mut String) { + let mut external_contribs = entries + .iter() + .filter_map(|entry| entry.hand_authored().map(|e| &e.author)) + .filter(|author| !is_maintainer(author)) + .collect::>(); + if external_contribs.is_empty() { + return; + } + external_contribs.sort(); + external_contribs.dedup(); + out.push_str("**Contributors**\nThank you for your contributions! ❤\n"); + for contributor_handle in external_contribs { + // retrieve all contributions this author made + let mut contribution_references = entries + .iter() + .filter(|entry| { + entry + .hand_authored() + .map(|e| e.author.eq_ignore_ascii_case(contributor_handle.as_str())) + .unwrap_or(false) + }) + .flat_map(|entry| { + entry + .hand_authored() + .unwrap() + .references + .iter() + .map(to_md_link) + }) + .collect::>(); + contribution_references.sort(); + contribution_references.dedup(); + let contribution_references = contribution_references.as_slice().join(", "); + out.push_str("- @"); + out.push_str(contributor_handle); + if !contribution_references.is_empty() { + write!(out, " ({})", contribution_references) + // The `Write` implementation for `String` is infallible, + // see https://doc.rust-lang.org/src/alloc/string.rs.html#2815 + .unwrap() + } + out.push('\n'); + } + out.push('\n'); +} + +fn render_details(summary: &str, body: &str, out: &mut String) { + out.push_str("

"); + out.push('\n'); + write!(out, "{}", summary).unwrap(); + out.push('\n'); + // A blank line is required for the body to be rendered properly + out.push('\n'); + out.push_str(body); + out.push_str("
"); + out.push('\n'); +} + +fn render_crate_versions(crate_version_metadata_map: CrateVersionMetadataMap, out: &mut String) { + if crate_version_metadata_map.is_empty() { + // If the map is empty, we choose to not render anything, as opposed to + // rendering the
element with empty contents and a user toggling + // it only to find out there is nothing in it. + return; + } + + out.push_str("**Crate Versions**"); + out.push('\n'); + + let mut table = String::new(); + render_table_row(["Crate", "Version"], &mut table); + render_table_row(["-", "-"], &mut table); + for (crate_name, version_metadata) in &crate_version_metadata_map { + render_table_row([crate_name, &version_metadata.version], &mut table); + } + + render_details("Click to expand to view crate versions...", &table, out); + out.push('\n'); +} + +/// Convert a list of changelog entries and crate versions into markdown. /// Returns (header, body) -fn render(entries: &[ChangelogEntry], release_header: &str) -> (String, String) { +fn render( + entries: &[ChangelogEntry], + crate_version_metadata_map: CrateVersionMetadataMap, + release_header: &str, +) -> (String, String) { let mut header = String::new(); header.push_str(release_header); header.push('\n'); @@ -349,49 +468,8 @@ fn render(entries: &[ChangelogEntry], release_header: &str) -> (String, String) entries.iter().filter_map(ChangelogEntry::aws_sdk_model), &mut out, ); - - let mut external_contribs = entries - .iter() - .filter_map(|entry| entry.hand_authored().map(|e| &e.author)) - .filter(|author| !is_maintainer(author)) - .collect::>(); - external_contribs.sort(); - external_contribs.dedup(); - if !external_contribs.is_empty() { - out.push_str("**Contributors**\nThank you for your contributions! ❤\n"); - for contributor_handle in external_contribs { - // retrieve all contributions this author made - let mut contribution_references = entries - .iter() - .filter(|entry| { - entry - .hand_authored() - .map(|e| e.author.eq_ignore_ascii_case(contributor_handle.as_str())) - .unwrap_or(false) - }) - .flat_map(|entry| { - entry - .hand_authored() - .unwrap() - .references - .iter() - .map(to_md_link) - }) - .collect::>(); - contribution_references.sort(); - contribution_references.dedup(); - let contribution_references = contribution_references.as_slice().join(", "); - out.push_str("- @"); - out.push_str(contributor_handle); - if !contribution_references.is_empty() { - write!(&mut out, " ({})", contribution_references) - // The `Write` implementation for `String` is infallible, - // see https://doc.rust-lang.org/src/alloc/string.rs.html#2815 - .unwrap() - } - out.push('\n'); - } - } + render_external_contributors(entries, &mut out); + render_crate_versions(crate_version_metadata_map, &mut out); (header, out) } @@ -399,11 +477,15 @@ fn render(entries: &[ChangelogEntry], release_header: &str) -> (String, String) #[cfg(test)] mod test { use super::{date_based_release_metadata, render, Changelog, ChangelogEntries, ChangelogEntry}; - use smithy_rs_tool_common::changelog::SdkAffected; + use smithy_rs_tool_common::{ + changelog::SdkAffected, + package::PackageCategory, + versions_manifest::{CrateVersion, CrateVersionMetadataMap}, + }; use time::OffsetDateTime; fn render_full(entries: &[ChangelogEntry], release_header: &str) -> String { - let (header, body) = render(entries, release_header); + let (header, body) = render(entries, CrateVersionMetadataMap::new(), release_header); format!("{header}{body}") } @@ -494,6 +576,7 @@ v0.3.0 (January 4th, 2022) Thank you for your contributions! ❤ - @another-contrib ([smithy-rs#200](https://github.com/awslabs/smithy-rs/issues/200)) - @external-contrib ([smithy-rs#446](https://github.com/awslabs/smithy-rs/issues/446)) + "# .trim_start(); pretty_assertions::assert_str_eq!(smithy_rs_expected, smithy_rs_rendered); @@ -518,6 +601,7 @@ v0.1.0 (January 4th, 2022) **Contributors** Thank you for your contributions! ❤ - @external-contrib ([smithy-rs#446](https://github.com/awslabs/smithy-rs/issues/446)) + "# .trim_start(); pretty_assertions::assert_str_eq!(aws_sdk_expected, aws_sdk_rust_rendered); @@ -592,9 +676,69 @@ author = "rcoh" #[test] fn test_empty_render() { let smithy_rs = Vec::::new(); - let (release_title, release_notes) = render(&smithy_rs, "some header"); + let (release_title, release_notes) = + render(&smithy_rs, CrateVersionMetadataMap::new(), "some header"); assert_eq!(release_title, "some header\n===========\n"); assert_eq!(release_notes, ""); } + + #[test] + fn test_crate_versions() { + let mut crate_version_metadata_map = CrateVersionMetadataMap::new(); + crate_version_metadata_map.insert( + "aws-config".to_owned(), + CrateVersion { + category: PackageCategory::AwsRuntime, + version: "0.54.1".to_owned(), + source_hash: "e93380cfbd05e68d39801cbf0113737ede552a5eceb28f4c34b090048d539df9" + .to_owned(), + model_hash: None, + }, + ); + crate_version_metadata_map.insert( + "aws-sdk-accessanalyzer".to_owned(), + CrateVersion { + category: PackageCategory::AwsSdk, + version: "0.24.0".to_owned(), + source_hash: "a7728756b41b33d02f68a5865d3456802b7bc3949ec089790bc4e726c0de8539" + .to_owned(), + model_hash: Some( + "71f1f130504ebd55396c3166d9441513f97e49b281a5dd420fd7e2429860b41b".to_owned(), + ), + }, + ); + crate_version_metadata_map.insert( + "aws-smithy-async".to_owned(), + CrateVersion { + category: PackageCategory::SmithyRuntime, + version: "0.54.1".to_owned(), + source_hash: "8ced52afc783cbb0df47ee8b55260b98e9febdc95edd796ed14c43db5199b0a9" + .to_owned(), + model_hash: None, + }, + ); + let (release_title, release_notes) = render( + &Vec::::new(), + crate_version_metadata_map, + "some header", + ); + + assert_eq!(release_title, "some header\n===========\n"); + let expected_body = r#" +**Crate Versions** +
+Click to expand to view crate versions... + +|Crate|Version| +|-|-| +|aws-config|0.54.1| +|aws-sdk-accessanalyzer|0.24.0| +|aws-smithy-async|0.54.1| +
+ +"# + .trim_start(); + pretty_assertions::assert_str_eq!(release_notes, expected_body); + } } diff --git a/tools/ci-build/changelogger/tests/e2e_test.rs b/tools/ci-build/changelogger/tests/e2e_test.rs index 07dc64a839..745e73ef47 100644 --- a/tools/ci-build/changelogger/tests/e2e_test.rs +++ b/tools/ci-build/changelogger/tests/e2e_test.rs @@ -45,6 +45,37 @@ const SDK_MODEL_SOURCE_TOML: &str = r#" message = "Some API change" "#; +const VERSIONS_TOML: &str = r#" + smithy_rs_revision = '41ca31b85b4ba8c0ad680fe62a230266cc52cc44' + aws_doc_sdk_examples_revision = '97a177aab8c3d2fef97416cb66e4b4d0da840138' + + [manual_interventions] + crates_to_remove = [] + [crates.aws-config] + category = 'AwsRuntime' + version = '0.54.1' + source_hash = 'e93380cfbd05e68d39801cbf0113737ede552a5eceb28f4c34b090048d539df9' + + [crates.aws-sdk-accessanalyzer] + category = 'AwsSdk' + version = '0.24.0' + source_hash = 'a7728756b41b33d02f68a5865d3456802b7bc3949ec089790bc4e726c0de8539' + model_hash = '71f1f130504ebd55396c3166d9441513f97e49b281a5dd420fd7e2429860b41b' + + [crates.aws-smithy-async] + category = 'SmithyRuntime' + version = '0.54.1' + source_hash = '8ced52afc783cbb0df47ee8b55260b98e9febdc95edd796ed14c43db5199b0a9' + + [release] + tag = 'release-2023-01-26' + + [release.crates] + aws-config = "0.54.1" + aws-sdk-accessanalyzer = '0.24.0' + aws-smithy-async = '0.54.1' +"#; + fn create_fake_repo_root( path: &Path, smithy_rs_version: &str, @@ -98,7 +129,7 @@ fn create_fake_repo_root( } #[test] -fn split_aws_sdk_test() { +fn split_aws_sdk() { let tmp_dir = TempDir::new().unwrap(); let source_path = tmp_dir.path().join("source.toml"); let dest_path = tmp_dir.path().join("dest.toml"); @@ -226,7 +257,7 @@ fn split_aws_sdk_test() { } #[test] -fn render_smithy_rs_test() { +fn render_smithy_rs() { let tmp_dir = TempDir::new().unwrap(); let source_path = tmp_dir.path().join("source.toml"); let dest_path = tmp_dir.path().join("dest.md"); @@ -253,6 +284,7 @@ fn render_smithy_rs_test() { changelog_output: dest_path.clone(), release_manifest_output: Some(tmp_dir.path().into()), date_override: Some(OffsetDateTime::UNIX_EPOCH), + current_release_versions_manifest: None, previous_release_versions_manifest: None, smithy_rs_location: Some(tmp_dir.path().into()), }) @@ -274,6 +306,7 @@ January 1st, 1970 Thank you for your contributions! ❤ - @another-dev ([smithy-rs#1234](https://github.com/awslabs/smithy-rs/issues/1234)) + v0.41.0 (Some date in the past) ========= @@ -285,7 +318,7 @@ Old entry contents r#"{ "tagName": "release-1970-01-01", "name": "January 1st, 1970", - "body": "**New this release:**\n- (all, [smithy-rs#1234](https://github.com/awslabs/smithy-rs/issues/1234), @another-dev) Another change\n\n**Contributors**\nThank you for your contributions! ❤\n- @another-dev ([smithy-rs#1234](https://github.com/awslabs/smithy-rs/issues/1234))\n", + "body": "**New this release:**\n- (all, [smithy-rs#1234](https://github.com/awslabs/smithy-rs/issues/1234), @another-dev) Another change\n\n**Contributors**\nThank you for your contributions! ❤\n- @another-dev ([smithy-rs#1234](https://github.com/awslabs/smithy-rs/issues/1234))\n\n", "prerelease": true }"#, release_manifest @@ -293,13 +326,13 @@ Old entry contents } #[test] -fn render_aws_sdk_test() { +fn render_aws_sdk() { let tmp_dir = TempDir::new().unwrap(); let source1_path = tmp_dir.path().join("source1.toml"); let source2_path = tmp_dir.path().join("source2.toml"); let dest_path = tmp_dir.path().join("dest.md"); let release_manifest_path = tmp_dir.path().join("aws-sdk-rust-release-manifest.json"); - let versions_manifest_path = tmp_dir.path().join("versions.toml"); + let previous_versions_manifest_path = tmp_dir.path().join("versions.toml"); let (release_1_commit, release_2_commit) = create_fake_repo_root(tmp_dir.path(), "0.42.0", "0.12.0"); @@ -322,7 +355,7 @@ fn render_aws_sdk_test() { .unwrap(); fs::write(&release_manifest_path, "overwrite-me").unwrap(); fs::write( - &versions_manifest_path, + &previous_versions_manifest_path, format!( "smithy_rs_revision = '{release_1_commit}' aws_doc_sdk_examples_revision = 'not-relevant' @@ -339,7 +372,8 @@ fn render_aws_sdk_test() { changelog_output: dest_path.clone(), release_manifest_output: Some(tmp_dir.path().into()), date_override: Some(OffsetDateTime::UNIX_EPOCH), - previous_release_versions_manifest: Some(versions_manifest_path), + current_release_versions_manifest: None, + previous_release_versions_manifest: Some(previous_versions_manifest_path), smithy_rs_location: Some(tmp_dir.path().into()), }) .unwrap(); @@ -368,6 +402,7 @@ January 1st, 1970 Thank you for your contributions! ❤ - @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)) + v0.41.0 (Some date in the past) ========= @@ -379,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", + "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", "prerelease": true }"#, release_manifest @@ -450,7 +485,7 @@ author = "LukeMathWalker" ), ) .unwrap(); - fs::write(&release_manifest_path, "overwrite-me").unwrap(); + fs::write(release_manifest_path, "overwrite-me").unwrap(); subcommand_render(&RenderArgs { change_set: ChangeSet::SmithyRs, @@ -460,6 +495,7 @@ author = "LukeMathWalker" changelog_output: dest_path.clone(), release_manifest_output: Some(tmp_dir.path().into()), date_override: Some(OffsetDateTime::UNIX_EPOCH), + current_release_versions_manifest: None, previous_release_versions_manifest: None, smithy_rs_location: Some(tmp_dir.path().into()), }) @@ -487,6 +523,7 @@ Thank you for your contributions! ❤ - @another-dev ([smithy-rs#2](https://github.com/awslabs/smithy-rs/issues/2)) - @server-dev ([smithy-rs#1](https://github.com/awslabs/smithy-rs/issues/1)) + v0.41.0 (Some date in the past) ========= @@ -559,7 +596,7 @@ author = "rcoh" ), ) .unwrap(); - fs::write(&release_manifest_path, "overwrite-me").unwrap(); + fs::write(release_manifest_path, "overwrite-me").unwrap(); let result = subcommand_render(&RenderArgs { change_set: ChangeSet::SmithyRs, @@ -569,6 +606,7 @@ author = "rcoh" changelog_output: dest_path, release_manifest_output: Some(tmp_dir.path().into()), date_override: Some(OffsetDateTime::UNIX_EPOCH), + current_release_versions_manifest: None, previous_release_versions_manifest: None, smithy_rs_location: Some(tmp_dir.path().into()), }); @@ -582,3 +620,86 @@ author = "rcoh" panic!("This should have been error that aws-sdk-rust has a target entry"); } } + +#[test] +fn render_crate_versions() { + let tmp_dir = TempDir::new().unwrap(); + let source_path = tmp_dir.path().join("source.toml"); + let dest_path = tmp_dir.path().join("dest.md"); + let release_manifest_path = tmp_dir.path().join("smithy-rs-release-manifest.json"); + let current_versions_manifest_path = tmp_dir.path().join("versions.toml"); + + create_fake_repo_root(tmp_dir.path(), "0.54.1", "0.24.0"); + + fs::write(&source_path, SOURCE_TOML).unwrap(); + fs::write( + &dest_path, + format!( + "{}\nv0.54.0 (Some date in the past)\n=========\n\nOld entry contents\n", + USE_UPDATE_CHANGELOGS + ), + ) + .unwrap(); + fs::write(&release_manifest_path, "overwrite-me").unwrap(); + fs::write(¤t_versions_manifest_path, VERSIONS_TOML).unwrap(); + + subcommand_render(&RenderArgs { + change_set: ChangeSet::SmithyRs, + independent_versioning: true, + source: vec![source_path.clone()], + source_to_truncate: source_path.clone(), + changelog_output: dest_path.clone(), + release_manifest_output: Some(tmp_dir.path().into()), + date_override: Some(OffsetDateTime::UNIX_EPOCH), + current_release_versions_manifest: Some(current_versions_manifest_path), + previous_release_versions_manifest: None, + smithy_rs_location: Some(tmp_dir.path().into()), + }) + .unwrap(); + + let source = fs::read_to_string(&source_path).unwrap(); + let dest = fs::read_to_string(&dest_path).unwrap(); + let release_manifest = fs::read_to_string(&release_manifest_path).unwrap(); + + // source file should be empty + pretty_assertions::assert_str_eq!(EXAMPLE_ENTRY.trim(), source); + pretty_assertions::assert_str_eq!( + r#" +January 1st, 1970 +================= +**New this release:** +- (all, [smithy-rs#1234](https://github.com/awslabs/smithy-rs/issues/1234), @another-dev) Another change + +**Contributors** +Thank you for your contributions! ❤ +- @another-dev ([smithy-rs#1234](https://github.com/awslabs/smithy-rs/issues/1234)) + +**Crate Versions** +
+Click to expand to view crate versions... + +|Crate|Version| +|-|-| +|aws-config|0.54.1| +|aws-sdk-accessanalyzer|0.24.0| +|aws-smithy-async|0.54.1| +
+ + +v0.54.0 (Some date in the past) +========= + +Old entry contents +"#, + dest + ); + pretty_assertions::assert_str_eq!( + r#"{ + "tagName": "release-1970-01-01", + "name": "January 1st, 1970", + "body": "**New this release:**\n- (all, [smithy-rs#1234](https://github.com/awslabs/smithy-rs/issues/1234), @another-dev) Another change\n\n**Contributors**\nThank you for your contributions! ❤\n- @another-dev ([smithy-rs#1234](https://github.com/awslabs/smithy-rs/issues/1234))\n\n**Crate Versions**\n
\nClick to expand to view crate versions...\n\n|Crate|Version|\n|-|-|\n|aws-config|0.54.1|\n|aws-sdk-accessanalyzer|0.24.0|\n|aws-smithy-async|0.54.1|\n
\n\n", + "prerelease": true +}"#, + release_manifest + ); +} diff --git a/tools/ci-build/crate-hasher/Cargo.lock b/tools/ci-build/crate-hasher/Cargo.lock index df4ff9f4ff..c6f1912dd2 100644 --- a/tools/ci-build/crate-hasher/Cargo.lock +++ b/tools/ci-build/crate-hasher/Cargo.lock @@ -504,8 +504,8 @@ version = "1.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e334db67871c14c18fc066ad14af13f9fdf5f9a91c61af432d1e3a39c8c6a141" dependencies = [ - "hex", - "sha2", + "hex", + "sha2", ] [[package]] diff --git a/tools/ci-build/crate-hasher/Cargo.toml b/tools/ci-build/crate-hasher/Cargo.toml index 3456cdff45..3f9376443a 100644 --- a/tools/ci-build/crate-hasher/Cargo.toml +++ b/tools/ci-build/crate-hasher/Cargo.toml @@ -22,6 +22,6 @@ sha256 = "1.1" [dev-dependencies] flate2 = "1.0" -pretty_assertions = "1.2" +pretty_assertions = "1.3" tar = "0.4" tempdir = "0.3" diff --git a/tools/ci-build/difftags/src/html.rs b/tools/ci-build/difftags/src/html.rs index e56c93a4e5..61c5594de5 100644 --- a/tools/ci-build/difftags/src/html.rs +++ b/tools/ci-build/difftags/src/html.rs @@ -18,7 +18,7 @@ pub fn write_html( ) -> Result<()> { for (page_num, page) in pages.iter().enumerate() { let file_path = file_path(output_dir, page_num); - let mut file = fs::File::create(&file_path)?; + let mut file = fs::File::create(file_path)?; write_header(&mut file, title.as_deref(), subtitle.as_deref(), pages)?; for (file_num, page_file) in page.files.iter().enumerate() { diff --git a/tools/ci-build/difftags/src/page.rs b/tools/ci-build/difftags/src/page.rs index 94ab30588c..76a1491f83 100644 --- a/tools/ci-build/difftags/src/page.rs +++ b/tools/ci-build/difftags/src/page.rs @@ -223,7 +223,7 @@ index 422b64415..9561909ed 100644 30 "#; let mut patch = PatchSet::new(); - patch.parse(&diff_str).unwrap(); + patch.parse(diff_str).unwrap(); let hunk = &patch.files()[0].hunks()[0]; let section: Section = hunk.into(); @@ -292,7 +292,7 @@ index 422b64415..9561909ed 100644 36 "#; let mut patch = PatchSet::new(); - patch.parse(&diff_str).unwrap(); + patch.parse(diff_str).unwrap(); let hunk = &patch.files()[0].hunks()[0]; let section: Section = hunk.into(); diff --git a/tools/ci-build/publisher/Cargo.lock b/tools/ci-build/publisher/Cargo.lock index 98b686679d..450e0b81b5 100644 --- a/tools/ci-build/publisher/Cargo.lock +++ b/tools/ci-build/publisher/Cargo.lock @@ -4,27 +4,18 @@ version = 3 [[package]] name = "aho-corasick" -version = "0.7.19" +version = "0.7.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b4f55bd91a0978cbfd91c457a164bab8b4001c833b7f323132c0a4e1922dd44e" +checksum = "cc936419f96fa211c1b9166887b38e5e40b19958e5b895be7c1f93adec7071ac" dependencies = [ "memchr", ] -[[package]] -name = "ansi_term" -version = "0.12.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d52a9bb7ec0cf484c551830a7ce27bd20d67eac647e1befb56b0be4ee39a55d2" -dependencies = [ - "winapi", -] - [[package]] name = "anyhow" -version = "1.0.65" +version = "1.0.69" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "98161a4e3e2184da77bb14f02184cdd111e83bbbcc9979dfee3c44b9a85f5602" +checksum = "224afbd727c3d6e4b90103ece64b8d1b67fbb1973b1046c2281eed3f3803f800" [[package]] name = "async-recursion" @@ -39,9 +30,9 @@ dependencies = [ [[package]] name = "async-trait" -version = "0.1.57" +version = "0.1.64" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "76464446b8bc32758d7e88ee1a804d9914cd9b1cb264c029899680b0be29826f" +checksum = "1cd7fce9ba8c3c042128ce72d8b2ddbf3a05747efb67ea0313c635e10bda47a2" dependencies = [ "proc-macro2", "quote", @@ -54,7 +45,7 @@ version = "0.2.14" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8" dependencies = [ - "hermit-abi", + "hermit-abi 0.1.19", "libc", "winapi", ] @@ -67,9 +58,9 @@ checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" [[package]] name = "base64" -version = "0.13.0" +version = "0.21.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "904dfeac50f3cdaba28fc6f57fdcddb75f49ed61346676a78c4ffe55877802fd" +checksum = "a4a4ddaa51a5bc52a6948f74c06d20aaaddb71924eab79b8c97a8c556e942d6a" [[package]] name = "bitflags" @@ -97,15 +88,15 @@ dependencies = [ [[package]] name = "bumpalo" -version = "3.11.0" +version = "3.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c1ad822118d20d2c234f427000d5acc36eabe1e29a348c89b63dd60b13f28e5d" +checksum = "0d261e256854913907f67ed06efbc3338dfe6179796deefc1ff763fc1aee5535" [[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" @@ -120,9 +111,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" @@ -132,9 +123,9 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" [[package]] name = "chrono" -version = "0.4.22" +version = "0.4.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bfd4d1b31faaa3a89d7934dbded3111da0d2ef28e3ebccdb4f0179f5929d1ef1" +checksum = "16b0a3d9ed01224b22057780a37bb8c5dbfe1be8ba48678e7bf57ec4b385411f" dependencies = [ "num-integer", "num-traits", @@ -249,9 +240,9 @@ dependencies = [ [[package]] name = "ctor" -version = "0.1.23" +version = "0.1.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cdffe87e1d521a10f9696f833fe502293ea446d7f256c06128293a4119bdf4cb" +checksum = "6d2301688392eb071b0bf1a37be05c469d3cc4dbbd95df672fe28ab021e6a096" dependencies = [ "quote", "syn", @@ -286,9 +277,9 @@ dependencies = [ [[package]] name = "digest" -version = "0.10.5" +version = "0.10.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "adfbc57365a37acbd2ebf2b64d7e69bb766e2fea813521ed536f5d0520dcf86c" +checksum = "8168378f4e5023e7218c89c891c0fd8ecdb5e5e4f18cb78f38cf245dd021e76f" dependencies = [ "block-buffer 0.10.3", "crypto-common", @@ -302,9 +293,9 @@ checksum = "a357d28ed41a50f9c765dbfe56cbc04a64e53e5fc58ba79fbc34c10ef3df831f" [[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", ] @@ -356,9 +347,9 @@ checksum = "0845fa252299212f0389d64ba26f34fa32cfe41588355f21ed507c59a0f64541" [[package]] name = "futures" -version = "0.3.24" +version = "0.3.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f21eda599937fba36daeb58a22e8f5cee2d14c4a17b5b7739c7c8e5e3b8230c" +checksum = "13e2792b0ff0340399d58445b88fd9770e3489eff258a4cbc1523418f12abf84" dependencies = [ "futures-channel", "futures-core", @@ -371,9 +362,9 @@ dependencies = [ [[package]] name = "futures-channel" -version = "0.3.24" +version = "0.3.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "30bdd20c28fadd505d0fd6712cdfcb0d4b5648baf45faef7f852afb2399bb050" +checksum = "2e5317663a9089767a1ec00a487df42e0ca174b61b4483213ac24448e4664df5" dependencies = [ "futures-core", "futures-sink", @@ -381,15 +372,15 @@ dependencies = [ [[package]] name = "futures-core" -version = "0.3.24" +version = "0.3.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4e5aa3de05362c3fb88de6531e6296e85cde7739cccad4b9dfeeb7f6ebce56bf" +checksum = "ec90ff4d0fe1f57d600049061dc6bb68ed03c7d2fbd697274c41805dcb3f8608" [[package]] name = "futures-executor" -version = "0.3.24" +version = "0.3.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9ff63c23854bee61b6e9cd331d523909f238fc7636290b96826e9cfa5faa00ab" +checksum = "e8de0a35a6ab97ec8869e32a2473f4b1324459e14c29275d14b10cb1fd19b50e" dependencies = [ "futures-core", "futures-task", @@ -398,15 +389,15 @@ dependencies = [ [[package]] name = "futures-io" -version = "0.3.24" +version = "0.3.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bbf4d2a7a308fd4578637c0b17c7e1c7ba127b8f6ba00b29f717e9655d85eb68" +checksum = "bfb8371b6fb2aeb2d280374607aeabfc99d95c72edfe51692e42d3d7f0d08531" [[package]] name = "futures-macro" -version = "0.3.24" +version = "0.3.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "42cd15d1c7456c04dbdf7e88bcd69760d74f3a798d6444e16974b505b0e62f17" +checksum = "95a73af87da33b5acf53acfebdc339fe592ecf5357ac7c0a7734ab9d8c876a70" dependencies = [ "proc-macro2", "quote", @@ -415,21 +406,21 @@ dependencies = [ [[package]] name = "futures-sink" -version = "0.3.24" +version = "0.3.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "21b20ba5a92e727ba30e72834706623d94ac93a725410b6a6b6fbc1b07f7ba56" +checksum = "f310820bb3e8cfd46c80db4d7fb8353e15dfff853a127158425f31e0be6c8364" [[package]] name = "futures-task" -version = "0.3.24" +version = "0.3.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a6508c467c73851293f390476d4491cf4d227dbabcd4170f3bb6044959b294f1" +checksum = "dcf79a1bf610b10f42aea489289c5a2c478a786509693b80cd39c44ccd936366" [[package]] name = "futures-util" -version = "0.3.24" +version = "0.3.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "44fb6cb1be61cc1d2e43b262516aafcf63b241cffdb1d3fa115f91d9c7b09c90" +checksum = "9c1d6de3acfef38d2be4b1f543f553131788603495be83da675e180c8d6b7bd1" dependencies = [ "futures-channel", "futures-core", @@ -455,9 +446,9 @@ dependencies = [ [[package]] name = "h2" -version = "0.3.14" +version = "0.3.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5ca32592cf21ac7ccab1825cd87f6c9b3d9022c44d086172ed0966bec8af30be" +checksum = "5f9f29bc9dda355256b2916cf526ab02ce0aeaaaf2bad60d65ef3f12f11dd0f4" dependencies = [ "bytes", "fnv", @@ -474,9 +465,9 @@ dependencies = [ [[package]] name = "handlebars" -version = "4.3.4" +version = "4.3.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "56b224eaa4987c03c30b251de7ef0c15a6a59f34222905850dbc3026dfb24d5f" +checksum = "035ef95d03713f2c347a72547b7cd38cbc9af7cd51e6099fb62d586d4a6dee3a" dependencies = [ "log", "pest", @@ -494,9 +485,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" @@ -507,6 +498,15 @@ dependencies = [ "libc", ] +[[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" @@ -549,9 +549,9 @@ checksum = "c4a1e36c821dbe04574f602848a19f742f4fb3c98d40449f11bcad18d6b17421" [[package]] name = "hyper" -version = "0.14.20" +version = "0.14.24" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "02c929dc5c39e335a03c405292728118860721b10190d98c2a0f0efd5baafbac" +checksum = "5e011372fa0b68db8350aa7a248930ecc7839bf46d8485577d69f117a75f164c" dependencies = [ "bytes", "futures-channel", @@ -596,9 +596,9 @@ dependencies = [ [[package]] name = "indexmap" -version = "1.9.1" +version = "1.9.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "10a35a97730320ffe8e2d410b5d3b69279b98d2c14bdb8b70ea89ecf7888d41e" +checksum = "1885e79c1fc4b10f0e172c475f458b7f7b93061064d98c3293e98c5ba0c8b399" dependencies = [ "autocfg", "hashbrown", @@ -615,21 +615,21 @@ dependencies = [ [[package]] name = "ipnet" -version = "2.5.0" +version = "2.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "879d54834c8c76457ef4293a689b2a8c59b076067ad77b15efafbb05f92a592b" +checksum = "30e22bd8629359895450b59ea7a776c850561b96a3b1d31321c1949d9e6c9146" [[package]] name = "itoa" -version = "1.0.3" +version = "1.0.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6c8af84674fe1f223a982c933a0ee1086ac4d4052aa0fb8060c12c6ad838e754" +checksum = "fad582f4b9e86b6caa621cabeb0963332d92eea04729ab12892c2533951e6440" [[package]] name = "js-sys" -version = "0.3.60" +version = "0.3.61" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "49409df3e3bf0856b916e2ceaca09ee28e6871cf7d9ce97a692cacfdb2a25a47" +checksum = "445dde2150c55e483f3d8416706b97ec8e8237c307e5b7b4b8dd15e6af2a0730" dependencies = [ "wasm-bindgen", ] @@ -642,9 +642,9 @@ checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" [[package]] name = "libc" -version = "0.2.134" +version = "0.2.139" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "329c933548736bc49fd575ee68c89e8be4d260064184389a5b77517cddd99ffb" +checksum = "201de327520df007757c1f0adce6e827fe8562fbc28bfd9c15571c66ca1f5f79" [[package]] name = "lock_api" @@ -688,21 +688,21 @@ checksum = "2a60c7ce501c71e03a9c9c0d35b861413ae925bd979cc7a4e30d060069aaac8d" [[package]] name = "mio" -version = "0.8.4" +version = "0.8.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "57ee1c23c7c63b0c9250c339ffdc69255f110b298b901b9f6c82547b7b87caaf" +checksum = "e5d732bc30207a6423068df043e3d02e0735b155ad7ce1a6f76fe2baa5b158de" dependencies = [ "libc", "log", "wasi", - "windows-sys", + "windows-sys 0.42.0", ] [[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", @@ -716,6 +716,16 @@ dependencies = [ "tempfile", ] +[[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" @@ -737,19 +747,19 @@ dependencies = [ [[package]] name = "num_cpus" -version = "1.13.1" +version = "1.15.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "19e64526ebdee182341572e50e9ad03965aa510cd94427a4549448f285e957a1" +checksum = "0fac9e2da13b5eb447a6ce3d392f23a29d8694bff781bf03a16cd9ac8697593b" dependencies = [ - "hermit-abi", + "hermit-abi 0.2.6", "libc", ] [[package]] name = "once_cell" -version = "1.15.0" +version = "1.17.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e82dad04139b71a90c080c8463fe0dc7902db5192d939bd0950f074d014339e1" +checksum = "6f61fba1741ea2b3d6a1e3178721804bb716a68a6aeba1149b5d52e3d464ea66" [[package]] name = "opaque-debug" @@ -759,9 +769,9 @@ checksum = "624a8340c38c1b80fd549087862da4ba43e08858af025b236e509b6649fc13d5" [[package]] name = "openssl" -version = "0.10.42" +version = "0.10.45" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "12fc0523e3bd51a692c8850d075d74dc062ccf251c0110668cbd921917118a13" +checksum = "b102428fd03bc5edf97f62620f7298614c45cedf287c271e7ed450bbaf83f2e1" dependencies = [ "bitflags", "cfg-if", @@ -791,9 +801,9 @@ checksum = "ff011a302c396a5197692431fc1948019154afc178baf7d8e37367442a4601cf" [[package]] name = "openssl-sys" -version = "0.9.76" +version = "0.9.80" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5230151e44c0f05157effb743e8d517472843121cf9243e8b81393edb5acd9ce" +checksum = "23bbbf7854cd45b83958ebe919f0e8e516793727652e27fda10a8384cfc790b7" dependencies = [ "autocfg", "cc", @@ -804,9 +814,9 @@ dependencies = [ [[package]] name = "os_str_bytes" -version = "6.3.0" +version = "6.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9ff7415e9ae3fff1225851df9e0d9e4e5479f947619774677a63572e55e80eff" +checksum = "9b7820b9daea5457c9f21c69448905d723fbd21136ccf521748f23fd49e723ee" [[package]] name = "output_vt100" @@ -817,6 +827,12 @@ dependencies = [ "winapi", ] +[[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" @@ -829,15 +845,15 @@ dependencies = [ [[package]] name = "parking_lot_core" -version = "0.9.3" +version = "0.9.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "09a279cbf25cb0757810394fbc1e359949b59e348145c643a939a525692e6929" +checksum = "9069cbb9f99e3a5083476ccb29ceb1de18b9118cafa53e90c9551235de2b9521" dependencies = [ "cfg-if", "libc", "redox_syscall", "smallvec", - "windows-sys", + "windows-sys 0.45.0", ] [[package]] @@ -848,9 +864,9 @@ checksum = "478c572c3d73181ff3c2539045f6eb99e5491218eae919370993b890cdbdd98e" [[package]] name = "pest" -version = "2.4.0" +version = "2.5.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dbc7bc69c062e492337d74d59b120c274fd3d261b6bf6d3207d499b4b379c41a" +checksum = "4ab62d2fa33726dbe6321cc97ef96d8cde531e3eeaf858a058de53a8a6d40d8f" dependencies = [ "thiserror", "ucd-trie", @@ -858,9 +874,9 @@ dependencies = [ [[package]] name = "pest_derive" -version = "2.4.0" +version = "2.5.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "60b75706b9642ebcb34dab3bc7750f811609a0eb1dd8b88c2d15bf628c1c65b2" +checksum = "8bf026e2d0581559db66d837fe5242320f525d85c76283c61f4d51a1238d65ea" dependencies = [ "pest", "pest_generator", @@ -868,9 +884,9 @@ dependencies = [ [[package]] name = "pest_generator" -version = "2.4.0" +version = "2.5.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f4f9272122f5979a6511a749af9db9bfc810393f63119970d7085fed1c4ea0db" +checksum = "2b27bd18aa01d91c8ed2b61ea23406a676b42d82609c6e2581fba42f0c15f17f" dependencies = [ "pest", "pest_meta", @@ -881,13 +897,13 @@ dependencies = [ [[package]] name = "pest_meta" -version = "2.4.0" +version = "2.5.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4c8717927f9b79515e565a64fe46c38b8cd0427e64c40680b14a7365ab09ac8d" +checksum = "9f02b677c1859756359fc9983c2e56a0237f18624a3789528804406b7e915e5d" dependencies = [ "once_cell", "pest", - "sha1", + "sha2 0.10.6", ] [[package]] @@ -904,9 +920,9 @@ checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" [[package]] name = "pkg-config" -version = "0.3.25" +version = "0.3.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1df8c4ec4b0627e53bdf214615ad287367e482558cf84b109250b37464dc03ae" +checksum = "6ac9a59f73473f1b8d852421e59e64809f025994837ef743615c6d0c5b305160" [[package]] name = "pretty_assertions" @@ -946,9 +962,9 @@ dependencies = [ [[package]] name = "proc-macro2" -version = "1.0.46" +version = "1.0.51" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "94e2ef8dbfc347b10c094890f778ee2e36ca9bb4262e86dc99cd217e35f3470b" +checksum = "5d727cae5b39d21da60fa540906919ad737832fe0b1c165da3a34d6548c849d6" dependencies = [ "unicode-ident", ] @@ -984,9 +1000,9 @@ dependencies = [ [[package]] name = "quote" -version = "1.0.21" +version = "1.0.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bbe448f377a7d6961e30f5955f9b8d106c3f5e449d493ee1b125c1d43c2b5179" +checksum = "8856d8364d252a14d474036ea1358d63c9e6965c8e5c1885c18f73d70bff9c7b" dependencies = [ "proc-macro2", ] @@ -1002,9 +1018,9 @@ dependencies = [ [[package]] name = "regex" -version = "1.6.0" +version = "1.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4c4eb3267174b8c6c2f654116623910a0fef09c4753f8dd83db29c48a0df988b" +checksum = "48aaa5748ba571fb95cd2c85c09f629215d3a6ece942baa100950af03a34f733" dependencies = [ "aho-corasick", "memchr", @@ -1022,9 +1038,9 @@ dependencies = [ [[package]] name = "regex-syntax" -version = "0.6.27" +version = "0.6.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a3f87b73ce11b1619a3c6332f45341e0047173771e8b8b73f87bfeefb7b56244" +checksum = "456c603be3e8d448b072f410900c09faf164fbce2d480456f50eea6e25f9c848" [[package]] name = "remove_dir_all" @@ -1037,9 +1053,9 @@ dependencies = [ [[package]] name = "reqwest" -version = "0.11.12" +version = "0.11.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "431949c384f4e2ae07605ccaa56d1d9d2ecdb5cadd4f9577ccfab29f2e5149fc" +checksum = "21eed90ec8570952d53b772ecf8f206aa1ec9a3d76b2521c56c42973f2d91ee9" dependencies = [ "base64", "bytes", @@ -1074,18 +1090,17 @@ dependencies = [ [[package]] name = "ryu" -version = "1.0.11" +version = "1.0.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4501abdff3ae82a1c1b477a17252eb69cee9e66eb915c1abaa4f44d873df9f09" +checksum = "7b4b9743ed687d4b4bcedf9ff5eaa7398495ae14e61cba0a295704edbc7decde" [[package]] name = "schannel" -version = "0.1.20" +version = "0.1.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "88d6731146462ea25d9244b2ed5fd1d716d25c52e4d54aa4fb0f3c4e9854dbe2" +checksum = "713cfb06c7059f3588fb8044c0fad1d09e3c01d225e25b9220dbfdcf16dbb1b3" dependencies = [ - "lazy_static", - "windows-sys", + "windows-sys 0.42.0", ] [[package]] @@ -1096,9 +1111,9 @@ checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd" [[package]] name = "security-framework" -version = "2.7.0" +version = "2.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2bc1bb97804af6631813c55739f771071e0f2ed33ee20b68c86ec505d906356c" +checksum = "a332be01508d814fed64bf28f798a146d73792121129962fdf335bb3c49a4254" dependencies = [ "bitflags", "core-foundation", @@ -1109,9 +1124,9 @@ dependencies = [ [[package]] name = "security-framework-sys" -version = "2.6.1" +version = "2.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0160a13a177a45bfb43ce71c01580998474f556ad854dcbca936dd2841a5c556" +checksum = "31c9bb296072e961fcbd8853511dd39c2d8be2deb1e17c6860b1d30732b323b4" dependencies = [ "core-foundation-sys", "libc", @@ -1119,24 +1134,24 @@ dependencies = [ [[package]] name = "semver" -version = "1.0.14" +version = "1.0.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e25dfac463d778e353db5be2449d1cce89bd6fd23c9f1ea21310ce6e5a1b29c4" +checksum = "58bc9567378fc7690d6b2addae4e60ac2eeea07becb2c64b9f218b53865cba2a" [[package]] name = "serde" -version = "1.0.145" +version = "1.0.152" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "728eb6351430bccb993660dfffc5a72f91ccc1295abaa8ce19b27ebe4f75568b" +checksum = "bb7d1f0d3021d347a83e556fc4683dea2ea09d87bccdf88ff5c12545d89d5efb" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.145" +version = "1.0.152" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "81fa1584d3d1bcacd84c277a0dfe21f5b0f6accf4a23d04d4c6d61f1af522b4c" +checksum = "af487d118eecd09402d70a5d72551860e788df87b464af30e5ea6a38c75c541e" dependencies = [ "proc-macro2", "quote", @@ -1145,9 +1160,9 @@ dependencies = [ [[package]] name = "serde_json" -version = "1.0.85" +version = "1.0.92" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e55a28e3aaef9d5ce0506d0a14dbba8054ddc7e499ef522dd8b26859ec9d4a44" +checksum = "7434af0dc1cbd59268aa98b4c22c131c0584d2232f6fb166efb993e2832e896a" dependencies = [ "itoa", "ryu", @@ -1167,37 +1182,37 @@ dependencies = [ ] [[package]] -name = "sha1" -version = "0.10.5" +name = "sha2" +version = "0.9.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f04293dc80c3993519f2d7f6f511707ee7094fe0c6d3406feb330cdb3540eba3" +checksum = "4d58a1e1bf39749807d89cf2d98ac2dfa0ff1cb3faa38fbb64dd88ac8013d800" dependencies = [ + "block-buffer 0.9.0", "cfg-if", "cpufeatures", - "digest 0.10.5", + "digest 0.9.0", + "opaque-debug", ] [[package]] name = "sha2" -version = "0.9.9" +version = "0.10.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4d58a1e1bf39749807d89cf2d98ac2dfa0ff1cb3faa38fbb64dd88ac8013d800" +checksum = "82e6b795fe2e3b1e845bafcb27aa35405c4d47cdfc92af5fc8d3002f76cebdc0" dependencies = [ - "block-buffer 0.9.0", "cfg-if", "cpufeatures", - "digest 0.9.0", - "opaque-debug", + "digest 0.10.6", ] [[package]] name = "sha256" -version = "1.0.3" +version = "1.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4e84a7f596c081d359de5e06a83877138bc3c4483591e1af1916e1472e6e146e" +checksum = "e334db67871c14c18fc066ad14af13f9fdf5f9a91c61af432d1e3a39c8c6a141" dependencies = [ "hex", - "sha2", + "sha2 0.9.9", ] [[package]] @@ -1268,9 +1283,9 @@ checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623" [[package]] name = "syn" -version = "1.0.101" +version = "1.0.107" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e90cde112c4b9690b8cbe810cba9ddd8bc1d7472e2cae317b69e9438c1cba7d2" +checksum = "1f4064b5b16e03ae50984a5a8ed5d4f8803e6bc1fd170a3cda91a1be4b18e3f5" dependencies = [ "proc-macro2", "quote", @@ -1293,9 +1308,9 @@ dependencies = [ [[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", ] @@ -1312,24 +1327,24 @@ dependencies = [ [[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 = "thiserror" -version = "1.0.37" +version = "1.0.38" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "10deb33631e3c9018b9baf9dcbbc4f737320d2b576bac10f6aefa048fa407e3e" +checksum = "6a9cd18aa97d5c45c6603caea1da6628790b37f7a34b6ca89522331c5180fed0" dependencies = [ "thiserror-impl", ] [[package]] name = "thiserror-impl" -version = "1.0.37" +version = "1.0.38" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "982d17546b47146b28f7c22e3d08465f6b8903d0ea13c1660d9d84a6e7adcdbb" +checksum = "1fb327af4685e4d03fa8cbcf1716380da910eeb2bb8be417e7f9fd3fb164f36f" dependencies = [ "proc-macro2", "quote", @@ -1356,15 +1371,15 @@ 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.25.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a9e03c497dc955702ba729190dc4aac6f2a0ce97f913e5b1b5912fc5039d9099" +checksum = "c8e00990ebabbe4c14c08aca901caed183ecd5c09562a12c824bb53d3c3fd3af" dependencies = [ "autocfg", "bytes", @@ -1377,14 +1392,14 @@ dependencies = [ "signal-hook-registry", "socket2", "tokio-macros", - "winapi", + "windows-sys 0.42.0", ] [[package]] name = "tokio-macros" -version = "1.8.0" +version = "1.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9724f9a975fb987ef7a3cd9be0350edcbe130698af5b8f7a631e23d42d052484" +checksum = "d266c00fde287f55d3f1c3e96c500c362a2b8c695076ec180f27918820bc6df8" dependencies = [ "proc-macro2", "quote", @@ -1417,9 +1432,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", @@ -1433,9 +1448,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", @@ -1445,9 +1460,9 @@ dependencies = [ [[package]] name = "tracing-attributes" -version = "0.1.22" +version = "0.1.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "11c75893af559bc8e10716548bdef5cb2b983f8e637db9d0e15126b61b484ee2" +checksum = "4017f8f45139870ca7e672686113917c71c7a6e02d4924eda67186083c03081a" dependencies = [ "proc-macro2", "quote", @@ -1456,9 +1471,9 @@ dependencies = [ [[package]] name = "tracing-core" -version = "0.1.29" +version = "0.1.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5aeea4303076558a00714b823f9ad67d58a3bbda1df83d8827d21193156e22f7" +checksum = "24eb03ba0eab1fd845050058ce5e616558e8f8d8fca633e6b163fe25c797213a" dependencies = [ "once_cell", "valuable", @@ -1477,12 +1492,12 @@ dependencies = [ [[package]] name = "tracing-subscriber" -version = "0.3.15" +version = "0.3.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "60db860322da191b40952ad9affe65ea23e7dd6a5c442c2c42865810c6ab8e6b" +checksum = "a6176eae26dd70d0c919749377897b54a9276bd7061339665dd68777926b5a70" dependencies = [ - "ansi_term", "matchers", + "nu-ansi-term", "once_cell", "regex", "sharded-slab", @@ -1495,15 +1510,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 = "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 = "ucd-trie" @@ -1513,15 +1528,15 @@ checksum = "9e79c4d996edb816c91e4308506774452e55e95c3c9de07b6729e17e15a5ef81" [[package]] name = "unicode-bidi" -version = "0.3.8" +version = "0.3.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "099b7128301d285f79ddd55b9a83d5e6b9e97c92e0ea0daebee7263e932de992" +checksum = "d54675592c1dbefd78cbd98db9bacd89886e1ca50692a0692baefffdeb92dd58" [[package]] name = "unicode-ident" -version = "1.0.4" +version = "1.0.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dcc811dc4066ac62f84f11307873c4850cb653bfa9b1719cee2bd2204a4bc5dd" +checksum = "84a22b9f218b40614adcb3f4ff08b703773ad44fa9423e4e0d346d5db86e4ebc" [[package]] name = "unicode-normalization" @@ -1585,9 +1600,9 @@ checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" [[package]] name = "wasm-bindgen" -version = "0.2.83" +version = "0.2.84" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eaf9f5aceeec8be17c128b2e93e031fb8a4d469bb9c4ae2d7dc1888b26887268" +checksum = "31f8dcbc21f30d9b8f2ea926ecb58f6b91192c17e9d33594b3df58b2007ca53b" dependencies = [ "cfg-if", "wasm-bindgen-macro", @@ -1595,9 +1610,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-backend" -version = "0.2.83" +version = "0.2.84" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4c8ffb332579b0557b52d268b91feab8df3615f265d5270fec2a8c95b17c1142" +checksum = "95ce90fd5bcc06af55a641a86428ee4229e44e07033963a2290a8e241607ccb9" dependencies = [ "bumpalo", "log", @@ -1610,9 +1625,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-futures" -version = "0.4.33" +version = "0.4.34" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "23639446165ca5a5de86ae1d8896b737ae80319560fbaa4c2887b7da6e7ebd7d" +checksum = "f219e0d211ba40266969f6dbdd90636da12f75bee4fc9d6c23d1260dadb51454" dependencies = [ "cfg-if", "js-sys", @@ -1622,9 +1637,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro" -version = "0.2.83" +version = "0.2.84" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "052be0f94026e6cbc75cdefc9bae13fd6052cdcaf532fa6c45e7ae33a1e6c810" +checksum = "4c21f77c0bedc37fd5dc21f897894a5ca01e7bb159884559461862ae90c0b4c5" dependencies = [ "quote", "wasm-bindgen-macro-support", @@ -1632,9 +1647,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro-support" -version = "0.2.83" +version = "0.2.84" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "07bc0c051dc5f23e307b13285f9d75df86bfdf816c5721e573dec1f9b8aa193c" +checksum = "2aff81306fcac3c7515ad4e177f521b5c9a15f2b08f4e32d823066102f35a5f6" dependencies = [ "proc-macro2", "quote", @@ -1645,15 +1660,15 @@ dependencies = [ [[package]] name = "wasm-bindgen-shared" -version = "0.2.83" +version = "0.2.84" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1c38c045535d93ec4f0b4defec448e4291638ee608530863b1e2ba115d4fff7f" +checksum = "0046fef7e28c3804e5e38bfa31ea2a0f73905319b677e57ebe37e49358989b5d" [[package]] name = "web-sys" -version = "0.3.60" +version = "0.3.61" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bcda906d8be16e728fd5adc5b729afad4e444e106ab28cd1c7256e54fa61510f" +checksum = "e33b99f4b23ba3eec1a53ac264e35a755f00e966e0065077d6027c0f575b0b97" dependencies = [ "js-sys", "wasm-bindgen", @@ -1692,46 +1707,84 @@ checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" [[package]] name = "windows-sys" -version = "0.36.1" +version = "0.42.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ea04155a16a59f9eab786fe12a4a450e75cdb175f9e0d80da1e17db09f55b8d2" +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" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "75283be5efb2831d37ea142365f009c02ec203cd29a3ebecbc093d52315b66d0" +dependencies = [ + "windows-targets", +] + +[[package]] +name = "windows-targets" +version = "0.42.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e2522491fbfcd58cc84d47aeb2958948c4b8982e9a2d8a2a35bbaed431390e7" +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.42.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8c9864e83243fdec7fc9c5444389dcbbfd258f745e7853198f365e3c4968a608" + [[package]] name = "windows_aarch64_msvc" -version = "0.36.1" +version = "0.42.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9bb8c3fd39ade2d67e9874ac4f3db21f0d710bee00fe7cab16949ec184eeaa47" +checksum = "4c8b1b673ffc16c47a9ff48570a9d85e25d265735c503681332589af6253c6c7" [[package]] name = "windows_i686_gnu" -version = "0.36.1" +version = "0.42.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "180e6ccf01daf4c426b846dfc66db1fc518f074baa793aa7d9b9aaeffad6a3b6" +checksum = "de3887528ad530ba7bdbb1faa8275ec7a1155a45ffa57c37993960277145d640" [[package]] name = "windows_i686_msvc" -version = "0.36.1" +version = "0.42.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e2e7917148b2812d1eeafaeb22a97e4813dfa60a3f8f78ebe204bcc88f12f024" +checksum = "bf4d1122317eddd6ff351aa852118a2418ad4214e6613a50e0191f7004372605" [[package]] name = "windows_x86_64_gnu" -version = "0.36.1" +version = "0.42.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c1040f221285e17ebccbc2591ffdc2d44ee1f9186324dd3e84e99ac68d699c45" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.42.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4dcd171b8776c41b97521e5da127a2d86ad280114807d0b2ab1e462bc764d9e1" +checksum = "628bfdf232daa22b0d64fdb62b09fcc36bb01f05a3939e20ab73aaf9470d0463" [[package]] name = "windows_x86_64_msvc" -version = "0.36.1" +version = "0.42.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c811ca4a8c853ef420abd8592ba53ddbbac90410fab6903b3e79972a631f7680" +checksum = "447660ad36a13288b1db4d4248e857b510e8c3a225c822ba4fb748c0aafecffd" [[package]] name = "winreg" diff --git a/tools/ci-build/publisher/Cargo.toml b/tools/ci-build/publisher/Cargo.toml index fb543a9daa..f076cedacf 100644 --- a/tools/ci-build/publisher/Cargo.toml +++ b/tools/ci-build/publisher/Cargo.toml @@ -38,4 +38,4 @@ tracing-subscriber = { version = "0.3.15", features = ["env-filter"] } tempfile = "3.3.0" [dev-dependencies] -pretty_assertions = "1.2.1" +pretty_assertions = "1.3" diff --git a/tools/ci-build/publisher/src/publish.rs b/tools/ci-build/publisher/src/publish.rs index 7d95a838eb..1a835a7a49 100644 --- a/tools/ci-build/publisher/src/publish.rs +++ b/tools/ci-build/publisher/src/publish.rs @@ -40,7 +40,7 @@ pub async fn publish(handle: &PackageHandle, crate_path: &Path) -> anyhow::Resul 5, Duration::from_secs(60), || async { - cargo::Publish::new(handle.clone(), &crate_path) + cargo::Publish::new(handle.clone(), crate_path) .spawn() .await?; Result::<_, BoxError>::Ok(()) diff --git a/tools/ci-build/publisher/src/retry.rs b/tools/ci-build/publisher/src/retry.rs index 12db87a920..1608da8448 100644 --- a/tools/ci-build/publisher/src/retry.rs +++ b/tools/ci-build/publisher/src/retry.rs @@ -51,7 +51,7 @@ where } ErrorClass::Retry => { info!( - "{} failed on attempt {} with retryable error: {}. Will retry after {:?}", + "{} failed on attempt {} with retryable error: {:?}. Will retry after {:?}", what, attempt, err.into(), backoff ); } diff --git a/tools/ci-build/publisher/src/sort.rs b/tools/ci-build/publisher/src/sort.rs index a6da808a4e..5c1d273294 100644 --- a/tools/ci-build/publisher/src/sort.rs +++ b/tools/ci-build/publisher/src/sort.rs @@ -19,11 +19,7 @@ pub fn dependency_order(packages: Vec) -> Result> { let mut visited = BTreeSet::new(); let mut to_visit: Vec<&Package> = packages.iter().map(|e| e.1).collect(); - to_visit.sort_by(|a, b| { - (*a).local_dependencies - .len() - .cmp(&(*b).local_dependencies.len()) - }); + to_visit.sort_by(|a, b| a.local_dependencies.len().cmp(&b.local_dependencies.len())); // Depth-first search topological sort while let Some(package) = to_visit.iter().find(|e| !visited.contains(&e.handle)) { 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 719cb167f7..e06c79c6fa 100644 --- a/tools/ci-build/publisher/src/subcommand/generate_version_manifest.rs +++ b/tools/ci-build/publisher/src/subcommand/generate_version_manifest.rs @@ -45,7 +45,7 @@ pub async fn subcommand_generate_version_manifest( ) -> Result<()> { verify_crate_hasher_available()?; - let repo_root = find_git_repository_root("smithy-rs", &std::env::current_dir()?)?; + let repo_root = find_git_repository_root("smithy-rs", std::env::current_dir()?)?; let smithy_rs_revision = GitCLI::new(&repo_root)? .get_head_revision() .context("get smithy-rs revision")?; @@ -197,7 +197,7 @@ fn hash_models(projection: &SmithyBuildProjection) -> Result { // Must match `hashModels` in `CrateVersioner.kt` let mut hashes = String::new(); for import in &projection.imports { - hashes.push_str(&sha256::digest_file(import).context("hash model")?); + hashes.push_str(&sha256::try_digest(import.as_path())?); hashes.push('\n'); } Ok(sha256::digest(hashes)) @@ -217,7 +217,7 @@ impl SmithyBuildRoot { #[derive(Debug, Deserialize)] struct SmithyBuildProjection { - imports: Vec, + imports: Vec, } #[cfg(test)] @@ -348,10 +348,7 @@ mod tests { fs::write(&model1b, "bar").unwrap(); let hash = hash_models(&SmithyBuildProjection { - imports: vec![ - model1a.to_str().unwrap().to_string(), - model1b.to_str().unwrap().to_string(), - ], + imports: vec![model1a, model1b], }) .unwrap(); diff --git a/tools/ci-build/publisher/src/subcommand/hydrate_readme.rs b/tools/ci-build/publisher/src/subcommand/hydrate_readme.rs index bf63116406..683a570f40 100644 --- a/tools/ci-build/publisher/src/subcommand/hydrate_readme.rs +++ b/tools/ci-build/publisher/src/subcommand/hydrate_readme.rs @@ -38,7 +38,7 @@ pub fn subcommand_hydrate_readme( ) -> Result<()> { let versions_manifest = VersionsManifest::from_file(versions_manifest) .with_context(|| format!("Failed to read versions manifest at {versions_manifest:?}"))?; - let template = fs::read_to_string(&input) + let template = fs::read_to_string(input) .with_context(|| format!("Failed to read README template file at {input:?}"))?; let mut context = json!({ "msrv": msrv }); diff --git a/tools/ci-build/publisher/src/subcommand/publish.rs b/tools/ci-build/publisher/src/subcommand/publish.rs index 5d76e5ff01..dfa2c7af54 100644 --- a/tools/ci-build/publisher/src/subcommand/publish.rs +++ b/tools/ci-build/publisher/src/subcommand/publish.rs @@ -52,7 +52,7 @@ pub async fn subcommand_publish( // Don't proceed unless the user confirms the plan confirm_plan(&batches, stats, *skip_confirmation)?; - for batch in batches { + for batch in &batches { let mut any_published = false; for package in batch { // Only publish if it hasn't been published yet. @@ -65,13 +65,12 @@ pub async fn subcommand_publish( // Sometimes it takes a little bit of time for the new package version // to become available after publish. If we proceed too quickly, then // the next package publish can fail if it depends on this package. - wait_for_eventual_consistency(&package).await?; - info!("Successfully published `{}`", package.handle); + wait_for_eventual_consistency(package).await?; + info!("Successfully published `{}`", &package.handle); any_published = true; } else { - info!("`{}` was already published", package.handle); + info!("`{}` was already published", &package.handle); } - correct_owner(&package.handle, &package.category).await?; } if any_published { info!("Sleeping 30 seconds after completion of the batch"); @@ -81,6 +80,12 @@ pub async fn subcommand_publish( } } + for batch in &batches { + for package in batch { + correct_owner(&package.handle, &package.category).await?; + } + } + Ok(()) } @@ -142,6 +147,11 @@ async fn wait_for_eventual_consistency(package: &Package) -> Result<()> { /// Corrects the crate ownership. pub async fn correct_owner(handle: &PackageHandle, category: &PackageCategory) -> Result<()> { + // https://github.com/orgs/awslabs/teams/smithy-rs-server + const SMITHY_RS_SERVER_OWNER: &str = "github:awslabs:smithy-rs-server"; + // https://github.com/orgs/awslabs/teams/rust-sdk-owners + const RUST_SDK_OWNER: &str = "github:awslabs:rust-sdk-owners"; + run_with_retry( &format!("Correcting ownership of `{}`", handle.name), 3, @@ -151,7 +161,7 @@ pub async fn correct_owner(handle: &PackageHandle, category: &PackageCategory) - let expected_owners = expected_package_owners(category, &handle.name); let owners_to_be_added = expected_owners.difference(&actual_owners); - let incorrect_owners = actual_owners.difference(&expected_owners); + let owners_to_be_removed = actual_owners.difference(&expected_owners); let mut added_individual = false; for crate_owner in owners_to_be_added { @@ -162,21 +172,26 @@ pub async fn correct_owner(handle: &PackageHandle, category: &PackageCategory) - // Teams in crates.io start with `github:` while individuals are just the GitHub user name added_individual |= !crate_owner.starts_with("github:"); } - for incorrect_owner in incorrect_owners { + for crate_owner in owners_to_be_removed { + // Trying to remove them will result in an error due to a bug in crates.io + // Upstream tracking issue: https://github.com/rust-lang/crates.io/issues/2736 + if crate_owner == SMITHY_RS_SERVER_OWNER || crate_owner == RUST_SDK_OWNER { + continue; + } // Adding an individual owner requires accepting an invite, so don't attempt to remove // anyone if an owner was added, as removing the last individual owner may break. // The next publish run will remove the incorrect owner. if !added_individual { - cargo::RemoveOwner::new(&handle.name, incorrect_owner) + cargo::RemoveOwner::new(&handle.name, crate_owner) .spawn() .await - .context(format!("remove incorrect owner `{}` from crate `{}`", incorrect_owner, handle))?; + .with_context(|| format!("remove incorrect owner `{}` from crate `{}`", crate_owner, handle))?; info!( "Removed incorrect owner `{}` from crate `{}`", - incorrect_owner, handle + crate_owner, handle ); } else { - info!("Skipping removal of incorrect owner `{}` from crate `{}` due to new owners", incorrect_owner, handle); + info!("Skipping removal of incorrect owner `{}` from crate `{}` due to new owners", crate_owner, handle); } } Result::<_, BoxError>::Ok(()) diff --git a/tools/ci-build/publisher/src/subcommand/upgrade_runtime_crates_version.rs b/tools/ci-build/publisher/src/subcommand/upgrade_runtime_crates_version.rs index 0bf17e3c01..8132e1fa66 100644 --- a/tools/ci-build/publisher/src/subcommand/upgrade_runtime_crates_version.rs +++ b/tools/ci-build/publisher/src/subcommand/upgrade_runtime_crates_version.rs @@ -7,6 +7,7 @@ use crate::fs::Fs; use anyhow::{anyhow, bail, Context}; use clap::Parser; use regex::Regex; +use std::borrow::Cow; use std::path::{Path, PathBuf}; #[derive(Parser, Debug)] @@ -27,35 +28,42 @@ pub async fn subcommand_upgrade_runtime_crates_version( .with_context(|| format!("{} is not a valid semver version", &args.version))?; let fs = Fs::Real; let gradle_properties = read_gradle_properties(fs, &args.gradle_properties_path).await?; + let updated_gradle_properties = update_gradle_properties(&gradle_properties, &upgraded_version) + .with_context(|| { + format!( + "Failed to extract the expected runtime crates version from `{:?}`", + &args.gradle_properties_path + ) + })?; + update_gradle_properties_file( + fs, + &args.gradle_properties_path, + updated_gradle_properties.as_ref(), + ) + .await?; + Ok(()) +} + +fn update_gradle_properties<'a>( + gradle_properties: &'a str, + upgraded_version: &'a semver::Version, +) -> Result, anyhow::Error> { let version_regex = - Regex::new(r"(?Psmithy\.rs\.runtime\.crate\.version=)(?P\d+\.\d+\.\d+-.*)") + Regex::new(r"(?Psmithy\.rs\.runtime\.crate\.version=)(?P\d+\.\d+\.\d+.*)") .unwrap(); - let current_version = version_regex.captures(&gradle_properties).ok_or_else(|| { - anyhow!( - "Failed to extract the expected runtime crates version from `{:?}`", - &args.gradle_properties_path - ) - })?; + let current_version = version_regex + .captures(gradle_properties) + .ok_or_else(|| anyhow!("Failed to extract the expected runtime crates version"))?; let current_version = current_version.name("version").unwrap(); let current_version = semver::Version::parse(current_version.as_str()) .with_context(|| format!("{} is not a valid semver version", current_version.as_str()))?; - if current_version > upgraded_version + if ¤t_version > upgraded_version // Special version tag used on the `main` branch && current_version != semver::Version::parse("0.0.0-smithy-rs-head").unwrap() { bail!("Moving from {current_version} to {upgraded_version} would be a *downgrade*. This command doesn't allow it!"); } - let updated_gradle_properties = version_regex.replace( - &gradle_properties, - format!("${{field}}{}", upgraded_version), - ); - update_gradle_properties( - fs, - &args.gradle_properties_path, - updated_gradle_properties.as_ref(), - ) - .await?; - Ok(()) + Ok(version_regex.replace(gradle_properties, format!("${{field}}{}", upgraded_version))) } async fn read_gradle_properties(fs: Fs, path: &Path) -> Result { @@ -65,7 +73,7 @@ async fn read_gradle_properties(fs: Fs, path: &Path) -> Result anyhow::Result { let anchor_start = anchors.0.as_ref(); let anchor_end = anchors.1.as_ref(); - let start = haystack.find(&anchor_start); + let start = haystack.find(anchor_start); if start.is_none() { if haystack.contains(anchor_end) { bail!("found end anchor but no start anchor"); @@ -33,8 +33,8 @@ pub fn replace_anchor( haystack.push_str(anchor_end); return Ok(true); } - let start = start.unwrap_or_else(|| haystack.find(&anchor_start).expect("must be present")); - let end = match haystack[start..].find(&anchor_end) { + let start = start.unwrap_or_else(|| haystack.find(anchor_start).expect("must be present")); + let end = match haystack[start..].find(anchor_end) { Some(end) => end + start, None => bail!("expected matching end anchor {}", anchor_end), }; @@ -77,9 +77,6 @@ mod test { ); // no replacement should return false - assert_eq!( - replace_anchor(&mut text, &anchors("foo"), "goodbye!").unwrap(), - false - ) + assert!(!replace_anchor(&mut text, &anchors("foo"), "goodbye!").unwrap(),) } } diff --git a/tools/ci-build/sdk-lints/src/main.rs b/tools/ci-build/sdk-lints/src/main.rs index 40fc730b85..501b485218 100644 --- a/tools/ci-build/sdk-lints/src/main.rs +++ b/tools/ci-build/sdk-lints/src/main.rs @@ -65,20 +65,17 @@ enum Args { } fn load_vcs_files() -> Result> { - let tracked_files = Command::new("git") - .arg("ls-tree") - .arg("-r") - .arg("HEAD") - .arg("--name-only") + // This includes files in the index and the working tree (staged or unstaged), as well as + // unignored untracked files. + let vcs_files = Command::new("git") + .arg("ls-files") + .arg("--cached") + .arg("--others") + .arg("--exclude-standard") .current_dir(load_repo_root()?) .output() - .context("couldn't load VCS tracked files")?; - let mut output = String::from_utf8(tracked_files.stdout)?; - let changed_files = Command::new("git") - .arg("diff") - .arg("--name-only") - .output()?; - output.push_str(std::str::from_utf8(changed_files.stdout.as_slice())?); + .context("couldn't load VCS files")?; + let output = String::from_utf8(vcs_files.stdout)?; let files = output .lines() .map(|line| PathBuf::from(line.trim().to_string())); diff --git a/tools/ci-build/sdk-versioner/Cargo.lock b/tools/ci-build/sdk-versioner/Cargo.lock index 00fdd6c997..2862d9eeb4 100644 --- a/tools/ci-build/sdk-versioner/Cargo.lock +++ b/tools/ci-build/sdk-versioner/Cargo.lock @@ -704,7 +704,7 @@ dependencies = [ "pretty_assertions", "smithy-rs-tool-common", "tempfile", - "toml", + "toml_edit", ] [[package]] @@ -925,6 +925,23 @@ dependencies = [ "serde", ] +[[package]] +name = "toml_datetime" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3ab8ed2edee10b50132aed5f331333428b011c99402b5a534154ed15746f9622" + +[[package]] +name = "toml_edit" +version = "0.19.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "08de71aa0d6e348f070457f85af8bd566e2bc452156a423ddf22861b3a953fae" +dependencies = [ + "indexmap", + "toml_datetime", + "winnow", +] + [[package]] name = "tower-service" version = "0.3.2" @@ -1179,6 +1196,15 @@ version = "0.36.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c811ca4a8c853ef420abd8592ba53ddbbac90410fab6903b3e79972a631f7680" +[[package]] +name = "winnow" +version = "0.3.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ee7b2c67f962bf5042bfd8b6a916178df33a26eec343ae064cb8e069f638fa6f" +dependencies = [ + "memchr", +] + [[package]] name = "winreg" version = "0.10.1" diff --git a/tools/ci-build/sdk-versioner/Cargo.toml b/tools/ci-build/sdk-versioner/Cargo.toml index 13122eecce..f7fc947722 100644 --- a/tools/ci-build/sdk-versioner/Cargo.toml +++ b/tools/ci-build/sdk-versioner/Cargo.toml @@ -15,9 +15,9 @@ opt-level = 0 [dependencies] anyhow = "1.0" clap = { version = "~3.1.18", features = ["derive"] } -toml = { version = "0.5.8", features = ["preserve_order"] } +toml_edit = { version = "0.19.6" } smithy-rs-tool-common = { version = "0.1", path = "../smithy-rs-tool-common" } [dev-dependencies] -pretty_assertions = "1" +pretty_assertions = "1.3" tempfile = "3" diff --git a/tools/ci-build/sdk-versioner/src/main.rs b/tools/ci-build/sdk-versioner/src/main.rs index 9ba312776a..77cf9ff606 100644 --- a/tools/ci-build/sdk-versioner/src/main.rs +++ b/tools/ci-build/sdk-versioner/src/main.rs @@ -3,7 +3,7 @@ * SPDX-License-Identifier: Apache-2.0 */ -use anyhow::{bail, Result}; +use anyhow::{bail, Context, Result}; use clap::Parser; use smithy_rs_tool_common::package::{PackageCategory, SDK_PREFIX}; use smithy_rs_tool_common::versions_manifest::VersionsManifest; @@ -11,7 +11,7 @@ use std::ffi::OsStr; use std::fs; use std::path::{Path, PathBuf}; use std::time::Instant; -use toml::value::{Table, Value}; +use toml_edit::{Document, InlineTable, Item, Table, Value}; #[derive(Parser, Debug)] #[clap( @@ -29,6 +29,9 @@ enum Args { /// Path(s) to recursively update Cargo.toml files in #[clap()] crate_paths: Vec, + /// Makes each individual crate its own workspace + #[clap(long)] + isolate_crates: bool, }, /// Revise crates to use version numbers in dependencies UseVersionDependencies { @@ -38,6 +41,9 @@ enum Args { /// Path(s) to recursively update Cargo.toml files in #[clap()] crate_paths: Vec, + /// Makes each individual crate its own workspace + #[clap(long)] + isolate_crates: bool, }, /// Revise crates to use version numbers AND paths in dependencies UsePathAndVersionDependencies { @@ -50,6 +56,9 @@ enum Args { /// Path(s) to recursively update Cargo.toml files in #[clap()] crate_paths: Vec, + /// Makes each individual crate its own workspace + #[clap(long)] + isolate_crates: bool, }, } @@ -62,6 +71,14 @@ impl Args { } } + fn isolate_crates(&self) -> bool { + *match self { + Self::UsePathDependencies { isolate_crates, .. } => isolate_crates, + Self::UseVersionDependencies { isolate_crates, .. } => isolate_crates, + Self::UsePathAndVersionDependencies { isolate_crates, .. } => isolate_crates, + } + } + fn validate(self) -> Result { if self.crate_paths().is_empty() { bail!("Must provide at least one crate path to recursively update"); @@ -84,7 +101,7 @@ fn main() -> Result<()> { }, Args::UseVersionDependencies { versions_toml, .. } => DependencyContext { sdk_path: None, - versions_manifest: Some(VersionsManifest::from_file(&versions_toml)?), + versions_manifest: Some(VersionsManifest::from_file(versions_toml)?), }, Args::UsePathAndVersionDependencies { sdk_path, @@ -92,7 +109,7 @@ fn main() -> Result<()> { .. } => DependencyContext { sdk_path: Some(sdk_path), - versions_manifest: Some(VersionsManifest::from_file(&versions_toml)?), + versions_manifest: Some(VersionsManifest::from_file(versions_toml)?), }, }; @@ -103,7 +120,7 @@ fn main() -> Result<()> { } for manifest_path in manifest_paths { - update_manifest(&manifest_path, &dependency_context)?; + update_manifest(&manifest_path, &dependency_context, args.isolate_crates())?; } println!("Finished in {:?}", start_time.elapsed()); @@ -113,10 +130,16 @@ fn main() -> Result<()> { fn update_manifest( manifest_path: &Path, dependency_context: &DependencyContext, + isolate_crates: bool, ) -> anyhow::Result<()> { println!("Updating {:?}...", manifest_path); - let mut metadata: Value = toml::from_slice(&fs::read(manifest_path)?)?; + let mut metadata: Document = String::from_utf8( + fs::read(manifest_path).with_context(|| format!("failed to read {manifest_path:?}"))?, + ) + .with_context(|| format!("{manifest_path:?} has invalid UTF-8"))? + .parse::() + .with_context(|| format!("failed to parse {manifest_path:?}"))?; let mut changed = false; for set in ["dependencies", "dev-dependencies", "build-dependencies"] { if let Some(dependencies) = metadata.get_mut(set) { @@ -132,9 +155,20 @@ fn update_manifest( || changed; } } + if isolate_crates && !metadata.contains_key("workspace") { + let package_position = metadata["package"] + .as_table() + .expect("has a package") + .position() + .unwrap_or_default(); + let mut workspace = Table::new(); + workspace.set_position(package_position); + metadata.insert("workspace", Item::Table(workspace)); + changed = true; + } if changed { - fs::write(manifest_path, &toml::to_vec(&metadata)?)?; + fs::write(manifest_path, metadata.to_string())?; } Ok(()) @@ -146,12 +180,18 @@ fn update_dependencies( ) -> Result { let mut changed = false; for (key, value) in dependencies.iter_mut() { - let category = PackageCategory::from_package_name(key); + let category = PackageCategory::from_package_name(key.get()); if !matches!(category, PackageCategory::Unknown) { - if !value.is_table() { - *value = Value::Table(Table::new()); - } - update_dependency_value(key, value.as_table_mut().unwrap(), dependency_context)?; + let old_value = match value { + Item::Table(table) => table.clone(), + Item::Value(Value::InlineTable(inline)) => inline.clone().into_table(), + _ => Table::new(), + }; + *value = Item::Value(Value::InlineTable(updated_dependency_value( + key.get(), + old_value, + dependency_context, + )?)); changed = true; } } @@ -169,11 +209,13 @@ fn crate_path_name(name: &str) -> &str { } } -fn update_dependency_value( +fn updated_dependency_value( crate_name: &str, - value: &mut Table, + old_value: Table, dependency_context: &DependencyContext, -) -> Result<()> { +) -> Result { + let mut value = old_value; + // Remove keys that will be replaced value.remove("git"); value.remove("branch"); @@ -183,25 +225,19 @@ fn update_dependency_value( // 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.insert( - "path".to_string(), - Value::String( - crate_path - .as_os_str() - .to_str() - .expect("valid utf-8 path") - .to_string(), - ), + value["path"] = toml_edit::value( + crate_path + .as_os_str() + .to_str() + .expect("valid utf-8 path") + .to_string(), ); } // 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) { - value.insert( - "version".to_string(), - Value::String(crate_metadata.version.clone()), - ); + value["version"] = toml_edit::value(crate_metadata.version.clone()); } else { bail!( "Crate `{}` was missing from the `versions.toml`", @@ -210,7 +246,8 @@ fn update_dependency_value( } } - Ok(()) + value.sort_values_by(|a, _, b, _| b.cmp(a)); + Ok(value.into_inline_table()) } /// Recursively discovers Cargo.toml files in the given `path` and adds them to `manifests`. @@ -238,7 +275,6 @@ mod tests { use smithy_rs_tool_common::package::PackageCategory; use smithy_rs_tool_common::versions_manifest::{CrateVersion, VersionsManifest}; use std::path::PathBuf; - use toml::Value; fn versions_toml_for(crates: &[(&str, &str)]) -> VersionsManifest { VersionsManifest { @@ -264,35 +300,44 @@ mod tests { } const TEST_MANIFEST: &[u8] = br#" - [package] - name = "test" - version = "0.1.0" - - [dependencies] - aws-config = "0.4.1" - aws-sdk-s3 = "0.4.1" - aws-smithy-types = "0.34.1" - aws-smithy-http = { version = "0.34.1", features = ["test-util"] } - something-else = "0.1" - "#; +[package] +name = "test" +version = "0.1.0" + +# Some comment that should be preserved +[dependencies] +aws-config = "0.4.1" +aws-sdk-s3 = "0.4.1" +aws-smithy-types = "0.34.1" +aws-smithy-http = { version = "0.34.1", 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"] +"#; #[track_caller] - fn test_with_context(context: DependencyContext, expected: &[u8]) { + 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(); std::fs::write(&manifest_path, TEST_MANIFEST).unwrap(); - update_manifest(&manifest_path, &context).expect("success"); + update_manifest(&manifest_path, &context, isolate_crates).expect("success"); - let actual = toml::from_slice(&std::fs::read(&manifest_path).expect("read tmp file")) - .expect("valid toml"); - let expected: Value = toml::from_slice(expected).unwrap(); + let actual = + String::from_utf8(std::fs::read(&manifest_path).expect("read tmp file")).unwrap(); + let expected = std::str::from_utf8(expected).unwrap(); assert_eq!(expected, actual); } #[test] fn update_dependencies_with_versions() { test_with_context( + false, DependencyContext { sdk_path: None, versions_manifest: Some(versions_toml_for(&[ @@ -303,45 +348,99 @@ mod tests { ])), }, br#" - [package] - name = "test" - version = "0.1.0" - - [dependencies] - aws-config = { version = "0.5.0" } - aws-sdk-s3 = { version = "0.13.0" } - aws-smithy-types = { version = "0.10.0" } - aws-smithy-http = { version = "0.9.0", features = ["test-util"] } - something-else = "0.1" - "#, +[package] +name = "test" +version = "0.1.0" + +# Some comment that should be preserved +[dependencies] +aws-config = { version = "0.5.0" } +aws-sdk-s3 = { version = "0.13.0" } +aws-smithy-types = { version = "0.10.0" } +aws-smithy-http = { version = "0.9.0", 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"] +"#, ); } #[test] fn update_dependencies_with_paths() { test_with_context( + false, DependencyContext { sdk_path: Some(&PathBuf::from("/foo/asdf/")), versions_manifest: None, }, br#" - [package] - name = "test" - version = "0.1.0" - - [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"] } - something-else = "0.1" - "#, +[package] +name = "test" +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"] } +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"] +"#, ); } #[test] fn update_dependencies_with_versions_and_paths() { test_with_context( + false, + DependencyContext { + sdk_path: Some(&PathBuf::from("/foo/asdf/")), + versions_manifest: Some(versions_toml_for(&[ + ("aws-config", "0.5.0"), + ("aws-sdk-s3", "0.13.0"), + ("aws-smithy-types", "0.10.0"), + ("aws-smithy-http", "0.9.0"), + ])), + }, + br#" +[package] +name = "test" +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"] } +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"] +"# + ); + } + + #[test] + fn update_dependencies_isolate_crates() { + test_with_context( + true, DependencyContext { sdk_path: Some(&PathBuf::from("/foo/asdf/")), versions_manifest: Some(versions_toml_for(&[ @@ -352,17 +451,27 @@ mod tests { ])), }, br#" - [package] - name = "test" - version = "0.1.0" - - [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"] } - something-else = "0.1" - "# +[package] +name = "test" +version = "0.1.0" + +[workspace] + +# 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"] } +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"] +"# ); } } 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 e6524dc9be..4944c7aa4c 100644 --- a/tools/ci-build/smithy-rs-tool-common/src/changelog.rs +++ b/tools/ci-build/smithy-rs-tool-common/src/changelog.rs @@ -389,7 +389,7 @@ mod tests { let res = changelog.validate(); assert!(res.is_ok()); if let Err(e) = res { - assert!(false, "some error has been produced {e:?}"); + panic!("some error has been produced {e:?}"); } assert_eq!(changelog.smithy_rs[1].meta.target, Some(SdkAffected::All)); } diff --git a/tools/ci-build/smithy-rs-tool-common/src/versions_manifest.rs b/tools/ci-build/smithy-rs-tool-common/src/versions_manifest.rs index 33afc04abd..e8d2c55509 100644 --- a/tools/ci-build/smithy-rs-tool-common/src/versions_manifest.rs +++ b/tools/ci-build/smithy-rs-tool-common/src/versions_manifest.rs @@ -15,6 +15,8 @@ use std::fs; use std::path::Path; use std::str::FromStr; +pub type CrateVersionMetadataMap = BTreeMap; + /// Root struct representing a `versions.toml` manifest #[derive(Clone, Debug, Deserialize, Serialize, Eq, PartialEq)] pub struct VersionsManifest { @@ -31,7 +33,7 @@ pub struct VersionsManifest { pub manual_interventions: ManualInterventions, /// All SDK crate version metadata - pub crates: BTreeMap, + pub crates: CrateVersionMetadataMap, /// Crate versions that were a part of this SDK release. /// Releases may not release every single crate, which can happen if a crate has no changes. diff --git a/tools/ci-cdk/canary-lambda/src/canary.rs b/tools/ci-cdk/canary-lambda/src/canary.rs index 9158b4751f..a7d497b52e 100644 --- a/tools/ci-cdk/canary-lambda/src/canary.rs +++ b/tools/ci-cdk/canary-lambda/src/canary.rs @@ -11,8 +11,8 @@ use std::pin::Pin; use aws_config::SdkConfig; use tracing::{info_span, Instrument}; -use crate::paginator_canary; -use crate::{s3_canary, transcribe_canary}; +use crate::current_canary::paginator_canary; +use crate::current_canary::{s3_canary, transcribe_canary}; #[macro_export] macro_rules! mk_canary { diff --git a/tools/ci-cdk/canary-lambda/src/latest.rs b/tools/ci-cdk/canary-lambda/src/latest.rs new file mode 100644 index 0000000000..238c361166 --- /dev/null +++ b/tools/ci-cdk/canary-lambda/src/latest.rs @@ -0,0 +1,8 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +pub(crate) mod paginator_canary; +pub(crate) mod s3_canary; +pub(crate) mod transcribe_canary; diff --git a/tools/ci-cdk/canary-lambda/src/latest/paginator_canary.rs b/tools/ci-cdk/canary-lambda/src/latest/paginator_canary.rs new file mode 100644 index 0000000000..d50c4f2be8 --- /dev/null +++ b/tools/ci-cdk/canary-lambda/src/latest/paginator_canary.rs @@ -0,0 +1,71 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +use crate::mk_canary; +use anyhow::bail; + +use aws_sdk_ec2 as ec2; +use aws_sdk_ec2::types::InstanceType; + +use crate::CanaryEnv; +use tokio_stream::StreamExt; + +mk_canary!( + "ec2_paginator", + |sdk_config: &aws_config::SdkConfig, env: &CanaryEnv| { + paginator_canary(ec2::Client::new(sdk_config), env.page_size) + } +); + +pub async fn paginator_canary(client: ec2::Client, page_size: usize) -> anyhow::Result<()> { + let mut history = client + .describe_spot_price_history() + .instance_types(InstanceType::M1Medium) + .into_paginator() + .page_size(page_size as i32) + .send(); + + let mut num_pages = 0; + while let Some(page) = history.try_next().await? { + let items_in_page = page.spot_price_history.unwrap_or_default().len(); + if items_in_page > page_size { + bail!( + "failed to retrieve results of correct page size (expected {}, got {})", + page_size, + items_in_page + ) + } + num_pages += 1; + } + if dbg!(num_pages) < 2 { + bail!( + "expected 3+ pages containing ~60 results but got {} pages", + num_pages + ) + } + + // https://github.com/awslabs/aws-sdk-rust/issues/405 + let _ = client + .describe_vpcs() + .into_paginator() + .items() + .send() + .collect::, _>>() + .await?; + + Ok(()) +} + +#[cfg(test)] +mod test { + use crate::latest::paginator_canary::paginator_canary; + + #[tokio::test] + async fn test_paginator() { + let conf = aws_config::load_from_env().await; + let client = aws_sdk_ec2::Client::new(&conf); + paginator_canary(client, 20).await.unwrap() + } +} diff --git a/tools/ci-cdk/canary-lambda/src/latest/s3_canary.rs b/tools/ci-cdk/canary-lambda/src/latest/s3_canary.rs new file mode 100644 index 0000000000..fbcba976d8 --- /dev/null +++ b/tools/ci-cdk/canary-lambda/src/latest/s3_canary.rs @@ -0,0 +1,140 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +use crate::canary::CanaryError; +use crate::{mk_canary, CanaryEnv}; +use anyhow::Context; +use aws_config::SdkConfig; +use aws_sdk_s3 as s3; +use s3::presigning::PresigningConfig; +use s3::primitives::ByteStream; +use std::time::Duration; +use uuid::Uuid; + +const METADATA_TEST_VALUE: &str = "some value"; + +mk_canary!("s3", |sdk_config: &SdkConfig, env: &CanaryEnv| s3_canary( + s3::Client::new(sdk_config), + env.s3_bucket_name.clone() +)); + +pub async fn s3_canary(client: s3::Client, s3_bucket_name: String) -> anyhow::Result<()> { + let test_key = Uuid::new_v4().as_u128().to_string(); + + // Look for the test object and expect that it doesn't exist + match client + .get_object() + .bucket(&s3_bucket_name) + .key(&test_key) + .send() + .await + { + Ok(_) => { + return Err( + CanaryError(format!("Expected object {} to not exist in S3", test_key)).into(), + ); + } + Err(err) => { + let err = err.into_service_error(); + // If we get anything other than "No such key", we have a problem + if !err.is_no_such_key() { + return Err(err).context("unexpected s3::GetObject failure"); + } + } + } + + // Put the test object + client + .put_object() + .bucket(&s3_bucket_name) + .key(&test_key) + .body(ByteStream::from_static(b"test")) + .metadata("something", METADATA_TEST_VALUE) + .send() + .await + .context("s3::PutObject")?; + + // Get the test object and verify it looks correct + let output = client + .get_object() + .bucket(&s3_bucket_name) + .key(&test_key) + .send() + .await + .context("s3::GetObject[2]")?; + + // repeat the test with a presigned url + let uri = client + .get_object() + .bucket(&s3_bucket_name) + .key(&test_key) + .presigned(PresigningConfig::expires_in(Duration::from_secs(120)).unwrap()) + .await + .unwrap(); + let response = reqwest::get(uri.uri().to_string()) + .await + .context("s3::presigned")? + .text() + .await?; + if response != "test" { + return Err(CanaryError(format!("presigned URL returned bad data: {:?}", response)).into()); + } + + let mut result = Ok(()); + match output.metadata() { + Some(map) => { + // Option::as_deref doesn't work here since the deref of &String is String + let value = map.get("something").map(|s| s.as_str()).unwrap_or(""); + if value != METADATA_TEST_VALUE { + result = Err(CanaryError(format!( + "S3 metadata was incorrect. Expected `{}` but got `{}`.", + METADATA_TEST_VALUE, value + )) + .into()); + } + } + None => { + result = Err(CanaryError("S3 metadata was missing".into()).into()); + } + } + + let payload = output + .body + .collect() + .await + .context("download s3::GetObject[2] body")? + .into_bytes(); + if std::str::from_utf8(payload.as_ref()).context("s3 payload")? != "test" { + result = Err(CanaryError("S3 object body didn't match what was put there".into()).into()); + } + + // Delete the test object + client + .delete_object() + .bucket(&s3_bucket_name) + .key(&test_key) + .send() + .await + .context("s3::DeleteObject")?; + + result +} + +// This test runs against an actual AWS account. Comment out the `ignore` to run it. +// Be sure to set the `TEST_S3_BUCKET` environment variable to the S3 bucket to use, +// and also make sure the credential profile sets the region (or set `AWS_DEFAULT_PROFILE`). +#[ignore] +#[cfg(test)] +#[tokio::test] +async fn test_s3_canary() { + let config = aws_config::load_from_env().await; + let client = s3::Client::new(&config); + s3_canary( + client, + std::env::var("TEST_S3_BUCKET").expect("TEST_S3_BUCKET must be set"), + ) + .await + .expect("success"); +} diff --git a/tools/ci-cdk/canary-lambda/src/latest/transcribe_canary.rs b/tools/ci-cdk/canary-lambda/src/latest/transcribe_canary.rs new file mode 100644 index 0000000000..8f6420fc1b --- /dev/null +++ b/tools/ci-cdk/canary-lambda/src/latest/transcribe_canary.rs @@ -0,0 +1,92 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +use crate::canary::CanaryError; +use crate::mk_canary; +use async_stream::stream; +use aws_config::SdkConfig; +use aws_sdk_transcribestreaming as transcribe; +use bytes::BufMut; +use transcribe::primitives::Blob; +use transcribe::types::{ + AudioEvent, AudioStream, LanguageCode, MediaEncoding, TranscriptResultStream, +}; + +const CHUNK_SIZE: usize = 8192; +use crate::canary::CanaryEnv; + +mk_canary!( + "transcribe_canary", + |sdk_config: &SdkConfig, env: &CanaryEnv| { + transcribe_canary( + transcribe::Client::new(sdk_config), + env.expected_transcribe_result.clone(), + ) + } +); + +pub async fn transcribe_canary( + client: transcribe::Client, + expected_transcribe_result: String, +) -> anyhow::Result<()> { + let input_stream = stream! { + let pcm = pcm_data(); + for chunk in pcm.chunks(CHUNK_SIZE) { + yield Ok(AudioStream::AudioEvent(AudioEvent::builder().audio_chunk(Blob::new(chunk)).build())); + } + }; + + let mut output = client + .start_stream_transcription() + .language_code(LanguageCode::EnGb) + .media_sample_rate_hertz(8000) + .media_encoding(MediaEncoding::Pcm) + .audio_stream(input_stream.into()) + .send() + .await?; + + let mut full_message = String::new(); + while let Some(event) = output.transcript_result_stream.recv().await? { + match event { + TranscriptResultStream::TranscriptEvent(transcript_event) => { + let transcript = transcript_event.transcript.unwrap(); + for result in transcript.results.unwrap_or_default() { + if !result.is_partial { + let first_alternative = &result.alternatives.as_ref().unwrap()[0]; + full_message += first_alternative.transcript.as_ref().unwrap(); + full_message.push(' '); + } + } + } + otherwise => panic!("received unexpected event type: {:?}", otherwise), + } + } + + if expected_transcribe_result != full_message.trim() { + Err(CanaryError(format!( + "Transcription from Transcribe doesn't look right:\n\ + Expected: `{}`\n\ + Actual: `{}`\n", + expected_transcribe_result, + full_message.trim() + )) + .into()) + } else { + Ok(()) + } +} + +fn pcm_data() -> Vec { + let reader = + hound::WavReader::new(&include_bytes!("../../audio/hello-transcribe-8000.wav")[..]) + .expect("valid wav data"); + let samples_result: hound::Result> = reader.into_samples::().collect(); + + let mut pcm: Vec = Vec::new(); + for sample in samples_result.unwrap() { + pcm.put_i16_le(sample); + } + pcm +} diff --git a/tools/ci-cdk/canary-lambda/src/main.rs b/tools/ci-cdk/canary-lambda/src/main.rs index 3bb723c802..688462031d 100644 --- a/tools/ci-cdk/canary-lambda/src/main.rs +++ b/tools/ci-cdk/canary-lambda/src/main.rs @@ -19,35 +19,18 @@ use tracing_subscriber::layer::SubscriberExt; use tracing_subscriber::EnvFilter; use tracing_texray::TeXRayLayer; -/// Conditionally include the module based on the $version feature gate -/// -/// When the module is not included, an `mk_canary` function will be generated that returns `None`. -macro_rules! canary_module { - ($name: ident, since: $version: expr) => { - #[cfg(feature = $version)] - mod $name; - - #[cfg(not(feature = $version))] - mod $name { - pub(crate) fn mk_canary( - _clients: &aws_config::SdkConfig, - _env: &crate::canary::CanaryEnv, - ) -> Option<(&'static str, crate::canary::CanaryFuture)> { - tracing::warn!(concat!( - stringify!($name), - " is disabled because it is not supported by this version of the SDK." - )); - None - } - } - }; -} - mod canary; -mod s3_canary; -canary_module!(paginator_canary, since: "v0.4.1"); -mod transcribe_canary; +#[cfg(feature = "latest")] +mod latest; +#[cfg(feature = "latest")] +pub(crate) use latest as current_canary; + +// NOTE: This module can be deleted 3 releases after release-2023-01-26 +#[cfg(feature = "release-2023-01-26")] +mod release_2023_01_26; +#[cfg(feature = "release-2023-01-26")] +pub(crate) use release_2023_01_26 as current_canary; #[tokio::main] async fn main() -> Result<(), Error> { diff --git a/tools/ci-cdk/canary-lambda/src/release_2023_01_26.rs b/tools/ci-cdk/canary-lambda/src/release_2023_01_26.rs new file mode 100644 index 0000000000..238c361166 --- /dev/null +++ b/tools/ci-cdk/canary-lambda/src/release_2023_01_26.rs @@ -0,0 +1,8 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +pub(crate) mod paginator_canary; +pub(crate) mod s3_canary; +pub(crate) mod transcribe_canary; diff --git a/tools/ci-cdk/canary-lambda/src/paginator_canary.rs b/tools/ci-cdk/canary-lambda/src/release_2023_01_26/paginator_canary.rs similarity index 100% rename from tools/ci-cdk/canary-lambda/src/paginator_canary.rs rename to tools/ci-cdk/canary-lambda/src/release_2023_01_26/paginator_canary.rs diff --git a/tools/ci-cdk/canary-lambda/src/s3_canary.rs b/tools/ci-cdk/canary-lambda/src/release_2023_01_26/s3_canary.rs similarity index 78% rename from tools/ci-cdk/canary-lambda/src/s3_canary.rs rename to tools/ci-cdk/canary-lambda/src/release_2023_01_26/s3_canary.rs index cb56797f01..70e3d18c55 100644 --- a/tools/ci-cdk/canary-lambda/src/s3_canary.rs +++ b/tools/ci-cdk/canary-lambda/src/release_2023_01_26/s3_canary.rs @@ -8,8 +8,9 @@ use crate::{mk_canary, CanaryEnv}; use anyhow::Context; use aws_config::SdkConfig; use aws_sdk_s3 as s3; -use s3::error::{GetObjectError, GetObjectErrorKind}; +use aws_sdk_s3::presigning::config::PresigningConfig; use s3::types::ByteStream; +use std::time::Duration; use uuid::Uuid; const METADATA_TEST_VALUE: &str = "some value"; @@ -35,15 +36,13 @@ pub async fn s3_canary(client: s3::Client, s3_bucket_name: String) -> anyhow::Re CanaryError(format!("Expected object {} to not exist in S3", test_key)).into(), ); } - Err(err) => match err.into_service_error() { - GetObjectError { - kind: GetObjectErrorKind::NoSuchKey(..), - .. - } => { - // good + Err(err) => { + let err = err.into_service_error(); + // If we get anything other than "No such key", we have a problem + if !err.is_no_such_key() { + return Err(err).context("unexpected s3::GetObject failure"); } - err => Err(err).context("unexpected s3::GetObject failure")?, - }, + } } // Put the test object @@ -66,6 +65,23 @@ pub async fn s3_canary(client: s3::Client, s3_bucket_name: String) -> anyhow::Re .await .context("s3::GetObject[2]")?; + // repeat the test with a presigned url + let uri = client + .get_object() + .bucket(&s3_bucket_name) + .key(&test_key) + .presigned(PresigningConfig::expires_in(Duration::from_secs(120)).unwrap()) + .await + .unwrap(); + let response = reqwest::get(uri.uri().to_string()) + .await + .context("s3::presigned")? + .text() + .await?; + if response != "test" { + return Err(CanaryError(format!("presigned URL returned bad data: {:?}", response)).into()); + } + let mut result = Ok(()); match output.metadata() { Some(map) => { diff --git a/tools/ci-cdk/canary-lambda/src/transcribe_canary.rs b/tools/ci-cdk/canary-lambda/src/release_2023_01_26/transcribe_canary.rs similarity index 94% rename from tools/ci-cdk/canary-lambda/src/transcribe_canary.rs rename to tools/ci-cdk/canary-lambda/src/release_2023_01_26/transcribe_canary.rs index c0e2db2fdb..554f4c3ddf 100644 --- a/tools/ci-cdk/canary-lambda/src/transcribe_canary.rs +++ b/tools/ci-cdk/canary-lambda/src/release_2023_01_26/transcribe_canary.rs @@ -79,8 +79,9 @@ pub async fn transcribe_canary( } fn pcm_data() -> Vec { - let reader = hound::WavReader::new(&include_bytes!("../audio/hello-transcribe-8000.wav")[..]) - .expect("valid wav data"); + let reader = + hound::WavReader::new(&include_bytes!("../../audio/hello-transcribe-8000.wav")[..]) + .expect("valid wav data"); let samples_result: hound::Result> = reader.into_samples::().collect(); let mut pcm: Vec = Vec::new(); diff --git a/tools/ci-cdk/canary-runner/Cargo.lock b/tools/ci-cdk/canary-runner/Cargo.lock index 3bdfd38331..adccd1cddc 100644 --- a/tools/ci-cdk/canary-runner/Cargo.lock +++ b/tools/ci-cdk/canary-runner/Cargo.lock @@ -10,9 +10,9 @@ checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" [[package]] name = "aho-corasick" -version = "0.7.19" +version = "0.7.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b4f55bd91a0978cbfd91c457a164bab8b4001c833b7f323132c0a4e1922dd44e" +checksum = "cc936419f96fa211c1b9166887b38e5e40b19958e5b895be7c1f93adec7071ac" dependencies = [ "memchr", ] @@ -26,20 +26,11 @@ dependencies = [ "libc", ] -[[package]] -name = "ansi_term" -version = "0.12.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d52a9bb7ec0cf484c551830a7ce27bd20d67eac647e1befb56b0be4ee39a55d2" -dependencies = [ - "winapi", -] - [[package]] name = "anyhow" -version = "1.0.65" +version = "1.0.69" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "98161a4e3e2184da77bb14f02184cdd111e83bbbcc9979dfee3c44b9a85f5602" +checksum = "224afbd727c3d6e4b90103ece64b8d1b67fbb1973b1046c2281eed3f3803f800" [[package]] name = "async-recursion" @@ -54,9 +45,9 @@ dependencies = [ [[package]] name = "async-trait" -version = "0.1.57" +version = "0.1.64" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "76464446b8bc32758d7e88ee1a804d9914cd9b1cb264c029899680b0be29826f" +checksum = "1cd7fce9ba8c3c042128ce72d8b2ddbf3a05747efb67ea0313c635e10bda47a2" dependencies = [ "proc-macro2", "quote", @@ -69,7 +60,7 @@ version = "0.2.14" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8" dependencies = [ - "hermit-abi", + "hermit-abi 0.1.19", "libc", "winapi", ] @@ -101,7 +92,7 @@ dependencies = [ "http", "hyper", "ring", - "time 0.3.15", + "time 0.3.19", "tokio", "tower", "tracing", @@ -286,7 +277,7 @@ dependencies = [ "percent-encoding", "regex", "ring", - "time 0.3.15", + "time 0.3.19", "tracing", ] @@ -422,7 +413,7 @@ dependencies = [ "itoa", "num-integer", "ryu", - "time 0.3.15", + "time 0.3.19", ] [[package]] @@ -458,9 +449,15 @@ checksum = "3441f0f7b02788e948e47f457ca01f1d7e6d92c693bc132c22b087d3141c03ff" [[package]] name = "base64" -version = "0.13.0" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9e1b586273c5702936fe7b7d6896644d8be71e6314cfe09d3167c95f712589e8" + +[[package]] +name = "base64" +version = "0.21.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "904dfeac50f3cdaba28fc6f57fdcddb75f49ed61346676a78c4ffe55877802fd" +checksum = "a4a4ddaa51a5bc52a6948f74c06d20aaaddb71924eab79b8c97a8c556e942d6a" [[package]] name = "bitflags" @@ -479,9 +476,9 @@ dependencies = [ [[package]] name = "bumpalo" -version = "3.11.0" +version = "3.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c1ad822118d20d2c234f427000d5acc36eabe1e29a348c89b63dd60b13f28e5d" +checksum = "0d261e256854913907f67ed06efbc3338dfe6179796deefc1ff763fc1aee5535" [[package]] name = "byteorder" @@ -515,7 +512,7 @@ dependencies = [ "aws-sdk-cloudwatch", "aws-sdk-lambda", "aws-sdk-s3", - "base64 0.13.0", + "base64 0.13.1", "clap", "hex", "lazy_static", @@ -535,9 +532,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" @@ -547,25 +544,25 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" [[package]] name = "chrono" -version = "0.4.22" +version = "0.4.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bfd4d1b31faaa3a89d7934dbded3111da0d2ef28e3ebccdb4f0179f5929d1ef1" +checksum = "16b0a3d9ed01224b22057780a37bb8c5dbfe1be8ba48678e7bf57ec4b385411f" dependencies = [ "iana-time-zone", "js-sys", "num-integer", "num-traits", "serde", - "time 0.1.44", + "time 0.1.45", "wasm-bindgen", "winapi", ] [[package]] name = "clap" -version = "3.2.22" +version = "3.2.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "86447ad904c7fb335a790c9d7fe3d0d971dc523b8ccd1561a520de9a85302750" +checksum = "71655c45cb9845d3270c9d6df84ebe72b4dad3c2ba3f7023ad47c144e4e473a5" dependencies = [ "atty", "bitflags", @@ -600,6 +597,16 @@ 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" @@ -655,9 +662,9 @@ dependencies = [ [[package]] name = "crossbeam-utils" -version = "0.8.12" +version = "0.8.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "edbafec5fa1f196ca66527c1b12c2ec4745ca14b50f1ad8f9f6f720b55d11fac" +checksum = "4fb766fa798726286dbbb842f174001dab8abc7b627a1dd86e0b7222a95d929f" dependencies = [ "cfg-if", ] @@ -683,10 +690,54 @@ dependencies = [ [[package]] name = "ctor" -version = "0.1.23" +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 = "cdffe87e1d521a10f9696f833fe502293ea446d7f256c06128293a4119bdf4cb" +checksum = "086c685979a698443656e5cf7856c95c642295a38599f12fb1ff76fb28d19892" dependencies = [ + "proc-macro2", "quote", "syn", ] @@ -699,9 +750,9 @@ checksum = "56254986775e3233ffa9c4d7d3faaf6d36a2c09d30b20687e9f88bc8bafc16c8" [[package]] name = "digest" -version = "0.10.5" +version = "0.10.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "adfbc57365a37acbd2ebf2b64d7e69bb766e2fea813521ed536f5d0520dcf86c" +checksum = "8168378f4e5023e7218c89c891c0fd8ecdb5e5e4f18cb78f38cf245dd021e76f" dependencies = [ "block-buffer", "crypto-common", @@ -709,39 +760,39 @@ dependencies = [ [[package]] name = "dyn-clone" -version = "1.0.9" +version = "1.0.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4f94fa09c2aeea5b8839e414b7b841bf429fd25b9c522116ac97ee87856d88b2" +checksum = "c9b0705efd4599c15a38151f4721f7bc388306f61084d3bfd50bd07fbca5cb60" [[package]] name = "either" -version = "1.8.0" +version = "1.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "90e5c1c8368803113bf0c9584fc495a58b86dc8a29edbf8fe877d21d9507e797" +checksum = "7fcaabb2fef8c910e7f4c7ce9f67a1283a1715879a7c230ca9d6d1ae31f16d91" [[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" +version = "1.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a7a407cfaa3385c4ae6b23e84623d48c2798d06e3e6a1878f7f59f17b3f86499" +checksum = "e51093e27b0797c359783294ca4f0a911c270184cb10f85783b118614a1501be" dependencies = [ "instant", ] [[package]] name = "flate2" -version = "1.0.24" +version = "1.0.25" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f82b0f4c27ad9f8bfd1f3208d882da2b09c301bc1c828fd3a00d0216d2fbbff6" +checksum = "a8a2db397cb1c8772f31494cb8917e48cd1e64f0fa7efac59fbd741a0a8ce841" dependencies = [ "crc32fast", "miniz_oxide", @@ -780,9 +831,9 @@ dependencies = [ [[package]] name = "futures" -version = "0.3.24" +version = "0.3.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f21eda599937fba36daeb58a22e8f5cee2d14c4a17b5b7739c7c8e5e3b8230c" +checksum = "13e2792b0ff0340399d58445b88fd9770e3489eff258a4cbc1523418f12abf84" dependencies = [ "futures-channel", "futures-core", @@ -795,9 +846,9 @@ dependencies = [ [[package]] name = "futures-channel" -version = "0.3.24" +version = "0.3.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "30bdd20c28fadd505d0fd6712cdfcb0d4b5648baf45faef7f852afb2399bb050" +checksum = "2e5317663a9089767a1ec00a487df42e0ca174b61b4483213ac24448e4664df5" dependencies = [ "futures-core", "futures-sink", @@ -805,15 +856,15 @@ dependencies = [ [[package]] name = "futures-core" -version = "0.3.24" +version = "0.3.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4e5aa3de05362c3fb88de6531e6296e85cde7739cccad4b9dfeeb7f6ebce56bf" +checksum = "ec90ff4d0fe1f57d600049061dc6bb68ed03c7d2fbd697274c41805dcb3f8608" [[package]] name = "futures-executor" -version = "0.3.24" +version = "0.3.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9ff63c23854bee61b6e9cd331d523909f238fc7636290b96826e9cfa5faa00ab" +checksum = "e8de0a35a6ab97ec8869e32a2473f4b1324459e14c29275d14b10cb1fd19b50e" dependencies = [ "futures-core", "futures-task", @@ -822,15 +873,15 @@ dependencies = [ [[package]] name = "futures-io" -version = "0.3.24" +version = "0.3.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bbf4d2a7a308fd4578637c0b17c7e1c7ba127b8f6ba00b29f717e9655d85eb68" +checksum = "bfb8371b6fb2aeb2d280374607aeabfc99d95c72edfe51692e42d3d7f0d08531" [[package]] name = "futures-macro" -version = "0.3.24" +version = "0.3.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "42cd15d1c7456c04dbdf7e88bcd69760d74f3a798d6444e16974b505b0e62f17" +checksum = "95a73af87da33b5acf53acfebdc339fe592ecf5357ac7c0a7734ab9d8c876a70" dependencies = [ "proc-macro2", "quote", @@ -839,21 +890,21 @@ dependencies = [ [[package]] name = "futures-sink" -version = "0.3.24" +version = "0.3.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "21b20ba5a92e727ba30e72834706623d94ac93a725410b6a6b6fbc1b07f7ba56" +checksum = "f310820bb3e8cfd46c80db4d7fb8353e15dfff853a127158425f31e0be6c8364" [[package]] name = "futures-task" -version = "0.3.24" +version = "0.3.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a6508c467c73851293f390476d4491cf4d227dbabcd4170f3bb6044959b294f1" +checksum = "dcf79a1bf610b10f42aea489289c5a2c478a786509693b80cd39c44ccd936366" [[package]] name = "futures-util" -version = "0.3.24" +version = "0.3.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "44fb6cb1be61cc1d2e43b262516aafcf63b241cffdb1d3fa115f91d9c7b09c90" +checksum = "9c1d6de3acfef38d2be4b1f543f553131788603495be83da675e180c8d6b7bd1" dependencies = [ "futures-channel", "futures-core", @@ -879,9 +930,9 @@ dependencies = [ [[package]] name = "getrandom" -version = "0.2.7" +version = "0.2.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4eb1a864a501629691edf6c15a593b7a51eebaa1e8468e9ddc623de7c9b58ec6" +checksum = "c05aeb6a22b8f62540c194aac980f2115af067bfe15a0734d7277a768d396b31" dependencies = [ "cfg-if", "libc", @@ -890,9 +941,9 @@ dependencies = [ [[package]] name = "h2" -version = "0.3.14" +version = "0.3.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5ca32592cf21ac7ccab1825cd87f6c9b3d9022c44d086172ed0966bec8af30be" +checksum = "5f9f29bc9dda355256b2916cf526ab02ce0aeaaaf2bad60d65ef3f12f11dd0f4" dependencies = [ "bytes", "fnv", @@ -915,9 +966,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" @@ -928,6 +979,15 @@ dependencies = [ "libc", ] +[[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" @@ -936,9 +996,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", @@ -970,9 +1030,9 @@ checksum = "c4a1e36c821dbe04574f602848a19f742f4fb3c98d40449f11bcad18d6b17421" [[package]] name = "hyper" -version = "0.14.20" +version = "0.14.24" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "02c929dc5c39e335a03c405292728118860721b10190d98c2a0f0efd5baafbac" +checksum = "5e011372fa0b68db8350aa7a248930ecc7839bf46d8485577d69f117a75f164c" dependencies = [ "bytes", "futures-channel", @@ -1011,13 +1071,13 @@ dependencies = [ [[package]] name = "hyper-rustls" -version = "0.23.0" +version = "0.23.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d87c48c02e0dc5e3b849a2041db3029fd066650f8f717c07bf8ed78ccb895cac" +checksum = "1788965e61b367cd03a62950836d5cd41560c3577d90e40e0819373194d1661c" dependencies = [ "http", "hyper", - "rustls 0.20.6", + "rustls 0.20.8", "tokio", "tokio-rustls 0.23.4", ] @@ -1041,7 +1101,7 @@ version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5617e92fc2f2501c3e2bc6ce547cad841adba2bae5b921c7e52510beca6d084c" dependencies = [ - "base64 0.13.0", + "base64 0.13.1", "bytes", "http", "httpdate", @@ -1053,17 +1113,28 @@ dependencies = [ [[package]] name = "iana-time-zone" -version = "0.1.50" +version = "0.1.53" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fd911b35d940d2bd0bea0f9100068e5b97b51a1cbe13d13382f132e0365257a0" +checksum = "64c122667b287044802d6ce17ee2ddf13207ed924c712de9a66a5814d5b64765" dependencies = [ "android_system_properties", "core-foundation-sys", + "iana-time-zone-haiku", "js-sys", "wasm-bindgen", "winapi", ] +[[package]] +name = "iana-time-zone-haiku" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0703ae284fc167426161c2e3f1da3ea71d94b21bedbcc9494e92b28e334e3dca" +dependencies = [ + "cxx", + "cxx-build", +] + [[package]] name = "idna" version = "0.2.3" @@ -1077,9 +1148,9 @@ dependencies = [ [[package]] name = "indexmap" -version = "1.9.1" +version = "1.9.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "10a35a97730320ffe8e2d410b5d3b69279b98d2c14bdb8b70ea89ecf7888d41e" +checksum = "1885e79c1fc4b10f0e172c475f458b7f7b93061064d98c3293e98c5ba0c8b399" dependencies = [ "autocfg", "hashbrown", @@ -1096,21 +1167,21 @@ dependencies = [ [[package]] name = "ipnet" -version = "2.5.0" +version = "2.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "879d54834c8c76457ef4293a689b2a8c59b076067ad77b15efafbb05f92a592b" +checksum = "30e22bd8629359895450b59ea7a776c850561b96a3b1d31321c1949d9e6c9146" [[package]] name = "itoa" -version = "1.0.3" +version = "1.0.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6c8af84674fe1f223a982c933a0ee1086ac4d4052aa0fb8060c12c6ad838e754" +checksum = "fad582f4b9e86b6caa621cabeb0963332d92eea04729ab12892c2533951e6440" [[package]] name = "js-sys" -version = "0.3.60" +version = "0.3.61" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "49409df3e3bf0856b916e2ceaca09ee28e6871cf7d9ce97a692cacfdb2a25a47" +checksum = "445dde2150c55e483f3d8416706b97ec8e8237c307e5b7b4b8dd15e6af2a0730" dependencies = [ "wasm-bindgen", ] @@ -1143,9 +1214,18 @@ checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" [[package]] name = "libc" -version = "0.2.134" +version = "0.2.139" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "329c933548736bc49fd575ee68c89e8be4d260064184389a5b77517cddd99ffb" +checksum = "201de327520df007757c1f0adce6e827fe8562fbc28bfd9c15571c66ca1f5f79" + +[[package]] +name = "link-cplusplus" +version = "1.0.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ecd207c9c713c34f95a097a5b029ac2ce6010530c7b49d7fea24d977dede04f5" +dependencies = [ + "cc", +] [[package]] name = "lock_api" @@ -1178,9 +1258,9 @@ dependencies = [ [[package]] name = "matches" -version = "0.1.9" +version = "0.1.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a3e378b66a060d48947b590737b30a1be76706c8dd7b8ba0f2fe3989c68a853f" +checksum = "2532096657941c2fea9c289d370a250971c689d4f143798ff67113ec042024a5" [[package]] name = "md-5" @@ -1215,30 +1295,30 @@ dependencies = [ [[package]] name = "miniz_oxide" -version = "0.5.4" +version = "0.6.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "96590ba8f175222643a85693f33d26e9c8a015f599c216509b1a6894af675d34" +checksum = "b275950c28b37e794e8c55d88aeb5e139d0ce23fdbbeda68f8d7174abdf9e8fa" dependencies = [ "adler", ] [[package]] name = "mio" -version = "0.8.4" +version = "0.8.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "57ee1c23c7c63b0c9250c339ffdc69255f110b298b901b9f6c82547b7b87caaf" +checksum = "5b9d9a46eff5b4ff64b45a9e316a6d1e0bc719ef429cbec4dc630684212bfdf9" dependencies = [ "libc", "log", "wasi 0.11.0+wasi-snapshot-preview1", - "windows-sys", + "windows-sys 0.45.0", ] [[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", @@ -1252,6 +1332,16 @@ dependencies = [ "tempfile", ] +[[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-bigint" version = "0.2.6" @@ -1284,20 +1374,11 @@ dependencies = [ [[package]] name = "num_cpus" -version = "1.13.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "19e64526ebdee182341572e50e9ad03965aa510cd94427a4549448f285e957a1" -dependencies = [ - "hermit-abi", - "libc", -] - -[[package]] -name = "num_threads" -version = "0.1.6" +version = "1.15.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2819ce041d2ee131036f4fc9d6ae7ae125a3a40e97ba64d04fe799ad9dabbb44" +checksum = "0fac9e2da13b5eb447a6ce3d392f23a29d8694bff781bf03a16cd9ac8697593b" dependencies = [ + "hermit-abi 0.2.6", "libc", ] @@ -1333,15 +1414,15 @@ dependencies = [ [[package]] name = "once_cell" -version = "1.15.0" +version = "1.17.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e82dad04139b71a90c080c8463fe0dc7902db5192d939bd0950f074d014339e1" +checksum = "b7e5500299e16ebb147ae15a00a942af264cf3688f47923b8fc2cd5858f23ad3" [[package]] name = "openssl" -version = "0.10.42" +version = "0.10.45" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "12fc0523e3bd51a692c8850d075d74dc062ccf251c0110668cbd921917118a13" +checksum = "b102428fd03bc5edf97f62620f7298614c45cedf287c271e7ed450bbaf83f2e1" dependencies = [ "bitflags", "cfg-if", @@ -1371,9 +1452,9 @@ checksum = "ff011a302c396a5197692431fc1948019154afc178baf7d8e37367442a4601cf" [[package]] name = "openssl-sys" -version = "0.9.76" +version = "0.9.80" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5230151e44c0f05157effb743e8d517472843121cf9243e8b81393edb5acd9ce" +checksum = "23bbbf7854cd45b83958ebe919f0e8e516793727652e27fda10a8384cfc790b7" dependencies = [ "autocfg", "cc", @@ -1403,9 +1484,9 @@ dependencies = [ [[package]] name = "os_str_bytes" -version = "6.3.0" +version = "6.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9ff7415e9ae3fff1225851df9e0d9e4e5479f947619774677a63572e55e80eff" +checksum = "9b7820b9daea5457c9f21c69448905d723fbd21136ccf521748f23fd49e723ee" [[package]] name = "output_vt100" @@ -1416,6 +1497,12 @@ dependencies = [ "winapi", ] +[[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" @@ -1428,15 +1515,15 @@ dependencies = [ [[package]] name = "parking_lot_core" -version = "0.9.3" +version = "0.9.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "09a279cbf25cb0757810394fbc1e359949b59e348145c643a939a525692e6929" +checksum = "9069cbb9f99e3a5083476ccb29ceb1de18b9118cafa53e90c9551235de2b9521" dependencies = [ "cfg-if", "libc", "redox_syscall", "smallvec", - "windows-sys", + "windows-sys 0.45.0", ] [[package]] @@ -1445,7 +1532,7 @@ version = "0.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fd56cbd21fea48d0c440b41cd69c589faacade08c992d9a54e471b79d0fd13eb" dependencies = [ - "base64 0.13.0", + "base64 0.13.1", "once_cell", "regex", ] @@ -1490,15 +1577,15 @@ checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" [[package]] name = "pkg-config" -version = "0.3.25" +version = "0.3.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1df8c4ec4b0627e53bdf214615ad287367e482558cf84b109250b37464dc03ae" +checksum = "6ac9a59f73473f1b8d852421e59e64809f025994837ef743615c6d0c5b305160" [[package]] name = "ppv-lite86" -version = "0.2.16" +version = "0.2.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eb9f9e6e233e5c4a35559a617bf40a4ec447db2e84c20b55a6f83167b7e57872" +checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de" [[package]] name = "pretty_assertions" @@ -1538,18 +1625,18 @@ dependencies = [ [[package]] name = "proc-macro2" -version = "1.0.46" +version = "1.0.51" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "94e2ef8dbfc347b10c094890f778ee2e36ca9bb4262e86dc99cd217e35f3470b" +checksum = "5d727cae5b39d21da60fa540906919ad737832fe0b1c165da3a34d6548c849d6" dependencies = [ "unicode-ident", ] [[package]] name = "quote" -version = "1.0.21" +version = "1.0.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bbe448f377a7d6961e30f5955f9b8d106c3f5e449d493ee1b125c1d43c2b5179" +checksum = "8856d8364d252a14d474036ea1358d63c9e6965c8e5c1885c18f73d70bff9c7b" dependencies = [ "proc-macro2", ] @@ -1595,9 +1682,9 @@ dependencies = [ [[package]] name = "regex" -version = "1.6.0" +version = "1.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4c4eb3267174b8c6c2f654116623910a0fef09c4753f8dd83db29c48a0df988b" +checksum = "48aaa5748ba571fb95cd2c85c09f629215d3a6ece942baa100950af03a34f733" dependencies = [ "aho-corasick", "memchr", @@ -1615,9 +1702,9 @@ dependencies = [ [[package]] name = "regex-syntax" -version = "0.6.27" +version = "0.6.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a3f87b73ce11b1619a3c6332f45341e0047173771e8b8b73f87bfeefb7b56244" +checksum = "456c603be3e8d448b072f410900c09faf164fbce2d480456f50eea6e25f9c848" [[package]] name = "remove_dir_all" @@ -1630,11 +1717,11 @@ dependencies = [ [[package]] name = "reqwest" -version = "0.11.12" +version = "0.11.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "431949c384f4e2ae07605ccaa56d1d9d2ecdb5cadd4f9577ccfab29f2e5149fc" +checksum = "21eed90ec8570952d53b772ecf8f206aa1ec9a3d76b2521c56c42973f2d91ee9" dependencies = [ - "base64 0.13.0", + "base64 0.21.0", "bytes", "encoding_rs", "futures-core", @@ -1643,7 +1730,7 @@ dependencies = [ "http", "http-body", "hyper", - "hyper-rustls 0.23.0", + "hyper-rustls 0.23.2", "hyper-tls", "ipnet", "js-sys", @@ -1654,7 +1741,7 @@ dependencies = [ "once_cell", "percent-encoding", "pin-project-lite", - "rustls 0.20.6", + "rustls 0.20.8", "rustls-pemfile", "serde", "serde_json", @@ -1737,9 +1824,9 @@ dependencies = [ [[package]] name = "retry-policies" -version = "0.1.1" +version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "47f9e19b18c6cdd796cc70aea8a9ea5ee7b813be611c6589e3624fcdbfd05f9d" +checksum = "e09bbcb5003282bcb688f0bae741b278e9c7e8f378f561522c9806c58e075d9b" dependencies = [ "anyhow", "chrono", @@ -1776,7 +1863,7 @@ version = "0.19.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "35edb675feee39aec9c99fa5ff985081995a06d594114ae14cbe797ad7b7a6d7" dependencies = [ - "base64 0.13.0", + "base64 0.13.1", "log", "ring", "sct 0.6.1", @@ -1785,9 +1872,9 @@ dependencies = [ [[package]] name = "rustls" -version = "0.20.6" +version = "0.20.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5aab8ee6c7097ed6057f43c187a62418d0c05a4bd5f18b3571db50ee0f9ce033" +checksum = "fff78fc74d175294f4e83b28343315ffcfb114b156f0185e9741cb5570f50e2f" dependencies = [ "log", "ring", @@ -1809,27 +1896,26 @@ dependencies = [ [[package]] name = "rustls-pemfile" -version = "1.0.1" +version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0864aeff53f8c05aa08d86e5ef839d3dfcf07aeba2db32f12db0ef716e87bd55" +checksum = "d194b56d58803a43635bdc398cd17e383d6f71f9182b9a192c127ca42494a59b" dependencies = [ - "base64 0.13.0", + "base64 0.21.0", ] [[package]] name = "ryu" -version = "1.0.11" +version = "1.0.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4501abdff3ae82a1c1b477a17252eb69cee9e66eb915c1abaa4f44d873df9f09" +checksum = "7b4b9743ed687d4b4bcedf9ff5eaa7398495ae14e61cba0a295704edbc7decde" [[package]] name = "schannel" -version = "0.1.20" +version = "0.1.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "88d6731146462ea25d9244b2ed5fd1d716d25c52e4d54aa4fb0f3c4e9854dbe2" +checksum = "713cfb06c7059f3588fb8044c0fad1d09e3c01d225e25b9220dbfdcf16dbb1b3" dependencies = [ - "lazy_static", - "windows-sys", + "windows-sys 0.42.0", ] [[package]] @@ -1866,6 +1952,12 @@ 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" @@ -1888,9 +1980,9 @@ dependencies = [ [[package]] name = "security-framework" -version = "2.7.0" +version = "2.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2bc1bb97804af6631813c55739f771071e0f2ed33ee20b68c86ec505d906356c" +checksum = "a332be01508d814fed64bf28f798a146d73792121129962fdf335bb3c49a4254" dependencies = [ "bitflags", "core-foundation", @@ -1901,9 +1993,9 @@ dependencies = [ [[package]] name = "security-framework-sys" -version = "2.6.1" +version = "2.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0160a13a177a45bfb43ce71c01580998474f556ad854dcbca936dd2841a5c556" +checksum = "31c9bb296072e961fcbd8853511dd39c2d8be2deb1e17c6860b1d30732b323b4" dependencies = [ "core-foundation-sys", "libc", @@ -1911,24 +2003,24 @@ dependencies = [ [[package]] name = "semver" -version = "1.0.14" +version = "1.0.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e25dfac463d778e353db5be2449d1cce89bd6fd23c9f1ea21310ce6e5a1b29c4" +checksum = "58bc9567378fc7690d6b2addae4e60ac2eeea07becb2c64b9f218b53865cba2a" [[package]] name = "serde" -version = "1.0.145" +version = "1.0.152" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "728eb6351430bccb993660dfffc5a72f91ccc1295abaa8ce19b27ebe4f75568b" +checksum = "bb7d1f0d3021d347a83e556fc4683dea2ea09d87bccdf88ff5c12545d89d5efb" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.145" +version = "1.0.152" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "81fa1584d3d1bcacd84c277a0dfe21f5b0f6accf4a23d04d4c6d61f1af522b4c" +checksum = "af487d118eecd09402d70a5d72551860e788df87b464af30e5ea6a38c75c541e" dependencies = [ "proc-macro2", "quote", @@ -1948,9 +2040,9 @@ dependencies = [ [[package]] name = "serde_json" -version = "1.0.85" +version = "1.0.93" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e55a28e3aaef9d5ce0506d0a14dbba8054ddc7e499ef522dd8b26859ec9d4a44" +checksum = "cad406b69c91885b5107daf2c29572f6c8cdb3c66826821e286c533490c0bc76" dependencies = [ "itoa", "ryu", @@ -2002,9 +2094,9 @@ 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", ] @@ -2022,9 +2114,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", ] @@ -2076,9 +2168,9 @@ checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623" [[package]] name = "syn" -version = "1.0.101" +version = "1.0.107" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e90cde112c4b9690b8cbe810cba9ddd8bc1d7472e2cae317b69e9438c1cba7d2" +checksum = "1f4064b5b16e03ae50984a5a8ed5d4f8803e6bc1fd170a3cda91a1be4b18e3f5" dependencies = [ "proc-macro2", "quote", @@ -2110,33 +2202,33 @@ dependencies = [ [[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 = "thiserror" -version = "1.0.37" +version = "1.0.38" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "10deb33631e3c9018b9baf9dcbbc4f737320d2b576bac10f6aefa048fa407e3e" +checksum = "6a9cd18aa97d5c45c6603caea1da6628790b37f7a34b6ca89522331c5180fed0" dependencies = [ "thiserror-impl", ] [[package]] name = "thiserror-impl" -version = "1.0.37" +version = "1.0.38" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "982d17546b47146b28f7c22e3d08465f6b8903d0ea13c1660d9d84a6e7adcdbb" +checksum = "1fb327af4685e4d03fa8cbcf1716380da910eeb2bb8be417e7f9fd3fb164f36f" dependencies = [ "proc-macro2", "quote", @@ -2145,18 +2237,19 @@ dependencies = [ [[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 = "time" -version = "0.1.44" +version = "0.1.45" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6db9e6914ab8b1ae1c260a4ae7a49b6c5611b40328a735b21862567685e73255" +checksum = "1b797afad3f312d1c66a56d11d0316f916356d11bd158fbc6ca6389ff6bf805a" dependencies = [ "libc", "wasi 0.10.0+wasi-snapshot-preview1", @@ -2165,12 +2258,28 @@ dependencies = [ [[package]] name = "time" -version = "0.3.15" +version = "0.3.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d634a985c4d4238ec39cacaed2e7ae552fbd3c476b552c1deac3021b7d7eaf0c" +checksum = "53250a3b3fed8ff8fd988587d8925d26a83ac3845d9e03b220b37f34c2b8d6c2" dependencies = [ - "libc", - "num_threads", + "serde", + "time-core", + "time-macros", +] + +[[package]] +name = "time-core" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2e153e1f1acaef8acc537e68b44906d2db6436e2b35ac2c6b42640fff91f00fd" + +[[package]] +name = "time-macros" +version = "0.2.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a460aeb8de6dcb0f381e1ee05f1cd56fcf5a5f6eb8187ff3d8f0b11078d38b7c" +dependencies = [ + "time-core", ] [[package]] @@ -2184,15 +2293,15 @@ 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.25.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a9e03c497dc955702ba729190dc4aac6f2a0ce97f913e5b1b5912fc5039d9099" +checksum = "c8e00990ebabbe4c14c08aca901caed183ecd5c09562a12c824bb53d3c3fd3af" dependencies = [ "autocfg", "bytes", @@ -2205,14 +2314,14 @@ dependencies = [ "signal-hook-registry", "socket2", "tokio-macros", - "winapi", + "windows-sys 0.42.0", ] [[package]] name = "tokio-macros" -version = "1.8.0" +version = "1.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9724f9a975fb987ef7a3cd9be0350edcbe130698af5b8f7a631e23d42d052484" +checksum = "d266c00fde287f55d3f1c3e96c500c362a2b8c695076ec180f27918820bc6df8" dependencies = [ "proc-macro2", "quote", @@ -2221,9 +2330,9 @@ dependencies = [ [[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", @@ -2246,16 +2355,16 @@ version = "0.23.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c43ee83903113e03984cb9e5cebe6c04a5116269e900e3ddba8f068a62adda59" dependencies = [ - "rustls 0.20.6", + "rustls 0.20.8", "tokio", "webpki 0.22.0", ] [[package]] name = "tokio-stream" -version = "0.1.10" +version = "0.1.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f6edf2d6bc038a43d31353570e27270603f4648d18f5ed10c0e179abe43255af" +checksum = "8fb52b74f05dbf495a8fba459fdc331812b96aa086d9eb78101fa0d4569c3313" dependencies = [ "futures-core", "pin-project-lite", @@ -2264,9 +2373,9 @@ dependencies = [ [[package]] name = "tokio-util" -version = "0.7.4" +version = "0.7.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0bb2e075f03b3d66d8d8785356224ba688d2906a371015e225beeb65ca92c740" +checksum = "5427d89453009325de0d8f342c9490009f76e999cb7672d77e46267448f7e6b2" dependencies = [ "bytes", "futures-core", @@ -2278,9 +2387,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", @@ -2304,9 +2413,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" @@ -2316,9 +2425,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", @@ -2329,9 +2438,9 @@ dependencies = [ [[package]] name = "tracing-attributes" -version = "0.1.22" +version = "0.1.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "11c75893af559bc8e10716548bdef5cb2b983f8e637db9d0e15126b61b484ee2" +checksum = "4017f8f45139870ca7e672686113917c71c7a6e02d4924eda67186083c03081a" dependencies = [ "proc-macro2", "quote", @@ -2340,9 +2449,9 @@ dependencies = [ [[package]] name = "tracing-core" -version = "0.1.29" +version = "0.1.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5aeea4303076558a00714b823f9ad67d58a3bbda1df83d8827d21193156e22f7" +checksum = "24eb03ba0eab1fd845050058ce5e616558e8f8d8fca633e6b163fe25c797213a" dependencies = [ "once_cell", "valuable", @@ -2375,12 +2484,12 @@ dependencies = [ [[package]] name = "tracing-subscriber" -version = "0.3.15" +version = "0.3.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "60db860322da191b40952ad9affe65ea23e7dd6a5c442c2c42865810c6ab8e6b" +checksum = "a6176eae26dd70d0c919749377897b54a9276bd7061339665dd68777926b5a70" dependencies = [ - "ansi_term", "matchers", + "nu-ansi-term", "once_cell", "regex", "sharded-slab", @@ -2393,15 +2502,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 = "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 = "unicase" @@ -2414,15 +2523,15 @@ dependencies = [ [[package]] name = "unicode-bidi" -version = "0.3.8" +version = "0.3.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "099b7128301d285f79ddd55b9a83d5e6b9e97c92e0ea0daebee7263e932de992" +checksum = "d54675592c1dbefd78cbd98db9bacd89886e1ca50692a0692baefffdeb92dd58" [[package]] name = "unicode-ident" -version = "1.0.4" +version = "1.0.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dcc811dc4066ac62f84f11307873c4850cb653bfa9b1719cee2bd2204a4bc5dd" +checksum = "84a22b9f218b40614adcb3f4ff08b703773ad44fa9423e4e0d346d5db86e4ebc" [[package]] name = "unicode-normalization" @@ -2433,6 +2542,12 @@ 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" @@ -2505,9 +2620,9 @@ checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" [[package]] name = "wasm-bindgen" -version = "0.2.83" +version = "0.2.84" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eaf9f5aceeec8be17c128b2e93e031fb8a4d469bb9c4ae2d7dc1888b26887268" +checksum = "31f8dcbc21f30d9b8f2ea926ecb58f6b91192c17e9d33594b3df58b2007ca53b" dependencies = [ "cfg-if", "wasm-bindgen-macro", @@ -2515,9 +2630,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-backend" -version = "0.2.83" +version = "0.2.84" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4c8ffb332579b0557b52d268b91feab8df3615f265d5270fec2a8c95b17c1142" +checksum = "95ce90fd5bcc06af55a641a86428ee4229e44e07033963a2290a8e241607ccb9" dependencies = [ "bumpalo", "log", @@ -2530,9 +2645,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-futures" -version = "0.4.33" +version = "0.4.34" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "23639446165ca5a5de86ae1d8896b737ae80319560fbaa4c2887b7da6e7ebd7d" +checksum = "f219e0d211ba40266969f6dbdd90636da12f75bee4fc9d6c23d1260dadb51454" dependencies = [ "cfg-if", "js-sys", @@ -2542,9 +2657,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro" -version = "0.2.83" +version = "0.2.84" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "052be0f94026e6cbc75cdefc9bae13fd6052cdcaf532fa6c45e7ae33a1e6c810" +checksum = "4c21f77c0bedc37fd5dc21f897894a5ca01e7bb159884559461862ae90c0b4c5" dependencies = [ "quote", "wasm-bindgen-macro-support", @@ -2552,9 +2667,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro-support" -version = "0.2.83" +version = "0.2.84" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "07bc0c051dc5f23e307b13285f9d75df86bfdf816c5721e573dec1f9b8aa193c" +checksum = "2aff81306fcac3c7515ad4e177f521b5c9a15f2b08f4e32d823066102f35a5f6" dependencies = [ "proc-macro2", "quote", @@ -2565,15 +2680,15 @@ dependencies = [ [[package]] name = "wasm-bindgen-shared" -version = "0.2.83" +version = "0.2.84" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1c38c045535d93ec4f0b4defec448e4291638ee608530863b1e2ba115d4fff7f" +checksum = "0046fef7e28c3804e5e38bfa31ea2a0f73905319b677e57ebe37e49358989b5d" [[package]] name = "web-sys" -version = "0.3.60" +version = "0.3.61" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bcda906d8be16e728fd5adc5b729afad4e444e106ab28cd1c7256e54fa61510f" +checksum = "e33b99f4b23ba3eec1a53ac264e35a755f00e966e0065077d6027c0f575b0b97" dependencies = [ "js-sys", "wasm-bindgen", @@ -2601,9 +2716,9 @@ dependencies = [ [[package]] name = "webpki-roots" -version = "0.22.5" +version = "0.22.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "368bfe657969fb01238bb756d351dcade285e0f6fcbd36dcb23359a5169975be" +checksum = "b6c71e40d7d2c34a5106301fb632274ca37242cd0c9d3e64dbece371a40a2d87" dependencies = [ "webpki 0.22.0", ] @@ -2641,46 +2756,84 @@ checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" [[package]] name = "windows-sys" -version = "0.36.1" +version = "0.42.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ea04155a16a59f9eab786fe12a4a450e75cdb175f9e0d80da1e17db09f55b8d2" +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" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "75283be5efb2831d37ea142365f009c02ec203cd29a3ebecbc093d52315b66d0" +dependencies = [ + "windows-targets", +] + +[[package]] +name = "windows-targets" +version = "0.42.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e2522491fbfcd58cc84d47aeb2958948c4b8982e9a2d8a2a35bbaed431390e7" +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.42.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8c9864e83243fdec7fc9c5444389dcbbfd258f745e7853198f365e3c4968a608" + [[package]] name = "windows_aarch64_msvc" -version = "0.36.1" +version = "0.42.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9bb8c3fd39ade2d67e9874ac4f3db21f0d710bee00fe7cab16949ec184eeaa47" +checksum = "4c8b1b673ffc16c47a9ff48570a9d85e25d265735c503681332589af6253c6c7" [[package]] name = "windows_i686_gnu" -version = "0.36.1" +version = "0.42.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "180e6ccf01daf4c426b846dfc66db1fc518f074baa793aa7d9b9aaeffad6a3b6" +checksum = "de3887528ad530ba7bdbb1faa8275ec7a1155a45ffa57c37993960277145d640" [[package]] name = "windows_i686_msvc" -version = "0.36.1" +version = "0.42.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e2e7917148b2812d1eeafaeb22a97e4813dfa60a3f8f78ebe204bcc88f12f024" +checksum = "bf4d1122317eddd6ff351aa852118a2418ad4214e6613a50e0191f7004372605" [[package]] name = "windows_x86_64_gnu" -version = "0.36.1" +version = "0.42.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4dcd171b8776c41b97521e5da127a2d86ad280114807d0b2ab1e462bc764d9e1" +checksum = "c1040f221285e17ebccbc2591ffdc2d44ee1f9186324dd3e84e99ac68d699c45" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.42.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "628bfdf232daa22b0d64fdb62b09fcc36bb01f05a3939e20ab73aaf9470d0463" [[package]] name = "windows_x86_64_msvc" -version = "0.36.1" +version = "0.42.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c811ca4a8c853ef420abd8592ba53ddbbac90410fab6903b3e79972a631f7680" +checksum = "447660ad36a13288b1db4d4248e857b510e8c3a225c822ba4fb748c0aafecffd" [[package]] name = "winreg" @@ -2711,9 +2864,9 @@ checksum = "c394b5bd0c6f669e7275d9c20aa90ae064cb22e75a1cad54e1b34088034b149f" [[package]] name = "zip" -version = "0.6.2" +version = "0.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bf225bcf73bb52cbb496e70475c7bd7a3f769df699c0020f6c7bd9a96dcf0b8d" +checksum = "0445d0fbc924bb93539b4316c11afb121ea39296f99a3c4c9edad09e3658cdef" dependencies = [ "byteorder", "crc32fast", diff --git a/tools/ci-cdk/canary-runner/Cargo.toml b/tools/ci-cdk/canary-runner/Cargo.toml index aaa0872a0c..d2325d59f9 100644 --- a/tools/ci-cdk/canary-runner/Cargo.toml +++ b/tools/ci-cdk/canary-runner/Cargo.toml @@ -33,4 +33,4 @@ tracing-subscriber = { version = "0.3.15", features = ["env-filter", "fmt"] } zip = { version = "0.6.2", default-features = false, features = ["deflate"] } [dev-dependencies] -pretty_assertions = "1.1" +pretty_assertions = "1.3" diff --git a/tools/ci-cdk/canary-runner/src/build_bundle.rs b/tools/ci-cdk/canary-runner/src/build_bundle.rs index aa3857f330..464ee2e4ad 100644 --- a/tools/ci-cdk/canary-runner/src/build_bundle.rs +++ b/tools/ci-cdk/canary-runner/src/build_bundle.rs @@ -53,6 +53,7 @@ tracing-subscriber = { version = "0.3", features = ["fmt", "env-filter"] } uuid = { version = "0.8", features = ["v4"] } tokio-stream = "0" tracing-texray = "0.1.1" +reqwest = { version = "0.11.14", features = ["rustls-tls"], default-features = false } "#; const REQUIRED_SDK_CRATES: &[&str] = &[ @@ -64,7 +65,7 @@ const REQUIRED_SDK_CRATES: &[&str] = &[ lazy_static! { static ref NOTABLE_SDK_RELEASE_TAGS: Vec = vec![ - ReleaseTag::from_str("v0.4.1").unwrap(), // first version to add support for paginators + ReleaseTag::from_str("release-2023-01-26").unwrap(), // last version before the crate reorg ]; } @@ -114,12 +115,19 @@ enum CrateSource { fn enabled_features(crate_source: &CrateSource) -> Vec { let mut enabled = Vec::new(); if let CrateSource::VersionsManifest { release_tag, .. } = crate_source { + // we want to select the newest module specified after this release for notable in NOTABLE_SDK_RELEASE_TAGS.iter() { - if notable < release_tag { + tracing::debug!(release_tag = ?release_tag, notable = ?notable, "considering if release tag came before notable release"); + if release_tag <= notable { + tracing::debug!("selecting {} as chosen release", notable); enabled.push(notable.as_str().into()); + break; } } } + if enabled.is_empty() { + enabled.push("latest".into()); + } enabled } @@ -157,11 +165,12 @@ fn generate_crate_manifest(crate_source: CrateSource) -> Result { } } write!(output, "\n[features]\n").unwrap(); + writeln!(output, "latest = []").unwrap(); for release_tag in NOTABLE_SDK_RELEASE_TAGS.iter() { writeln!( output, "\"{release_tag}\" = []", - release_tag = release_tag.as_str().replace('-', "_") + release_tag = release_tag.as_str() ) .unwrap(); } @@ -254,7 +263,7 @@ pub async fn build_bundle(opt: BuildBundleArgs) -> Result> { path.join("release") }; let bin_path = target_path.join("bootstrap"); - let bundle_path = target_path.join(&name_bundle( + let bundle_path = target_path.join(name_bundle( &bin_path, opt.rust_version.as_deref(), opt.sdk_release_tag.as_ref(), @@ -324,7 +333,6 @@ mod tests { "--sdk-release-tag", "release-2022-07-26" ]) - .ok() .expect("valid args") ); assert_eq!( @@ -344,7 +352,6 @@ mod tests { "--canary-path", "some-canary-path" ]) - .ok() .expect("valid args") ); assert_eq!( @@ -364,7 +371,6 @@ mod tests { "--musl", "--manifest-only" ]) - .ok() .expect("valid args") ); assert_eq!( @@ -386,7 +392,6 @@ mod tests { "--canary-path", "some-canary-path" ]) - .ok() .expect("valid args") ); } @@ -428,14 +433,16 @@ tracing-subscriber = { version = "0.3", features = ["fmt", "env-filter"] } uuid = { version = "0.8", features = ["v4"] } tokio-stream = "0" tracing-texray = "0.1.1" +reqwest = { version = "0.11.14", features = ["rustls-tls"], default-features = false } aws-config = { path = "some/sdk/path/aws-config" } aws-sdk-s3 = { path = "some/sdk/path/s3" } aws-sdk-ec2 = { path = "some/sdk/path/ec2" } aws-sdk-transcribestreaming = { path = "some/sdk/path/transcribestreaming" } [features] -"v0.4.1" = [] -default = [] +latest = [] +"release-2023-01-26" = [] +default = ["latest"] "#, generate_crate_manifest(CrateSource::Path("some/sdk/path".into())).expect("success") ); @@ -490,14 +497,16 @@ tracing-subscriber = { version = "0.3", features = ["fmt", "env-filter"] } uuid = { version = "0.8", features = ["v4"] } tokio-stream = "0" tracing-texray = "0.1.1" +reqwest = { version = "0.11.14", features = ["rustls-tls"], default-features = false } aws-config = "0.46.0" aws-sdk-s3 = "0.20.0" aws-sdk-ec2 = "0.19.0" aws-sdk-transcribestreaming = "0.16.0" [features] -"v0.4.1" = [] -default = ["v0.4.1"] +latest = [] +"release-2023-01-26" = [] +default = ["latest"] "#, generate_crate_manifest(CrateSource::VersionsManifest { versions: VersionsManifest { @@ -514,7 +523,7 @@ default = ["v0.4.1"] .collect(), release: None, }, - release_tag: ReleaseTag::from_str("release-2022-07-26").unwrap(), + release_tag: ReleaseTag::from_str("release-2023-05-26").unwrap(), }) .expect("success") ); @@ -557,4 +566,37 @@ default = ["v0.4.1"] .unwrap(), ); } + + #[test] + fn test_notable_versions() { + let versions = VersionsManifest { + smithy_rs_revision: "some-revision-smithy-rs".into(), + aws_doc_sdk_examples_revision: "some-revision-docs".into(), + manual_interventions: Default::default(), + crates: [].into_iter().collect(), + release: None, + }; + assert_eq!( + enabled_features(&CrateSource::VersionsManifest { + versions: versions.clone(), + release_tag: "release-2023-02-23".parse().unwrap(), + }), + vec!["latest".to_string()] + ); + + assert_eq!( + enabled_features(&CrateSource::VersionsManifest { + versions: versions.clone(), + release_tag: "release-2023-01-26".parse().unwrap(), + }), + vec!["release-2023-01-26".to_string()] + ); + assert_eq!( + enabled_features(&CrateSource::VersionsManifest { + versions, + release_tag: "release-2023-01-13".parse().unwrap(), + }), + vec!["release-2023-01-26".to_string()] + ); + } } diff --git a/tools/ci-scripts/check-aws-sdk-canary b/tools/ci-scripts/check-aws-sdk-canary index fbeb310e03..73ce35f047 100755 --- a/tools/ci-scripts/check-aws-sdk-canary +++ b/tools/ci-scripts/check-aws-sdk-canary @@ -20,10 +20,11 @@ pushd tools/ci-cdk/canary-lambda # The canary-lambda doesn't have a Cargo.toml in a fresh checkout of smithy-rs, so generate one before checks pushd ../canary-runner echo "${C_YELLOW}## Generating Cargo.toml for the canary-lambda...${C_RESET}" -cargo run -- build-bundle --manifest-only --canary-path ../canary-lambda --sdk-path "${SDK_PATH}" +cargo run -- build-bundle --manifest-only --canary-path ../canary-lambda --sdk-path "${SDK_PATH}" --musl popd +# Note: This only checks the `latest` canary, and not any previous version features echo "${C_YELLOW}## Running 'cargo clippy' for the canary-lambda...${C_RESET}" -cargo clippy --all-features +cargo clippy popd diff --git a/tools/ci-scripts/check-only-aws-sdk-services b/tools/ci-scripts/check-only-aws-sdk-services index 27c93ea479..03d000445d 100755 --- a/tools/ci-scripts/check-only-aws-sdk-services +++ b/tools/ci-scripts/check-only-aws-sdk-services @@ -20,3 +20,11 @@ for test_dir in tests/*; do cargo check --all-targets --all-features --manifest-path "${test_dir}/Cargo.toml" fi done + +# TODO(CrateReorganization): Uncomment when cleaning up `enableNewCrateOrganizationScheme` +# large_file_count="$(find sdk -iname '*.rs' -type f -size +1M | wc -l | bc)" +# if [[ "${large_file_count}" != "0" ]]; then +# echo "Found ${large_file_count} generated code files larger than 1 MB:" +# find sdk -iname '*.rs' -type f -size +1M +# exit 1 +# fi diff --git a/tools/ci-scripts/check-server-e2e-test b/tools/ci-scripts/check-server-e2e-test index 8b17890724..2178b3d74b 100755 --- a/tools/ci-scripts/check-server-e2e-test +++ b/tools/ci-scripts/check-server-e2e-test @@ -4,6 +4,6 @@ # SPDX-License-Identifier: Apache-2.0 set -eux -cd smithy-rs/rust-runtime/aws-smithy-http-server/examples +cd smithy-rs/examples make test clippy diff --git a/tools/ci-scripts/codegen-diff-revisions.py b/tools/ci-scripts/codegen-diff-revisions.py index ae2bc42011..0892719bcc 100755 --- a/tools/ci-scripts/codegen-diff-revisions.py +++ b/tools/ci-scripts/codegen-diff-revisions.py @@ -14,22 +14,21 @@ # # ``` # $ cd test/smithy-rs -# $ ../../smithy-rs/tools/codegen-diff-revisions.py . +# $ ../../smithy-rs/tools/ci-scripts/codegen-diff-revisions.py . # ``` # # It will diff the generated code from HEAD against any commit hash you feed it. If you want to test # a specific range, change the HEAD of the test repository. # -# This script requires `diff2html-cli` to be installed from NPM: +# This script requires `difftags` to be installed from `tools/ci-build/difftags`: # ``` -# $ npm install -g diff2html-cli@5.1.11 +# $ cargo install --path tools/ci-build/difftags # ``` # Make sure the local version matches the version referenced from the GitHub Actions workflow. import os import sys import subprocess -import tempfile import shlex @@ -89,29 +88,35 @@ def main(): def generate_and_commit_generated_code(revision_sha): # Clean the build artifacts before continuing run("rm -rf aws/sdk/build") + run("cd rust-runtime/aws-smithy-http-server-python/examples && make distclean", shell=True) run("./gradlew codegen-core:clean codegen-client:clean codegen-server:clean aws:sdk-codegen:clean") # Generate code - run("./gradlew --rerun-tasks :aws:sdk:assemble") - run("./gradlew --rerun-tasks :codegen-server-test:assemble") - run("./gradlew --rerun-tasks :codegen-server-test:python:assemble") + run("./gradlew --rerun-tasks aws:sdk:assemble codegen-client-test:assemble codegen-server-test:assemble") + run("cd rust-runtime/aws-smithy-http-server-python/examples && make build", shell=True, check=False) # Move generated code into codegen-diff/ directory run(f"rm -rf {OUTPUT_PATH}") run(f"mkdir {OUTPUT_PATH}") run(f"mv aws/sdk/build/aws-sdk {OUTPUT_PATH}/") + run(f"mv codegen-client-test/build/smithyprojections/codegen-client-test {OUTPUT_PATH}/") run(f"mv codegen-server-test/build/smithyprojections/codegen-server-test {OUTPUT_PATH}/") - run(f"mv codegen-server-test/python/build/smithyprojections/codegen-server-test-python {OUTPUT_PATH}/") + run(f"mv rust-runtime/aws-smithy-http-server-python/examples/pokemon-service-server-sdk/ {OUTPUT_PATH}/codegen-server-test-python/", check=False) + + # Clean up the SDK directory + run(f"rm -f {OUTPUT_PATH}/aws-sdk/versions.toml") + + # Clean up the client-test folder + run(f"rm -rf {OUTPUT_PATH}/codegen-client-test/source") + run(f"find {OUTPUT_PATH}/codegen-client-test | " + f"grep -E 'smithy-build-info.json|sources/manifest|model.json' | " + f"xargs rm -f", shell=True) # Clean up the server-test folder run(f"rm -rf {OUTPUT_PATH}/codegen-server-test/source") - run(f"rm -rf {OUTPUT_PATH}/codegen-server-test-python/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"git add -f {OUTPUT_PATH}") run(f"git -c 'user.name=GitHub Action (generated code preview)' " @@ -155,6 +160,10 @@ def make_diffs(base_commit_sha, head_commit_sha): head_commit_sha, "aws-sdk", whitespace=True) sdk_nows = make_diff("AWS SDK", f"{OUTPUT_PATH}/aws-sdk", base_commit_sha, head_commit_sha, "aws-sdk-ignore-whitespace", whitespace=False) + client_ws = make_diff("Client Test", f"{OUTPUT_PATH}/codegen-client-test", base_commit_sha, + head_commit_sha, "client-test", whitespace=True) + client_nows = make_diff("Client Test", f"{OUTPUT_PATH}/codegen-client-test", base_commit_sha, + head_commit_sha, "client-test-ignore-whitespace", whitespace=False) server_ws = make_diff("Server Test", f"{OUTPUT_PATH}/codegen-server-test", base_commit_sha, head_commit_sha, "server-test", whitespace=True) server_nows = make_diff("Server Test", f"{OUTPUT_PATH}/codegen-server-test", base_commit_sha, @@ -166,6 +175,8 @@ def make_diffs(base_commit_sha, head_commit_sha): sdk_links = diff_link('AWS SDK', 'No codegen difference in the AWS SDK', sdk_ws, 'ignoring whitespace', sdk_nows) + client_links = diff_link('Client Test', 'No codegen difference in the Client Test', + client_ws, 'ignoring whitespace', client_nows) server_links = diff_link('Server Test', 'No codegen difference in the Server Test', server_ws, 'ignoring whitespace', server_nows) server_links_python = diff_link('Server Test Python', 'No codegen difference in the Server Test Python', @@ -173,6 +184,7 @@ def make_diffs(base_commit_sha, head_commit_sha): # 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" @@ -188,10 +200,10 @@ def eprint(*args, **kwargs): # Runs a shell command -def run(command, shell=False): +def run(command, shell=False, check=True): if not shell: command = shlex.split(command) - subprocess.run(command, stdout=sys.stderr, stderr=sys.stderr, shell=shell, check=True) + subprocess.run(command, stdout=sys.stderr, stderr=sys.stderr, shell=shell, check=check) # Returns the output from a shell command. Bails if the command failed diff --git a/tools/echo-server/Cargo.lock b/tools/echo-server/Cargo.lock index 2efeb1952f..07ba693807 100644 --- a/tools/echo-server/Cargo.lock +++ b/tools/echo-server/Cargo.lock @@ -235,9 +235,9 @@ checksum = "c4a1e36c821dbe04574f602848a19f742f4fb3c98d40449f11bcad18d6b17421" [[package]] name = "hyper" -version = "0.14.20" +version = "0.14.25" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "02c929dc5c39e335a03c405292728118860721b10190d98c2a0f0efd5baafbac" +checksum = "cc5e554ff619822309ffd57d8734d77cd5ce6238bc956f037ea06c58238c9899" dependencies = [ "bytes", "futures-channel", diff --git a/tools/echo-server/Cargo.toml b/tools/echo-server/Cargo.toml index f11225b64c..df50f243be 100644 --- a/tools/echo-server/Cargo.toml +++ b/tools/echo-server/Cargo.toml @@ -11,4 +11,4 @@ tokio = { version = "1.20.1", features = ["full"] } tracing = "0.1" tracing-subscriber = { version = "0.3.15", features = ["env-filter"] } tower = { version = "0.4", features = ["util", "filter"] } -hyper = { version = "0.14.12", features = ["full"] } +hyper = { version = "0.14.25", features = ["full"] }