Skip to content

Commit

Permalink
Fix: add fmt and clippy to CI, fix ceramic-event dependency, require …
Browse files Browse the repository at this point in the history
…docs and error on warnings
  • Loading branch information
dbcfd committed May 9, 2023
1 parent 872e7a5 commit 5b76cd4
Show file tree
Hide file tree
Showing 6 changed files with 54 additions and 12 deletions.
4 changes: 4 additions & 0 deletions .github/workflows/rust.yml
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,10 @@ jobs:
- uses: actions/checkout@v3
- name: Build
run: cargo build --verbose
- name: Check formatting
run: cargo fmt --all -- --check
- name: Check clippy
run: cargo clippy --workspace --all-targets --all-features -- -D warnings
- name: Start Ceramic
run: docker compose -f it/docker-compose.yml up -d
- name: Wait for Ceramic
Expand Down
4 changes: 2 additions & 2 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,8 @@ publish = false

[dependencies]
anyhow = "1"
#ceramic-event = { git = "https://github.com/3box/rust-ceramic", branch = "main" }
ceramic-event = { path = "/Users/dbrowning/code/3box/rust-ceramic/event" }
ceramic-event = { git = "https://github.com/3box/rust-ceramic", branch = "feat-event-usage" }
#ceramic-event = { path = "/Users/dbrowning/code/3box/rust-ceramic/event" }
json-patch = "0.3.0"
reqwest = { version = "0.11.14", features = ["json"], optional = true }
schemars = "0.8.12"
Expand Down
8 changes: 8 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
# Ceramic HTTP API Client

Ceramic [HTTP API](https://developers.ceramic.network/build/http/api/) client written in [rust](https://www.rust-lang.org/).
This library can either generate [serde](https://serde.rs/) compatible requests for use with any http client library, or make requests
against a Ceramic HTTP Api using [reqwest](https://docs.rs/reqwest/latest/reqwest/) when the `remote` feature flag is used (enabled by default).

Please see the [tests](./src/lib.rs) for more information on how to use the library.

12 changes: 5 additions & 7 deletions src/api.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
use ceramic_event::{Base64String, Jws, MultiBase32String, MultiBase36String, StreamId, StreamIdType};
use ceramic_event::{
Base64String, Jws, MultiBase32String, MultiBase36String, StreamId, StreamIdType,
};
use serde::{Deserialize, Serialize};

#[derive(Serialize)]
Expand Down Expand Up @@ -69,9 +71,7 @@ pub struct PostResponse {
#[derive(Deserialize)]
#[serde(untagged)]
pub enum PostResponseOrError {
Error {
error: String,
},
Error { error: String },
Ok(PostResponse),
}

Expand All @@ -81,9 +81,7 @@ impl PostResponseOrError {
Self::Error { error } => {
anyhow::bail!(format!("{}: {}", context, error))
}
Self::Ok(resp) => {
Ok(resp)
}
Self::Ok(resp) => Ok(resp),
}
}
}
Expand Down
25 changes: 22 additions & 3 deletions src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
//! Ceramic HTTP API
//!
//! This crate provides a client for interacting with the Ceramic HTTP API.
#![deny(warnings)]
#![deny(missing_docs)]
mod api;
mod model_definition;

Expand All @@ -20,21 +22,25 @@ struct CeramicHttpClient {
}

impl CeramicHttpClient {
/// Create a new client, using a signer and private key
pub fn new(signer: DidDocument, private_key: &str) -> Self {
Self {
signer,
private_key: private_key.to_string(),
}
}

/// Get the streams endpoint
pub fn streams_endpoint(&self) -> &'static str {
"/api/v0/streams"
}

/// Get the commits endpoint
pub fn commits_endpoint(&self) -> &'static str {
"/api/v0/commits"
}

/// Create a serde compatible request for model creation
pub async fn create_model_request(
&self,
model: &ModelDefinition,
Expand All @@ -61,6 +67,7 @@ impl CeramicHttpClient {
})
}

/// Create a serde compatible request for a single instance per account creation of a model
pub async fn create_single_instance_request(
&self,
model_id: &StreamId,
Expand Down Expand Up @@ -88,6 +95,7 @@ impl CeramicHttpClient {
})
}

/// Create a serde compatible request for a list instance per account creation of a model
pub async fn create_list_instance_request<T: Serialize>(
&self,
model_id: &StreamId,
Expand Down Expand Up @@ -117,6 +125,7 @@ impl CeramicHttpClient {
})
}

/// Create a serde compatible request to update an existing model instance
pub async fn create_update_request(
&self,
model: &StreamId,
Expand All @@ -127,7 +136,7 @@ impl CeramicHttpClient {
anyhow::bail!("StreamId was not a document");
}
let tip = Cid::from_str(get.commits[0].cid.as_ref())?;
let args = EventArgs::new_with_parent(&self.signer, &model);
let args = EventArgs::new_with_parent(&self.signer, model);
let commit = args.update(&patch, &self.private_key, &tip).await?;
let controllers: Vec<_> = args.controllers().map(|c| c.id.clone()).collect();
let data = Base64String::from(commit.linked_block.as_ref());
Expand All @@ -151,16 +160,20 @@ impl CeramicHttpClient {
}
}

/// Remote HTTP Functionality
#[cfg(feature = "remote")]
pub mod remote {
use super::*;

/// Ceramic remote http client
pub struct CeramicRemoteHttpClient {
cli: CeramicHttpClient,
remote: reqwest::Client,
url: url::Url,
}

impl CeramicRemoteHttpClient {
/// Create a new ceramic remote http client for a signer, private key, and url
pub fn new(signer: DidDocument, private_key: &str, remote: url::Url) -> Self {
Self {
cli: CeramicHttpClient::new(signer, private_key),
Expand All @@ -169,11 +182,13 @@ pub mod remote {
}
}

/// Utility function to get a url for this client's base url, given a path
pub fn url_for_path(&self, path: &str) -> anyhow::Result<url::Url> {
let u = self.url.join(path)?;
Ok(u)
}

/// Create a model on the remote ceramic
pub async fn create_model(&self, model: &ModelDefinition) -> anyhow::Result<StreamId> {
let req = self.cli.create_model_request(model).await?;
let resp: api::PostResponseOrError = self
Expand All @@ -187,6 +202,7 @@ pub mod remote {
Ok(resp.resolve("create_model")?.stream_id)
}

/// Create an instance of a model that allows a single instance on the remote ceramic
pub async fn create_single_instance(
&self,
model_id: &StreamId,
Expand All @@ -203,6 +219,7 @@ pub mod remote {
Ok(resp.resolve("create_single_instance")?.stream_id)
}

/// Create an instance of a model allowing multiple instances on a remote ceramic
pub async fn create_list_instance<T: Serialize>(
&self,
model_id: &StreamId,
Expand All @@ -223,6 +240,7 @@ pub mod remote {
Ok(resp.resolve("create_list_instance")?.stream_id)
}

/// Update an instance that was previously created
pub async fn update(
&self,
model: &StreamId,
Expand All @@ -242,13 +260,15 @@ pub mod remote {
res.resolve("Update failed")
}

/// Get an instance of model
pub async fn get(&self, stream_id: &StreamId) -> anyhow::Result<api::GetResponse> {
let endpoint = format!("{}/{}", self.cli.commits_endpoint(), stream_id);
let endpoint = self.url_for_path(&endpoint)?;
let resp: api::GetResponse = self.remote.get(endpoint).send().await?.json().await?;
Ok(resp)
}

/// Get the content of an instance of a model as a serde compatible type
pub async fn get_as<T: DeserializeOwned>(&self, stream_id: &StreamId) -> anyhow::Result<T> {
let mut resp = self.get(stream_id).await?;
if let Some(commit) = resp.commits.pop() {
Expand All @@ -261,8 +281,7 @@ pub mod remote {
}
}

//#[cfg(all(test, feature = "remote"))]
#[cfg(test)]
#[cfg(all(test, feature = "remote"))]
pub mod tests {
use super::remote::*;
use super::*;
Expand Down
13 changes: 13 additions & 0 deletions src/model_definition.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,20 +4,23 @@ use schemars::JsonSchema;
use serde::{Deserialize, Serialize};
use std::collections::HashMap;

/// Type of account relation, whether single instance per account or multiple (list)
#[derive(Debug, Deserialize, Serialize)]
#[serde(rename_all = "camelCase", tag = "type")]
pub enum ModelAccountRelation {
List,
Single,
}

/// How a model is related, whether by account or document
#[derive(Debug, Deserialize, Serialize)]
#[serde(rename_all = "camelCase", tag = "type")]
pub enum ModelRelationDefinition {
Account,
Document { model: StreamId },
}

/// Describe how model views are created
#[derive(Debug, Deserialize, Serialize)]
#[serde(rename_all = "camelCase", tag = "type")]
pub enum ModelViewDefinition {
Expand All @@ -28,10 +31,12 @@ pub enum ModelViewDefinition {
RelationCountFrom { model: StreamId, property: String },
}

/// Schema encoded as Cbor
#[derive(Debug, Deserialize, Serialize)]
#[repr(transparent)]
pub struct CborSchema(serde_json::Value);

/// Definition of a model for use when creating instances
#[derive(Debug, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct ModelDefinition {
Expand All @@ -48,6 +53,7 @@ pub struct ModelDefinition {
}

impl ModelDefinition {
/// Create a new definition for a type that implements `GetRootSchema`
pub fn new<T: GetRootSchema>(
name: &str,
account_relation: ModelAccountRelation,
Expand All @@ -65,28 +71,35 @@ impl ModelDefinition {
})
}

/// Schema of this definition
pub fn schema(&self) -> anyhow::Result<RootSchema> {
let s = serde_json::from_value(self.schema.0.clone())?;
Ok(s)
}

/// Apply description to this definition
pub fn with_description(&mut self, description: String) -> &mut Self {
self.description = Some(description);
self
}

/// Apply a relation to this definition
pub fn with_relation(&mut self, key: String, relation: ModelRelationDefinition) -> &mut Self {
self.relations.insert(key, relation);
self
}

/// Apply a view to this definition
pub fn with_view(&mut self, key: String, view: ModelViewDefinition) -> &mut Self {
self.views.insert(key, view);
self
}
}

/// A trait which helps convert a type that implements `JsonSchema` into a `RootSchema` with
/// appropriate attributes
pub trait GetRootSchema: JsonSchema {
/// Convert this object into a `RootSchema` with appropriate attributes
fn root_schema() -> RootSchema {
let settings = schemars::gen::SchemaSettings::default().with(|s| {
s.meta_schema = Some("https://json-schema.org/draft/2020-12/schema".to_string());
Expand Down

0 comments on commit 5b76cd4

Please sign in to comment.