

Las traducciones son generadas a través de traducción automática. En caso de conflicto entre la traducción y la version original de inglés, prevalecerá la version en inglés.

# Uso de AWS SDK para Rust
<a name="using"></a>

Descubra las formas habituales y recomendadas de utilizar el AWS SDK para Rust para trabajar con los servicios de AWS.

**Topics**
+ [Realización de solicitudes de servicio](make-request.md)
+ [Prácticas recomendadas](best-practices.md)
+ [Concurrency (Simultaneidad)](concurrency.md)
+ [Creación de funciones de Lambda](lambda.md)
+ [Creación de URL prefirmadas](presigned-urls.md)
+ [Gestión de errores](error-handling.md)
+ [Paginación](paginating.md)
+ [Pruebas unitarias](testing.md)
+ [Waiters](waiters.md)

# Realización de Servicio de AWS solicitudes con AWS SDK para Rust
<a name="make-request"></a>

 Para acceder mediante programación a Servicios de AWS, AWS SDK para Rust usa una estructura de cliente para cada Servicio de AWS. Por ejemplo, si una aplicación necesita acceder a Amazon EC2, esta crea una estructura de cliente de Amazon EC2 para interactuar con ese servicio. A continuación, utiliza el cliente de servicio para realizar solicitudes al mismo Servicio de AWS. 

Para realizar una solicitud a un Servicio de AWS, primero debe crear y [configurar](configure.md) un cliente de servicio. Cada Servicio de AWS que utiliza el código tiene su propia caja y su propio tipo dedicado para interactuar con él. El cliente expone un método para cada operación de API expuesta por el servicio. 

 Para interactuar con Servicios de AWS en AWS SDK para Rust, cree un cliente específico para el servicio, utilice sus métodos de API con un encadenamiento fluido de tipo generador y llame a `send()` para ejecutar la solicitud.

El `Client` expone un método para cada operación de API expuesta por el servicio. El valor de retorno de cada uno de estos métodos es un «generador fluido», en el que se añaden diferentes entradas para esa API mediante un encadenamiento de llamadas a funciones similar al de un generador. Después de llamar a los métodos del servicio, llame a `send()` para obtener un [https://doc.rust-lang.org/nightly/core/future/trait.Future.html](https://doc.rust-lang.org/nightly/core/future/trait.Future.html) que dé como resultado una salida correcta o un `SdkError`. Para obtener más información sobre `SdkError`, consulte [Manejo de errores en el AWS SDK de Rust](error-handling.md). 

El siguiente ejemplo muestra una operación básica con Amazon S3 para crear un bucket en la Región de AWS de `us-west-2`: 

```
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;
```

Cada caja de servicios cuenta con módulos adicionales que se utilizan para las entradas de la API, como los siguientes: 
+ El módulo`types` tiene estructuras o enumeraciones para proporcionar información estructurada más compleja.
+  El módulo `primitives` tiene tipos más simples para representar datos, como fechas y horas o bloques binarios.

 Consulte la [documentación de referencia de la API](https://awslabs.github.io/aws-sdk-rust/) de la caja de servicios para obtener información y organización más detalladas de la caja. Por ejemplo, la caja `aws-sdk-s3` de Amazon Simple Storage Service tiene varios [módulos](https://docs.rs/aws-sdk-s3/latest/aws_sdk_s3/#modules). Dos de los cuales son:
+ [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)

# Prácticas recomendadas en el uso de AWS SDK para Rust
<a name="best-practices"></a>

A continuación, se indican las prácticas recomendadas para el uso de AWS SDK para Rust. 

## Reutilización de clientes del SDK siempre que sea posible
<a name="bp-reuseClient"></a>

Según cómo esté construido un cliente del SDK, la creación de un cliente nuevo puede dar lugar a que cada cliente mantenga sus propios grupos de conexiones HTTP, cachés de identidades, etc. Recomendamos compartir un cliente o, al menos, compartir `SdkConfig` para evitar la sobrecarga que supone la costosa creación de recursos. Todos los clientes del SDK implementan `Clone` como una única actualización de recuento de referencias atómico. 

## Configurar los tiempos de espera de la API
<a name="bp-apiTimeouts"></a>

 El SDK proporciona valores predeterminados para algunas opciones de tiempo de espera, como el tiempo de espera de la conexión y los tiempos de espera del socket, pero no para los tiempos de espera de las llamadas a la API ni para los intentos de llamadas individuales a la API. Se recomienda establecer tiempos de espera tanto para el intento individual como para toda la solicitud. Esto garantizará que la aplicación pueda responder rápido a los errores y de forma óptima cuando se produzcan problemas transitorios que puedan provocar que los intentos de solicitud tarden más en completarse o surjan problemas graves de red. 

Para obtener más información sobre la configuración de tiempos de espera de las operaciones, consulte [Configuración de los tiempos de espera en el AWS SDK para Rust](timeouts.md). 

# Simultaneidad en el AWS SDK para Rust
<a name="concurrency"></a>

 AWS SDK para Rust No proporciona control de simultaneidad, pero los usuarios tienen muchas opciones para implementar el suyo propio.

## Términos
<a name="conc-terms"></a>

Los términos relacionados con este tema son fáciles de confundir y algunos términos se han convertido en sinónimos a pesar de que originalmente representaban conceptos distintos. En esta guía, se define lo siguiente:
+  **Tarea**: “unidad de trabajo” que el programa ejecutará hasta su finalización o intentará ejecutarla hasta completarla. 
+  **Computación secuencial**: cuando se ejecutan varias tareas una tras otra. 
+  **Computación simultánea**: cuando se ejecutan varias tareas en períodos de tiempo superpuestos.
+  **Simultaneidad**: capacidad de un equipo para completar varias tareas en un orden arbitrario. 
+  **Multitarea**: capacidad de un equipo para ejecutar varias tareas al mismo tiempo. 
+  **Condición de carrera**: cuando el comportamiento del programa cambia en función del momento en que se inicia una tarea o del tiempo que tarda en procesarla. 
+  **Contención**: conflicto por el acceso a un recurso compartido. Cuando dos o más tareas desean acceder a un recurso al mismo tiempo, ese recurso está “en contención”. 
+  **Interbloqueo**: estado en el que no se puede avanzar más. Suele suceder porque dos tareas desean adquirir los recursos de la otra, pero ninguna de ellas liberará sus recursos hasta que el recurso de la otra esté disponible. Los interbloqueos hacen que un programa deje de responder de manera parcial o total. 

## Ejemplo sencillo
<a name="conc-simple"></a>

Nuestro primer ejemplo es un programa secuencial. En ejemplos posteriores, cambiaremos este código mediante técnicas de simultaneidad. Los ejemplos posteriores reutilizan el mismo método `build_client_and_list_objects_to_download()` y hacen cambios en `main()`. Ejecute el siguiente comando para agregar dependencias al proyecto:
+ `cargo add aws-sdk-s3`
+ `cargo add aws-config tokio --features tokio/full`

El siguiente ejemplo de tarea consiste en descargar todos los archivos de un bucket de Amazon Simple Storage Service:

1.  Comience por enumerar todos los archivos. Guarde las claves en una lista. 

1.  Repase la lista y descargue cada archivo uno por uno. 

```
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");
    }
}
```

**nota**  
 En estos ejemplos, no trataremos los errores y daremos por sentado que el bucket de ejemplo no tiene objetos con claves que parezcan rutas de archivos. Por lo tanto, no abordaremos la creación de directorios anidados.

Debido a la arquitectura de los equipos modernos, podemos reescribir este programa para que sea mucho más eficiente. Lo haremos en un ejemplo posterior, pero primero, veamos algunos conceptos más.

## Propiedad y mutabilidad
<a name="conc-ownership"></a>

Cada valor en Rust tiene un único propietario. Cuando un propietario queda fuera del ámbito de aplicación, también se eliminarán todos los valores que posea. El propietario puede proporcionar una o varias referencias inmutables a un valor **o** a una única referencia mutable. El compilador de Rust se encarga de garantizar que ninguna referencia sobreviva a su propietario.

Se necesita una planificación y un diseño adicionales cuando varias tareas necesitan acceder de forma mutable al mismo recurso. En la computación secuencial, cada tarea puede acceder de forma mutable al mismo recurso sin problemas, ya que se ejecutan una tras otra en una secuencia. Sin embargo, en la computación simultánea, las tareas se pueden ejecutar en cualquier orden y al mismo tiempo. Por lo tanto, debemos hacer más para demostrarle al compilador que es imposible hacer múltiples referencias mutables (o, al menos, que se produzca un fallo si se dan).

La biblioteca estándar de Rust proporciona muchas herramientas para ayudarnos a lograrlo. Para obtener más información sobre estos temas, consulte [Variables y mutabilidad](https://doc.rust-lang.org/book/ch03-01-variables-and-mutability.html) y [Descripción de la propiedad](https://doc.rust-lang.org/book/ch04-00-understanding-ownership.html) en el libro Lenguaje de programación Rust.

## ¡Más términos\$1
<a name="conc-moreTerms"></a>

A continuación se enumeran los “objetos de sincronización”. En conjunto, son las herramientas necesarias para convencer al compilador de que nuestro programa simultáneo no infringirá las reglas de propiedad. 

 [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): un ***p**untero con **r**ecuento de referencias atómico*. Cuando los datos están encapsulados en un `Arc`, se pueden compartir libremente, sin preocuparse de que ningún propietario específico elimine el valor antes de tiempo. En este sentido, la propiedad del valor pasa a ser “compartida”. Los valores dentro de un `Arc` no pueden ser mutables, pero pueden tener [mutabilidad interior](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): garantiza que varios subprocesos esperen a que los demás lleguen a un punto del programa antes de continuar con la ejecución conjunta. 
+ [https://doc.rust-lang.org/std/sync/struct.Condvar.html](https://doc.rust-lang.org/std/sync/struct.Condvar.html): ***variable** de condición **que per**mite* bloquear un subproceso mientras se espera a que ocurra un evento. 
+ [https://doc.rust-lang.org/std/sync/struct.Mutex.html](https://doc.rust-lang.org/std/sync/struct.Mutex.html): ***mecan**ismo de **exclusión mutua*** que garantiza que, como máximo, un subproceso pueda acceder a algunos datos. En términos generales, un bloqueo `Mutex` nunca debe mantenerse en un punto `.await` del código. 

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

Si bien AWS SDKs están pensados para ser `async` independientes del tiempo de ejecución, recomendamos el uso de objetos de `tokio` sincronización para casos específicos.
+ [https://docs.rs/tokio/latest/tokio/sync/struct.Mutex.html](https://docs.rs/tokio/latest/tokio/sync/struct.Mutex.html): similar al `Mutex` de la biblioteca estándar, pero con un coste ligeramente superior. A diferencia del `Mutex` estándar, este se puede mantener en un punto `.await` del código.
+ [https://docs.rs/tokio/latest/tokio/sync/struct.Semaphore.html](https://docs.rs/tokio/latest/tokio/sync/struct.Semaphore.html): variable que se utiliza para controlar el acceso a un recurso común por parte de varias tareas.

## Reescritura de nuestro ejemplo para que sea más eficiente (simultaneidad de un solo subproceso)
<a name="conc_singleThread"></a>

En el siguiente ejemplo modificado, utilizamos [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) para ejecutar **TODAS** las solicitudes `get_object` de forma simultánea. Ejecute el siguiente comando para agregar una nueva dependencia al proyecto.
+ `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;
}
```

 Esta es la forma más sencilla de beneficiarse de la simultaneidad, pero también presenta algunos problemas que pueden no resultar evidentes a primera vista:

1.  Creamos todas las entradas de solicitud al mismo tiempo. Si no tenemos suficiente memoria para almacenar todas las entradas de la `get_object` solicitud, nos encontraremos con un «» out-of-memory error de asignación. 

1.  Al mismo tiempo, creamos y esperamos todos los futuros. Amazon S3 limita las solicitudes si intentamos descargar demasiadas de una sola vez. 

Para solucionar estos dos problemas, debemos limitar la cantidad de solicitudes que enviamos en un momento dado. Lo haremos con un [semáforo](https://docs.rs/tokio/latest/tokio/sync/struct.Semaphore.html) `tokio`:

```
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;
}
```

Hemos solucionado el posible problema de uso de la memoria trasladando la creación de solicitudes al bloque `async`. De esta forma, las solicitudes no se crearán hasta que llegue el momento de enviarlas. 

**nota**  
 Si dispone de memoria suficiente, puede que sea más eficiente crear todas las entradas de solicitud a la vez y mantenerlas en la memoria hasta que estén listas para enviarse. Para intentarlo, traslade la creación de entradas de solicitudes fuera del bloque `async`. 

 También hemos solucionado el problema del envío de demasiadas solicitudes a la vez limitando las solicitudes en tránsito a `CONCURRENCY_LIMIT`. 

**nota**  
 El valor correcto de `CONCURRENCY_LIMIT` es diferente para cada proyecto. Al crear y enviar sus propias solicitudes, intente establecerlo lo más alto que pueda sin que se produzcan errores de limitación. Si bien es posible actualizar de forma dinámica el límite de simultaneidad en función de la proporción entre las respuestas correctas y las limitadas que devuelve un servicio, esto queda fuera del ámbito de esta guía debido a su complejidad. 

## Reescritura de nuestro ejemplo para que sea más eficiente (simultaneidad multiproceso)
<a name="conc-multiThread"></a>

 En los dos ejemplos anteriores, realizamos nuestras solicitudes de forma simultánea. Aunque esto es más eficiente que ejecutarlas de forma sincrónica, podemos hacer que sean aún más eficientes mediante el uso de varios subprocesos. Para hacerlo con `tokio`, tendremos que generarlos como tareas independientes. 

**nota**  
 Este ejemplo requiere que utilice el tiempo de ejecución multiproceso de `tokio`. Este tiempo de ejecución está protegido por la característica `rt-multi-thread`. Y, por supuesto, tendrás que ejecutar su programa en una máquina de varios núcleos. 

Ejecute el siguiente comando para agregar una nueva dependencia al proyecto.
+ `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;
}
```

La división del trabajo en tareas puede resultar compleja. Hacer I/O (*entrada/salida*) suele ser un bloqueo. Los tiempos de ejecución pueden tener dificultades para equilibrar las necesidades de las tareas de larga duración con las de las tareas de corta duración. Sea cual sea el tiempo de ejecución que elija, asegúrese de leer sus recomendaciones para encontrar la forma más eficiente de dividir el trabajo en tareas. Para ver las recomendaciones sobre el tiempo de ejecución `tokio`, consulte [Módulo `tokio::task`](https://docs.rs/tokio/latest/tokio/task/index.html).

## Depuración de aplicaciones con varios subprocesos
<a name="conc-debug"></a>

Las tareas que se ejecutan de forma simultánea se pueden ejecutar en cualquier orden. Por lo tanto, los registros de los programas simultáneos pueden resultar muy difíciles de leer. En el SDK de Rust, recomendamos utilizar el sistema de registro `tracing`. Puede agrupar los registros con sus tareas específicas, sin importar cuándo se ejecuten. Para obtener instrucciones, consulte [Configuración y uso del registro en AWS SDK de Rust](logging.md). 

Una herramienta muy útil para identificar las tareas que se han bloqueado es [https://github.com/tokio-rs/console](https://github.com/tokio-rs/console), una herramienta de diagnóstico y depuración de programas asincrónicos de Rust. Al instrumentar y ejecutar el programa, y luego ejecutar la aplicación `tokio-console`, puede ver una vista en tiempo real de las tareas que está ejecutando el programa. Esta vista incluye información útil, como la cantidad de tiempo que una tarea ha dedicado a esperar para adquirir recursos compartidos o la cantidad de veces que se ha sondeado. 

# Creación de funciones de Lambda en AWS SDK para Rust
<a name="lambda"></a>

Para obtener documentación detallada sobre el desarrollo de funciones AWS Lambda con AWS SDK para Rust, consulte [Creación de funciones de Lambda con Rust](https://docs.aws.amazon.com/lambda/latest/dg/lambda-rust.html) en la *Guía para desarrolladores de AWS Lambda*. Esta documentación le guiará en el uso de:
+ La caja de cliente de tiempo de ejecución de Rust Lambda para las funciones principales, [https://github.com/awslabs/aws-lambda-rust-runtime](https://github.com/awslabs/aws-lambda-rust-runtime). 
+ La herramienta de línea de comandos recomendada para implementar la función binaria de Rust en Lambda con [Cargo Lambda](https://www.cargo-lambda.info/guide/what-is-cargo-lambda.html). 

Además de los ejemplos orientativos que se encuentran en la *Guía para desarrolladores de AWS Lambda*, también hay ejemplos de calculadoras Lambda disponibles en el [repositorio de ejemplos de código de AWS SDK](https://github.com/awsdocs/aws-doc-sdk-examples/tree/main/rustv1/lambda) en GitHub.

# Creación de URL prefirmadas mediante el AWS SDK para Rust
<a name="presigned-urls"></a>

 Puede prefirmar las solicitudes de algunas operaciones de la API de AWS para que otro intermediario pueda utilizar la solicitud más adelante sin tener que presentar sus credenciales. 

 Por ejemplo, supongamos que Alicia tiene acceso a un objeto Amazon Simple Storage Service (Amazon S3) y desea compartir temporalmente el acceso a ese objeto con Alejandro. Alicia puede generar una solicitud de `GetObject` prefirmada para compartir el acceso con Alejandro de forma que él pueda descargar el objeto sin necesidad de acceder a las credenciales de Alicia ni tener las suyas propias. Las credenciales que utiliza la URL prefirmada son las de Alicia, ya que ella es el usuario de AWS que generó la URL.

Para obtener más información sobre las URL prefirmadas en Amazon S3, consulte [Uso de URL prefirmadas](https://docs.aws.amazon.com/AmazonS3/latest/userguide/using-presigned-url.html) en la *Guía del usuario de Amazon Simple Storage Service*.

## Conceptos básicos sobre la firma previa
<a name="presign-basics"></a>

 El AWS SDK para Rust proporciona un método `presigned()` en los generadores de operaciones fluidos que se puede utilizar para obtener una solicitud prefirmada. 

 En el siguiente ejemplo, se crea una solicitud `GetObject` prefirmada para Amazon S3. La solicitud es válida durante 5 minutos después de la creación. 

```
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?;
```

 El método `presigned()` devuelve un `Result<PresignedRequest, SdkError<E, R>>`. 

La `PresignedRequest` que se devuelve contiene métodos para acceder a los componentes de una solicitud HTTP, incluidos el método, el URI y cualquier encabezado. Todos estos deben enviarse al servicio, si están presentes, para que la solicitud sea válida. Sin embargo, muchas solicitudes prefirmadas se pueden representar únicamente mediante el URI. 

## Firma previa de solicitudes `POST` y `PUT`
<a name="presign-post-put"></a>

 Muchas operaciones que se pueden prefirmar solo requieren una URL y deben enviarse como solicitudes `GET` HTTP. Sin embargo, algunas operaciones requieren un cuerpo y, en algunos casos, deben enviarse como una solicitud `POST` HTTP o `PUT` HTTP junto con encabezados. La firma previa de estas solicitudes es idéntica a la de solicitudes `GET`, si bien invocar la solicitud prefirmada es más complicado. 

 El siguiente es un ejemplo de firma previa de una solicitud `PutObject` de Amazon S3 y convertirla en una [https://docs.rs/http/latest/http/request/struct.Request.html](https://docs.rs/http/latest/http/request/struct.Request.html) que pueda enviarse mediante el cliente HTTP que elija. 

Para usar el método `into_http_1x_request()`, agregue la característica `http-1x` a la caja `aws-sdk-s3` del archivo `Cargo.toml`:

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

Archivo fuente:

```
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);
```

## Firmante independiente
<a name="standalone-signer"></a>

**nota**  
Este es un caso de uso avanzado. No es necesario ni recomendable para la mayoría de los usuarios.

Hay algunos casos de uso en los que es necesario crear una solicitud firmada fuera del contexto del SDK para Rust. Para ello, puede utilizar la caja [https://docs.rs/aws-sigv4/latest/aws_sigv4/index.html](https://docs.rs/aws-sigv4/latest/aws_sigv4/index.html) de manera independiente del SDK. 

 A continuación se muestra un ejemplo para demostrar los elementos básicos; consulte la documentación de la caja para obtener información más detallada. 

Agregue las cajas `http` y `aws-sigv4` y a su archivo `Cargo.toml`:

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

Archivo fuente:

```
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);
```

# Manejo de errores en el AWS SDK de Rust
<a name="error-handling"></a>

Entender cómo y cuándo se AWS SDK para Rust generan errores es importante para crear aplicaciones de alta calidad con el SDK. En las siguientes secciones se describen los distintos errores que se pueden producir en el SDK y cómo gestionarlos adecuadamente. 

Cada operación devuelve un tipo `Result` con el tipo de error establecido en [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). `SdkError` es una enumeración con varios tipos posibles, denominados variantes.

## Errores de servicio
<a name="serviceErrors"></a>

El tipo de error más común es [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). Este error representa una respuesta de error de un Servicio de AWS. Por ejemplo, si intenta obtener un objeto de Amazon S3 que no existe, Amazon S3 devuelve una respuesta de error.

Si encuentras un error`SdkError::ServiceError`, significa que tu solicitud se envió correctamente Servicio de AWS , pero no se pudo procesar. Esto puede ser debido a errores en los parámetros de la solicitud o a problemas en el servicio. 

 Se incluyen detalles sobre la respuesta del error en la variante de error. El siguiente ejemplo muestra cómo acceder fácilmente a la variante `ServiceError` subyacente y gestionar diferentes casos de error:

```
// 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())
    }
};
```

## Metadatos de error
<a name="errorMetadata"></a>

 Cada error de servicio tiene metadatos adicionales a los que se puede acceder importando características específicas del servicio. 
+ La característica `<service>::error::ProvideErrorMetadata` proporciona acceso a cualquier código de error subyacente sin procesar disponible y a cualquier mensaje de error devuelto por el servicio.
  + En Amazon S3, esta característica es [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). 

También puede obtener información que podría ser útil para solucionar errores de servicio:
+ La `<service>::operation::RequestId` característica agrega métodos de extensión para recuperar el ID de AWS solicitud único que generó el servicio. 
  + Para Amazon S3, esta característica es [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).
+ La característica `<service>::operation::RequestIdExt` agrega el método `extended_request_id()` para obtener un ID de solicitud extendida adicional. 
  + Solo se admite en algunos servicios.
  +  En Amazon S3, este indicador es [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).

## Impresión de errores detallados con `DisplayErrorContext`
<a name="displayErrorContext"></a>

 Los errores del SDK suelen ser el resultado de una cadena de errores, como:

1. No se pudo enviar una solicitud porque el conector devolvió un error.

1. El conector devolvió un error porque el proveedor de credenciales también lo hizo. 

1. El proveedor de credenciales devolvió un error porque llamó a un servicio y este devolvió un error.

1. El servicio devolvió un error porque la solicitud de credenciales no tenía la autorización correcta.

De forma predeterminada, este error solo muestra “dispatch failure”. No proporciona detalles que ayuden a solucionar el error. El SDK para Rust proporciona un simple informante de errores llamado `DisplayErrorContext`.
+  La estructura `<service>::error::DisplayErrorContext` agrega funcionalidad para mostrar el contexto completo del error.
  + En Amazon S3, esta estructura es [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).

Cuando encapsulamos el error que se va a mostrar y lo imprimimos, `DisplayErrorContext` proporciona un mensaje mucho más detallado similar al siguiente:

```
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 
        } 
    }
)
```

# Uso de resultados paginados en AWS SDK para Rust
<a name="paginating"></a>

Muchas operaciones de AWS devuelven resultados truncados cuando la carga útil es demasiado grande para que se devuelva en una sola respuesta. En su lugar, el servicio devuelve una parte de los datos y un token para recuperar el siguiente conjunto de elementos. Este patrón se conoce como paginación. 

AWS SDK para Rust incluye métodos de extensión `into_paginator` en los generadores de operaciones que se pueden utilizar para paginar automáticamente los resultados. Solo tiene que escribir el código que procesa los resultados. Todos los generadores de operaciones de paginación tienen un método `into_paginator()` disponible que expone un [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) para paginar los resultados.
+ Un ejemplo de ello en Amazon S3 es [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). 

En los siguientes ejemplos, se utiliza Amazon Simple Storage Service. Sin embargo, los conceptos son los mismos para cualquier servicio que tenga una o más API paginadas. 

 El siguiente ejemplo de código muestra el ejemplo más simple que utiliza el método [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) para recopilar todos los resultados paginados en un `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<_>>();
```

A veces, es mejor tener un mayor control de la paginación y no guardar todo en la memoria de una sola vez. El siguiente ejemplo itera los objetos en un bucket de Amazon S3 hasta que no haya más.

```
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);
    }
}
```

# Incorporación de pruebas unitarias a la aplicación AWS SDK para Rust
<a name="testing"></a>

Si bien hay muchas maneras de implementar pruebas unitarias en un proyecto AWS SDK para Rust, le recomendamos algunas:
+ [Pruebas unitarias con `mockall`](testing-automock.md): use `automock` de la caja `mockall` para generar y ejecutar automáticamente las pruebas.
+ [Reproducción estática](testing-replay.md): use AWS el `StaticReplayClient` del tiempo de ejecución de Smithy para crear un cliente HTTP falso que pueda usarse en lugar del cliente HTTP estándar que suelen usar los Servicios de AWS. Este cliente devuelve las respuestas HTTP que especifique en lugar de comunicarse con el servicio a través de la red, de modo que las pruebas obtengan datos conocidos para fines de prueba.
+ [Pruebas unitarias con `aws-smithy-mocks`](testing-smithy-mocks.md): use `mock` y `mock_client` de la caja `aws-smithy-mocks` para simular las respuestas del cliente AWS SDK y crear reglas simuladas que definan cómo debe responder el SDK a solicitudes específicas.

# Genera simulaciones automáticamente utilizando `mockall` el AWS SDK para Rust
<a name="testing-automock"></a>

 AWS SDK para Rust Proporciona varios enfoques para probar el código con Servicios de AWS el que interactúa. Puede generar automáticamente la mayoría de las implementaciones simuladas que necesitan las pruebas utilizando el popular `[automock](https://docs.rs/mockall/latest/mockall/attr.automock.html)` de la caja `[mockall](https://docs.rs/mockall/latest/mockall)`.

En este ejemplo se prueba un método personalizado denominado `determine_prefix_file_size()`. Este método llama a un método de contenedor `list_objects()` personalizado que llama a Amazon S3. Al simular `list_objects()`, el método `determine_prefix_file_size()` se puede probar sin contactar con Amazon S3. 

1. En la línea de comandos del directorio del proyecto, agregue la caja `[mockall](https://docs.rs/mockall/latest/mockall)` como dependencia:

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

   Al usar la [opción](https://doc.rust-lang.org/cargo/commands/cargo-add.html) `--dev`, se agrega la caja a la sección `[dev-dependencies]` del archivo `Cargo.toml`. Como [dependencia de desarrollo](https://doc.rust-lang.org/cargo/reference/specifying-dependencies.html#development-dependencies), no se compila ni se incluye en el binario final que se utiliza para el código de producción.

   Este código de ejemplo también utiliza Amazon Simple Storage Service como el Servicio de AWS de ejemplo.

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

   De esta forma, se agrega la caja a la sección `[dependencies]` del archivo `Cargo.toml`.

1. Incluya el módulo `automock` de la caja `mockall`. 

   Incluya también cualquier otra biblioteca relacionada con la Servicio de AWS que esté probando, en este caso, Amazon S3.

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

1. A continuación, agregue el código que determina cuál de las dos implementaciones de la estructura de encapsulador de Amazon S3 de la aplicación debe utilizarse. 
   + La real, escrita para acceder a Amazon S3 a través de la red.
   + La implementación simulada generada por `mockall`.

   En este ejemplo, a la que está seleccionada se le asigna el nombre `S3`. La selección es condicional y se basa en el atributo `test`:

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

1. La `S3Impl` estructura es la implementación de la estructura contenedora de Amazon S3 a la que realmente envía las solicitudes. AWS
   + Cuando las pruebas están habilitadas, este código no se usa porque la solicitud se envía a la simulación y no a AWS. El atributo `dead_code` le indica al linter que no informe de ningún problema si no se utiliza el tipo `S3Impl`.
   +  El `#[cfg_attr(test, automock)]` condicional indica que cuando las pruebas están habilitadas, se debe establecer el atributo `automock`. Esto indica a `mockall` que hay que generar una simulación de `S3Impl` al que se le asignará el nombre `MockS3Impl`.
   + En este ejemplo, el método `list_objects()` es la llamada que quiere que se simule. `automock` creará automáticamente un método `expect_list_objects()` para usted. 

   ```
   #[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. Cree las funciones de prueba en un módulo denominado `test`.
   + El `#[cfg(test)]` condicional indica que `mockall` debe crear el módulo de prueba si el atributo `test` es `true`.

   ```
   #[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);
       }
   }
   ```
   + Cada prueba utiliza `let mut mock = MockS3Impl::default();` para crear una instancia `mock` de `MockS3Impl`. 
   + Usa el método `expect_list_objects()` de la simulación (que `automock` creó automáticamente) para establecer el resultado esperado cuando el método `list_objects()` se usa en otra parte del código.
   + Una vez establecidas las expectativas, las usa para probar la función llamando a `determine_prefix_file_size()`. El valor devuelto se comprueba para confirmar que es correcto, utilizando una aserción.

1. La función `determine_prefix_file_size()` usa el encapsulador de Amazon S3 para obtener el tamaño del archivo de prefijos:

   ```
   #[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)
   }
   ```

El tipo `S3` se usa para llamar a las funciones encapsuladas de SDK para Rust sean compatibles con `S3Impl` y `MockS3Impl` cuando se realizan solicitudes HTTP. La simulación generada automáticamente por `mockall` informa de cualquier error en las pruebas cuando estas están habilitadas.

Puede [ver el código completo de estos ejemplos](https://github.com/awsdocs/aws-doc-sdk-examples/tree/main/rustv1/examples/testing) en. GitHub

# Simule el tráfico HTTP mediante la reproducción estática en el AWS SDK para Rust
<a name="testing-replay"></a>

 AWS SDK para Rust Proporciona varios enfoques para probar el código con Servicios de AWS el que interactúa. En este tema se describe cómo usar el `StaticReplayClient` para crear un cliente HTTP falso que pueda usarse en lugar del cliente HTTP estándar que suelen usar los Servicios de AWS. Este cliente devuelve las respuestas HTTP que especifique en lugar de comunicarse con el servicio a través de la red, de modo que las pruebas obtengan datos conocidos para fines de prueba.

La caja `aws-smithy-http-client` incluye una clase de utilidad de prueba denominada [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). Esta clase de cliente HTTP se puede especificar en lugar del cliente HTTP predeterminado al crear un Servicio de AWS objeto.

Al inicializar el `StaticReplayClient`, se proporciona una lista de pares de solicitudes y respuestas HTTP como objetos `ReplayEvent`. Mientras se ejecuta la prueba, se registra cada solicitud HTTP y el cliente devuelve la siguiente respuesta HTTP, que se encuentra en el siguiente `ReplayEvent` de la lista de eventos como respuesta del cliente HTTP. Esto permite que la prueba se ejecute con datos conocidos y sin conexión de red.

## Uso de reproducción estática
<a name="testing-replay-steps"></a>

Para utilizar la reproducción estática, no es necesario utilizar un encapsulador. En su lugar, determine cómo debe ser el tráfico de red real para los datos que utilizará la prueba y proporcione esos datos de tráfico al `StaticReplayClient` para que los utilice cada vez que el SDK envíe una solicitud desde el cliente del Servicio de AWS .

**nota**  
Hay varias formas de recopilar el tráfico de red esperado, incluidos muchos analizadores de AWS CLI tráfico de red y herramientas de detección de paquetes.
+ Cree una lista de objetos `ReplayEvent` que especifiquen las solicitudes HTTP esperadas y las respuestas que deben devolverse.
+ Cree un `StaticReplayClient` mediante la lista de transacciones HTTP creada en el paso anterior.
+ Cree un objeto de configuración para el AWS cliente, especificando el objeto `StaticReplayClient` como `Config` objeto. `http_client`
+ Cree el objeto de Servicio de AWS cliente mediante la configuración creada en el paso anterior.
+ Realice las operaciones que desee probar utilizando el objeto de servicio que está configurado para utilizar el `StaticReplayClient`. Cada vez que el SDK envía una solicitud de API a AWS, se utiliza la siguiente respuesta de la lista.
**nota**  
Siempre se devuelve la siguiente respuesta de la lista, incluso si la solicitud enviada no coincide con la del vector de objetos `ReplayEvent`.
+ Cuando se hayan realizado todas las solicitudes deseadas, llame a la función `StaticReplayClient.assert_requests_match()` para comprobar que las solicitudes enviadas por el SDK coinciden con las de la lista de objetos `ReplayEvent`.

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

Veamos las pruebas para la misma función `determine_prefix_file_size()` del ejemplo anterior, pero utilizando la reproducción estática en lugar de la simulación.

1. En una línea de comandos del directorio del proyecto, agregue la caja [https://crates.io/crates/aws-smithy-http-client](https://crates.io/crates/aws-smithy-http-client) como una dependencia:

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

   Al usar la [opción](https://doc.rust-lang.org/cargo/commands/cargo-add.html) `--dev`, se agrega la caja a la sección `[dev-dependencies]` del archivo `Cargo.toml`. Como [dependencia de desarrollo](https://doc.rust-lang.org/cargo/reference/specifying-dependencies.html#development-dependencies), no se compila ni se incluye en el binario final que se utiliza para el código de producción.

   Este código de ejemplo también utiliza Amazon Simple Storage Service como el Servicio de AWS de ejemplo.

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

   De esta forma, se agrega la caja a la sección `[dependencies]` del archivo `Cargo.toml`.

1. En el módulo de código de prueba, incluya los dos tipos que necesitará.

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

1. La prueba comienza con la creación de las estructuras `ReplayEvent` que representan cada una de las transacciones HTTP que deben tener lugar durante la prueba. Cada evento contiene un objeto de solicitud HTTP y un objeto de respuesta HTTP que representan la información con la que normalmente respondería el Servicio de AWS . Estos eventos se pasan a una llamada a `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]);
   ```

   El resultado se almacena en `replay_client`. Esto representa un cliente HTTP que luego puede usar el SDK para Rust especificándolo en la configuración del cliente.

1. Para crear el cliente de Amazon S3, llame a la función `from_conf()` de la clase del cliente para crear el cliente mediante un objeto de configuración:

   ```
           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(),
           );
   ```

   El objeto de configuración se especifica mediante el método `http_client()` del generador y las credenciales se especifican mediante el método `credentials_provider()`. Las credenciales se crean mediante una función denominada `make_s3_test_credentials()`, que devuelve una estructura de credenciales falsa:

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

   No es necesario que estas credenciales sean válidas porque, en realidad, no se enviarán a AWS.

1. Ejecute la prueba llamando a la función que debe probarse. En este ejemplo, el nombre de esa función es `determine_prefix_file_size()`. Su primer parámetro es el objeto del cliente de Amazon S3 que se utilizará para sus solicitudes. Por lo tanto, especifique el cliente creado con `StaticReplayClient` para que este se encargue de gestionar las solicitudes en lugar de que salgan a la red:

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

   Cuando finaliza la llamada a `determine_prefix_file_size()`, se utiliza una aserción para confirmar que el valor devuelto coincide con el valor esperado. A continuación, se llama a la función `assert_requests_match()` del método `StaticReplayClient`. Esta función analiza las solicitudes HTTP registradas y confirma que todas coinciden con las especificadas en la matriz de objetos `ReplayEvent` proporcionada al crear el cliente de reproducción.

Puedes [ver el código completo de estos ejemplos](https://github.com/awsdocs/aws-doc-sdk-examples/tree/main/rustv1/examples/testing) en GitHub.

# Pruebas unitarias con `aws-smithy-mocks` el AWS SDK para Rust
<a name="testing-smithy-mocks"></a>

 AWS SDK para Rust Proporciona varios enfoques para probar el código con Servicios de AWS el que interactúa. En este tema se describe cómo utilizar la caja [https://docs.rs/aws-smithy-mocks/latest/aws_smithy_mocks/](https://docs.rs/aws-smithy-mocks/latest/aws_smithy_mocks/), que ofrece una forma sencilla a la vez que eficaz de simular las respuestas del cliente AWS SDK para fines de prueba.

## Descripción general de
<a name="overview-smithy-mock"></a>

Al escribir pruebas para el código que se utiliza Servicios de AWS, a menudo se quiere evitar realizar llamadas de red reales. La caja `aws-smithy-mocks` proporciona una solución al permitirle:
+ Crear reglas simuladas que definan cómo debe responder el SDK a solicitudes específicas.
+ Devolver diferentes tipos de respuestas (éxitos, errores, respuestas HTTP).
+ Hacer coincidir las solicitudes en función de sus propiedades.
+ Definir secuencias de respuestas para probar el comportamiento de los reintentos.
+ Verificar que las reglas se hayan utilizado según lo previsto.

## Incorporación de la dependencia
<a name="dependency-smithy-mock"></a>

En una línea de comandos del directorio del proyecto, agregue la caja [https://crates.io/crates/aws-smithy-mocks](https://crates.io/crates/aws-smithy-mocks) como una dependencia:

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

Al usar la [opción](https://doc.rust-lang.org/cargo/commands/cargo-add.html) `--dev`, se agrega la caja a la sección `[dev-dependencies]` del archivo `Cargo.toml`. Como [dependencia de desarrollo](https://doc.rust-lang.org/cargo/reference/specifying-dependencies.html#development-dependencies), no se compila ni se incluye en el binario final que se utiliza para el código de producción.

Este código de ejemplo también usa Amazon Simple Storage Service como ejemplo Servicio de AWS y requiere una función`test-util`.

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

De esta forma, se agrega la caja a la sección `[dependencies]` del archivo `Cargo.toml`.

## Uso básico
<a name="basic-smithy-mocks"></a>

 A continuación, se muestra un ejemplo sencillo de cómo usar `aws-smithy-mocks` para probar código que interactúa con 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);
}
```

## Creación de reglas simuladas
<a name="creating-rules-smithy-mocks"></a>

Las reglas se crean mediante la macro `mock!`, que toma una operación de cliente como argumento. A continuación, puede configurar el comportamiento de la regla. 

### Coincidencia de solicitudes
<a name="matching-requests-smithy-mocks"></a>

 Puede hacer que las reglas sean más específicas haciendo coincidir las propiedades de las solicitudes:

```
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()
    });
```

### Tipos de respuestas diferentes
<a name="diff-response-smithy-mocks"></a>

Puede devolver diferentes tipos de respuestas:

```
// 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")
        )
    });
```

## Prueba del comportamiento de los reintentos
<a name="testing-retry-behavior-smithy-mocks"></a>

Una de las características más potentes de `aws-smithy-mocks` es la capacidad de probar el comportamiento de los reintentos mediante la definición de secuencias de respuestas:

```
// 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();
```

## Modos de reglas
<a name="rule-modes-smithy-mocks"></a>

Puede controlar cómo coinciden y se aplican las reglas mediante los modos de regla:

```
// 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]);
```

## Ejemplo: prueba del comportamiento de los reintentos
<a name="example-retry-smithy-mocks"></a>

Este es un ejemplo más completo que muestra cómo probar el comportamiento de los reintentos:

```
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);
}
```

## Ejemplo: diferentes respuestas según los parámetros de solicitud
<a name="example-request-param-smithy-mocks"></a>

También puede crear reglas que devuelvan diferentes respuestas en función de los parámetros de solicitud:

```
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(_)));
}
```

## Prácticas recomendadas
<a name="best-practices-smithy-mocks"></a>

Cuando se usa `aws-smithy-mocks` para realizar pruebas.

1.  Coincidencia de solicitudes específicas: utilice `match_requests()` para asegurarse de que las reglas solo se aplican a las solicitudes previstas, en particular con `RuleMode:::MatchAny`.

1.  Verificación del uso de las reglas: compruebe `rule.num_calls()` para asegurarse de que las reglas se hayan utilizado realmente.

1.  Prueba de la gestión de errores: cree reglas que devuelvan errores para comprobar cómo gestiona el código los errores.

1.  Prueba de la lógica de reintentos: utilice secuencias de respuesta para comprobar que el código gestiona correctamente cualquier clasificador de reintentos personalizado u otro comportamiento de los reintentos.

1. Centrado en las pruebas: cree pruebas independientes para diferentes escenarios en lugar de tratar de abarcar todo en una sola prueba.

# Uso de «waiters» en AWS SDK para Rust
<a name="waiters"></a>

 Los «waiters» son una abstracción del cliente que se utilizan para sondear un recurso hasta que se alcance el estado deseado o se determine que el recurso no entrará en el estado deseado. Esta es una tarea común cuando se trabaja con servicios que son eventualmente consistentes, como Amazon Simple Storage Service, o servicios que crean recursos de forma asíncrona, como Amazon Elastic Compute Cloud. Escribir lógica para sondear continuamente el estado de un recurso puede resultar engorroso y aumentar la probabilidad de errores. El objetivo de los «waiters» es trasladar esta responsabilidad del código de cliente al AWS SDK para Rust, que tiene un profundo conocimiento de los aspectos temporales de la operación de AWS. 

Servicios de AWS compatibles con los «waiters» incluyen un módulo `<service>::waiters`. 
+ El indicador `<service>::client::Waiters` proporciona métodos de «waiter» para el cliente. Los métodos se implementan para la estructura `Client`. Todos los métodos de «waiter» siguen una convención de nomenclatura estándar de `wait_until_<Condition>`. 
  + En Amazon S3, este indicador es [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). 

En el siguiente ejemplo, se utiliza Amazon S3. Sin embargo, los conceptos son los mismos para todos los Servicio de AWS que tengan uno o más «waiters» definidos. 

En el siguiente ejemplo de código se muestra el uso de una función de «waiter» en lugar de escribir una lógica de sondeo para esperar a que exista un cubo después de crearlo. 

```
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.
```

**nota**  
 Cada método de espera devuelve un `Result<FinalPoll<...>, WaiterError<...>> ` que se puede utilizar para obtener la respuesta final si se alcanza la condición deseada o se produce un error. Consulte [FinalPoll](https://docs.rs/aws-smithy-runtime-api/latest/aws_smithy_runtime_api/client/waiters/struct.FinalPoll.html) y [WaiterError](https://docs.rs/aws-smithy-runtime-api/latest/aws_smithy_runtime_api/client/waiters/error/enum.WaiterError.html) en la documentación de la API de Rust para obtener más información.