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>
80 lines
3.0 KiB
Rust
80 lines
3.0 KiB
Rust
use serde::{Deserialize, Serialize};
|
|
use serde_json::{Map, Value};
|
|
|
|
/// Response of `GET /health_check`.
|
|
///
|
|
/// Two different payloads arrive here depending on the deployment. A
|
|
/// self-hosted install returns the full `SystemHealth::check()` report
|
|
/// modelled below; the hosted app returns only `{"message": "", "errors":
|
|
/// []}`, since the check is not meaningful there. Every field defaults, so
|
|
/// both decode — test [`system_health`](Self::system_health) rather than
|
|
/// assuming the detail fields are populated.
|
|
#[derive(Debug, Clone, Default, Serialize, Deserialize)]
|
|
pub struct HealthCheck {
|
|
/// Overall verdict. Absent on hosted, where no check runs.
|
|
#[serde(default)]
|
|
pub system_health: Option<bool>,
|
|
#[serde(default, deserialize_with = "super::common::null_to_default")]
|
|
pub api_version: String,
|
|
#[serde(default)]
|
|
pub php_version: Option<PhpVersion>,
|
|
/// Required PHP extensions and whether each was found.
|
|
#[serde(default)]
|
|
pub extensions: Value,
|
|
#[serde(default)]
|
|
pub env_writable: Option<bool>,
|
|
#[serde(default)]
|
|
pub simple_db_check: Option<bool>,
|
|
#[serde(default)]
|
|
pub cache_enabled: Option<bool>,
|
|
#[serde(default)]
|
|
pub exec: Option<bool>,
|
|
#[serde(default)]
|
|
pub open_basedir: Option<bool>,
|
|
#[serde(default, deserialize_with = "super::common::null_to_default")]
|
|
pub mail_mailer: String,
|
|
#[serde(default, deserialize_with = "super::common::null_to_default")]
|
|
pub pdf_engine: String,
|
|
#[serde(default, deserialize_with = "super::common::null_to_default")]
|
|
pub queue: String,
|
|
/// Queue depth and the most recent failure, when the queue is readable.
|
|
#[serde(default)]
|
|
pub queue_data: Value,
|
|
#[serde(default)]
|
|
pub trailing_slash: Option<bool>,
|
|
#[serde(default, deserialize_with = "super::common::null_to_default")]
|
|
pub file_permissions: String,
|
|
#[serde(default)]
|
|
pub exchange_rate_api_not_configured: Option<bool>,
|
|
#[serde(default)]
|
|
pub is_docker: Option<bool>,
|
|
#[serde(default)]
|
|
pub pending_migrations: Option<bool>,
|
|
|
|
/// Set on the hosted app, which returns a bare message instead of a
|
|
/// report.
|
|
#[serde(default, deserialize_with = "super::common::null_to_default")]
|
|
pub message: String,
|
|
|
|
#[serde(flatten)]
|
|
pub extra: Map<String, Value>,
|
|
}
|
|
|
|
/// The PHP version sub-report of a health check.
|
|
#[derive(Debug, Clone, Default, Serialize, Deserialize)]
|
|
pub struct PhpVersion {
|
|
#[serde(default, deserialize_with = "super::common::null_to_default")]
|
|
pub current_php_version: String,
|
|
#[serde(default, deserialize_with = "super::common::null_to_default")]
|
|
pub minimum_php_version: String,
|
|
#[serde(default, deserialize_with = "super::common::null_to_default")]
|
|
pub current_php_cli_version: String,
|
|
#[serde(default)]
|
|
pub is_okay: Option<bool>,
|
|
#[serde(default, deserialize_with = "super::common::null_to_default")]
|
|
pub memory_limit: String,
|
|
|
|
#[serde(flatten)]
|
|
pub extra: Map<String, Value>,
|
|
}
|