From cbb4b2c08db5c0bca666cd07a41f42946eebae1b Mon Sep 17 00:00:00 2001 From: Sam Andreae Date: Sat, 4 Nov 2023 21:57:48 +0000 Subject: [PATCH 1/4] Move core methods into lib --- Cargo.toml | 13 +++- src/commands/{build/mod.rs => build.rs} | 25 ++----- src/commands/deploy.rs | 92 ++--------------------- src/commands/init.rs | 4 +- src/{commands => lib}/build/current.rs | 0 src/{commands => lib}/build/diff.rs | 0 src/{commands => lib}/build/executor.rs | 2 +- src/lib/build/mod.rs | 14 ++++ src/{commands => lib}/build/previous.rs | 0 src/{commands => lib}/build/write.rs | 2 +- src/lib/client.rs | 99 +++++++++++++++++++++++++ src/lib/lib.rs | 7 ++ src/{ => lib}/lock_file.rs | 0 src/{ => lib}/schema_file.rs | 0 src/{ => lib}/utils/files.rs | 0 src/{ => lib}/utils/key_pair.rs | 0 src/lib/utils/mod.rs | 4 + src/main.rs | 2 - src/utils/mod.rs | 3 +- src/{commands/build => utils}/print.rs | 8 +- 20 files changed, 157 insertions(+), 118 deletions(-) rename src/commands/{build/mod.rs => build.rs} (84%) rename src/{commands => lib}/build/current.rs (100%) rename src/{commands => lib}/build/diff.rs (100%) rename src/{commands => lib}/build/executor.rs (100%) create mode 100644 src/lib/build/mod.rs rename src/{commands => lib}/build/previous.rs (100%) rename src/{commands => lib}/build/write.rs (100%) create mode 100644 src/lib/client.rs create mode 100644 src/lib/lib.rs rename src/{ => lib}/lock_file.rs (100%) rename src/{ => lib}/schema_file.rs (100%) rename src/{ => lib}/utils/files.rs (100%) rename src/{ => lib}/utils/key_pair.rs (100%) create mode 100644 src/lib/utils/mod.rs rename src/{commands/build => utils}/print.rs (98%) diff --git a/Cargo.toml b/Cargo.toml index 96275ae..ddd4218 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,16 +1,21 @@ [package] name = "fishy" version = "0.1.0" -authors = [ - "adz ", - "sandreae ", -] +authors = ["adz ", "sandreae "] edition = "2021" description = "Create, manage and deploy p2panda schemas" repository = "https://github.com/p2panda/fishy" license = "AGPL-3.0-or-later" readme = "README.md" +[[bin]] +name = "fishy" +path = "src/main.rs" + +[lib] +name = "fishy" +path = "src/lib/lib.rs" + [profile.release] strip = true opt-level = "z" diff --git a/src/commands/build/mod.rs b/src/commands/build.rs similarity index 84% rename from src/commands/build/mod.rs rename to src/commands/build.rs index 912246d..48ffa1d 100644 --- a/src/commands/build/mod.rs +++ b/src/commands/build.rs @@ -1,28 +1,19 @@ // SPDX-License-Identifier: AGPL-3.0-or-later -mod current; -mod diff; -mod executor; -mod previous; -mod print; -mod write; - use std::path::PathBuf; use anyhow::{bail, Context, Result}; use dialoguer::Confirm; +use fishy::build::{ + execute_plan, get_current_schemas, get_diff, get_previous_schemas, write_to_lock_file, +}; +use fishy::lock_file::LockFile; +use fishy::schema_file::SchemaFile; +use fishy::utils::files::absolute_path; +use fishy::utils::key_pair; use p2panda_rs::test_utils::memory_store::MemoryStore; -use crate::commands::build::current::get_current_schemas; -use crate::commands::build::diff::get_diff; -use crate::commands::build::executor::execute_plan; -use crate::commands::build::previous::get_previous_schemas; -use crate::commands::build::print::print_plan; -use crate::commands::build::write::write_to_lock_file; -use crate::lock_file::LockFile; -use crate::schema_file::SchemaFile; -use crate::utils::files::absolute_path; -use crate::utils::key_pair; +use crate::utils::print::print_plan; use crate::utils::terminal::{print_title, print_variable}; /// Automatically creates and signs p2panda data from a key pair and the defined schemas. diff --git a/src/commands/deploy.rs b/src/commands/deploy.rs index 39e2872..20369fa 100644 --- a/src/commands/deploy.rs +++ b/src/commands/deploy.rs @@ -2,18 +2,13 @@ use std::path::PathBuf; -use anyhow::{anyhow, bail, Context, Result}; -use gql_client::Client; +use anyhow::{bail, Context, Result}; +use fishy::lock_file::LockFile; +use fishy::Client; use indicatif::ProgressBar; -use p2panda_rs::entry::decode::decode_entry; -use p2panda_rs::entry::traits::AsEntry; -use p2panda_rs::entry::{LogId, SeqNum}; -use p2panda_rs::hash::Hash; -use serde::Deserialize; -use crate::lock_file::LockFile; -use crate::utils::files::absolute_path; use crate::utils::terminal::{print_title, print_variable}; +use fishy::utils::files::absolute_path; /// Deploy created schemas on a node. pub async fn deploy(lock_path: PathBuf, endpoint: &str) -> Result<()> { @@ -41,61 +36,12 @@ pub async fn deploy(lock_path: PathBuf, endpoint: &str) -> Result<()> { let client = Client::new(endpoint); for commit in commits { - let entry = decode_entry(&commit.entry).unwrap(); + let published = client.publish(commit).await?; - let query = format!( - r#" - {{ - nextArgs(publicKey: "{}", viewId: "{}") {{ - logId - seqNum - skiplink - backlink - }} - }} - "#, - entry.public_key(), - commit.entry_hash, - ); - - let response = client.query_unwrap::(&query).await; - - if let Ok(result) = response { - let args = result.next_args; - - if entry.log_id() != &args.log_id { - bail!("Inconsistency between local commits and node detected"); - } - - // Check if node already knows about this commit - if entry.seq_num() < &args.seq_num { - skipped += 1; - progress.inc(1); - - // Skip this one - continue; - } + if !published { + skipped += 1; } - let query = format!( - r#" - mutation Publish {{ - publish(entry: "{}", operation: "{}") {{ - logId - seqNum - skiplink - backlink - }} - }} - "#, - commit.entry, commit.operation - ); - - client - .query_unwrap::(&query) - .await - .map_err(|err| anyhow!("GraphQL request to node failed: {err}"))?; - progress.inc(1); } @@ -113,27 +59,3 @@ pub async fn deploy(lock_path: PathBuf, endpoint: &str) -> Result<()> { Ok(()) } - -#[derive(Deserialize, Debug)] -#[serde(rename_all = "camelCase")] -#[allow(dead_code)] -struct NextArguments { - log_id: LogId, - seq_num: SeqNum, - skiplink: Option, - backlink: Option, -} - -/// GraphQL response for `nextArgs` query. -#[derive(Deserialize, Debug)] -#[serde(rename_all = "camelCase")] -struct NextArgsResponse { - next_args: NextArguments, -} - -#[derive(Deserialize, Debug)] -#[serde(rename_all = "camelCase")] -#[allow(dead_code)] -struct PublishResponse { - publish: NextArguments, -} diff --git a/src/commands/init.rs b/src/commands/init.rs index eec3416..1e9e81c 100644 --- a/src/commands/init.rs +++ b/src/commands/init.rs @@ -4,12 +4,12 @@ use std::path::{Path, PathBuf}; use anyhow::{bail, Result}; use dialoguer::Input; +use fishy::utils::files::{absolute_path, write_file}; +use fishy::utils::key_pair::write_key_pair; use p2panda_rs::identity::KeyPair; use p2panda_rs::schema::validate::validate_name; use crate::constants::{PRIVATE_KEY_FILE_NAME, SCHEMA_FILE_NAME}; -use crate::utils::files::{absolute_path, write_file}; -use crate::utils::key_pair::write_key_pair; use crate::utils::terminal::{print_title, print_variable}; /// Initialises all files for a new fishy project in a given folder. diff --git a/src/commands/build/current.rs b/src/lib/build/current.rs similarity index 100% rename from src/commands/build/current.rs rename to src/lib/build/current.rs diff --git a/src/commands/build/diff.rs b/src/lib/build/diff.rs similarity index 100% rename from src/commands/build/diff.rs rename to src/lib/build/diff.rs diff --git a/src/commands/build/executor.rs b/src/lib/build/executor.rs similarity index 100% rename from src/commands/build/executor.rs rename to src/lib/build/executor.rs index 294812f..53d9943 100644 --- a/src/commands/build/executor.rs +++ b/src/lib/build/executor.rs @@ -16,8 +16,8 @@ use p2panda_rs::schema::{FieldType as PandaFieldType, Schema, SchemaId}; use p2panda_rs::test_utils::memory_store::helpers::send_to_store; use p2panda_rs::test_utils::memory_store::MemoryStore; -use crate::lock_file::Commit; use crate::schema_file::{FieldType, RelationType}; +use crate::lock_file::Commit; use super::diff::{FieldDiff, FieldTypeDiff, SchemaDiff}; diff --git a/src/lib/build/mod.rs b/src/lib/build/mod.rs new file mode 100644 index 0000000..3f5ba8a --- /dev/null +++ b/src/lib/build/mod.rs @@ -0,0 +1,14 @@ +// SPDX-License-Identifier: AGPL-3.0-or-later + +mod current; +mod diff; +mod executor; +mod previous; +mod write; + +pub use current::get_current_schemas; +pub use diff::{get_diff, FieldTypeDiff}; +pub use executor::{execute_plan, Plan}; +pub use previous::get_previous_schemas; +pub use previous::PreviousSchemas; +pub use write::write_to_lock_file; diff --git a/src/commands/build/previous.rs b/src/lib/build/previous.rs similarity index 100% rename from src/commands/build/previous.rs rename to src/lib/build/previous.rs diff --git a/src/commands/build/write.rs b/src/lib/build/write.rs similarity index 100% rename from src/commands/build/write.rs rename to src/lib/build/write.rs index d158b93..f3d7847 100644 --- a/src/commands/build/write.rs +++ b/src/lib/build/write.rs @@ -4,8 +4,8 @@ use std::path::PathBuf; use anyhow::Result; -use crate::lock_file::{Commit, LockFile}; use crate::utils::files::{self}; +use crate::lock_file::{Commit, LockFile}; /// Write commits to lock file. pub fn write_to_lock_file( diff --git a/src/lib/client.rs b/src/lib/client.rs new file mode 100644 index 0000000..0518243 --- /dev/null +++ b/src/lib/client.rs @@ -0,0 +1,99 @@ +// SPDX-License-Identifier: AGPL-3.0-or-later + +use anyhow::{anyhow, bail, Result}; +use gql_client::Client as GQLClient; +use p2panda_rs::entry::decode::decode_entry; +use p2panda_rs::entry::traits::AsEntry; +use p2panda_rs::entry::{LogId, SeqNum}; +use p2panda_rs::hash::Hash; +use serde::Deserialize; + +use crate::lock_file::Commit; + +#[derive(Deserialize, Debug)] +#[serde(rename_all = "camelCase")] +#[allow(dead_code)] +struct NextArguments { + log_id: LogId, + seq_num: SeqNum, + skiplink: Option, + backlink: Option, +} + +/// GraphQL response for `nextArgs` query. +#[derive(Deserialize, Debug)] +#[serde(rename_all = "camelCase")] +struct NextArgsResponse { + next_args: NextArguments, +} + +#[derive(Deserialize, Debug)] +#[serde(rename_all = "camelCase")] +#[allow(dead_code)] +struct PublishResponse { + publish: NextArguments, +} + +pub struct Client(GQLClient); + +impl Client { + pub fn new(endpoint: &str) -> Self { + Self(GQLClient::new(endpoint)) + } + + pub async fn publish(&self, commit: Commit) -> Result { + let entry = decode_entry(&commit.entry).unwrap(); + + let query = format!( + r#" + {{ + nextArgs(publicKey: "{}", viewId: "{}") {{ + logId + seqNum + skiplink + backlink + }} + }} + "#, + entry.public_key(), + commit.entry_hash, + ); + + let response = self.0.query_unwrap::(&query).await; + + if let Ok(result) = response { + let args = result.next_args; + + if entry.log_id() != &args.log_id { + bail!("Inconsistency between local commits and node detected"); + } + + // Check if node already knows about this commit + if entry.seq_num() < &args.seq_num { + // Skip this one + return Ok(false); + } + } + + let query = format!( + r#" + mutation Publish {{ + publish(entry: "{}", operation: "{}") {{ + logId + seqNum + skiplink + backlink + }} + }} + "#, + commit.entry, commit.operation + ); + + self.0 + .query_unwrap::(&query) + .await + .map_err(|err| anyhow!("GraphQL request to node failed: {err}"))?; + + return Ok(true); + } +} diff --git a/src/lib/lib.rs b/src/lib/lib.rs new file mode 100644 index 0000000..f175244 --- /dev/null +++ b/src/lib/lib.rs @@ -0,0 +1,7 @@ +pub mod build; +mod client; +pub mod lock_file; +pub mod schema_file; +pub mod utils; + +pub use client::Client; diff --git a/src/lock_file.rs b/src/lib/lock_file.rs similarity index 100% rename from src/lock_file.rs rename to src/lib/lock_file.rs diff --git a/src/schema_file.rs b/src/lib/schema_file.rs similarity index 100% rename from src/schema_file.rs rename to src/lib/schema_file.rs diff --git a/src/utils/files.rs b/src/lib/utils/files.rs similarity index 100% rename from src/utils/files.rs rename to src/lib/utils/files.rs diff --git a/src/utils/key_pair.rs b/src/lib/utils/key_pair.rs similarity index 100% rename from src/utils/key_pair.rs rename to src/lib/utils/key_pair.rs diff --git a/src/lib/utils/mod.rs b/src/lib/utils/mod.rs new file mode 100644 index 0000000..5147879 --- /dev/null +++ b/src/lib/utils/mod.rs @@ -0,0 +1,4 @@ +// SPDX-License-Identifier: AGPL-3.0-or-later + +pub mod files; +pub mod key_pair; diff --git a/src/main.rs b/src/main.rs index 3293973..7bd2fb0 100644 --- a/src/main.rs +++ b/src/main.rs @@ -2,8 +2,6 @@ mod commands; mod constants; -mod lock_file; -mod schema_file; mod utils; use std::path::PathBuf; diff --git a/src/utils/mod.rs b/src/utils/mod.rs index ea5e516..1de6dbe 100644 --- a/src/utils/mod.rs +++ b/src/utils/mod.rs @@ -1,5 +1,4 @@ // SPDX-License-Identifier: AGPL-3.0-or-later -pub mod files; -pub mod key_pair; pub mod terminal; +pub mod print; diff --git a/src/commands/build/print.rs b/src/utils/print.rs similarity index 98% rename from src/commands/build/print.rs rename to src/utils/print.rs index 77015b3..07a5cf2 100644 --- a/src/commands/build/print.rs +++ b/src/utils/print.rs @@ -12,13 +12,13 @@ use p2panda_rs::schema::{ FieldName, FieldType as PandaFieldType, SchemaDescription, SchemaId, SchemaName, }; -use crate::schema_file::{ +use fishy::schema_file::{ FieldType, RelationId, RelationSchema, RelationType, SchemaField, SchemaFields, }; -use super::diff::FieldTypeDiff; -use super::executor::Plan; -use super::previous::PreviousSchemas; +use fishy::build::FieldTypeDiff; +use fishy::build::Plan; +use fishy::build::PreviousSchemas; /// Shows the execution plan to the user. pub fn print_plan( From 7424c9525f65fa9433ceecdc3432fd64197b8d2f Mon Sep 17 00:00:00 2001 From: Sam Andreae Date: Sat, 4 Nov 2023 22:06:15 +0000 Subject: [PATCH 2/4] Clippy & fmt --- src/lib/build/current.rs | 2 +- src/lib/build/executor.rs | 2 +- src/lib/build/write.rs | 2 +- src/lib/client.rs | 2 +- src/lib/schema_file.rs | 11 +++++++++++ src/utils/mod.rs | 2 +- 6 files changed, 16 insertions(+), 5 deletions(-) diff --git a/src/lib/build/current.rs b/src/lib/build/current.rs index 1c040f0..23f6dac 100644 --- a/src/lib/build/current.rs +++ b/src/lib/build/current.rs @@ -10,7 +10,7 @@ pub fn get_current_schemas(schema_file: &SchemaFile) -> Result bool { + self.0.is_empty() + } + /// Inserts a new field. pub fn insert(&mut self, field_name: &FieldName, field: &SchemaField) { self.0.insert(field_name.clone(), field.clone()); @@ -81,6 +86,12 @@ impl SchemaFields { } } +impl Default for SchemaFields { + fn default() -> Self { + Self::new() + } +} + /// Definition of a single schema field. #[derive(Debug, Clone, Eq, PartialEq, Serialize, Deserialize)] #[serde(untagged, deny_unknown_fields)] diff --git a/src/utils/mod.rs b/src/utils/mod.rs index 1de6dbe..a14eda4 100644 --- a/src/utils/mod.rs +++ b/src/utils/mod.rs @@ -1,4 +1,4 @@ // SPDX-License-Identifier: AGPL-3.0-or-later -pub mod terminal; pub mod print; +pub mod terminal; From 4d7052518a62c9cef28ceb6b348b35b1706a75c7 Mon Sep 17 00:00:00 2001 From: Sam Andreae Date: Sun, 5 Nov 2023 08:52:12 +0000 Subject: [PATCH 3/4] Add getters to Lockfile --- src/commands/deploy.rs | 2 +- src/lib/build/previous.rs | 5 +---- src/lib/build/write.rs | 8 ++------ src/lib/lock_file.rs | 19 +++++++++++++++++-- 4 files changed, 21 insertions(+), 13 deletions(-) diff --git a/src/commands/deploy.rs b/src/commands/deploy.rs index 20369fa..48499c6 100644 --- a/src/commands/deploy.rs +++ b/src/commands/deploy.rs @@ -22,7 +22,7 @@ pub async fn deploy(lock_path: PathBuf, endpoint: &str) -> Result<()> { lock_path.display() ))?; - let commits = lock_file.commits.unwrap_or(Vec::new()); + let commits = lock_file.commits(); if commits.is_empty() { bail!("No data given to deploy to node. Please run `update` command first."); } diff --git a/src/lib/build/previous.rs b/src/lib/build/previous.rs index 5ad722e..de2ab46 100644 --- a/src/lib/build/previous.rs +++ b/src/lib/build/previous.rs @@ -21,11 +21,8 @@ pub async fn get_previous_schemas( store: &MemoryStore, lock_file: &LockFile, ) -> Result { - // Sometimes `commits` is not defined in the .toml file, set an empty array as a fallback - let commits = lock_file.commits.clone().unwrap_or_default(); - // Publish every commit in our temporary, in-memory "node" to materialize schema documents - for commit in commits { + for commit in lock_file.commits() { // Check entry hash integrity if commit.entry_hash != commit.entry.hash() { bail!( diff --git a/src/lib/build/write.rs b/src/lib/build/write.rs index d158b93..2807e53 100644 --- a/src/lib/build/write.rs +++ b/src/lib/build/write.rs @@ -10,17 +10,13 @@ use crate::utils::files::{self}; /// Write commits to lock file. pub fn write_to_lock_file( mut new_commits: Vec, - mut lock_file: LockFile, + lock_file: LockFile, lock_path: PathBuf, ) -> Result<()> { // Add new commits to the existing ones let applied_commits_count = new_commits.len(); - let mut commits: Vec = Vec::new(); - - if let Some(current_commits) = lock_file.commits.as_mut() { - commits.append(current_commits); - } + let mut commits: Vec = lock_file.commits(); commits.append(&mut new_commits); // Write everything to .toml file diff --git a/src/lib/lock_file.rs b/src/lib/lock_file.rs index 639c2c8..3d15a46 100644 --- a/src/lib/lock_file.rs +++ b/src/lib/lock_file.rs @@ -31,8 +31,8 @@ use crate::utils::files; #[derive(Debug, Serialize, Deserialize)] #[serde(deny_unknown_fields)] pub struct LockFile { - pub version: LockFileVersion, - pub commits: Option>, + version: LockFileVersion, + commits: Option>, } impl LockFile { @@ -52,6 +52,21 @@ impl LockFile { toml::from_str(&data).with_context(|| "Invalid TOML syntax in lock file")?; Ok(schema_file) } + + /// Version of the lockfile. + pub fn version(&self) -> &LockFileVersion { + &self.version + } + + /// Commits contained in the lockfile. + /// + /// Returns an empty vec if no `commits` were defined in the .toml file yet. + pub fn commits(&self) -> Vec { + match &self.commits { + Some(commits) => commits.clone(), + None => Vec::new(), + } + } } /// Known versions of lock file format. From ea22d4142bed21529f648675677bbde4b890263c Mon Sep 17 00:00:00 2001 From: Sam Andreae Date: Sun, 5 Nov 2023 08:52:28 +0000 Subject: [PATCH 4/4] fmt --- src/lib/lock_file.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lib/lock_file.rs b/src/lib/lock_file.rs index 3d15a46..7fbc146 100644 --- a/src/lib/lock_file.rs +++ b/src/lib/lock_file.rs @@ -59,7 +59,7 @@ impl LockFile { } /// Commits contained in the lockfile. - /// + /// /// Returns an empty vec if no `commits` were defined in the .toml file yet. pub fn commits(&self) -> Vec { match &self.commits {