Skip to main content

bitwarden_access_connector/api/
models.rs

1//! Domain types produced by the [`super`] API wrapper; the wire DTOs come from
2//! `bitwarden_api_api::models`.
3
4use uuid::Uuid;
5
6use crate::{auth::session::SessionLost, error::Retry};
7
8/// A claimable rotation job returned by the poll endpoint.
9#[derive(Debug, Clone)]
10pub(crate) struct JobRef {
11    pub(crate) id: Uuid,
12}
13
14/// Errors returned by the [`super::AccessConnectorClient`] wrapper. They never include response
15/// bodies, which can contain sensitive data.
16#[derive(Debug, thiserror::Error)]
17pub(crate) enum ApiError {
18    /// The connector's session was terminally lost (revoked or closed).
19    #[error("session lost: {0:?}")]
20    SessionLost(SessionLost),
21
22    /// The server returned 409. A claim maps it to `Ok(None)` instead.
23    #[error("rejected (HTTP {status})")]
24    Rejected {
25        /// The HTTP status, typically 409.
26        status: u16,
27    },
28
29    /// A 404 on an attempt-scoped route: the server does not know the attempt, so it should be
30    /// abandoned unreported.
31    #[error("attempt not found (404)")]
32    UnknownAttempt,
33
34    /// A 404 on a connector or job route: the server does not consider this connector eligible.
35    #[error("access connector not eligible (404 on connector route)")]
36    NotEligible,
37
38    /// A transport error, 429, 5xx, a 401 that survived the refresh retry, or any other unexpected
39    /// status. The message is a bounded status or error kind, never body content.
40    #[error("transient error: {0}")]
41    Transient(String),
42
43    /// The response could not be decoded or lacked a required field.
44    #[error("protocol error: {0}")]
45    Protocol(String),
46}
47
48impl ApiError {
49    /// Only a transient error is worth retrying.
50    pub(crate) fn retry_class(&self) -> Retry {
51        match self {
52            ApiError::Transient(_) => Retry::Transient,
53            _ => Retry::Permanent,
54        }
55    }
56}