diff --git a/src/api.rs b/src/api.rs index a1db58c..a2db02b 100644 --- a/src/api.rs +++ b/src/api.rs @@ -175,6 +175,18 @@ pub struct IndexModelData { pub models: Vec, } +/// Request to list indexed models +#[derive(Serialize)] +pub struct ListIndexedModelsRequest {} + +/// Response list of indexed models +#[derive(Debug, Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct ListIndexedModelsResponse { + /// List of indexed models + pub models: Vec, +} + /// Response from call to admin api /getCode #[derive(Debug, Deserialize)] #[serde(rename_all = "camelCase")] @@ -202,6 +214,13 @@ pub struct AdminApiRequest { jws: String, } +impl AdminApiRequest { + /// JWS Compact Serialization string. + pub fn jws(&self) -> &str { + self.jws.as_ref() + } +} + impl TryFrom for AdminApiRequest { type Error = anyhow::Error; fn try_from(value: Jws) -> Result { @@ -333,6 +352,121 @@ pub struct TypedQueryResponse { #[derive(Serialize)] pub struct HealthcheckRequest {} +/// Node status request for http api +#[derive(Serialize)] +pub struct NodeStatusRequest {} + +/// Node status response for http api +#[derive(Debug, Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct NodeStatusResponse { + /// A random UUID that is generated each time a node starts up. + /// Can be used to detect when a node restarts. + pub run_id: String, + /// How long the node has been running. + pub uptime_ms: i64, + /// The Ceramic network the node is connected to. + pub network: String, + /// Information about the anchoring service. + pub anchor: AnchorStatus, + /// Information about the connected IPFS node. + pub ipfs: IpfsStatus, + /// Information about the ComposeDB operations. + pub compose_db: Option, +} + +/// Information about the anchoring service. +#[derive(Debug, Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct AnchorStatus { + /// The URL of the Ceramic Anchor Service used to request anchors. + pub anchor_service_url: String, + /// The ethereum rpc endpoint used to validate anchor transactions. If null, likely means + /// the node is using the default, rate-limited ethereum provider. + pub ethereum_rpc_endpoint: Option, + /// The ethereum chainId used for anchors. + pub chain_id: String, +} + +/// Information about the connected IPFS node. +#[derive(Debug, Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct IpfsStatus { + /// PeerId of the connected ipfs node + pub peer_id: String, + /// IPFS Swarm multiaddrs of the connected ipfs node + pub addresses: Vec, +} + +/// Status about the ComposeDB specific operations of the node. +#[derive(Debug, Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct ComposeDBStatus { + /// The list of models Ids that are being indexed. + pub indexed_models: Vec, + /// The set of active sync operations. + pub syncs: Option, +} + +/// Status of all sync operations. +#[derive(Debug, Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct SyncStatus { + /// Status of currently active sync operations. + pub active_syncs: Vec, + /// Status of continuously running sync operations. + pub continuous_sync: Vec, + /// Status of pending sync operations. + pub pending_syncs: Vec, +} + +/// Status of currently active sync operations. +#[derive(Debug, Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct ActiveSyncStatus { + /// The block the sync starts at + pub start_block: i32, + /// The block the sync is currently processing + pub current_block: i32, + /// The block the sync will end on + pub end_block: i32, + /// Models that are being synced + pub models: Vec, + /// Date when the sync was requested + pub created_at: String, + /// Date when the sync started + pub started_at: String, +} + +/// Status of continuously running sync operations. +#[derive(Debug, Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct ContinuousSyncStatus { + /// The first block recevied form the chain on node startup + pub start_block: i32, + /// The latest block received from the chain + pub latest_block: i32, + /// The number of blocks we wait for before we process a block + pub confirmations: i32, + /// The block we are currently processing (should be latestBlock - confirmations) + pub current_block: i32, + /// Models that are being synced + pub models: Vec, +} +/// Status of pending sync operations. +#[derive(Debug, Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct PendingSyncStatus { + /// The block the sync starts at + pub start_block: i32, + /// The block the sync will end on + pub end_block: i32, + /// Models that are being synced + pub models: Vec, + /// Date when the sync was requested + pub created_at: String, +} + #[cfg(test)] mod tests { use super::*; diff --git a/src/lib.rs b/src/lib.rs index 156c15c..8e3c233 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -65,12 +65,21 @@ impl CeramicHttpClient { pub fn index_endpoint(&self) -> &'static str { "/api/v0/admin/modelData" } + /// Get the models endpoint + pub fn models_endpoint(&self) -> &'static str { + "/api/v0/admin/models" + } /// Get the healthcheck endpoint pub fn healthcheck_endpoint(&self) -> &'static str { "/api/v0/node/healthcheck" } + /// Get the status endpoint + pub fn node_status_endpoint(&self) -> &'static str { + "/api/v0/admin/status" + } + /// Create a serde compatible request for model creation pub async fn create_model_request( &self, @@ -118,6 +127,21 @@ impl CeramicHttpClient { api::AdminApiRequest::try_from(jws) } + /// Create a serde compatible request for listing indexed models + pub async fn create_list_indexed_models_request( + &self, + code: &str, + ) -> anyhow::Result { + let data = api::ListIndexedModelsRequest {}; + let req = api::AdminApiPayload { + code: code.to_string(), + request_path: self.models_endpoint().to_string(), + request_body: data, + }; + let jws = Jws::for_data(&self.signer, &req).await?; + api::AdminApiRequest::try_from(jws) + } + /// Create a serde compatible request for a single instance per account creation of a model pub async fn create_single_instance_request( &self, @@ -249,6 +273,20 @@ impl CeramicHttpClient { pub async fn create_healthcheck_request(&self) -> anyhow::Result { Ok(api::HealthcheckRequest {}) } + /// Create a serde compatible request for the node status + pub async fn create_node_status_request( + &self, + code: &str, + ) -> anyhow::Result { + let data = api::NodeStatusRequest {}; + let req = api::AdminApiPayload { + code: code.to_string(), + request_path: self.node_status_endpoint().to_string(), + request_body: data, + }; + let jws = Jws::for_data(&self.signer, &req).await?; + api::AdminApiRequest::try_from(jws) + } } /// Remote HTTP Functionality @@ -329,6 +367,33 @@ pub mod remote { } } + /// List indexed models on the remote ceramic + pub async fn list_indexed_models(&self) -> anyhow::Result { + let resp: api::AdminCodeResponse = self + .remote + .get(self.url_for_path(self.cli.admin_code_endpoint())?) + .send() + .await? + .json() + .await?; + let req = self + .cli + .create_list_indexed_models_request(&resp.code) + .await?; + let resp = self + .remote + .get(self.url_for_path(self.cli.models_endpoint())?) + .header( + reqwest::header::AUTHORIZATION, + format!("Basic {}", req.jws()), + ) + .send() + .await? + .json() + .await?; + Ok(resp) + } + /// Create an instance of a model that allows a single instance on the remote ceramic pub async fn create_single_instance( &self, @@ -487,6 +552,30 @@ pub mod remote { .await?; Ok(resp) } + + /// Get the node status + pub async fn node_status(&self) -> anyhow::Result { + let resp: api::AdminCodeResponse = self + .remote + .get(self.url_for_path(self.cli.admin_code_endpoint())?) + .send() + .await? + .json() + .await?; + let req = self.cli.create_node_status_request(&resp.code).await?; + let resp = self + .remote + .get(self.url_for_path(self.cli.node_status_endpoint())?) + .header( + reqwest::header::AUTHORIZATION, + format!("Basic {}", req.jws()), + ) + .send() + .await? + .json() + .await?; + Ok(resp) + } } }