Retry: - `RetryPolicy` (3 retries, full-jitter exponential backoff) applied in a new `execute` dispatch chokepoint, configurable via `with_retry_policy`. `Retry-After` is honoured, and one longer than `max_delay` is surfaced rather than slept through. - What may be replayed depends on the method, not just the status. A 429 means the server refused the request without processing it, so any method is safe. A 5xx or timeout gives no such guarantee, so only idempotent methods are retried: this is a billing API, and replaying a POST the server had already applied would duplicate an invoice, payment, or email. Response types: - `statics`, `search`, and `health_check` return `Statics`, `SearchResults`, and `HealthCheck` instead of being generic. `HealthCheck` covers both deployments — hosted returns a bare message rather than a report — and `SearchResults` covers both the Elasticsearch and non-Elasticsearch bucket sets. - Each keeps the `extra` convention, and `statics_as` / `search_as` / `health_check_as` remain for pinning shapes left untyped. Nested config blobs (gateways, templates, extensions) stay `Value`, same reasoning as `Company::settings`. - `send_email` previously decoded the response as a bare body, but the controller returns `itemResponse(...)` — a `data`-wrapped entity. It now unwraps, so `T` is the entity. Still generic, since the endpoint serves invoices, quotes, credits, and purchase orders. - `chart` stays generic on purpose: the eleven charts share no response shape. - Adds `id_as_string`, since `/statics` returns `payment_types.id` as a string and the other tables return numbers. Adds CHANGELOG.md (Keep a Changelog), and a CLAUDE.md note to keep it current. 17 new tests, 43 in client_requests.rs total. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
28 lines
1.0 KiB
TOML
28 lines
1.0 KiB
TOML
[package]
|
|
name = "invoiceninjars"
|
|
version = "0.1.0"
|
|
edition = "2021"
|
|
description = "An async Rust client for the Invoice Ninja v5 API"
|
|
license = "MIT"
|
|
repository = "https://github.com/bryant/invoiceninjars"
|
|
|
|
[dependencies]
|
|
reqwest = { version = "0.12", default-features = false, features = ["json", "native-tls"] }
|
|
serde = { version = "1", features = ["derive"] }
|
|
serde_json = "1"
|
|
thiserror = "2"
|
|
# Already in the tree via reqwest's `url`; used to escape ids before they are
|
|
# interpolated into request paths.
|
|
percent-encoding = "2"
|
|
# Already in the tree as reqwest's runtime; only `time` is needed, for the
|
|
# sleep between retries.
|
|
tokio = { version = "1", features = ["time"] }
|
|
|
|
[dev-dependencies]
|
|
tokio = { version = "1", features = ["rt-multi-thread", "macros"] }
|
|
# Used to assert that ListParams urlencodes the way reqwest will send it.
|
|
serde_urlencoded = "0.7"
|
|
# Loopback HTTP server for exercising the request plumbing (pagination, path
|
|
# escaping, headers, error mapping) without reaching the network.
|
|
wiremock = "0.6"
|