> ## Documentation Index
> Fetch the complete documentation index at: https://docs.stockshift.io/llms.txt
> Use this file to discover all available pages before exploring further.

# Rust

> Un cliente de Rust para la API de StockShift. Tiene un método por cada operación de la referencia de la API y un tipo para cada esquema, generado a partir del documento OpenAPI de la API.

Es asíncrono y está construido sobre [reqwest](https://docs.rs/reqwest) y [tokio](https://tokio.rs). Requiere Rust 1.85 o posterior.

## Instalación

```bash theme={null}
cargo add stockshift
cargo add tokio --features macros,rt-multi-thread
```

TLS usa rustls de forma predeterminada. Para usar en su lugar la biblioteca TLS de la plataforma, desactiva las features predeterminadas y activa `native-tls`:

```toml theme={null}
stockshift = { version = "1", default-features = false, features = ["native-tls"] }
```

## Inicio rápido

Crea un artículo y luego lista las existencias disponibles. `try_next` viene del crate [futures-util](https://docs.rs/futures-util).

```rust theme={null}
use futures_util::TryStreamExt;
use stockshift::Client;
use stockshift::types::{CreateItemRequest, ItemType, MakeOrBuy};

#[tokio::main]
async fn main() -> Result<(), stockshift::Error> {
    let client = Client::new(std::env::var("STOCKSHIFT_TOKEN").expect("STOCKSHIFT_TOKEN is set"));

    let item = CreateItemRequest::new("BOLT-M4", "M4 hex bolt", ItemType::Stocked, MakeOrBuy::Buy);
    client.catalog().create_item(&item).await?;

    let mut stock = client.inventory().list_stock_on_hand().stream();
    while let Some(row) = stock.try_next().await? {
        println!("{} {} {}", row.sku, row.location_code, row.quantity);
    }
    Ok(())
}
```

## Autenticación

Pasa una clave de API o un token de acceso OAuth a `Client::new`. Crea una clave de API en la app, en Configuración > Acceso > Claves de API. La clave actúa como el miembro que la creó, y cada llamada necesita tanto el permiso del miembro como uno de los alcances de la clave.

Para un token OAuth que vence, pasa un `Token::provider`. El cliente lo llama antes de cada solicitud:

```rust theme={null}
use stockshift::{Client, Token};

async fn fresh_token() -> Result<String, std::io::Error> {
    // Refresh the OAuth access token here.
    Ok(String::new())
}

fn client() -> stockshift::Result<Client> {
    Client::builder(Token::provider(fresh_token)).build()
}
```

`Client::builder` acepta más ajustes:

| Método | Valor predeterminado | Significado |
| - | - | - |
| `base_url` | `https://app.stockshift.io/api/v1` | Dónde está la API. |
| `max_retries` | `2` | Reintentos para una llamada que se puede reintentar. |
| `timeout` | 60 segundos | Cuánto esperar en cada intento. |
| `max_retry_after` | 60 segundos | El `Retry-After` más largo que vale la pena esperar. |
| `header` | ninguno | Un encabezado que se envía en cada llamada. |
| `http_client` | un `reqwest::Client` nuevo | El cliente con el que enviar las llamadas, para un proxy o TLS personalizado. |

Clonar un `Client` es barato. Los clones comparten un solo grupo de conexiones.

## Hacer llamadas

Cada área de la API tiene un método, como `catalog()` o `inventory()`, con un método por cada operación. Los parámetros de ruta y el cuerpo son argumentos. Los parámetros de consulta opcionales son métodos de la llamada. Usa `await` en la llamada para enviarla:

```rust theme={null}
use stockshift::types::ItemStatus;

async fn calls(client: &stockshift::Client) -> stockshift::Result<()> {
    let item = client.catalog().get_item("01J8QZ4M...").await?;
    println!("{} {}", item.sku, item.name);

    let page = client.catalog().list_items().search("bolt").status(ItemStatus::Active).await?;
    for item in page.items {
        println!("{}", item.sku);
    }
    Ok(())
}
```

La documentación de cada método indica su ruta, como `GET /catalog/items/{item}`.

Una llamada devuelve lo que contiene el `data` de la respuesta, así que `get_item` devuelve un `ItemResource`. Una respuesta vacía devuelve `()`, una exportación CSV un `String` y un archivo `Bytes`.

Cada esquema de la referencia de la API tiene un tipo en `stockshift::types`. Un tipo de solicitud se construye con `new`, que recibe sus campos obligatorios, y un setter para cada campo opcional:

```rust theme={null}
use stockshift::types::{CreateItemRequest, ItemType, MakeOrBuy};

let mut item = CreateItemRequest::new("BOLT-M4", "M4 hex bolt", ItemType::Stocked, MakeOrBuy::Buy)
    .brand("Acme")
    .weight("0.0040");
// A field the API lets you clear is an `Option<Option<T>>`: `None` leaves
// it out and `Some(None)` sends null.
item.description = Some(None);
```

Un enum conserva un valor que no conoce en su variante `Unknown`, así que un valor que la API agregue después aún se decodifica.

Para una operación que esta versión del crate no tiene, usa `Client::request` con la ruta y cualquier tipo que implemente `Deserialize`.

## Paginación

Una lista que puede crecer devuelve una página a la vez. Llama a `stream()` en la llamada para obtener cada registro. Obtiene la página siguiente cuando se agotan los registros de la anterior:

```rust theme={null}
use futures_util::TryStreamExt;

async fn skus(client: &stockshift::Client) -> stockshift::Result<Vec<String>> {
    client.catalog().list_items().search("bolt").stream().map_ok(|item| item.sku).try_collect().await
}
```

Para paginar a mano, pasa el `next_cursor` de cada página con `after()` hasta que sea `None`.

## Errores

Cada llamada devuelve `Result<T, stockshift::Error>`. Cuando la API responde con un error, el error es `Error::Api`, que contiene el cuerpo de detalles del problema de RFC 9457. Decide según su código, que es estable. El detalle está escrito para personas y puede cambiar.

```rust theme={null}
use stockshift::types::CreateItemRequest;

async fn create(client: &stockshift::Client, item: &CreateItemRequest) {
    match client.catalog().create_item(item).await {
        Ok(created) => println!("created {}", created.id),
        Err(error) if error.code() == Some("validation_failed") => {
            let api = error.api().expect("an API error has problem details");
            println!("{:?}", api.errors()); // {"sku": ["Another item already has the SKU BOLT-M4."]}
        }
        Err(error) => eprintln!("{error}"),
    }
}
```

`ApiError` también tiene `status`, `detail`, `problem` y `headers`. Una llamada que no recibe respuesta, porque falló la red o el intento agotó el tiempo de espera, falla con `Error::Connection`.

## Reintentos y claves de idempotencia

El cliente reintenta un `429` después del tiempo que indica `Retry-After`. Lo hace con todos los métodos, porque la API no ejecutó la llamada. Si `Retry-After` es mayor que `max_retry_after`, la llamada falla en lugar de esperar.

Un `5xx` o una falla de red se reintenta para `GET`, `PUT` y `DELETE`, con una breve espera entre intentos. Un `POST` o `PATCH` solo se reintenta cuando le das una clave de idempotencia, para que un reintento no pueda hacer el cambio dos veces:

```rust theme={null}
async fn push(client: &stockshift::Client, connection: &str, key: &str) -> stockshift::Result<()> {
    client.integrations().push_stock_now(connection).idempotency_key(key).await?;
    Ok(())
}
```

Usa una clave nueva, como un UUID, para cada cambio que quieras hacer. Los endpoints que respetan la clave lo indican en la referencia de la API.

## Webhooks

Revisa el encabezado `StockShift-Signature` antes de confiar en una entrega. `Webhook::verify` necesita el cuerpo sin procesar, exactamente como llegó:

```rust theme={null}
use stockshift::webhook::{EventData, Webhook};

/// Returns the HTTP status to answer a delivery with.
fn receive(body: &[u8], signature: Option<&str>, secret: &str) -> u16 {
    let Ok(event) = Webhook::new(secret).verify(body, signature) else {
        return 400;
    };
    if let EventData::InventoryStockAdjusted(adjusted) = &event.data {
        println!("{} {}", adjusted.item_id, adjusted.quantity);
    }
    204
}
```

Rechaza una entrega de más de cinco minutos de antigüedad. Llama a `tolerance` para cambiarlo. Los reintentos de un evento conservan su `id`, así que guárdalo y omite los eventos que ya procesaste. Un evento que esta versión del crate no conoce llega como `EventData::Unknown` con sus datos como JSON.

Para probar tu propio receptor, `Webhook::sign` firma un payload igual que StockShift.

## Versiones

La versión mayor cambia cuando un cambio en la API puede romper tu código, como un campo eliminado o un parámetro obligatorio nuevo. Los endpoints, campos y valores de enum nuevos llegan en versiones menores. `stockshift::API_VERSION` contiene la versión de la referencia de la API con la que se compiló el crate.

Una versión menor puede subir la versión mínima compatible de Rust.


## Related topics

- [StockShift para desarrolladores](https://docs.stockshift.io/es/index.md)
- [SDK](https://docs.stockshift.io/es/sdks/index.md)


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.