bapp-api-client 0.4.2

BAPP Auto API Client for Rust
Documentation
# BAPP Auto API Client — Rust

Official Rust client for the [BAPP Auto API](https://www.bapp.ro). Provides a
simple, consistent interface for authentication, entity CRUD, and task execution.

## Getting Started

### 1. Install

Add to `Cargo.toml`:

```toml
[dependencies]
bapp-api-client = "0.4.2"
```

### 2. Create a client

```rust
use bapp_api_client::BappApiClient;

let client = BappApiClient::new().with_token("your-api-key");
```

### 3. Make your first request

```rust
// List with filters
let countries = client.list("core.country", Some(&[("page", "1")])).await?;

// Get by ID
let country = client.get("core.country", "42").await?;

// Create
let data = serde_json::json!({"name": "Romania", "code": "RO"});
let created = client.create("core.country", Some(&data)).await?;

// Patch (partial update)
let patch = serde_json::json!({"code": "RO"});
client.patch("core.country", "42", Some(&patch)).await?;

// Delete
client.delete("core.country", "42").await?;
```

## Authentication

The client supports **Token** (API key) and **Bearer** (JWT / OAuth) authentication.
Token auth already includes a tenant binding, so you don't need to specify `tenant` separately.

```rust
// Static API token (tenant is included in the token)
let client = BappApiClient::new().with_token("your-api-key");

// Bearer (JWT / OAuth)
let client = BappApiClient::new().with_bearer("eyJhbG...").with_tenant("1");
```

## Configuration

`tenant` and `app` can be changed at any time after construction:

```rust
client.tenant = Some("2".to_string());
client.app = "wms".to_string();
```

## API Reference

### Client options

| Option | Description | Default |
|--------|-------------|---------|
| `token` | Static API token (`Token <value>`) — includes tenant ||
| `bearer` | Bearer / JWT token ||
| `host` | API base URL | `https://panel.bapp.ro/api` |
| `tenant` | Tenant ID (`x-tenant-id` header) | `None` |
| `app` | App slug (`x-app-slug` header) | `"account"` |

### Methods

| Method | Description |
|--------|-------------|
| `me()` | Get current user profile |
| `get_app(app_slug)` | Get app configuration by slug |
| `list(content_type, **filters)` | List entities (paginated) |
| `get(content_type, id)` | Get a single entity |
| `create(content_type, data)` | Create an entity |
| `update(content_type, id, data)` | Full update (PUT) |
| `patch(content_type, id, data)` | Partial update (PATCH) |
| `delete(content_type, id)` | Delete an entity |
| `list_introspect(content_type)` | Get list view metadata |
| `detail_introspect(content_type)` | Get detail view metadata |
| `get_document_views(record)` | Extract available views from a record |
| `get_document_url(record, output?, label?, variation?)` | Build a render/download URL |
| `get_document_content(record, output?, label?, variation?)` | Fetch document bytes (PDF, HTML, JPG) |
| `list_tasks()` | List available task codes |
| `detail_task(code)` | Get task configuration |
| `run_task(code, payload?)` | Execute a task |
| `run_task_async(code, payload?)` | Run a long-running task and poll until done |

### Paginated responses

`list()` returns the results directly as a list/array. Pagination metadata is
available as extra attributes:

- `count` — total number of items across all pages
- `next` — URL of the next page (or `null`)
- `previous` — URL of the previous page (or `null`)

## File Uploads

When data contains file objects, the client automatically switches from JSON to
`multipart/form-data`. Mix regular fields and files in the same call:

```rust
// Use request_multipart for file uploads
client.request_multipart(
    Method::POST,
    "/content-type/myapp.document/",
    &[("name", "Report")],        // text fields
    &[("file", "report.pdf")],    // file fields (field_name, file_path)
).await?;
```

## Document Views

Records may include `public_view` and/or `view_token` fields with JWT tokens
for rendering documents (invoices, orders, reports, etc.) as HTML, PDF, or images.

The SDK normalises both formats and builds the correct URL automatically:

```rust
let order = client.get("company_order.order", "42").await?.unwrap();

// Get a PDF download URL (auto-detects public_view vs view_token)
let url = client.get_document_url(&order, "pdf", None, None);

// Pick a specific view by label
let url = client.get_document_url(&order, "html", Some("Comanda interna"), None);

// Use a variation
let url = client.get_document_url(&order, "pdf", None, Some("v4"));

// Fetch the actual content as bytes
if let Some(pdf_bytes) = client.get_document_content(&order, "pdf", None, None).await? {
    tokio::fs::write("order.pdf", &pdf_bytes).await?;
}

// Enumerate all available views
let views = BappApiClient::get_document_views(&order);
for v in &views {
    println!("{} {}", v["label"], v["type"]);
}
```

`get_document_views()` returns a list of normalised view entries with `label`,
`token`, `type` (`"public_view"` or `"view_token"`), `variations`, and
`default_variation`. Use it to enumerate available views (e.g. for a dropdown).

## Tasks

Tasks are server-side actions identified by a dotted code (e.g. `myapp.export_report`).

```rust
let tasks = client.list_tasks().await?;

let cfg = client.detail_task("myapp.export_report").await?;

// Run without payload (GET)
let result = client.run_task("myapp.export_report", None).await?;

// Run with payload (POST)
let payload = serde_json::json!({"format": "csv"});
let result = client.run_task("myapp.export_report", Some(&payload)).await?;
```

### Long-running tasks

Some tasks run asynchronously on the server. When triggered, they return an `id`
that can be polled via `bapp_framework.taskdata`. Use `run_task_async()` to
handle this automatically — it polls until `finished` is `true` and returns the
final task data (which includes a `file` URL when the task produces a download).

## License

MIT