noosphere_core/api/v0alpha2/
data.rs

1use crate::{
2    api::StatusCode,
3    data::{Did, Jwt, Link, MemoIpld},
4    error::NoosphereError,
5};
6use serde::{Deserialize, Serialize};
7use thiserror::Error;
8
9/// The body payload expected by the "push" API route
10#[derive(Debug, Clone, Serialize, Deserialize)]
11pub struct PushBody {
12    /// The DID of the local sphere whose revisions are being pushed
13    pub sphere: Did,
14    /// The base revision represented by the payload being pushed; if the
15    /// entire history is being pushed, then this should be None
16    pub local_base: Option<Link<MemoIpld>>,
17    /// The tip of the history represented by the payload being pushed
18    pub local_tip: Link<MemoIpld>,
19    /// The last received tip of the counterpart sphere
20    pub counterpart_tip: Option<Link<MemoIpld>>,
21    /// An optional name record to publish to the Noosphere Name System
22    pub name_record: Option<Jwt>,
23}
24
25/// The possible responses from the "push" API route
26#[derive(Debug, PartialEq, Eq, Serialize, Deserialize)]
27pub enum PushResponse {
28    /// The new history was accepted
29    Accepted {
30        /// This is the new tip of the "counterpart" sphere after accepting
31        /// the latest history from the local sphere. This is guaranteed to be
32        /// at least one revision ahead of the latest revision being tracked
33        /// by the client (because it points to the newly received tip of the
34        /// local sphere's history)
35        new_tip: Link<MemoIpld>,
36    },
37    /// The history was already known by the API host, so no changes were made
38    NoChange,
39}
40
41/// Error types for typical "push" API failure conditions
42#[allow(missing_docs)]
43#[derive(Serialize, Deserialize, Error, Debug)]
44pub enum PushError {
45    #[error("Stream of blocks up to the gateway was interrupted")]
46    BrokenUpstream,
47    #[error("Stream of blocks down from the gateway was interrupted")]
48    BrokenDownstream,
49    #[error("First block in upstream was missing or unexpected type")]
50    UnexpectedBody,
51    #[error("Pushed history conflicts with canonical history")]
52    Conflict,
53    #[error("Missing some implied history")]
54    MissingHistory,
55    #[error("Replica is up to date")]
56    UpToDate,
57    #[error("Internal error: {0:?}")]
58    Internal(Option<String>),
59}
60
61impl From<&PushError> for StatusCode {
62    fn from(value: &PushError) -> Self {
63        match value {
64            PushError::BrokenUpstream => StatusCode::BAD_GATEWAY,
65            PushError::BrokenDownstream => StatusCode::BAD_GATEWAY,
66            PushError::UnexpectedBody => StatusCode::UNPROCESSABLE_ENTITY,
67            PushError::Conflict => StatusCode::CONFLICT,
68            PushError::MissingHistory => StatusCode::FAILED_DEPENDENCY,
69            PushError::UpToDate => StatusCode::NOT_MODIFIED,
70            PushError::Internal(_) => StatusCode::INTERNAL_SERVER_ERROR,
71        }
72    }
73}
74
75impl From<NoosphereError> for PushError {
76    fn from(error: NoosphereError) -> Self {
77        error.into()
78    }
79}
80
81impl From<anyhow::Error> for PushError {
82    fn from(value: anyhow::Error) -> Self {
83        PushError::Internal(Some(format!("{value}")))
84    }
85}