

翻訳は機械翻訳により提供されています。提供された翻訳内容と英語版の間で齟齬、不一致または矛盾がある場合、英語版が優先します。

# AWS SDK for Rust の使用
<a name="using"></a>

AWS SDK for Rust を使用して、AWS サービスを操作する一般的な方法と推奨される方法について説明します。

**Topics**
+ [サービスリクエストの作成](make-request.md)
+ [ベストプラクティス](best-practices.md)
+ [同時実行](concurrency.md)
+ [Lambda 関数の作成](lambda.md)
+ [署名付き URL の作成](presigned-urls.md)
+ [エラー処理](error-handling.md)
+ [Pagination (ページ分割)](paginating.md)
+ [ユニットテスト](testing.md)
+ [ウェイター](waiters.md)

# AWS SDK for Rust を使用した AWS のサービス リクエストの作成
<a name="make-request"></a>

 プログラムを使用して AWS のサービスにアクセスするために、AWS SDK for Rust は各 AWS のサービスにクライアント構造体を使用します。例えば、アプリケーションが Amazon EC2 にアクセスする必要がある場合、アプリケーションはそのサービスとインターフェイスをとる Amazon EC2 クライアント構造体を作成します。次に、サービスクライアントを使用して、その AWS のサービス に対してリクエストを実行します。

AWS のサービス にリクエストするには、最初にサービスクライアントを作成して[設定](configure.md)する必要があります。コードが使用する各 AWS のサービス には、専用のクレートと、やり取りを行うための専用の型があります。クライアントは、サービスが提供する各 API オペレーションに対応するメソッドをそれぞれ公開しています。

 AWS SDK for Rust で AWS のサービスとやり取りするには、サービス固有のクライアントを作成して、その API メソッドを fluent builder スタイルの連鎖で使用し、`send()` を呼び出してリクエストを実行します。

`Client` は、サービスが提供する各 API オペレーションに対応するメソッドをそれぞれ公開しています。これらの各メソッドの戻り値は「fluent builder」であり、その API に対するさまざまな入力がビルダースタイルの関数呼び出しの連鎖によって追加されます。サービスのメソッドを呼び出した後に `send()` を呼び出して [https://doc.rust-lang.org/nightly/core/future/trait.Future.html](https://doc.rust-lang.org/nightly/core/future/trait.Future.html) を取得します。これにより、正常な出力または `SdkError` のいずれかが生成されます。`SdkError` の詳細については、「[AWS SDK for Rust でのエラーの処理](error-handling.md)」を参照してください。

次の例は、Amazon S3 を使用して `us-west-2` AWS リージョン にバケットを作成する基本的なオペレーションを示しています。

```
let config = aws_config::defaults(BehaviorVersion::latest())
    .load()
    .await;
  
let s3 = aws_sdk_s3::Client::new(&config);
  
let result = s3.create_bucket()
    // Set some of the inputs for the operation.
    .bucket("my-bucket")
    .create_bucket_configuration(
        CreateBucketConfiguration::builder()
            .location_constraint(aws_sdk_s3::types::BucketLocationConstraint::UsWest2)
            .build()
        )
    // send() returns a Future that does nothing until awaited.
    .send()
    .await;
```

各サービスクレートには、次のような API 入力に使用される追加モジュールがあります。
+ `types` モジュールには、より複雑な構造化情報を提供するための構造体または列挙型があります。
+  `primitives` モジュールには、日時やバイナリ BLOB などのデータを表現するためのより単純な型があります。

 サービスクレートの詳細なクレート構成や情報の詳細については、「[API リファレンスドキュメント](https://awslabs.github.io/aws-sdk-rust/)」を参照してください。例えば、Amazon Simple Storage Service `aws-sdk-s3` のクレートには、複数の[モジュール](https://docs.rs/aws-sdk-s3/latest/aws_sdk_s3/#modules)があります。そのうちの 2 つは次のとおりです。
+ [https://docs.rs/aws-sdk-s3/latest/aws_sdk_s3/types/index.html](https://docs.rs/aws-sdk-s3/latest/aws_sdk_s3/types/index.html)
+ [https://docs.rs/aws-sdk-s3/latest/aws_sdk_s3/primitives/index.html](https://docs.rs/aws-sdk-s3/latest/aws_sdk_s3/primitives/index.html)

# AWS SDK for Rust の使用に関するベストプラクティス
<a name="best-practices"></a>

以下は、AWS SDK for Rust を使用するためのベストプラクティスです。

## 可能な限り SDK クライアントを再利用する
<a name="bp-reuseClient"></a>

SDK クライアントの構築方法によっては、新しいクライアントを作成すると、各クライアントが独自の HTTP 接続プール、ID キャッシュなどを維持する場合があります。高コストのリソース作成のオーバーヘッドを避けるため、クライアントを共有するか、少なくとも `SdkConfig` を共有することをお勧めします。すべての SDK クライアントは、単一のアトミック参照カウントの更新として `Clone` を実装します。

## API タイムアウトの設定
<a name="bp-apiTimeouts"></a>

 SDK は、接続タイムアウトやソケットタイムアウトなど、一部のタイムアウトオプションにはデフォルト値を提供しますが、API コールタイムアウトや個々の API コール試行にはデフォルト値を提供しません。個々の試行とリクエスト全体の両方にタイムアウトを設定するのがグッドプラクティスです。これにより、リクエストの完了までの時間が長くなるような一時的な問題や、致命的なネットワーク上の問題が発生した場合に、アプリケーションが最適な方法で迅速に失敗するようになります。

オペレーションの設定の詳細については、「[AWS SDK for Rust でのタイムアウトの設定](timeouts.md)」を参照してください。

# の同時実行数 AWS SDK for Rust
<a name="concurrency"></a>

は同時実行制御を提供し AWS SDK for Rust ませんが、ユーザーは独自の を実装するための多くのオプションがあります。

## 用語
<a name="conc-terms"></a>

このテーマに関連する用語は混同されやすく、もともと別々の概念を表していたにもかかわらず、同義語となった用語もあります。このガイドでは、以下の用語を定義します。
+  **タスク**: プログラムが完了するまで実行する、または完了するまで実行を試みる「作業単位」。
+  **シーケンシャルコンピューティング**: 複数のタスクが順番に実行される場合。
+  **同時実行コンピューティング**: 複数のタスクが時間的に重なり合って実行される場合。
+  **同時実行**: コンピュータが複数のタスクを任意の順序で完了する能力。
+  **マルチタスク**: コンピュータが複数のタスクを同時に実行する能力。
+  **レース条件**: タスクの開始タイミング、またはタスクの処理時間に基づいてプログラムの動作が変化する場合。
+  **競合**: 共有リソースへのアクセスの競合。複数のタスクがリソースに同時にアクセスする場合、そのリソースは「競合中」になります。
+  **デッドロック**: これ以上進行できない状態。これは通常、2 つのタスクが互いのリソースを取得しようとしているが、どちらのタスクも他方のリソースが利用可能になるまでリソースを解放しないために発生します。デッドロックが発生すると、プログラムが部分的または完全に応答しなくなります。

## シンプルな例
<a name="conc-simple"></a>

最初の例は、シーケンシャルプログラムです。後の例では、このコードを同時実行手法で変更します。後の例では、同じ `build_client_and_list_objects_to_download()` メソッドを再利用して、`main()` 内で変更を加えます。次のコマンドを実行して、プロジェクトに依存関係を追加します。
+ `cargo add aws-sdk-s3`
+ `cargo add aws-config tokio --features tokio/full`

次のタスク例では、Amazon Simple Storage Service バケット内のすべてのファイルをダウンロードしています。

1.  まず、すべてのファイルを一覧表示します。キーをリストに保存します。

1.  リストをイテレーションして、各ファイルを順番にダウンロードする 

```
use aws_sdk_s3::{Client, Error};
const EXAMPLE_BUCKET: &str = "amzn-s3-demo-bucket";  // Update to name of bucket you own.

// This initialization function won't be reproduced in
// examples following this one, in order to save space.
async fn build_client_and_list_objects_to_download() -> (Client, Vec<String>) {
    let cfg = aws_config::load_defaults(aws_config::BehaviorVersion::latest()).await;
    let client = Client::new(&cfg);
    let objects_to_download: Vec<_> = client
        .list_objects_v2()
        .bucket(EXAMPLE_BUCKET)
        .send()
        .await
        .expect("listing objects succeeds")
        .contents()
        .into_iter()
        .flat_map(aws_sdk_s3::types::Object::key)
        .map(ToString::to_string)
        .collect();
         
    (client, objects_to_download)
}
```

```
#[tokio::main]
async fn main() {
    let (client, objects_to_download) =
        build_client_and_list_objects_to_download().await;
    
    for object in objects_to_download {
        let res = client
            .get_object()
            .key(&object)
            .bucket(EXAMPLE_BUCKET)
            .send()
            .await
            .expect("get_object succeeds");
        let body = res.body.collect().await.expect("reading body succeeds").into_bytes();
        std::fs::write(object, body).expect("write succeeds");
    }
}
```

**注記**  
 これらの例では、エラーは処理されず、サンプルバケットにファイルパスに似たキーを持つオブジェクトが存在しないことを前提としています。したがって、ネストされたディレクトリの作成については説明しません。

最新のコンピュータのアーキテクチャを使用すれば、このプログラムをはるかに効率的なものに書き直すことができます。これについては後の例で説明しますが、まずは概念についてさらにいくつか説明します。

## 所有権と可変性
<a name="conc-ownership"></a>

Rust のそれぞれの値には単一の所有者を持ちます。所有者が範囲から外れると、その所有者が所有するすべての値も削除されます。所有者は、1 つ以上の値に対する変更不可能な参照**または** 1 つの変更可能な参照を提供できます。Rust コンパイラは、参照がその所有者を越えて存在しないことを保証します。

複数のタスクが同じリソースに可変的にアクセスする必要がある場合は、追加の計画と設計が必要になります。シーケンシャルコンピューティングでは、各タスクは順番に実行されるため、競合することなく可変的に同じリソースにアクセスできます。ただし、同時コンピューティングでは、タスクは任意の順序で同時に実行できます。したがって、複数の変更可能な参照が不可能であることをコンパイラに証明するため (または、少なくともクラッシュするように)、さらに多くの対策が必要です。

Rust 標準ライブラリには、これを実現するための多くのツールが用意されています。これらのトピックの詳細については、「Rust Programming Language」書籍の「[変数と可変性](https://doc.rust-lang.org/book/ch03-01-variables-and-mutability.html)」および「[所有権を理解する](https://doc.rust-lang.org/book/ch04-00-understanding-ownership.html)」を参照してください。

## その他の用語
<a name="conc-moreTerms"></a>

以下は「同期オブジェクト」の一覧です。これらは、コンパイラに対して同時実行プログラムが所有権ルールに反しないことを保証するために必要な手段です。

 [https://doc.rust-lang.org/std/sync/index.html](https://doc.rust-lang.org/std/sync/index.html): 
+ [https://doc.rust-lang.org/std/sync/struct.Arc.html](https://doc.rust-lang.org/std/sync/struct.Arc.html): ***ア**トミック**参**照**カ**ウント型*ポインタ。`Arc` でラップされたデータは、特定の所有者がその値を早期に削除することを気にせずに、自由に共有できます。この意味では、値の所有権は「共有」になります。`Arc` 内の値は変更できませんが、[内部可変性](https://doc.rust-lang.org/reference/interior-mutability.html)がある場合があります。
+ [https://doc.rust-lang.org/std/sync/struct.Barrier.html](https://doc.rust-lang.org/std/sync/struct.Barrier.html): これにより、複数のスレッドがプログラム内の特定のポイントに到達するまで互いに待機し、その後まとめて実行を継続します。
+ [https://doc.rust-lang.org/std/sync/struct.Condvar.html](https://doc.rust-lang.org/std/sync/struct.Condvar.html): ***条件****変数***で、イベントの発生を待機している間、スレッドをブロックする機能を提供します。
+ [https://doc.rust-lang.org/std/sync/struct.Mutex.html](https://doc.rust-lang.org/std/sync/struct.Mutex.html): ***相互****排他***メカニズムで、特定のデータに対して同時にアクセスできるスレッドが最大で 1 つであることを保証します。一般的に、`Mutex` ロックは、コード内の `.await` ポイントにまたがって保持することは避けてください。

 [https://docs.rs/tokio/latest/tokio/sync/index.html](https://docs.rs/tokio/latest/tokio/sync/index.html): 

 AWS SDKs は `async`-runtime-agnostic を想定していますが、特定のケースでは`tokio`同期オブジェクトを使用することをお勧めします。
+ [https://docs.rs/tokio/latest/tokio/sync/struct.Mutex.html](https://docs.rs/tokio/latest/tokio/sync/struct.Mutex.html): 標準ライブラリの `Mutex` に似ていますが、コストがわずかに高くなります。標準の `Mutex` とは異なり、これはコード内の 1 つの `.await` ポイントにまたがって保持できます。
+ [https://docs.rs/tokio/latest/tokio/sync/struct.Semaphore.html](https://docs.rs/tokio/latest/tokio/sync/struct.Semaphore.html): 変数の 1 つで、複数のタスクによる共通リソースへのアクセスを制御するために使用されます。

## 例をより効率的に書き直す (シングルスレッド同時実行)
<a name="conc_singleThread"></a>

次の変更例では、[https://docs.rs/futures-util/latest/futures_util/future/fn.join_all.html](https://docs.rs/futures-util/latest/futures_util/future/fn.join_all.html) を使用して **すべての** `get_object` リクエストを同時に実行します。次のコマンドを実行して、プロジェクトに新しい依存関係を追加します。
+ `cargo add futures-util`

```
#[tokio::main]
async fn main() {
    let (client, objects_to_download) =
        build_client_and_list_objects_to_download().await;
        
    let get_object_futures = objects_to_download.into_iter().map(|object| {
        let req = client
            .get_object()
            .key(&object)
            .bucket(EXAMPLE_BUCKET);

        async {
            let res = req
                .send()
                .await
                .expect("get_object succeeds");
            let body = res.body.collect().await.expect("body succeeds").into_bytes();
           // Note that we MUST use the async runtime's preferred way
           // of writing files. Otherwise, this call would block,
           // potentially causing a deadlock.
            tokio::fs::write(object, body).await.expect("write succeeds");
        }
    });

    futures_util::future::join_all(get_object_futures).await;
}
```

 これは同時実行のメリットを享受する最も簡単な方法ですが、最初はわかりにくい問題が一部あります。

1.  すべてのリクエスト入力を同時に作成します。すべての `get_object` リクエスト入力を保持するのに十分なメモリがない場合、「メモリ不足」割り当てエラーが発生します。

1.  すべての Future を同時に作成し、待機します。Amazon S3 では、一度に大量にダウンロードしようとするとリクエストをスロットリングします。

これらの問題を両方とも解決するには、一度に送信するリクエスト量を制限する必要があります。これを行うには、`tokio` [セマフォ](https://docs.rs/tokio/latest/tokio/sync/struct.Semaphore.html)を使用します。

```
use std::sync::Arc;
use tokio::sync::Semaphore;
const CONCURRENCY_LIMIT: usize = 50; 

#[tokio::main(flavor = "current_thread")]
async fn main() {
    let (client, objects_to_download) =
        build_client_and_list_objects_to_download().await;
    let concurrency_semaphore = Arc::new(Semaphore::new(CONCURRENCY_LIMIT));

    let get_object_futures = objects_to_download.into_iter().map(|object| {
        // Since each future needs to acquire a permit, we need to clone
        // the Arc'd semaphore before passing it in.
        let semaphore = concurrency_semaphore.clone();
        // We also need to clone the client so each task has its own handle.
        let client = client.clone();
        async move {
            let permit = semaphore
                .acquire()
                .await
                .expect("we'll get a permit if we wait long enough");
            let res = client
                .get_object()
                .key(&object)
                .bucket(EXAMPLE_BUCKET)
                .send()
                .await
                .expect("get_object succeeds");
            let body = res.body.collect().await.expect("body succeeds").into_bytes();
            tokio::fs::write(object, body).await.expect("write succeeds");
            std::mem::drop(permit);
        }
    });

    futures_util::future::join_all(get_object_futures).await;
}
```

リクエストの作成を `async` ブロックに移動することにより、潜在的なメモリ使用量の問題を修正しました。この方法により、リクエストは送信するタイミングまで作成されません。

**注記**  
 メモリに余裕がある場合は、すべてのリクエスト入力を一度に作成し、送信の準備ができるまでメモリに保持する方が効率的な場合があります。これを試すには、リクエスト入力の作成を `async` ブロックの外側に移動します。

 また、転送中のリクエストを `CONCURRENCY_LIMIT` に制限することにより、一度に大量のリクエストが送信される問題を修正しました。

**注記**  
 `CONCURRENCY_LIMIT` の適切な値はプロジェクトごとに異なります。独自のリクエストを作成して送信する場合は、スロットリングエラーが発生しないように、できるだけ高く設定してください。サービスが返す成功レスポンスとスロットリングレスポンスの比率に基づいて、同時実行の制限を動的に更新することは可能ですが、その複雑さのため、このガイドでは範囲外となります。

## 例をより効率的に書き直す (マルチスレッド同時実行)
<a name="conc-multiThread"></a>

 前の 2 つの例では、リクエストを同時に実行しました。これは同期的に実行するよりも効率的ですが、マルチスレッドを使用することにより、さらに効率が高まります。`tokio` でこれを行うには、それらを別々のタスクとして生成する必要があります。

**注記**  
 この例では、マルチスレッド `tokio` ランタイムを使用する必要があります。このランタイムは `rt-multi-thread` 機能の背後で制限されます。また、当然ですが、マルチコアマシンでプログラムを実行する必要があります。

次のコマンドを実行して、プロジェクトに新しい依存関係を追加します。
+ `cargo add tokio --features=rt-multi-thread`

```
// Set this based on the amount of cores your target machine has.
const THREADS: usize = 8; 

#[tokio::main(flavor = "multi_thread")]
async fn main() {
    let (client, objects_to_download) =
        build_client_and_list_objects_to_download().await;
    let concurrency_semaphore = Arc::new(Semaphore::new(THREADS));

    let get_object_task_handles = objects_to_download.into_iter().map(|object| {
        // Since each future needs to acquire a permit, we need to clone
        // the Arc'd semaphore before passing it in.
        let semaphore = concurrency_semaphore.clone();
        // We also need to clone the client so each task has its own handle.
        let client = client.clone();
        
        // Note this difference! We're using `tokio::task::spawn` to
        // immediately begin running these requests.
        tokio::task::spawn(async move {
            let permit = semaphore
                .acquire()
                .await
                .expect("we'll get a permit if we wait long enough");
            let res = client
                .get_object()
                .key(&object)
                .bucket(EXAMPLE_BUCKET)
                .send()
                .await
                .expect("get_object succeeds");
            let body = res.body.collect().await.expect("body succeeds").into_bytes();
            tokio::fs::write(object, body).await.expect("write succeeds");
            std::mem::drop(permit);
        })
    });

    futures_util::future::join_all(get_object_task_handles).await;
}
```

作業をタスクに分割するのは複雑な場合があります。通常、I/O (*入力/出力*) の実行はブロックされます。ランタイムは、長時間実行されるタスクのニーズと短時間実行されるタスクのニーズのバランスをとるのが難しい場合があります。どのランタイムを選択する場合でも、作業をタスクに分割する最も効率的な方法についての推奨事項を必ず確認してください。`tokio` ランタイムの推奨事項については、「[モジュール `tokio::task`](https://docs.rs/tokio/latest/tokio/task/index.html)」を参照してください。

## マルチスレッドアプリケーションのデバッグ
<a name="conc-debug"></a>

同時に実行されるタスクは、任意の順序で実行できます。そのため、同時プログラムのログは非常に読みにくい場合があります。SDK for Rust では、`tracing` ロギングシステムを使用することをお勧めします。実行中であっても、ログを特定のタスクでグループ化できます。ガイダンスについては、「[AWS SDK for Rust でのログ記録の設定と使用](logging.md)」を参照してください。

ロックされたタスクを特定するための非常に便利なツールとして [https://github.com/tokio-rs/console](https://github.com/tokio-rs/console) があります。これは、非同期 Rust プログラムの診断およびデバッグツールです。プログラムを実装して実行し、次に `tokio-console` アプリケーションを実行すると、プログラムが実行されているタスクのライブビューが表示されます。このビューには、タスクが共有リソースの取得を待機するのにかかった時間や、ポーリングされた時間などの有用な情報が含まれています。

# AWS SDK for Rust で Lambda 関数を作成する
<a name="lambda"></a>

AWS SDK for Rust を使用した AWS Lambda 関数の開発に関する詳細なドキュメントについては、「*AWS Lambda デベロッパーガイド*」の「[Rust で Lambda 関数を構築する](https://docs.aws.amazon.com/lambda/latest/dg/lambda-rust.html)」を参照してください。このドキュメントでは、以下のものを使用する方法について説明します。
+ コア機能の Rust Lambda ランタイムクライアントクレート、[https://github.com/awslabs/aws-lambda-rust-runtime](https://github.com/awslabs/aws-lambda-rust-runtime)。
+ [Cargo Lambda](https://www.cargo-lambda.info/guide/what-is-cargo-lambda.html) を使用して Rust 関数バイナリを Lambda にデプロイするための推奨コマンドラインツール。

「*AWS Lambda デベロッパーガイド*」に記載されているガイド付き例に加え、GitHub の「[AWS SDK コード例リポジトリ](https://github.com/awsdocs/aws-doc-sdk-examples/tree/main/rustv1/lambda)」には Lambda 計算ツールの例も用意されています。

# AWS SDK for Rust を使用した署名付き URL の作成
<a name="presigned-urls"></a>

 一部の AWS API オペレーションのリクエストに事前署名することができるため、あとでそのリクエストを別の発信者が自身の認証情報を提示せずに利用できます。

 例えば、Jane に Amazon Simple Storage Service (Amazon S3) オブジェクトへのアクセス権限があり、そのオブジェクトへのアクセス権限を一時的に Alejandro と共有するとします。Jane は、署名付き `GetObject` リクエストを生成して Alejandro と共有できます。これにより、Jane の認証情報へのアクセスや独自の認証情報の取得を必要とせずに、オブジェクトをダウンロードできます。署名付き URL で使用される認証情報は Jane のものです。これは、URL を生成した AWS ユーザーが Jane であるためです。

Amazon S3 における署名付き URL の詳細については、「*Amazon Simple Storage Service ユーザーガイド*」の「[署名付き URL の使用](https://docs.aws.amazon.com/AmazonS3/latest/userguide/using-presigned-url.html)」を参照してください。

## 事前署名の基本
<a name="presign-basics"></a>

 AWS SDK for Rust は、署名付きリクエストを取得するために使用できるオペレーション fluent-builder の `presigned()` メソッドを提供します。

 次の例では、Amazon S3 の署名付きの `GetObject` リクエストを作成します。このリクエストは、作成後 5 分間有効です。

```
use std::time::Duration;
use aws_config::BehaviorVersion;
use aws_sdk_s3::presigning::PresigningConfig;

let config = aws_config::defaults(BehaviorVersion::latest())
    .load()
    .await;

let s3 = aws_sdk_s3::Client::new(&config);

let presigned = s3.get_object()
    .presigned(
        PresigningConfig::builder()
            .expires_in(Duration::from_secs(60 * 5))
            .build()
            .expect("less than one week")
    )
    .await?;
```

 `presigned()` メソッドは `Result<PresignedRequest, SdkError<E, R>>` を返します。

返される `PresignedRequest` には、メソッド、URI、ヘッダーを含む HTTP リクエストのコンポーネントを取得するメソッドが含まれています。リクエストを有効にするには (存在する場合)、これらのすべてがサービスに送信されている必要があります。ただし、署名付きリクエストの多くは URI のみで表すことができます。

## `POST` および `PUT` リクエストの事前署名
<a name="presign-post-put"></a>

 事前署名可能なオペレーションの多くは URL のみを必要とし、HTTP `GET` リクエストとして送信する必要があります。ただし、一部のオペレーションには本文が必要で、場合によってはヘッダーと共に HTTP `POST` または HTTP `PUT` リクエストとして送信する必要があります。これらのリクエストの事前署名は `GET` リクエストの事前署名と同じですが、署名付きリクエストの呼び出しはさらに複雑です。

 以下は、Amazon S3 `PutObject` リクエストに事前署名して、選択した HTTP クライアントを使用して送信可能な [https://docs.rs/http/latest/http/request/struct.Request.html](https://docs.rs/http/latest/http/request/struct.Request.html) に変換する例です。

`into_http_1x_request()` メソッドを使用するには、`Cargo.toml` ファイルの `aws-sdk-s3` クレートに `http-1x` 機能を追加します。

```
aws-sdk-s3 = { version = "1", features = ["http-1x"] }
```

ソースファイル:

```
let presigned = s3.put_object()
    .presigned(
        PresigningConfig::builder()
            .expires_in(Duration::from_secs(60 * 5))
            .build()
            .expect("less than one week")
    )
    .await?;


let body = "Hello AWS SDK for Rust";
let http_req = presigned.into_http_1x_request(body);
```

## スタンドアロン署名者
<a name="standalone-signer"></a>

**注記**  
これは高度なユースケースです。ほとんどのユーザーには不要または推奨されません。

一部のユースケースでは、SDK for Rust コンテキストの外部で、署名付きリクエストを作成する必要があります。そのため、SDK とは別に [https://docs.rs/aws-sigv4/latest/aws_sigv4/index.html](https://docs.rs/aws-sigv4/latest/aws_sigv4/index.html) クレートを使用できます。

 以下の例は、基本的な要素を示しています。詳細については、クレートのドキュメントを参照してください。

以下は、`aws-sigv4` および `http` クレートを `Cargo.toml` ファイルに追加しています。

```
[dependencies]
aws-sigv4 = "1"
http = "1"
```

ソースファイル:

```
use aws_smithy_runtime_api::client::identity::Identity;
use aws_sigv4::http_request::{sign, SigningSettings, SigningParams, SignableRequest};
use aws_sigv4::sign::v4;
use std::time::SystemTime;

// Set up information and settings for the signing.
// You can obtain credentials from `SdkConfig`.
let identity = Credentials::new(
    "AKIDEXAMPLE",
    "wJalrXUtnFEMI/K7MDENG+bPxRfiCYEXAMPLEKEY",
    None,
    None,
    "hardcoded-credentials").into();

let settings = SigningSettings::default();

let params = v4::SigningParams::builder()
    .identity(&identity)
    .region("us-east-1")
    .name("service")
    .time(SystemTime::now())
    .settings(settings)
    .build()?
    .into();

// Convert the HTTP request into a signable request.
let signable = SignableRequest::new(
    "GET",
    "https://some-endpoint.some-region.amazonaws.com",
    std::iter::empty(),
    SignableBody::UnsignedPayload
)?;

// Sign and then apply the signature to the request.
let (signing_instructions, _signature) = sign(signable, &params)?.into_parts();

let mut my_req = http::Request::new("...");
signing_instructions.apply_to_request_http1x(&mut my_req);
```

# AWS SDK for Rust でのエラーの処理
<a name="error-handling"></a>

SDK を使用して高品質のアプリケーションを構築するには、 がエラーをいつどのように AWS SDK for Rust 返すかを理解することが重要です。以下のセクションでは、SDK で発生する可能性のあるさまざまなエラーと、その適切な処理方法について説明します。

すべてのオペレーションは、エラータイプが [https://docs.rs/aws-smithy-runtime-api/latest/aws_smithy_runtime_api/client/result/enum.SdkError.html](https://docs.rs/aws-smithy-runtime-api/latest/aws_smithy_runtime_api/client/result/enum.SdkError.html) に設定された `Result` タイプを返します。`SdkError` は、バリアントと呼ばれるいくつかの可能なタイプを持つ列挙型です。

## サービスエラー
<a name="serviceErrors"></a>

最も一般的なエラーのタイプは [https://docs.rs/aws-smithy-runtime-api/latest/aws_smithy_runtime_api/client/result/enum.SdkError.html#variant.ServiceError](https://docs.rs/aws-smithy-runtime-api/latest/aws_smithy_runtime_api/client/result/enum.SdkError.html#variant.ServiceError) です。このエラーは、 AWS のサービスからのエラーレスポンスを表します。例えば、存在しないオブジェクトを Amazon S3 から取得しようとすると、Amazon S3 はエラーレスポンスを返します。

が発生する`SdkError::ServiceError`と、リクエストは に正常に送信されました AWS のサービス が、処理できなかったことを意味します。これは、リクエストのパラメータに含まれるエラーまたはサービス側の問題が原因です。

 エラーレスポンスの詳細は、エラーバリアントに含まれています。次の例は、基盤となる `ServiceError` バリアントを簡単に取得し、さまざまなエラーケースを処理する方法を示しています。

```
// Needed to access the '.code()' function on the error type:
use aws_sdk_s3::error::ProvideErrorMetadata;

let result = s3.get_object()
    .bucket("my-bucket")
    .key("my-key")
    .send()
    .await;

match result {
    Ok(_output) => { /* Success. 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.code() returns the raw error code from the service and can be 
        //     used as a last resort for handling unmodeled service errors. 
        err if err.code() == Some("SomeUnmodeledError") => {}
        err => return Err(err.into())
    }
};
```

## エラーメタデータ
<a name="errorMetadata"></a>

 すべてのサービスエラーには、サービス固有のトレイトをインポートすることによりアクセスできる追加のメタデータがあります。
+ `<service>::error::ProvideErrorMetadata` トレイトは、サービスから返される利用可能な基盤となる生のエラーコードとエラーメッセージへのアクセスを提供します。
  + Amazon S3 の場合、このトレイトは [https://docs.rs/aws-sdk-s3/latest/aws_sdk_s3/error/trait.ProvideErrorMetadata.html](https://docs.rs/aws-sdk-s3/latest/aws_sdk_s3/error/trait.ProvideErrorMetadata.html) です。

以下のように、サービスエラーのトラブルシューティングに役立つ情報を取得することもできます。
+ `<service>::operation::RequestId` 特性は、サービスによって生成された一意の AWS リクエスト ID を取得するための拡張メソッドを追加します。
  + Amazon S3 の場合、このトレイトは [https://docs.rs/aws-sdk-s3/latest/aws_sdk_s3/operation/trait.RequestId.html](https://docs.rs/aws-sdk-s3/latest/aws_sdk_s3/operation/trait.RequestId.html) です。
+ `<service>::operation::RequestIdExt` トレイトによって `extended_request_id()` メソッドを追加して、追加の拡張リクエスト ID を取得します。
  + 一部のサービスでのみサポートされています。
  +  Amazon S3 の場合、このトレイトは [https://docs.rs/aws-sdk-s3/latest/aws_sdk_s3/operation/trait.RequestIdExt.html](https://docs.rs/aws-sdk-s3/latest/aws_sdk_s3/operation/trait.RequestIdExt.html) です。

## `DisplayErrorContext` による詳細なエラー出力
<a name="displayErrorContext"></a>

 SDK におけるエラーは、通常次のような一連の障害の結果として発生します。

1. コネクタによってエラーを返したため、リクエストのディスパッチに失敗しました。

1. 認証情報プロバイダーによってエラーを返したため、コネクタはエラーを返しました。

1. 認証情報プロバイダーによってサービスを呼び出し、そのサービスがエラーを返したため、エラーが返されました。

1. 認証情報リクエストに正しい認可がないため、サービスからエラーが返されました。

デフォルトでは、このエラーの表示は「ディスパッチ失敗」のみを出力します。これには、エラーのトラブルシューティングに役立つ詳細情報が不足しています。SDK for Rust には、`DisplayErrorContext` というシンプルなエラーレポーターが用意されています。
+  `<service>::error::DisplayErrorContext` 構造体は、完全なエラーコンテキストを出力する機能を追加します。
  + Amazon S3 の場合、この構造体は [https://docs.rs/aws-sdk-s3/latest/aws_sdk_s3/error/struct.DisplayErrorContext.html](https://docs.rs/aws-sdk-s3/latest/aws_sdk_s3/error/struct.DisplayErrorContext.html) です。

エラーを表示して出力するようにまとめると、`DisplayErrorContext` は次のようなより詳細なメッセージを表示します。

```
dispatch failure: other: Session token not found or invalid.
DispatchFailure(
    DispatchFailure { 
        source: ConnectorError { 
            kind: Other(None), 
            source: ProviderError(
                ProviderError { 
                    source: ProviderError(
                        ProviderError { 
                            source: ServiceError(
                                ServiceError { 
                                    source: UnauthorizedException(
                                        UnauthorizedException { 
                                            message: Some("Session token not found or invalid"), 
                                            meta: ErrorMetadata { 
                                                code: Some("UnauthorizedException"), 
                                                message: Some("Session token not found or invalid"), 
                                                extras: Some({"aws_request_id": "1b6d7476-f5ec-4a16-9890-7684ccee7d01"})
                                            } 
                                        }
                                    ), 
                                    raw: Response {
                                        status: StatusCode(401), 
                                        headers: Headers {
                                            headers: {
                                                "date": HeaderValue { _private: H0("Thu, 04 Jul 2024 07:41:21 GMT") }, 
                                                "content-type": HeaderValue { _private: H0("application/json") }, 
                                                "content-length": HeaderValue { _private: H0("114") }, 
                                                "access-control-expose-headers": HeaderValue { _private: H0("RequestId") }, 
                                                "access-control-expose-headers": HeaderValue { _private: H0("x-amzn-RequestId") }, 
                                                "requestid": HeaderValue { _private: H0("1b6d7476-f5ec-4a16-9890-7684ccee7d01") }, 
                                                "server": HeaderValue { _private: H0("AWS SSO") }, 
                                                "x-amzn-requestid": HeaderValue { _private: H0("1b6d7476-f5ec-4a16-9890-7684ccee7d01") }
                                            } 
                                        }, 
                                        body: SdkBody {
                                            inner: Once(
                                                Some(
                                                    b"{
                                                        \"message\":\"Session token not found or invalid\",
                                                        \"__type\":\"com.amazonaws.switchboard.portal#UnauthorizedException\"}"
                                                    )
                                                ), 
                                            retryable: true 
                                        }, 
                                        extensions: Extensions {
                                            extensions_02x: Extensions, 
                                            extensions_1x: Extensions 
                                        }
                                    } 
                                }
                            ) 
                        }
                    ) 
                }
            ), 
            connection: Unknown 
        } 
    }
)
```

# AWS SDK for Rust でページ分割された結果を使用する
<a name="paginating"></a>

多くの AWS オペレーションでは、ペイロードが大きすぎて 1 回の応答では返せない場合に、結果を一部切り捨てて返します。代わりに、サービスはデータの一部とトークンを返し、次の項目のセットを取得します。このパターンは、ページ分割と呼ばれています。

AWS SDK for Rust には、自動的に結果をページ分割するために使用できるオペレーションビルダーの拡張機能メソッド `into_paginator` が含まれています。ユーザーは、この結果を処理するコードを記述するだけで済みます。すべてのページ分割オペレーションビルダーには `into_paginator()` メソッドがあり、結果をページ分割するために [https://docs.rs/aws-smithy-async/latest/aws_smithy_async/future/pagination_stream/struct.PaginationStream.html](https://docs.rs/aws-smithy-async/latest/aws_smithy_async/future/pagination_stream/struct.PaginationStream.html) を公開します。
+ Amazon S3 では、その一例として [https://docs.rs/aws-sdk-s3/latest/aws_sdk_s3/operation/list_objects_v2/builders/struct.ListObjectsV2FluentBuilder.html#method.into_paginator](https://docs.rs/aws-sdk-s3/latest/aws_sdk_s3/operation/list_objects_v2/builders/struct.ListObjectsV2FluentBuilder.html#method.into_paginator) があります。

次の例では、Amazon Simple Storage Service を使用しています。ただし、ページ分割された API を 1 つ以上持つサービスについては同じ概念です。

 次のコード例は、[https://docs.rs/aws-smithy-async/latest/aws_smithy_async/future/pagination_stream/struct.PaginationStream.html#method.try_collect](https://docs.rs/aws-smithy-async/latest/aws_smithy_async/future/pagination_stream/struct.PaginationStream.html#method.try_collect) メソッドを使用してページ分割されたすべての結果を `Vec` に収集する最も単純な例を示しています。

```
let config = aws_config::defaults(BehaviorVersion::latest())
    .load()
    .await;

let s3 = aws_sdk_s3::Client::new(&config);

let all_objects = s3.list_objects_v2()
    .bucket("my-bucket")
    .into_paginator()
    .send()
    .try_collect()
    .await?
    .into_iter()
    .flat_map(|o| o.contents.unwrap_or_default())
    .collect::<Vec<_>>();
```

すべてが一度にメモリに取り込まれないように、ページングをより細かく制御する場合があります。次の例では、Amazon S3 バケット内のオブジェクトがなくなるまで反復処理しています。

```
let config = aws_config::defaults(BehaviorVersion::latest())
    .load()
    .await;

let s3 = aws_sdk_s3::Client::new(&config);

let mut paginator = s3.list_objects_v2()
    .bucket("my-bucket")
    .into_paginator()
    // customize the page size (max results per/response)
    .page_size(10)
    .send();

println!("Objects in bucket:");

while let Some(result) = paginator.next().await {
    let resp = result?;
    for obj in resp.contents() {
        println!("\t{:?}", obj);
    }
}
```

# AWS SDK for Rust アプリケーションへのユニットテストの追加
<a name="testing"></a>

AWS SDK for Rust プロジェクトにユニットテストを実装する方法は多数ありますが、以下の方法をお勧めします。
+ [`mockall` を使用したユニットテスト](testing-automock.md) – `automock` を使用して、テストを `mockall` クレートから自動的に生成して実行します。
+ [静的リプレイ](testing-replay.md) - AWS Smithy ランタイムの `StaticReplayClient` を使用して、AWS のサービスが通常使用する標準 HTTP クライアントの代わりに使用できるフェイク HTTP クライアントを作成する方法について説明します。このクライアントは、ネットワーク経由でサービスと通信する代わりに、指定した HTTP レスポンスを返します。これにより、テストはテスト目的で既知のデータを取得します。
+ [`aws-smithy-mocks` を使用したユニットテスト](testing-smithy-mocks.md) – `mock` と `mock_client` を使用して、`aws-smithy-mocks` クレートから AWS SDK クライアントレスポンスのモックを作成し、SDK が特定のリクエストにどのように応答するかを定義するモックルールを作成します。

# AWS SDK for Rust `mockall` で を使用してモックを自動的に生成する
<a name="testing-automock"></a>

 AWS SDK for Rust は、 とやり取りするコードをテストするための複数のアプローチを提供します AWS のサービス。`[mockall](https://docs.rs/mockall/latest/mockall)` クレートの一般的な `[automock](https://docs.rs/mockall/latest/mockall/attr.automock.html)` を使用することにより、テストに必要なモック実装の大部分を自動的に生成できます。

この例では、`determine_prefix_file_size()` というカスタムメソッドをテストします。このメソッドは、Amazon S3 を呼び出すカスタム `list_objects()` ラッパーメソッドを呼び出します。`list_objects()` のモックを作成することにより、Amazon S3 と実際にやり取りすることなく `determine_prefix_file_size()` メソッドをテストできます。

1. プロジェクトディレクトリのコマンドプロンプトで、`[mockall](https://docs.rs/mockall/latest/mockall)` クレートを次の依存関係として追加します。

   ```
   $ cargo add --dev mockall
   ```

   `--dev` [オプション](https://doc.rust-lang.org/cargo/commands/cargo-add.html)を使用すると、`Cargo.toml` ファイルの `[dev-dependencies]` セクションにクレートが追加されます。[開発の依存関係](https://doc.rust-lang.org/cargo/reference/specifying-dependencies.html#development-dependencies)として扱われるため、これはコンパイルされず、本番コードで使用される最終バイナリには含まれません。

   また、このサンプルコードは、 AWS のサービスの例として Amazon Simple Storage Service を使用します。

   ```
   $ cargo add aws-sdk-s3
   ```

   これにより、`[dependencies]` ファイルの `Cargo.toml` セクションにクレートが追加されます。

1. `mockall` クレートから `automock` モジュールを含めます。

   テスト AWS のサービス する に関連する他のライブラリ、この場合は Amazon S3 も含めます。

   ```
   use aws_sdk_s3 as s3;
   #[allow(unused_imports)]
   use mockall::automock;
   
   use s3::operation::list_objects_v2::{ListObjectsV2Error, ListObjectsV2Output};
   ```

1. 次に、アプリケーションの Amazon S3 ラッパー構造の 2 つの実装のうち、どちらを使用するかを決定するコードを追加します。
   + ネットワーク経由で Amazon S3 にアクセスするために実際に記述された実装。
   + `mockall` によって生成されたモック実装。

   この例では、選択した実装に `S3` という名前が付けられています。選択したものは、以下のように `test` 属性に基づく条件が付いています。

   ```
   #[cfg(test)]
   pub use MockS3Impl as S3;
   #[cfg(not(test))]
   pub use S3Impl as S3;
   ```

1. `S3Impl` 構造体は、 が実際にリクエストを送信する Amazon S3 ラッパー構造の実装です AWS。
   + テストが有効になっている場合、リクエストは AWSではなくモックに送信されるため、このコードは使用されません。`dead_code` 属性は、`S3Impl` タイプが使用されていない場合は問題を報告しないように linter に指示します。
   +  この条件付き `#[cfg_attr(test, automock)]` は、テストが有効になっている場合、`automock` 属性を設定する必要があることを示します。これにより、`MockS3Impl` という名前の `S3Impl` のモックを生成するように `mockall` に指示します。
   + この例では、`list_objects()` メソッドはモックを作成する呼び出しです。`automock` は自動的に `expect_list_objects()` メソッドを作成します。

   ```
   #[allow(dead_code)]
   pub struct S3Impl {
       inner: s3::Client,
   }
   
   #[cfg_attr(test, automock)]
   impl S3Impl {
       #[allow(dead_code)]
       pub fn new(inner: s3::Client) -> Self {
           Self { inner }
       }
   
       #[allow(dead_code)]
       pub async fn list_objects(
           &self,
           bucket: &str,
           prefix: &str,
           continuation_token: Option<String>,
       ) -> Result<ListObjectsV2Output, s3::error::SdkError<ListObjectsV2Error>> {
           self.inner
               .list_objects_v2()
               .bucket(bucket)
               .prefix(prefix)
               .set_continuation_token(continuation_token)
               .send()
               .await
       }
   }
   ```

1. `test` という名前のモジュールでテスト関数を作成します。
   + この条件付き `#[cfg(test)]` は、`test` 属性が `true` の場合、`mockall` によってテストモジュールを構築する必要があることを示します。

   ```
   #[cfg(test)]
   mod test {
       use super::*;
       use mockall::predicate::eq;
   
       #[tokio::test]
       async fn test_single_page() {
           let mut mock = MockS3Impl::default();
           mock.expect_list_objects()
               .with(eq("test-bucket"), eq("test-prefix"), eq(None))
               .return_once(|_, _, _| {
                   Ok(ListObjectsV2Output::builder()
                       .set_contents(Some(vec![
                           // Mock content for ListObjectsV2 response
                           s3::types::Object::builder().size(5).build(),
                           s3::types::Object::builder().size(2).build(),
                       ]))
                       .build())
               });
   
           // Run the code we want to test with it
           let size = determine_prefix_file_size(mock, "test-bucket", "test-prefix")
               .await
               .unwrap();
   
           // Verify we got the correct total size back
           assert_eq!(7, size);
       }
   
       #[tokio::test]
       async fn test_multiple_pages() {
           // Create the Mock instance with two pages of objects now
           let mut mock = MockS3Impl::default();
           mock.expect_list_objects()
               .with(eq("test-bucket"), eq("test-prefix"), eq(None))
               .return_once(|_, _, _| {
                   Ok(ListObjectsV2Output::builder()
                       .set_contents(Some(vec![
                           // Mock content for ListObjectsV2 response
                           s3::types::Object::builder().size(5).build(),
                           s3::types::Object::builder().size(2).build(),
                       ]))
                       .set_next_continuation_token(Some("next".to_string()))
                       .build())
               });
           mock.expect_list_objects()
               .with(
                   eq("test-bucket"),
                   eq("test-prefix"),
                   eq(Some("next".to_string())),
               )
               .return_once(|_, _, _| {
                   Ok(ListObjectsV2Output::builder()
                       .set_contents(Some(vec![
                           // Mock content for ListObjectsV2 response
                           s3::types::Object::builder().size(3).build(),
                           s3::types::Object::builder().size(9).build(),
                       ]))
                       .build())
               });
   
           // Run the code we want to test with it
           let size = determine_prefix_file_size(mock, "test-bucket", "test-prefix")
               .await
               .unwrap();
   
           assert_eq!(19, size);
       }
   }
   ```
   + 各テストでは、`let mut mock = MockS3Impl::default();` を使用して `MockS3Impl` の `mock` インスタンスを作成します。
   + モックの `expect_list_objects()` メソッド (`automock` によって自動的に作成された) を使用して、`list_objects()` メソッドがコードの他の場所で使用された場合に想定される結果を設定します。
   + 想定値が設定されたら、これらを使用して `determine_prefix_file_size()` を呼び出して関数をテストします。返された値は、アサーションを使用して、正しいことを確認するために検証します。

1. `determine_prefix_file_size()` 関数は、Amazon S3 ラッパーを使用してプレフィックスファイルのサイズを取得します。

   ```
   #[allow(dead_code)]
   pub async fn determine_prefix_file_size(
       // Now we take a reference to our trait object instead of the S3 client
       // s3_list: ListObjectsService,
       s3_list: S3,
       bucket: &str,
       prefix: &str,
   ) -> Result<usize, s3::Error> {
       let mut next_token: Option<String> = None;
       let mut total_size_bytes = 0;
       loop {
           let result = s3_list
               .list_objects(bucket, prefix, next_token.take())
               .await?;
   
           // Add up the file sizes we got back
           for object in result.contents() {
               total_size_bytes += object.size().unwrap_or(0) as usize;
           }
   
           // Handle pagination, and break the loop if there are no more pages
           next_token = result.next_continuation_token.clone();
           if next_token.is_none() {
               break;
           }
       }
       Ok(total_size_bytes)
   }
   ```

`S3` 型は、HTTP リクエストを行うときに、ラップされた SDK for Rust 関数を呼び出して `S3Impl` と `MockS3Impl` の両方をサポートするために使用されます。テストが有効になっている場合、`mockall` によって自動的に生成されたモックはテスト失敗を報告します。

GitHub で[これらの例の完全なコードを表示](https://github.com/awsdocs/aws-doc-sdk-examples/tree/main/rustv1/examples/testing)できます。

# AWS SDK for Rust で静的リプレイを使用して HTTP トラフィックをシミュレートする
<a name="testing-replay"></a>

 AWS SDK for Rust は、 とやり取りするコードをテストするための複数のアプローチを提供します AWS のサービス。このトピックでは、 AWS のサービスを使用して、`StaticReplayClient` が通常使用する標準 HTTP クライアントの代わりに使用できる擬似 HTTP クライアントを作成する方法について説明します。このクライアントは、ネットワーク経由でサービスと通信する代わりに、指定した HTTP レスポンスを返します。これにより、テストはテスト目的で既知のデータを取得します。

`aws-smithy-http-client` クレートには、[https://docs.rs/aws-smithy-http-client/latest/aws_smithy_http_client/test_util/struct.StaticReplayClient.html](https://docs.rs/aws-smithy-http-client/latest/aws_smithy_http_client/test_util/struct.StaticReplayClient.html) というテストユーティリティクラスが含まれています。この HTTP クライアントクラスは、 AWS のサービス オブジェクトの作成時にデフォルトの HTTP クライアントの代わりに指定できます。

`StaticReplayClient` を初期化する場合は、HTTP リクエストとレスポンスのペアのリストを `ReplayEvent` オブジェクトとして指定します。テストの実行中に、各 HTTP リクエストが記録され、クライアントはイベントリストの次の `ReplayEvent` にある次の HTTP レスポンスを HTTP クライアントのレスポンスとして返します。これにより、既知のデータを使用することにより、ネットワークに接続せずにテストを実行できます。

## 静的リプレイの使用
<a name="testing-replay-steps"></a>

静的リプレイを使用するには、ラッパーを使用する必要はありません。代わりに、テストで使用するデータに対し、実際のネットワークトラフィックがどのような状態かを判断し、SDK が AWS のサービス クライアントからリクエストを発行するたびに、そのトラフィックデータを `StaticReplayClient` に提供します。

**注記**  
予想されるネットワークトラフィックを収集するには、 AWS CLI や多数のネットワークトラフィックアナライザー、パケットスニッファーツールなど、いくつかの方法があります。
+ 予想される HTTP リクエストと、それに対して返されるレスポンスを指定する `ReplayEvent` オブジェクトのリストを作成します。
+ 前のステップで作成した HTTP トランザクションリストを使用して `StaticReplayClient` を作成します。
+  AWS クライアントの設定オブジェクトを作成し、 を`Config`オブジェクトの `StaticReplayClient`として指定します`http_client`。
+ 前のステップで作成した設定を使用して、 AWS のサービス クライアントオブジェクトを作成します。
+ `StaticReplayClient` を使用するように設定されたサービスオブジェクトを使用して、テストするオペレーションを実行します。SDK が API リクエストを送信するたびに AWS、リスト内の次のレスポンスが使用されます。
**注記**  
送信されたリクエストが `ReplayEvent` オブジェクトのベクトルのリクエストと一致しない場合でも、リスト内の次のレスポンスは常に返されます。
+ 必要なすべてのリクエストが実行されたら、`StaticReplayClient.assert_requests_match()` 関数を呼び出して、SDK によって送信されたリクエストが `ReplayEvent` オブジェクトのリスト内のリクエストと一致することを確認します。

## 例
<a name="testing-replay-example"></a>

前の例と同じ `determine_prefix_file_size()` 関数について、モックの代わりに静的リプレイを使用したリストを示します。

1. プロジェクトディレクトリのコマンドプロンプトで、[https://crates.io/crates/aws-smithy-http-client](https://crates.io/crates/aws-smithy-http-client) クレートを次の依存関係として追加します。

   ```
   $ cargo add --dev aws-smithy-http-client --features test-util
   ```

   `--dev` [オプション](https://doc.rust-lang.org/cargo/commands/cargo-add.html)を使用すると、`Cargo.toml` ファイルの `[dev-dependencies]` セクションにクレートが追加されます。[開発の依存関係](https://doc.rust-lang.org/cargo/reference/specifying-dependencies.html#development-dependencies)として扱われるため、これはコンパイルされず、本番コードで使用される最終バイナリには含まれません。

   また、このサンプルコードは、 AWS のサービスの例として Amazon Simple Storage Service を使用します。

   ```
   $ cargo add aws-sdk-s3
   ```

   これにより、`[dependencies]` ファイルの `Cargo.toml` セクションにクレートが追加されます。

1. テストコードモジュールには、必要となる両方のタイプを含めます。

   ```
   use aws_smithy_http_client::test_util::{ReplayEvent, StaticReplayClient};
   use aws_sdk_s3::primitives::SdkBody;
   ```

1. テストでは、テスト中に実行する必要がある各 HTTP トランザクションを表す `ReplayEvent` 構造を作成することから始めます。各イベントには、HTTP リクエストオブジェクトと、 AWS のサービス が通常応答する情報を表す HTTP レスポンスオブジェクトが含まれます。これらのイベントは、次の `StaticReplayClient::new()` への呼び出しに渡されます。

   ```
           let page_1 = ReplayEvent::new(
                   http::Request::builder()
                       .method("GET")
                       .uri("https://test-bucket.s3.us-east-1.amazonaws.com/?list-type=2&prefix=test-prefix")
                       .body(SdkBody::empty())
                       .unwrap(),
                   http::Response::builder()
                       .status(200)
                       .body(SdkBody::from(include_str!("./testing/response_multi_1.xml")))
                       .unwrap(),
               );
           let page_2 = ReplayEvent::new(
                   http::Request::builder()
                       .method("GET")
                       .uri("https://test-bucket.s3.us-east-1.amazonaws.com/?list-type=2&prefix=test-prefix&continuation-token=next")
                       .body(SdkBody::empty())
                       .unwrap(),
                   http::Response::builder()
                       .status(200)
                       .body(SdkBody::from(include_str!("./testing/response_multi_2.xml")))
                       .unwrap(),
               );
           let replay_client = StaticReplayClient::new(vec![page_1, page_2]);
   ```

   結果は `replay_client` に保存されます。これは HTTP クライアントを表しており、SDK for Rust ではクライアントの設定で指定することにより使用できます。

1. Amazon S3 クライアントを作成するには、設定オブジェクトを使用し、クライアントクラスの `from_conf()` 関数を呼び出してクライアントを作成します。

   ```
           let client: s3::Client = s3::Client::from_conf(
               s3::Config::builder()
                   .behavior_version(BehaviorVersion::latest())
                   .credentials_provider(make_s3_test_credentials())
                   .region(s3::config::Region::new("us-east-1"))
                   .http_client(replay_client.clone())
                   .build(),
           );
   ```

   設定オブジェクトはビルダーの `http_client()` メソッドを使用して指定され、認証情報は `credentials_provider()` メソッドを使用して指定されます。認証情報は、擬似の認証情報構造を返す `make_s3_test_credentials()` という関数を使用して作成されます。

   ```
   fn make_s3_test_credentials() -> s3::config::Credentials {
       s3::config::Credentials::new(
           "ATESTCLIENT",
           "astestsecretkey",
           Some("atestsessiontoken".to_string()),
           None,
           "",
       )
   }
   ```

   これらの認証情報は、実際には AWSに送信されないため、有効である必要はありません。

1. テストが必要な関数を呼び出してテストを実行します。この例では、呼び出す関数の名前は `determine_prefix_file_size()` です。最初のパラメータは、リクエストに使用する Amazon S3 クライアントオブジェクトです。したがって、`StaticReplayClient` を使用して作成されたクライアントを指定して、リクエストがネットワーク経由で処理されるのではなく、そのクライアントによって処理されるようにします。

   ```
           let size = determine_prefix_file_size(client, "test-bucket", "test-prefix")
               .await
               .unwrap();
   
           assert_eq!(19, size);
   
           replay_client.assert_requests_match(&[]);
   ```

   `determine_prefix_file_size()` への呼び出しが完了すると、アサートを使用して、返された値が想定値と一致することを確認します。次に、`StaticReplayClient` メソッドの `assert_requests_match()` 関数が呼び出されます。この関数は、記録された HTTP リクエストをスキャンし、それらがリプレイクライアントの作成時に提供された `ReplayEvent` オブジェクトの配列で指定されたものとすべて一致することを確認します。

GitHub で [これらの例の完全なコードを表示](https://github.com/awsdocs/aws-doc-sdk-examples/tree/main/rustv1/examples/testing)できます。

# AWS SDK for Rust `aws-smithy-mocks` での を使用したユニットテスト
<a name="testing-smithy-mocks"></a>

 AWS SDK for Rust は、 とやり取りするコードをテストするための複数のアプローチを提供します AWS のサービス。このトピックでは、テスト目的で AWS SDK クライアントのレスポンスのモックを作成するため、シンプルで強力な方法を提供する [https://docs.rs/aws-smithy-mocks/latest/aws_smithy_mocks/](https://docs.rs/aws-smithy-mocks/latest/aws_smithy_mocks/) クレートを使用する方法について説明します。

## 概要:
<a name="overview-smithy-mock"></a>

が使用するコードのテストを記述する場合 AWS のサービス、多くの場合、実際のネットワーク呼び出しは避けてください。`aws-smithy-mocks` クレートを使用すると、以下を実現するソリューションを提供します。
+ SDK が特定のリクエストにどのように応答するかを定義するモックルールを作成する。
+ さまざまなタイプのレスポンス (成功、エラー、HTTP レスポンス) を返す。
+ プロパティに基づいてリクエストを一致させる。
+ 再試行動作をテストするための応答のシーケンスを定義する。
+ ルールが想定どおりに使用されたことを確認する。

## 依存関係の追加
<a name="dependency-smithy-mock"></a>

プロジェクトディレクトリのコマンドプロンプトで、[https://crates.io/crates/aws-smithy-mocks](https://crates.io/crates/aws-smithy-mocks) クレートを次の依存関係として追加します。

```
$ cargo add --dev aws-smithy-mocks
```

`--dev` [オプション](https://doc.rust-lang.org/cargo/commands/cargo-add.html)を使用すると、`Cargo.toml` ファイルの `[dev-dependencies]` セクションにクレートが追加されます。[開発の依存関係](https://doc.rust-lang.org/cargo/reference/specifying-dependencies.html#development-dependencies)として扱われるため、これはコンパイルされず、本番コードで使用される最終バイナリには含まれません。

このコード例では、Amazon Simple Storage Service も例として使用されており AWS のサービス、機能 が必要です`test-util`。

```
$ cargo add aws-sdk-s3 --features test-util
```

これにより、`[dependencies]` ファイルの `Cargo.toml` セクションにクレートが追加されます。

## 基本的な使用法
<a name="basic-smithy-mocks"></a>

 `aws-smithy-mocks` を使用して Amazon Simple Storage Service (Amazon S3) とやり取りするコードをテストする方法の簡単な例を以下に示します。

```
use aws_sdk_s3::operation::get_object::GetObjectOutput;
use aws_sdk_s3::primitives::ByteStream;
use aws_smithy_mocks::{mock, mock_client};

#[tokio::test]
async fn test_s3_get_object() {
    // Create a rule that returns a successful response
    let get_object_rule = mock!(aws_sdk_s3::Client::get_object)
        .then_output(|| {
            GetObjectOutput::builder()
                .body(ByteStream::from_static(b"test-content"))
                .build()
        });

    // Create a mocked client with the rule
    let s3 = mock_client!(aws_sdk_s3, [&get_object_rule]);

    // Use the client as you would normally
    let result = s3
        .get_object()
        .bucket("test-bucket")
        .key("test-key")
        .send()
        .await
        .expect("success response");

    // Verify the response
    let data = result.body.collect().await.expect("successful read").to_vec();
    assert_eq!(data, b"test-content");

    // Verify the rule was used
    assert_eq!(get_object_rule.num_calls(), 1);
}
```

## モックルールの作成
<a name="creating-rules-smithy-mocks"></a>

ルールは、`mock!` マクロを使用して作成されます。マクロは、クライアントオペレーションを引数として受け取ります。その後、ルールがどのように動作するかを設定できます。

### リクエストの一致
<a name="matching-requests-smithy-mocks"></a>

 リクエストのプロパティを一致させることにより、ルールをより具体化できます。

```
let rule = mock!(Client::get_object)
    .match_requests(|req| req.bucket() == Some("test-bucket") && req.key() == Some("test-key"))
    .then_output(|| {
        GetObjectOutput::builder()
            .body(ByteStream::from_static(b"test-content"))
            .build()
    });
```

### さまざまなレスポンスタイプ
<a name="diff-response-smithy-mocks"></a>

さまざまなタイプのレスポンスを返すことができます。

```
// Return a successful response
let success_rule = mock!(Client::get_object)
    .then_output(|| GetObjectOutput::builder().build());

// Return an error
let error_rule = mock!(Client::get_object)
    .then_error(|| GetObjectError::NoSuchKey(NoSuchKey::builder().build()));

// Return a specific HTTP response
let http_rule = mock!(Client::get_object)
    .then_http_response(|| {
        HttpResponse::new(
            StatusCode::try_from(503).unwrap(),
            SdkBody::from("service unavailable")
        )
    });
```

## 再試行動作のテスト
<a name="testing-retry-behavior-smithy-mocks"></a>

`aws-smithy-mocks` の最も強力な機能の 1 つは、レスポンスのシーケンスを定義して再試行動作をテストできることです。

```
// Create a rule that returns 503 twice, then succeeds
let retry_rule = mock!(aws_sdk_s3::Client::get_object)
    .sequence()
    .http_status(503, None)                          // First call returns 503
    .http_status(503, None)                          // Second call returns 503
    .output(|| GetObjectOutput::builder().build())   // Third call succeeds
    .build();

// With repetition using times()
let retry_rule = mock!(Client::get_object)
    .sequence()
    .http_status(503, None)
    .times(2)                                        // First two calls return 503
    .output(|| GetObjectOutput::builder().build())   // Third call succeeds
    .build();
```

## ルールモード
<a name="rule-modes-smithy-mocks"></a>

ルールモードを使用して、ルールの一致と適用方法を以下のように制御できます。

```
// Sequential mode: Rules are tried in order, and when a rule is exhausted, the next rule is used
let client = mock_client!(aws_sdk_s3, RuleMode::Sequential, [&rule1, &rule2]);

// MatchAny mode: The first matching rule is used, regardless of order
let client = mock_client!(aws_sdk_s3, RuleMode::MatchAny, [&rule1, &rule2]);
```

## 例: 再試行動作のテスト
<a name="example-retry-smithy-mocks"></a>

再試行動作をテストする方法を示す、より完全な例を以下に示します。

```
use aws_sdk_s3::operation::get_object::GetObjectOutput;
use aws_sdk_s3::config::RetryConfig;
use aws_sdk_s3::primitives::ByteStream;
use aws_smithy_mocks::{mock, mock_client, RuleMode};

#[tokio::test]
async fn test_retry_behavior() {
    // Create a rule that returns 503 twice, then succeeds
    let retry_rule = mock!(aws_sdk_s3::Client::get_object)
        .sequence()
        .http_status(503, None)
        .times(2)
        .output(|| GetObjectOutput::builder()
            .body(ByteStream::from_static(b"success"))
            .build())
        .build();

    // Create a mocked client with the rule and custom retry configuration
    let s3 = mock_client!(
        aws_sdk_s3,
        RuleMode::Sequential,
        [&retry_rule],
        |client_builder| {
            client_builder.retry_config(RetryConfig::standard().with_max_attempts(3))
        }
    );

    // This should succeed after two retries
    let result = s3
        .get_object()
        .bucket("test-bucket")
        .key("test-key")
        .send()
        .await
        .expect("success after retries");

    // Verify the response
    let data = result.body.collect().await.expect("successful read").to_vec();
    assert_eq!(data, b"success");

    // Verify all responses were used
    assert_eq!(retry_rule.num_calls(), 3);
}
```

## 例: リクエストパラメータに基づくさまざまなレスポンス
<a name="example-request-param-smithy-mocks"></a>

リクエストパラメータに基づいて、異なるレスポンスを返すルールを作成することもできます。

```
use aws_sdk_s3::operation::get_object::{GetObjectOutput, GetObjectError};
use aws_sdk_s3::types::error::NoSuchKey;
use aws_sdk_s3::Client;
use aws_sdk_s3::primitives::ByteStream;
use aws_smithy_mocks::{mock, mock_client, RuleMode};

#[tokio::test]
async fn test_different_responses() {
    // Create rules for different request parameters
    let exists_rule = mock!(Client::get_object)
        .match_requests(|req| req.bucket() == Some("test-bucket") && req.key() == Some("exists"))
        .sequence()
        .output(|| GetObjectOutput::builder()
            .body(ByteStream::from_static(b"found"))
            .build())
        .build();

    let not_exists_rule = mock!(Client::get_object)
        .match_requests(|req| req.bucket() == Some("test-bucket") && req.key() == Some("not-exists"))
        .sequence()
        .error(|| GetObjectError::NoSuchKey(NoSuchKey::builder().build()))
        .build();

    // Create a mocked client with the rules in MatchAny mode
    let s3 = mock_client!(aws_sdk_s3, RuleMode::MatchAny, [&exists_rule, &not_exists_rule]);

    // Test the "exists" case
    let result1 = s3
        .get_object()
        .bucket("test-bucket")
        .key("exists")
        .send()
        .await
        .expect("object exists");

    let data = result1.body.collect().await.expect("successful read").to_vec();
    assert_eq!(data, b"found");

    // Test the "not-exists" case
    let result2 = s3
        .get_object()
        .bucket("test-bucket")
        .key("not-exists")
        .send()
        .await;

    assert!(result2.is_err());
    assert!(matches!(result2.unwrap_err().into_service_error(),
                    GetObjectError::NoSuchKey(_)));
}
```

## ベストプラクティス
<a name="best-practices-smithy-mocks"></a>

テストに `aws-smithy-mocks` を使用する場合:

1.  特定のリクエストに一致: `match_requests()` を使用して、ルールが意図したリクエスト (特に `RuleMode:::MatchAny`) にのみ適用されるようにします。

1.  ルールの使用状況を確認: `rule.num_calls()` を確認し、ルールが実際に使用されたことを確認します。

1.  テストエラー処理: エラーを返すルールを作成して、コードによって失敗を処理する方法をテストします。

1.  テスト再試行ロジック: レスポンスシーケンスを使用して、コードによってカスタム再試行分類子やその他の再試行動作を正しく処理することを確認します。

1. テストに集中: 1 つのテストですべてを網羅しようとするのではなく、シナリオごとに個別のテストを作成します。

# AWS SDK for Rust でのウェイターの使用
<a name="waiters"></a>

 ウェイターは、クライアント側の抽象化で、リソースが目的の状態に達するまで、またはそのリソースが目的の状態に入らないと判断されるまで、リソースをポーリングします。これは、Amazon Simple Storage Service などの結果整合性のあるサービスや、Amazon Elastic Compute Cloud などのリソースを非同期で作成するサービスを使用する場合の一般的なタスクです。リソースのステータスを定期的にポーリングするロジックを自分で記述するのは、手間がかかりエラーの原因にもなります。ウェイターの目的は、この責任を顧客コードから外し、AWS オペレーションのタイミングに関する深い知識を持つ AWS SDK for Rust に移すことです。

ウェイターのサポートを提供する AWS のサービスには、`<service>::waiters` モジュールが含まれます。
+ `<service>::client::Waiters` トレイトによって、クライアントのウェイターメソッドを提供します。メソッドは `Client` 構造体に対して実装されます。すべてのウェイターメソッドは、`wait_until_<Condition>` の標準的な命名規則に従います。
  + Amazon S3 の場合、このトレイトは [https://docs.rs/aws-sdk-s3/latest/aws_sdk_s3/client/trait.Waiters.html](https://docs.rs/aws-sdk-s3/latest/aws_sdk_s3/client/trait.Waiters.html) です。

次の例では、Amazon S3 を使用しています。ただし、1 つ以上のウェイターが定義されている AWS のサービス については、概念は同じです。

次のコード例は、バケット作成後にその存在を待つためのポーリングロジックを記述する代わりに、ウェイター関数を使用する方法を示しています。

```
use std::time::Duration;
use aws_config::BehaviorVersion;
// Import Waiters trait to get `wait_until_<Condition>` methods on Client.
use aws_sdk_s3::client::Waiters;


let config = aws_config::defaults(BehaviorVersion::latest())
    .load()
    .await;
    
let s3 = aws_sdk_s3::Client::new(&config);

// This initiates creating an S3 bucket and potentially returns before the bucket exists.
s3.create_bucket()
    .bucket("my-bucket")
    .send()
    .await?;

// When this function returns, the bucket either exists or an error is propagated.
s3.wait_until_bucket_exists()
    .bucket("my-bucket")
    .wait(Duration::from_secs(5))
    .await?;

// The bucket now exists.
```

**注記**  
 各待機メソッドは、目的の条件またはエラーに達した際の最終レスポンスで取得するために使用できる `Result<FinalPoll<...>, WaiterError<...>> ` を返します。詳細については、「Rust API ドキュメント」の「[FinalPoll](https://docs.rs/aws-smithy-runtime-api/latest/aws_smithy_runtime_api/client/waiters/struct.FinalPoll.html)」および「[WaiterError](https://docs.rs/aws-smithy-runtime-api/latest/aws_smithy_runtime_api/client/waiters/error/enum.WaiterError.html)」を参照してください。