xmtp_api_d14n/queries/
builder.rs

1//! Generic Builder for the backend API
2
3use derive_builder::UninitializedFieldError;
4use std::sync::Arc;
5use thiserror::Error;
6use xmtp_api_grpc::error::{GrpcBuilderError, GrpcError};
7use xmtp_common::{MaybeSend, MaybeSync};
8use xmtp_id::scw_verifier::VerifierError;
9use xmtp_proto::api::ApiClientError;
10use xmtp_proto::types::AppVersion;
11
12use crate::protocol::{CursorStore, FullXmtpApiArc, FullXmtpApiBox, NoCursorStore};
13use crate::{
14    AuthCallback, AuthHandle, ClientBundle, ClientBundleBuilder, ClientKind, D14nClient,
15    MultiNodeClientBuilderError, ReadWriteClientBuilderError, ReadonlyClientBuilderError, V3Client,
16};
17
18mod impls;
19
20/// Builder to access the backend XMTP API
21/// Passing a gateway host implicitly enables decentralization.
22#[derive(Clone, Default)]
23pub struct MessageBackendBuilder {
24    client_bundle: ClientBundleBuilder,
25    cursor_store: Option<Arc<dyn CursorStore>>,
26}
27
28#[derive(Error, Debug)]
29pub enum MessageBackendBuilderError {
30    #[error("V3 Host is required")]
31    MissingV3Host,
32    #[error(transparent)]
33    GrpcBuilder(#[from] GrpcBuilderError),
34    #[error(transparent)]
35    MultiNode(#[from] MultiNodeClientBuilderError),
36    #[error(transparent)]
37    Scw(#[from] VerifierError),
38    #[error("failed to build stateful local client, cursor store not replaced, type {0}")]
39    CursorStoreNotReplaced(&'static str),
40    #[error("error while building read/write api client {0},")]
41    UninitializedField(#[from] ReadWriteClientBuilderError),
42    #[error(transparent)]
43    ReadonlyBuilder(#[from] ReadonlyClientBuilderError),
44    #[error(transparent)]
45    Builder(#[from] UninitializedFieldError),
46    #[error("client kind {0} is currently unsupported")]
47    UnsupportedClient(ClientKind),
48}
49
50/// Indicates this api implementation can be type-erased
51/// and coerced into a [`Box`] or [`Arc`]
52pub trait ToDynApi: MaybeSend + MaybeSync {
53    type Error: MaybeSend + MaybeSync;
54    fn boxed(self) -> FullXmtpApiBox<Self::Error>;
55    fn arced(self) -> FullXmtpApiArc<Self::Error>;
56}
57
58impl MessageBackendBuilder {
59    /// An optional field which allows inbox apps to specify their version
60    pub fn app_version(&mut self, version: impl Into<AppVersion>) -> &mut Self {
61        self.client_bundle.app_version(version);
62        self
63    }
64
65    /// Specify the node host
66    /// for d14n this is the replication node
67    /// for v3 this is xmtp-node-go
68    ///
69    /// Required
70    pub fn v3_host<S: AsRef<str>>(&mut self, host: S) -> &mut Self {
71        self.client_bundle.v3_host(host.as_ref());
72        self
73    }
74
75    /// Specify the gateway host
76    /// the gateway is a d14n-specific host
77    /// specifying this fields implicitly enables decentralization
78    ///
79    /// Optional
80    pub fn gateway_host<S: AsRef<str>>(&mut self, host: S) -> &mut Self {
81        self.client_bundle.gateway_host(host.as_ref());
82        self
83    }
84
85    /// Specify the gateway host as an Option<T>
86    /// the gateway is a d14n-specific host
87    /// specifying this fields implicitly enables decentralization
88    ///
89    /// Optional
90    pub fn maybe_gateway_host<S: Into<String>>(&mut self, gateway_host: Option<S>) -> &mut Self {
91        self.client_bundle.maybe_gateway_host(gateway_host);
92        self
93    }
94
95    /// Indicate that the connection should use TLS
96    pub fn is_secure(&mut self, is_secure: bool) -> &mut Self {
97        self.client_bundle.is_secure(is_secure);
98        self
99    }
100
101    pub fn cursor_store(&mut self, store: impl CursorStore + 'static) -> &mut Self {
102        self.cursor_store = Some(Arc::new(store) as Arc<_>);
103        self
104    }
105
106    pub fn readonly(&mut self, readonly: bool) -> &mut Self {
107        self.client_bundle.readonly(readonly);
108        self
109    }
110
111    pub fn from_bundle(
112        &mut self,
113        bundle: ClientBundle<GrpcError>,
114    ) -> Result<FullXmtpApiArc<ApiClientError<GrpcError>>, MessageBackendBuilderError> {
115        let cursor_store = self
116            .cursor_store
117            .clone()
118            .unwrap_or(Arc::new(NoCursorStore) as Arc<dyn CursorStore>);
119
120        match bundle.kind() {
121            ClientKind::D14n => Ok(D14nClient::new(bundle, cursor_store)?.arced()),
122            ClientKind::V3 => Ok(V3Client::new(bundle, cursor_store).arced()),
123            ClientKind::Hybrid => Err(MessageBackendBuilderError::UnsupportedClient(
124                ClientKind::Hybrid,
125            )),
126        }
127    }
128
129    pub fn maybe_auth_callback(&mut self, callback: Option<Arc<dyn AuthCallback>>) -> &mut Self {
130        self.client_bundle.maybe_auth_callback(callback);
131        self
132    }
133
134    pub fn maybe_auth_handle(&mut self, handle: Option<AuthHandle>) -> &mut Self {
135        self.client_bundle.maybe_auth_handle(handle);
136        self
137    }
138
139    /// Build the client
140    pub fn build(
141        &mut self,
142    ) -> Result<FullXmtpApiArc<ApiClientError<GrpcError>>, MessageBackendBuilderError> {
143        let Self { client_bundle, .. } = self;
144        let bundle = client_bundle.build()?;
145        self.from_bundle(bundle)
146    }
147}