From ed8b079f99033f959f2222b778b3b589922b2404 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Guillem=20C=C3=B3rdoba?= Date: Wed, 15 Nov 2023 12:29:59 +0100 Subject: [PATCH 1/2] Refactor gets --- Cargo.lock | 46 ++-- Cargo.toml | 6 +- run_test.sh | 3 + src/scaffold/collection/coordinator.rs | 12 +- src/scaffold/entry_type/coordinator.rs | 208 ++++++++++++------ src/scaffold/entry_type/integrity.rs | 12 +- src/scaffold/link_type/integrity.rs | 92 ++++---- src/scaffold/zome/coordinator.rs | 2 +- ...{{kebab_case entry_type.name}}.test.ts.hbs | 14 +- ...kebab_case entry_type.name}}-detail.ts.hbs | 2 +- ...{{kebab_case entry_type.name}}.test.ts.hbs | 14 +- ...al_case entry_type.name}}Detail.svelte.hbs | 2 +- ...{{kebab_case entry_type.name}}.test.ts.hbs | 14 +- .../coordinator/hello_world/src/lib.rs.hbs | 4 +- ...{{kebab_case entry_type.name}}.test.ts.hbs | 14 +- ...ascal_case entry_type.name}}Detail.vue.hbs | 2 +- 16 files changed, 271 insertions(+), 176 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 5916e7857..f859dc7d8 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -420,7 +420,7 @@ checksum = "a66537f1bb974b254c98ed142ff995236e81b9d0fe4db0575f46612cb15eb0f9" dependencies = [ "proc-macro2", "quote", - "syn 2.0.38", + "syn 2.0.39", ] [[package]] @@ -880,7 +880,7 @@ dependencies = [ "heck 0.4.1", "proc-macro2", "quote", - "syn 2.0.38", + "syn 2.0.39", ] [[package]] @@ -1352,7 +1352,7 @@ dependencies = [ "ident_case", "proc-macro2", "quote", - "syn 2.0.38", + "syn 2.0.39", ] [[package]] @@ -1396,7 +1396,7 @@ checksum = "836a9bbc7ad63342d6d6e7b815ccab164bc77a2d95d84bc3117a8c0d5c98e2d5" dependencies = [ "darling_core 0.20.3", "quote", - "syn 2.0.38", + "syn 2.0.39", ] [[package]] @@ -1468,7 +1468,7 @@ checksum = "67e77553c4162a157adbf834ebae5b415acbecbeafc7a74b0e886657506a7611" dependencies = [ "proc-macro2", "quote", - "syn 2.0.38", + "syn 2.0.39", ] [[package]] @@ -1702,7 +1702,7 @@ dependencies = [ "darling 0.20.3", "proc-macro2", "quote", - "syn 2.0.38", + "syn 2.0.39", ] [[package]] @@ -2018,7 +2018,7 @@ checksum = "53b153fd91e4b0147f4aced87be237c98248656bb01050b96bf3ee89220a8ddb" dependencies = [ "proc-macro2", "quote", - "syn 2.0.38", + "syn 2.0.39", ] [[package]] @@ -2743,7 +2743,7 @@ dependencies = [ "serde_json", "serde_yaml 0.8.26", "structopt", - "syn 1.0.109", + "syn 2.0.39", "temp-dir", "thiserror", "time 0.3.23", @@ -4762,7 +4762,7 @@ checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c" dependencies = [ "proc-macro2", "quote", - "syn 2.0.38", + "syn 2.0.39", ] [[package]] @@ -5051,7 +5051,7 @@ dependencies = [ "pest_meta", "proc-macro2", "quote", - "syn 2.0.38", + "syn 2.0.39", ] [[package]] @@ -5082,7 +5082,7 @@ checksum = "4359fd9c9171ec6e8c62926d6faaf553a8dc3f64e1507e76da7911b4f6a04405" dependencies = [ "proc-macro2", "quote", - "syn 2.0.38", + "syn 2.0.39", ] [[package]] @@ -5233,12 +5233,12 @@ dependencies = [ [[package]] name = "prettyplease" -version = "0.1.25" +version = "0.2.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6c8646e95016a7a6c4adea95bafa8a16baab64b583356217f2c85db4a39d9a86" +checksum = "ae005bd773ab59b4725093fd7df83fd7892f7d8eafb48dbd7de6e024e4215f9d" dependencies = [ "proc-macro2", - "syn 1.0.109", + "syn 2.0.39", ] [[package]] @@ -6376,7 +6376,7 @@ checksum = "5dd83d6dde2b6b2d466e14d9d1acce8816dedee94f735eac6395808b3483c6d6" dependencies = [ "proc-macro2", "quote", - "syn 2.0.38", + "syn 2.0.39", ] [[package]] @@ -6830,9 +6830,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.38" +version = "2.0.39" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e96b79aaa137db8f61e26363a0c9b47d8b4ec75da28b7d1d614c2303e232408b" +checksum = "23e78b90f2fcf45d3e842032ce32e3f2d1545ba6636271dcbf24fa306d87be7a" dependencies = [ "proc-macro2", "quote", @@ -7076,7 +7076,7 @@ checksum = "266b2e40bc00e5a6c09c3584011e08b06f123c00362c92b975ba9843aaaa14b8" dependencies = [ "proc-macro2", "quote", - "syn 2.0.38", + "syn 2.0.39", ] [[package]] @@ -7250,7 +7250,7 @@ checksum = "630bdcf245f78637c13ec01ffae6187cca34625e8c63150d424b59e55af2675e" dependencies = [ "proc-macro2", "quote", - "syn 2.0.38", + "syn 2.0.39", ] [[package]] @@ -7497,7 +7497,7 @@ checksum = "34704c8d6ebcbc939824180af020566b01a7c01f80641264eba0999f6c2b6be7" dependencies = [ "proc-macro2", "quote", - "syn 2.0.38", + "syn 2.0.39", ] [[package]] @@ -8143,7 +8143,7 @@ dependencies = [ "once_cell", "proc-macro2", "quote", - "syn 2.0.38", + "syn 2.0.39", "wasm-bindgen-shared", ] @@ -8177,7 +8177,7 @@ checksum = "c5353b8dab669f5e10f5bd76df26a9360c748f054f862ff5f3f8aae0c7fb3907" dependencies = [ "proc-macro2", "quote", - "syn 2.0.38", + "syn 2.0.39", "wasm-bindgen-backend", "wasm-bindgen-shared", ] @@ -8863,7 +8863,7 @@ checksum = "a25f293fe55f0a48e7010d65552bb63704f6ceb55a1a385da10d41d8f78e4a3d" dependencies = [ "proc-macro2", "quote", - "syn 2.0.38", + "syn 2.0.39", ] [[package]] diff --git a/Cargo.toml b/Cargo.toml index 2b43f200e..47d947c50 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -18,7 +18,7 @@ name = "holochain_scaffolding_cli" path = "src/lib.rs" [dependencies] -holochain = { features=["test_utils"], version = "0.2.1"} +holochain = { features = ["test_utils"], version = "0.2.1" } holochain_types = "0.2.1" holochain_util = { features = ["backtrace"], version = "0.2.1" } mr_bundle = "0.2.1" @@ -41,10 +41,10 @@ time = "=0.3.23" tokio = { version = "1.11", features = ["full"] } toml = "0.5.9" convert_case = "0.6.0" -syn = { version = "1.0.102", features = ["full", "extra-traits"] } +syn = { version = "2.0.39", features = ["full", "extra-traits"] } quote = "1.0.21" pluralizer = "0.3.1" -prettyplease = "0.1.21" +prettyplease = "0.2.15" proc-macro2 = "1" handlebars = "4.3.5" include_dir = "0.7.3" diff --git a/run_test.sh b/run_test.sh index d0870eeb9..2e7ee45e7 100755 --- a/run_test.sh +++ b/run_test.sh @@ -47,6 +47,7 @@ set -e npm i npm run build -w ui npm t +npm run package " rm -rf /tmp/forum-vue @@ -78,6 +79,7 @@ set -e npm i npm run build -w ui npm t +npm run package " rm -rf /tmp/forum-lit @@ -111,6 +113,7 @@ npm run build -w ui npm run format -w ui npm run lint -w ui npm t +npm run package " diff --git a/src/scaffold/collection/coordinator.rs b/src/scaffold/collection/coordinator.rs index 2a83565ef..b920d8f0d 100644 --- a/src/scaffold/collection/coordinator.rs +++ b/src/scaffold/collection/coordinator.rs @@ -25,7 +25,10 @@ fn global_collection_getter( link_type_name: &String, entry_type_reference: &EntryTypeReference, ) -> String { - let snake_to_hash_type = entry_type_reference.hash_type().to_string().to_case(Case::Snake); + let snake_to_hash_type = entry_type_reference + .hash_type() + .to_string() + .to_case(Case::Snake); let snake_collection_name = collection_name.to_case(Case::Snake); format!( @@ -66,7 +69,10 @@ fn by_author_collection_getter( link_type_name: &String, entry_type_reference: &EntryTypeReference, ) -> String { - let snake_to_hash_type = entry_type_reference.hash_type().to_string().to_case(Case::Snake); + let snake_to_hash_type = entry_type_reference + .hash_type() + .to_string() + .to_case(Case::Snake); format!( r#"use hdk::prelude::*; @@ -184,7 +190,7 @@ fn add_create_link_in_create_function( if item_fn .attrs .iter() - .any(|a| a.path.segments.iter().any(|s| s.ident.eq("hdk_extern"))) + .any(|a| a.path().segments.iter().any(|s| s.ident.eq("hdk_extern"))) && item_fn.sig.ident.eq(&fn_name.sig.ident) { for new_stmt in stmts.clone() { diff --git a/src/scaffold/entry_type/coordinator.rs b/src/scaffold/entry_type/coordinator.rs index 1ad372286..c7bc77577 100644 --- a/src/scaffold/entry_type/coordinator.rs +++ b/src/scaffold/entry_type/coordinator.rs @@ -14,62 +14,100 @@ use crate::{ use super::{ crud::Crud, - definitions::{Cardinality, EntryDefinition}, + definitions::{Cardinality, EntryDefinition, FieldType}, integrity::find_ending_match_expr_in_block, }; pub fn no_update_read_handler(entry_def: &EntryDefinition) -> String { let hash_type = entry_def.referenceable().hash_type().to_string(); - let snake_entry_def = entry_def.name.to_case(Case::Snake); - - format!( - r#"#[hdk_extern] -pub fn get_{snake_entry_def}({snake_entry_def}_hash: {hash_type}) -> ExternResult> {{ - get({snake_entry_def}_hash, GetOptions::default()) + let snake_entry_def_name = entry_def.name.to_case(Case::Snake); + + match entry_def.referenceable().hash_type() { + FieldType::ActionHash => format!( + r#"#[hdk_extern] +pub fn get_{snake_entry_def_name}({snake_entry_def_name}_hash: {hash_type}) -> ExternResult> {{ + let Some(details) = get_details({snake_entry_def_name}_hash, GetOptions::default())? else {{ + return Ok(None); + }}; + match details {{ + Details::Record(details) => Ok(Some(details.record)), + _ => Err(wasm_error!(WasmErrorInner::Guest(String::from( + "Malformed get details response" + )))), + }} }}"#, - ) + ), + FieldType::EntryHash => format!( + r#"#[hdk_extern] +pub fn get_{snake_entry_def_name}({snake_entry_def_name}_hash: {hash_type}) -> ExternResult> {{ + let Some(details) = get_details({snake_entry_def_name}_hash, GetOptions::default())? else {{ + return Ok(None); + }}; + match details {{ + Details::Entry(details) => Ok(Some(Record::new(details.actions[0].clone(), Some(details.entry)))), + _ => Err(wasm_error!(WasmErrorInner::Guest(String::from( + "Malformed get details response" + )))), + }} +}}"#, + ), + _ => format!(""), + } } pub fn read_handler_without_linking_to_updates(entry_def: &EntryDefinition) -> String { - let entry_def_name = entry_def.name.clone(); + let snake_entry_def_name = entry_def.name.clone(); format!( r#"#[hdk_extern] -pub fn get_{}(original_{}_hash: ActionHash) -> ExternResult> {{ - get_latest_{}(original_{}_hash) +pub fn get_original_{snake_entry_def_name}(original_{snake_entry_def_name}_hash: ActionHash) -> ExternResult> {{ + let Some(details) = get_details(original_{snake_entry_def_name}_hash, GetOptions::default())? else {{ + return Ok(None); + }}; + match details {{ + Details::Record(details) => Ok(Some(details.record)), + _ => Err(wasm_error!(WasmErrorInner::Guest(String::from( + "Malformed get details response" + )))), + }} }} -fn get_latest_{}({}_hash: ActionHash) -> ExternResult> {{ - let details = get_details({}_hash, GetOptions::default())? - .ok_or(wasm_error!(WasmErrorInner::Guest("{} not found".into())))?; - - let record_details = match details {{ - Details::Entry(_) => Err(wasm_error!(WasmErrorInner::Guest( - "Malformed details".into() - ))), - Details::Record(record_details) => Ok(record_details) - }}?; - - // If there is some delete action, it means that the whole entry is deleted - if record_details.deletes.len() > 0 {{ - return Ok(None); - }} - - match record_details.updates.last() {{ - Some(update) => get_latest_{}(update.action_address().clone()), - None => Ok(Some(record_details.record)), - }} +#[hdk_extern] +pub fn get_latest_{snake_entry_def_name}(original_{snake_entry_def_name}_hash: ActionHash) -> ExternResult> {{ + let Some(details) = get_details(original_{snake_entry_def_name}_hash, GetOptions::default())? else {{ + return Ok(None); + }}; + + let record_details = match details {{ + Details::Entry(_) => Err(wasm_error!(WasmErrorInner::Guest( + "Malformed details".into() + ))), + Details::Record(record_details) => Ok(record_details) + }}?; + + match record_details.updates.last() {{ + Some(update) => get_latest_{snake_entry_def_name}(update.action_address().clone()), + None => Ok(Some(record_details.record)), + }} +}} + +#[hdk_extern] +pub fn get_all_{snake_entry_def_name}_revisions(original_{snake_entry_def_name}_hash: ActionHash) -> ExternResult> {{ + let Some(Details::Record(details)) = get_details(original_{snake_entry_def_name}_hash, GetOptions::default())? else {{ + return Ok(vec![]); + }}; + + let mut records = vec![details.record]; + + for update in details.updates {{ + let mut update_records = get_all_{snake_entry_def_name}_revisions(update.action_address().clone())?; + + records.append(&mut update_records); + }} + + Ok(records) }} "#, - entry_def_name.to_case(Case::Snake), - entry_def_name.to_case(Case::Snake), - entry_def_name.to_case(Case::Snake), - entry_def_name.to_case(Case::Snake), - entry_def_name.to_case(Case::Snake), - entry_def_name.to_case(Case::Snake), - entry_def_name.to_case(Case::Snake), - entry_def_name.to_case(Case::Pascal), - entry_def_name.to_case(Case::Snake) ) } @@ -78,30 +116,58 @@ pub fn updates_link_name(entry_def_name: &String) -> String { } pub fn read_handler_with_linking_to_updates(entry_def_name: &String) -> String { + let snake_entry_def_name = entry_def_name.to_case(Case::Snake); format!( r#"#[hdk_extern] -pub fn get_{}(original_{}_hash: ActionHash) -> ExternResult> {{ - let links = get_links(original_{}_hash.clone(), LinkTypes::{}, None)?; - - let latest_link = links.into_iter().max_by(|link_a, link_b| link_a.timestamp.cmp(&link_b.timestamp)); - - let latest_{}_hash = match latest_link {{ - Some(link) => link.target.clone().into_action_hash().ok_or(wasm_error!( - WasmErrorInner::Guest(String::from("No action hash associated with link")) - ))?, - None => original_{}_hash.clone() - }}; - - get(latest_{}_hash, GetOptions::default()) +pub fn get_latest_{snake_entry_def_name}(original_{snake_entry_def_name}_hash: ActionHash) -> ExternResult> {{ + let links = get_links(original_{snake_entry_def_name}_hash.clone(), LinkTypes::{}, None)?; + + let latest_link = links.into_iter().max_by(|link_a, link_b| link_a.timestamp.cmp(&link_b.timestamp)); + + let latest_{snake_entry_def_name}_hash = match latest_link {{ + Some(link) => link.target.clone().into_action_hash().ok_or(wasm_error!( + WasmErrorInner::Guest(String::from("No action hash associated with link")) + ))?, + None => original_{snake_entry_def_name}_hash.clone() + }}; + + get(latest_{snake_entry_def_name}_hash, GetOptions::default()) +}} + +#[hdk_extern] +pub fn get_original_{snake_entry_def_name}(original_{snake_entry_def_name}_hash: ActionHash) -> ExternResult> {{ + let Some(details) = get_details(original_{snake_entry_def_name}_hash, GetOptions::default())? else {{ + return Ok(None); + }}; + match details {{ + Details::Record(details) => Ok(Some(details.record)), + _ => Err(wasm_error!(WasmErrorInner::Guest(String::from( + "Malformed get details response" + )))), + }} +}} + +#[hdk_extern] +pub fn get_all_{snake_entry_def_name}_revisions(original_{snake_entry_def_name}_hash: ActionHash) -> ExternResult> {{ + let links = get_links(original_{snake_entry_def_name}_hash.clone(), LinkTypes::{}, None)?; + + let get_input: Vec = links + .into_iter() + .map(|link| Ok(GetInput::new( + link.target.into_action_hash().ok_or(wasm_error!(WasmErrorInner::Guest(String::from("No action hash associated with link"))))?.into(), + GetOptions::default(), + ))) + .collect::>>()?; + + // load the records for all the links + let records = HDK.with(|hdk| hdk.borrow().get(get_input))?; + let records: Vec = records.into_iter().filter_map(|r| r).collect(); + + Ok(records) }} "#, - entry_def_name.to_case(Case::Snake), - entry_def_name.to_case(Case::Snake), - entry_def_name.to_case(Case::Snake), updates_link_name(entry_def_name), - entry_def_name.to_case(Case::Snake), - entry_def_name.to_case(Case::Snake), - entry_def_name.to_case(Case::Snake), + updates_link_name(entry_def_name), ) } @@ -276,15 +342,29 @@ pub fn update_{}(input: Update{}Input) -> ExternResult {{ } pub fn delete_handler(entry_def_name: &String) -> String { + let snake_entry_def_name = entry_def_name.to_case(Case::Snake); format!( r#"#[hdk_extern] -pub fn delete_{}(original_{}_hash: ActionHash) -> ExternResult {{ - delete_entry(original_{}_hash) +pub fn delete_{snake_entry_def_name}(original_{snake_entry_def_name}_hash: ActionHash) -> ExternResult {{ + delete_entry(original_{snake_entry_def_name}_hash) +}} + +#[hdk_extern] +pub fn get_deletes_for_{snake_entry_def_name}( + original_{snake_entry_def_name}_hash: ActionHash, +) -> ExternResult>> {{ + let Some(details) = get_details(original_{snake_entry_def_name}_hash, GetOptions::default())? else {{ + return Ok(None); + }}; + + match details {{ + Details::Entry(_) => Err(wasm_error!(WasmErrorInner::Guest( + "Malformed details".into() + ))), + Details::Record(record_details) => Ok(Some(record_details.deletes)), + }} }} "#, - entry_def_name.to_case(Case::Snake), - entry_def_name.to_case(Case::Snake), - entry_def_name.to_case(Case::Snake) ) } diff --git a/src/scaffold/entry_type/integrity.rs b/src/scaffold/entry_type/integrity.rs index a3612a301..ce0928120 100644 --- a/src/scaffold/entry_type/integrity.rs +++ b/src/scaffold/entry_type/integrity.rs @@ -246,7 +246,7 @@ pub fn find_ending_match_expr_in_block<'a>( ) -> Option<&'a mut syn::ExprMatch> { if let Some(e) = block.stmts.last_mut() { match e { - syn::Stmt::Expr(syn::Expr::Match(e_m)) => Some(e_m), + syn::Stmt::Expr(syn::Expr::Match(e_m), _) => Some(e_m), _ => None, } } else { @@ -347,7 +347,7 @@ pub use {}::*; if let syn::Item::Fn(item_fn) = item { if item_fn.sig.ident.to_string().eq(&String::from("validate")) { for stmt in &mut item_fn.block.stmts { - if let syn::Stmt::Expr(syn::Expr::Match(match_expr)) = stmt { + if let syn::Stmt::Expr(syn::Expr::Match(match_expr),_) = stmt { if let syn::Expr::Try(try_expr) = &mut *match_expr.expr { if let syn::Expr::MethodCall(call) = &mut *try_expr.expr { if call.method.to_string().eq(&String::from("flattened")) @@ -357,7 +357,7 @@ pub use {}::*; turbofish.args.first_mut() { *first_arg = - syn::GenericMethodArgument::Type( + syn::GenericArgument::Type( syn::parse_str::( "EntryTypes", )?, @@ -380,7 +380,7 @@ pub use {}::*; .map(|mut i| { if let syn::Item::Enum(mut item_enum) = i.clone() { if item_enum.attrs.iter().any(|a| { - a.path.segments.iter().any(|s| s.ident.eq("hdk_entry_defs")) + a.path().segments.iter().any(|s| s.ident.eq("hdk_entry_defs")) }) { if item_enum .variants @@ -451,7 +451,7 @@ pub fn get_all_entry_types( if item_enum .attrs .iter() - .any(|a| a.path.segments.iter().any(|s| s.ident.eq("hdk_entry_defs"))) + .any(|a| a.path().segments.iter().any(|s| s.ident.eq("hdk_entry_defs"))) { return Some(item_enum.clone()); } @@ -526,7 +526,7 @@ fn add_entry_type_to_validation_arms( if let syn::Item::Fn(item_fn) = item { if item_fn.sig.ident.to_string().eq(&String::from("validate")) { for stmt in &mut item_fn.block.stmts { - if let syn::Stmt::Expr(syn::Expr::Match(match_expr)) = stmt { + if let syn::Stmt::Expr(syn::Expr::Match(match_expr),_) = stmt { if let syn::Expr::Try(try_expr) = &mut *match_expr.expr { if let syn::Expr::MethodCall(call) = &mut *try_expr.expr { if call.method.to_string().eq(&String::from("flattened")) { diff --git a/src/scaffold/link_type/integrity.rs b/src/scaffold/link_type/integrity.rs index 91467e629..95ac61445 100644 --- a/src/scaffold/link_type/integrity.rs +++ b/src/scaffold/link_type/integrity.rs @@ -75,11 +75,12 @@ pub fn add_link_type_to_integrity_zome( &|_path, file| { file.items.clone().into_iter().find(|i| { if let syn::Item::Enum(item_enum) = i.clone() { - if item_enum - .attrs - .iter() - .any(|a| a.path.segments.iter().any(|s| s.ident.eq("hdk_link_types"))) - { + if item_enum.attrs.iter().any(|a| { + a.path() + .segments + .iter() + .any(|s| s.ident.eq("hdk_link_types")) + }) { return true; } } @@ -134,21 +135,23 @@ pub fn add_link_type_to_integrity_zome( if let syn::Item::Fn(item_fn) = item { if item_fn.sig.ident.to_string().eq(&String::from("validate")) { for stmt in &mut item_fn.block.stmts { - if let syn::Stmt::Expr(syn::Expr::Match(match_expr)) = stmt { + if let syn::Stmt::Expr(syn::Expr::Match(match_expr), _) = stmt { if let syn::Expr::Try(try_expr) = &mut *match_expr.expr { if let syn::Expr::MethodCall(call) = &mut *try_expr.expr { - if call.method.to_string().eq(&String::from("flattened")) + if call + .method + .to_string() + .eq(&String::from("flattened")) { if let Some(turbofish) = &mut call.turbofish { if let Some(last_arg) = turbofish.args.last_mut() { - *last_arg = - syn::GenericMethodArgument::Type( - syn::parse_str::( - "LinkTypes", - )?, - ); + *last_arg = syn::GenericArgument::Type( + syn::parse_str::( + "LinkTypes", + )?, + ); } } } @@ -161,39 +164,42 @@ pub fn add_link_type_to_integrity_zome( } } - file.items = - file.items - .into_iter() - .map(|mut i| { - if let syn::Item::Enum(mut item_enum) = i.clone() { - if item_enum.attrs.iter().any(|a| { - a.path.segments.iter().any(|s| s.ident.eq("hdk_link_types")) - }) { - if item_enum - .variants - .iter() - .any(|v| v.ident.to_string().eq(&pascal_case_link_type_name)) - { - return Err(ScaffoldError::LinkTypeAlreadyExists( - link_type_name.clone(), - dna_manifest.name(), - zome_manifest.name.0.to_string(), - )); - } - - let new_variant = syn::parse_str::( - format!("{}", pascal_case_link_type_name).as_str(), - )?; - item_enum.variants.push(new_variant); - return Ok(syn::Item::Enum(item_enum)); + file.items = file + .items + .into_iter() + .map(|mut i| { + if let syn::Item::Enum(mut item_enum) = i.clone() { + if item_enum.attrs.iter().any(|a| { + a.path() + .segments + .iter() + .any(|s| s.ident.eq("hdk_link_types")) + }) { + if item_enum + .variants + .iter() + .any(|v| v.ident.to_string().eq(&pascal_case_link_type_name)) + { + return Err(ScaffoldError::LinkTypeAlreadyExists( + link_type_name.clone(), + dna_manifest.name(), + zome_manifest.name.0.to_string(), + )); } + + let new_variant = syn::parse_str::( + format!("{}", pascal_case_link_type_name).as_str(), + )?; + item_enum.variants.push(new_variant); + return Ok(syn::Item::Enum(item_enum)); } + } - add_link_type_to_validation_arms(&mut i, &link_type_name)?; + add_link_type_to_validation_arms(&mut i, &link_type_name)?; - Ok(i) - }) - .collect::>>()?; + Ok(i) + }) + .collect::>>()?; Ok(file) }, @@ -492,7 +498,7 @@ fn add_link_type_to_validation_arms( if let syn::Item::Fn(item_fn) = item { if item_fn.sig.ident.to_string().eq(&String::from("validate")) { for stmt in &mut item_fn.block.stmts { - if let syn::Stmt::Expr(syn::Expr::Match(match_expr)) = stmt { + if let syn::Stmt::Expr(syn::Expr::Match(match_expr), _) = stmt { if let syn::Expr::Try(try_expr) = &mut *match_expr.expr { if let syn::Expr::MethodCall(call) = &mut *try_expr.expr { if call.method.to_string().eq(&String::from("flattened")) { diff --git a/src/scaffold/zome/coordinator.rs b/src/scaffold/zome/coordinator.rs index c00c744de..e1c3d0e47 100644 --- a/src/scaffold/zome/coordinator.rs +++ b/src/scaffold/zome/coordinator.rs @@ -215,7 +215,7 @@ pub fn find_all_extern_functions(zome_file_tree: &ZomeFileTree) -> ScaffoldResul if item_fn .attrs .iter() - .any(|a| a.path.segments.iter().any(|s| s.ident.eq("hdk_extern"))) + .any(|a| a.path().segments.iter().any(|s| s.ident.eq("hdk_extern"))) { return Some(item_fn); } diff --git a/templates/lit/entry-type/tests/src/{{dna_role_name}}/{{coordinator_zome_manifest.name}}/{{kebab_case entry_type.name}}.test.ts.hbs b/templates/lit/entry-type/tests/src/{{dna_role_name}}/{{coordinator_zome_manifest.name}}/{{kebab_case entry_type.name}}.test.ts.hbs index 02f8b154e..0c2a05f5a 100644 --- a/templates/lit/entry-type/tests/src/{{dna_role_name}}/{{coordinator_zome_manifest.name}}/{{kebab_case entry_type.name}}.test.ts.hbs +++ b/templates/lit/entry-type/tests/src/{{dna_role_name}}/{{coordinator_zome_manifest.name}}/{{kebab_case entry_type.name}}.test.ts.hbs @@ -58,7 +58,7 @@ test('create and read {{pascal_case entry_type.name}}', async () => { // Bob gets the created {{pascal_case entry_type.name}} const createReadOutput: Record = await bob.cells[0].callZome({ zome_name: "{{coordinator_zome_manifest.name}}", - fn_name: "get_{{snake_case entry_type.name}}", + fn_name: "{{#if crud.update}}get_original_{{snake_case entry_type.name}}{{else}}get_{{snake_case entry_type.name}}{{/if}}", payload: {{#if entry_type.reference_entry_hash}}(record.signed_action.hashed.content as NewEntryAction).entry_hash{{else}}record.signed_action.hashed.hash{{/if}}, }); assert.deepEqual(sample, decode((createReadOutput.entry as any).Present.entry) as any); @@ -112,7 +112,7 @@ test('create and update {{pascal_case entry_type.name}}', async () => { // Bob gets the updated {{pascal_case entry_type.name}} const readUpdatedOutput0: Record = await bob.cells[0].callZome({ zome_name: "{{coordinator_zome_manifest.name}}", - fn_name: "get_{{snake_case entry_type.name}}", + fn_name: "get_latest_{{snake_case entry_type.name}}", payload: updatedRecord.signed_action.hashed.hash, }); assert.deepEqual(contentUpdate, decode((readUpdatedOutput0.entry as any).Present.entry) as any); @@ -140,7 +140,7 @@ test('create and update {{pascal_case entry_type.name}}', async () => { // Bob gets the updated {{pascal_case entry_type.name}} const readUpdatedOutput1: Record = await bob.cells[0].callZome({ zome_name: "{{coordinator_zome_manifest.name}}", - fn_name: "get_{{snake_case entry_type.name}}", + fn_name: "get_latest_{{snake_case entry_type.name}}", payload: updatedRecord.signed_action.hashed.hash, }); assert.deepEqual(contentUpdate, decode((readUpdatedOutput1.entry as any).Present.entry) as any); @@ -181,13 +181,13 @@ test('create and delete {{pascal_case entry_type.name}}', async () => { // Wait for the entry deletion to be propagated to the other node. await dhtSync([alice, bob], alice.cells[0].cell_id[0]); - // Bob tries to get the deleted {{pascal_case entry_type.name}} - const readDeletedOutput = await bob.cells[0].callZome({ + // Bob gets the deletions for {{pascal_case entry_type.name}} + const deletesFor{{title_case entry_type.name}} = await bob.cells[0].callZome({ zome_name: "{{coordinator_zome_manifest.name}}", - fn_name: "get_{{snake_case entry_type.name}}", + fn_name: "get_deletes_for_{{snake_case entry_type.name}}", payload: record.signed_action.hashed.hash, }); - assert.notOk(readDeletedOutput); + assert.equal(deletesFor{{title_case entry_type.name}}.length, 1); }); }); {{/if}} diff --git a/templates/lit/entry-type/ui/src/{{dna_role_name}}/{{coordinator_zome_manifest.name}}/{{kebab_case entry_type.name}}-detail.ts.hbs b/templates/lit/entry-type/ui/src/{{dna_role_name}}/{{coordinator_zome_manifest.name}}/{{kebab_case entry_type.name}}-detail.ts.hbs index e2166e67d..23d5fc68c 100644 --- a/templates/lit/entry-type/ui/src/{{dna_role_name}}/{{coordinator_zome_manifest.name}}/{{kebab_case entry_type.name}}-detail.ts.hbs +++ b/templates/lit/entry-type/ui/src/{{dna_role_name}}/{{coordinator_zome_manifest.name}}/{{kebab_case entry_type.name}}-detail.ts.hbs @@ -38,7 +38,7 @@ export class {{pascal_case entry_type.name}}Detail extends LitElement { cap_secret: null, role_name: '{{dna_role_name}}', zome_name: '{{coordinator_zome_manifest.name}}', - fn_name: 'get_{{snake_case entry_type.name}}', + fn_name: '{{#if crud.update}}get_latest_{{snake_case entry_type.name}}{{else}}get_{{snake_case entry_type.name}}{{/if}}', payload: {{camel_case entry_type.name}}Hash, }) as Promise, () => [this.{{camel_case entry_type.name}}Hash]); diff --git a/templates/svelte/entry-type/tests/src/{{dna_role_name}}/{{coordinator_zome_manifest.name}}/{{kebab_case entry_type.name}}.test.ts.hbs b/templates/svelte/entry-type/tests/src/{{dna_role_name}}/{{coordinator_zome_manifest.name}}/{{kebab_case entry_type.name}}.test.ts.hbs index 02f8b154e..42d6bceea 100644 --- a/templates/svelte/entry-type/tests/src/{{dna_role_name}}/{{coordinator_zome_manifest.name}}/{{kebab_case entry_type.name}}.test.ts.hbs +++ b/templates/svelte/entry-type/tests/src/{{dna_role_name}}/{{coordinator_zome_manifest.name}}/{{kebab_case entry_type.name}}.test.ts.hbs @@ -58,7 +58,7 @@ test('create and read {{pascal_case entry_type.name}}', async () => { // Bob gets the created {{pascal_case entry_type.name}} const createReadOutput: Record = await bob.cells[0].callZome({ zome_name: "{{coordinator_zome_manifest.name}}", - fn_name: "get_{{snake_case entry_type.name}}", + fn_name: "{{#if crud.update}}get_latest_{{snake_case entry_type.name}}{{else}}get_{{snake_case entry_type.name}}{{/if}}", payload: {{#if entry_type.reference_entry_hash}}(record.signed_action.hashed.content as NewEntryAction).entry_hash{{else}}record.signed_action.hashed.hash{{/if}}, }); assert.deepEqual(sample, decode((createReadOutput.entry as any).Present.entry) as any); @@ -112,7 +112,7 @@ test('create and update {{pascal_case entry_type.name}}', async () => { // Bob gets the updated {{pascal_case entry_type.name}} const readUpdatedOutput0: Record = await bob.cells[0].callZome({ zome_name: "{{coordinator_zome_manifest.name}}", - fn_name: "get_{{snake_case entry_type.name}}", + fn_name: "get_latest_{{snake_case entry_type.name}}", payload: updatedRecord.signed_action.hashed.hash, }); assert.deepEqual(contentUpdate, decode((readUpdatedOutput0.entry as any).Present.entry) as any); @@ -140,7 +140,7 @@ test('create and update {{pascal_case entry_type.name}}', async () => { // Bob gets the updated {{pascal_case entry_type.name}} const readUpdatedOutput1: Record = await bob.cells[0].callZome({ zome_name: "{{coordinator_zome_manifest.name}}", - fn_name: "get_{{snake_case entry_type.name}}", + fn_name: "get_latest_{{snake_case entry_type.name}}", payload: updatedRecord.signed_action.hashed.hash, }); assert.deepEqual(contentUpdate, decode((readUpdatedOutput1.entry as any).Present.entry) as any); @@ -181,13 +181,13 @@ test('create and delete {{pascal_case entry_type.name}}', async () => { // Wait for the entry deletion to be propagated to the other node. await dhtSync([alice, bob], alice.cells[0].cell_id[0]); - // Bob tries to get the deleted {{pascal_case entry_type.name}} - const readDeletedOutput = await bob.cells[0].callZome({ + // Bob gets the deletions for the {{pascal_case entry_type.name}} + const deletesFor{{pascal_case entry_type.name}} = await bob.cells[0].callZome({ zome_name: "{{coordinator_zome_manifest.name}}", - fn_name: "get_{{snake_case entry_type.name}}", + fn_name: "get_deletes_for_{{snake_case entry_type.name}}", payload: record.signed_action.hashed.hash, }); - assert.notOk(readDeletedOutput); + assert.equal(deletesFor{{pascal_case entry_type.name}}.length, 1); }); }); {{/if}} diff --git a/templates/svelte/entry-type/ui/src/{{dna_role_name}}/{{coordinator_zome_manifest.name}}/{{pascal_case entry_type.name}}Detail.svelte.hbs b/templates/svelte/entry-type/ui/src/{{dna_role_name}}/{{coordinator_zome_manifest.name}}/{{pascal_case entry_type.name}}Detail.svelte.hbs index aca90a7a4..68cecba60 100644 --- a/templates/svelte/entry-type/ui/src/{{dna_role_name}}/{{coordinator_zome_manifest.name}}/{{pascal_case entry_type.name}}Detail.svelte.hbs +++ b/templates/svelte/entry-type/ui/src/{{dna_role_name}}/{{coordinator_zome_manifest.name}}/{{pascal_case entry_type.name}}Detail.svelte.hbs @@ -61,7 +61,7 @@ async function fetch{{pascal_case entry_type.name}}() { cap_secret: null, role_name: '{{dna_role_name}}', zome_name: '{{coordinator_zome_manifest.name}}', - fn_name: 'get_{{snake_case entry_type.name}}', + fn_name: '{{#if crud.update}}get_latest_{{snake_case entry_type.name}}{{else}}get_{{snake_case entry_type.name}}{{/if}}', payload: {{camel_case entry_type.name}}Hash, }); if (record) { diff --git a/templates/vanilla/entry-type/tests/src/{{dna_role_name}}/{{coordinator_zome_manifest.name}}/{{kebab_case entry_type.name}}.test.ts.hbs b/templates/vanilla/entry-type/tests/src/{{dna_role_name}}/{{coordinator_zome_manifest.name}}/{{kebab_case entry_type.name}}.test.ts.hbs index 02f8b154e..f82cbdbe9 100644 --- a/templates/vanilla/entry-type/tests/src/{{dna_role_name}}/{{coordinator_zome_manifest.name}}/{{kebab_case entry_type.name}}.test.ts.hbs +++ b/templates/vanilla/entry-type/tests/src/{{dna_role_name}}/{{coordinator_zome_manifest.name}}/{{kebab_case entry_type.name}}.test.ts.hbs @@ -58,7 +58,7 @@ test('create and read {{pascal_case entry_type.name}}', async () => { // Bob gets the created {{pascal_case entry_type.name}} const createReadOutput: Record = await bob.cells[0].callZome({ zome_name: "{{coordinator_zome_manifest.name}}", - fn_name: "get_{{snake_case entry_type.name}}", + fn_name: "{{#if crud.update}}get_original_{{snake_case entry_type.name}}{{else}}get_{{snake_case entry_type.name}}{{/if}}", payload: {{#if entry_type.reference_entry_hash}}(record.signed_action.hashed.content as NewEntryAction).entry_hash{{else}}record.signed_action.hashed.hash{{/if}}, }); assert.deepEqual(sample, decode((createReadOutput.entry as any).Present.entry) as any); @@ -112,7 +112,7 @@ test('create and update {{pascal_case entry_type.name}}', async () => { // Bob gets the updated {{pascal_case entry_type.name}} const readUpdatedOutput0: Record = await bob.cells[0].callZome({ zome_name: "{{coordinator_zome_manifest.name}}", - fn_name: "get_{{snake_case entry_type.name}}", + fn_name: "get_latest_{{snake_case entry_type.name}}", payload: updatedRecord.signed_action.hashed.hash, }); assert.deepEqual(contentUpdate, decode((readUpdatedOutput0.entry as any).Present.entry) as any); @@ -140,7 +140,7 @@ test('create and update {{pascal_case entry_type.name}}', async () => { // Bob gets the updated {{pascal_case entry_type.name}} const readUpdatedOutput1: Record = await bob.cells[0].callZome({ zome_name: "{{coordinator_zome_manifest.name}}", - fn_name: "get_{{snake_case entry_type.name}}", + fn_name: "get_latest_{{snake_case entry_type.name}}", payload: updatedRecord.signed_action.hashed.hash, }); assert.deepEqual(contentUpdate, decode((readUpdatedOutput1.entry as any).Present.entry) as any); @@ -181,13 +181,13 @@ test('create and delete {{pascal_case entry_type.name}}', async () => { // Wait for the entry deletion to be propagated to the other node. await dhtSync([alice, bob], alice.cells[0].cell_id[0]); - // Bob tries to get the deleted {{pascal_case entry_type.name}} - const readDeletedOutput = await bob.cells[0].callZome({ + // Bob gets the deletions for the {{pascal_case entry_type.name}} + const deletesFor{{pascal_case entry_type.name}} = await bob.cells[0].callZome({ zome_name: "{{coordinator_zome_manifest.name}}", - fn_name: "get_{{snake_case entry_type.name}}", + fn_name: "get_deletes_for_{{snake_case entry_type.name}}", payload: record.signed_action.hashed.hash, }); - assert.notOk(readDeletedOutput); + assert.equal(deletesFor{{pascal_case entry_type.name}}.length, 1); }); }); {{/if}} diff --git a/templates/vanilla/example/dnas/hello_world/zomes/coordinator/hello_world/src/lib.rs.hbs b/templates/vanilla/example/dnas/hello_world/zomes/coordinator/hello_world/src/lib.rs.hbs index 5797591dd..e9c9be70f 100644 --- a/templates/vanilla/example/dnas/hello_world/zomes/coordinator/hello_world/src/lib.rs.hbs +++ b/templates/vanilla/example/dnas/hello_world/zomes/coordinator/hello_world/src/lib.rs.hbs @@ -2,7 +2,7 @@ use hdk::prelude::*; use hello_world_integrity::*; #[hdk_extern] -pub fn hello_world(message:String) -> ExternResult { +pub fn hello_world(message: String) -> ExternResult { // commit the Hello message let action_hash = create_entry(&EntryTypes::Hello(Hello{message}))?; @@ -27,7 +27,7 @@ pub struct HelloOutput { #[hdk_extern] pub fn get_hellos(_: ()) -> ExternResult> { - // get all of the hellos linked to the anachor + // get all of the hellos linked to the anchor let path = Path::from("hellos"); let links = get_links(path.path_entry_hash()?, LinkTypes::AllHellos, None)?; let get_input: Vec = links diff --git a/templates/vue/entry-type/tests/src/{{dna_role_name}}/{{coordinator_zome_manifest.name}}/{{kebab_case entry_type.name}}.test.ts.hbs b/templates/vue/entry-type/tests/src/{{dna_role_name}}/{{coordinator_zome_manifest.name}}/{{kebab_case entry_type.name}}.test.ts.hbs index 02f8b154e..42d6bceea 100644 --- a/templates/vue/entry-type/tests/src/{{dna_role_name}}/{{coordinator_zome_manifest.name}}/{{kebab_case entry_type.name}}.test.ts.hbs +++ b/templates/vue/entry-type/tests/src/{{dna_role_name}}/{{coordinator_zome_manifest.name}}/{{kebab_case entry_type.name}}.test.ts.hbs @@ -58,7 +58,7 @@ test('create and read {{pascal_case entry_type.name}}', async () => { // Bob gets the created {{pascal_case entry_type.name}} const createReadOutput: Record = await bob.cells[0].callZome({ zome_name: "{{coordinator_zome_manifest.name}}", - fn_name: "get_{{snake_case entry_type.name}}", + fn_name: "{{#if crud.update}}get_latest_{{snake_case entry_type.name}}{{else}}get_{{snake_case entry_type.name}}{{/if}}", payload: {{#if entry_type.reference_entry_hash}}(record.signed_action.hashed.content as NewEntryAction).entry_hash{{else}}record.signed_action.hashed.hash{{/if}}, }); assert.deepEqual(sample, decode((createReadOutput.entry as any).Present.entry) as any); @@ -112,7 +112,7 @@ test('create and update {{pascal_case entry_type.name}}', async () => { // Bob gets the updated {{pascal_case entry_type.name}} const readUpdatedOutput0: Record = await bob.cells[0].callZome({ zome_name: "{{coordinator_zome_manifest.name}}", - fn_name: "get_{{snake_case entry_type.name}}", + fn_name: "get_latest_{{snake_case entry_type.name}}", payload: updatedRecord.signed_action.hashed.hash, }); assert.deepEqual(contentUpdate, decode((readUpdatedOutput0.entry as any).Present.entry) as any); @@ -140,7 +140,7 @@ test('create and update {{pascal_case entry_type.name}}', async () => { // Bob gets the updated {{pascal_case entry_type.name}} const readUpdatedOutput1: Record = await bob.cells[0].callZome({ zome_name: "{{coordinator_zome_manifest.name}}", - fn_name: "get_{{snake_case entry_type.name}}", + fn_name: "get_latest_{{snake_case entry_type.name}}", payload: updatedRecord.signed_action.hashed.hash, }); assert.deepEqual(contentUpdate, decode((readUpdatedOutput1.entry as any).Present.entry) as any); @@ -181,13 +181,13 @@ test('create and delete {{pascal_case entry_type.name}}', async () => { // Wait for the entry deletion to be propagated to the other node. await dhtSync([alice, bob], alice.cells[0].cell_id[0]); - // Bob tries to get the deleted {{pascal_case entry_type.name}} - const readDeletedOutput = await bob.cells[0].callZome({ + // Bob gets the deletions for the {{pascal_case entry_type.name}} + const deletesFor{{pascal_case entry_type.name}} = await bob.cells[0].callZome({ zome_name: "{{coordinator_zome_manifest.name}}", - fn_name: "get_{{snake_case entry_type.name}}", + fn_name: "get_deletes_for_{{snake_case entry_type.name}}", payload: record.signed_action.hashed.hash, }); - assert.notOk(readDeletedOutput); + assert.equal(deletesFor{{pascal_case entry_type.name}}.length, 1); }); }); {{/if}} diff --git a/templates/vue/entry-type/ui/src/{{dna_role_name}}/{{coordinator_zome_manifest.name}}/{{pascal_case entry_type.name}}Detail.vue.hbs b/templates/vue/entry-type/ui/src/{{dna_role_name}}/{{coordinator_zome_manifest.name}}/{{pascal_case entry_type.name}}Detail.vue.hbs index d98938dd2..3e9c82c75 100644 --- a/templates/vue/entry-type/ui/src/{{dna_role_name}}/{{coordinator_zome_manifest.name}}/{{pascal_case entry_type.name}}Detail.vue.hbs +++ b/templates/vue/entry-type/ui/src/{{dna_role_name}}/{{coordinator_zome_manifest.name}}/{{pascal_case entry_type.name}}Detail.vue.hbs @@ -116,7 +116,7 @@ export default defineComponent({ cap_secret: null, role_name: '{{dna_role_name}}', zome_name: '{{coordinator_zome_manifest.name}}', - fn_name: 'get_{{snake_case entry_type.name}}', + fn_name: '{{#if crud.update}}get_latest_{{snake_case entry_type.name}}{{else}}get_{{snake_case entry_type.name}}{{/if}}', payload: this.{{camel_case entry_type.name}}Hash, }); From 5d2f0daf3f2b769d097bda3fccec8200642ffdd6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Guillem=20C=C3=B3rdoba?= Date: Mon, 20 Nov 2023 10:39:43 +0100 Subject: [PATCH 2/2] Added get_oldest_delete_for --- src/scaffold/entry_type/coordinator.rs | 28 +++++++++++++++---- ...{{kebab_case entry_type.name}}.test.ts.hbs | 19 ++++++++++++- ...{{kebab_case entry_type.name}}.test.ts.hbs | 19 ++++++++++++- ...{{kebab_case entry_type.name}}.test.ts.hbs | 19 ++++++++++++- ...{{kebab_case entry_type.name}}.test.ts.hbs | 19 ++++++++++++- 5 files changed, 95 insertions(+), 9 deletions(-) diff --git a/src/scaffold/entry_type/coordinator.rs b/src/scaffold/entry_type/coordinator.rs index c7bc77577..5acdffc34 100644 --- a/src/scaffold/entry_type/coordinator.rs +++ b/src/scaffold/entry_type/coordinator.rs @@ -92,7 +92,7 @@ pub fn get_latest_{snake_entry_def_name}(original_{snake_entry_def_name}_hash: A }} #[hdk_extern] -pub fn get_all_{snake_entry_def_name}_revisions(original_{snake_entry_def_name}_hash: ActionHash) -> ExternResult> {{ +pub fn get_all_revisions_for_{snake_entry_def_name}(original_{snake_entry_def_name}_hash: ActionHash) -> ExternResult> {{ let Some(Details::Record(details)) = get_details(original_{snake_entry_def_name}_hash, GetOptions::default())? else {{ return Ok(vec![]); }}; @@ -100,7 +100,7 @@ pub fn get_all_{snake_entry_def_name}_revisions(original_{snake_entry_def_name}_ let mut records = vec![details.record]; for update in details.updates {{ - let mut update_records = get_all_{snake_entry_def_name}_revisions(update.action_address().clone())?; + let mut update_records = get_all_revisions_for_{snake_entry_def_name}(update.action_address().clone())?; records.append(&mut update_records); }} @@ -148,7 +148,11 @@ pub fn get_original_{snake_entry_def_name}(original_{snake_entry_def_name}_hash: }} #[hdk_extern] -pub fn get_all_{snake_entry_def_name}_revisions(original_{snake_entry_def_name}_hash: ActionHash) -> ExternResult> {{ +pub fn get_all_revisions_for_{snake_entry_def_name}(original_{snake_entry_def_name}_hash: ActionHash) -> ExternResult> {{ + let Some(original_record) = get_original_{snake_entry_def_name}(original_{snake_entry_def_name}_hash.clone())? else {{ + return Ok(vec![]); + }}; + let links = get_links(original_{snake_entry_def_name}_hash.clone(), LinkTypes::{}, None)?; let get_input: Vec = links @@ -161,7 +165,8 @@ pub fn get_all_{snake_entry_def_name}_revisions(original_{snake_entry_def_name}_ // load the records for all the links let records = HDK.with(|hdk| hdk.borrow().get(get_input))?; - let records: Vec = records.into_iter().filter_map(|r| r).collect(); + let mut records: Vec = records.into_iter().filter_map(|r| r).collect(); + records.insert(0, original_record); Ok(records) }} @@ -350,7 +355,7 @@ pub fn delete_{snake_entry_def_name}(original_{snake_entry_def_name}_hash: Actio }} #[hdk_extern] -pub fn get_deletes_for_{snake_entry_def_name}( +pub fn get_all_deletes_for_{snake_entry_def_name}( original_{snake_entry_def_name}_hash: ActionHash, ) -> ExternResult>> {{ let Some(details) = get_details(original_{snake_entry_def_name}_hash, GetOptions::default())? else {{ @@ -364,6 +369,19 @@ pub fn get_deletes_for_{snake_entry_def_name}( Details::Record(record_details) => Ok(Some(record_details.deletes)), }} }} + +#[hdk_extern] +pub fn get_oldest_delete_for_{snake_entry_def_name}( + original_{snake_entry_def_name}_hash: ActionHash, +) -> ExternResult> {{ + let Some(mut deletes) = get_all_deletes_for_{snake_entry_def_name}(original_{snake_entry_def_name}_hash)? else {{ + return Ok(None); + }}; + + deletes.sort_by(|delete_a, delete_b| delete_a.action().timestamp().cmp(&delete_b.action().timestamp())); + + Ok(deletes.first().cloned()) +}} "#, ) } diff --git a/templates/lit/entry-type/tests/src/{{dna_role_name}}/{{coordinator_zome_manifest.name}}/{{kebab_case entry_type.name}}.test.ts.hbs b/templates/lit/entry-type/tests/src/{{dna_role_name}}/{{coordinator_zome_manifest.name}}/{{kebab_case entry_type.name}}.test.ts.hbs index 0c2a05f5a..61804cda6 100644 --- a/templates/lit/entry-type/tests/src/{{dna_role_name}}/{{coordinator_zome_manifest.name}}/{{kebab_case entry_type.name}}.test.ts.hbs +++ b/templates/lit/entry-type/tests/src/{{dna_role_name}}/{{coordinator_zome_manifest.name}}/{{kebab_case entry_type.name}}.test.ts.hbs @@ -144,6 +144,15 @@ test('create and update {{pascal_case entry_type.name}}', async () => { payload: updatedRecord.signed_action.hashed.hash, }); assert.deepEqual(contentUpdate, decode((readUpdatedOutput1.entry as any).Present.entry) as any); + + // Bob gets all the revisions for {{pascal_case entry_type.name}} + const revisions: Record[] = await bob.cells[0].callZome({ + zome_name: "{{coordinator_zome_manifest.name}}", + fn_name: "get_all_revisions_for_{{snake_case entry_type.name}}", + payload: originalActionHash, + }); + assert.equal(revisions.length, 3); + assert.deepEqual(contentUpdate, decode((revisions[2].entry as any).Present.entry) as any); }); }); {{/if}} @@ -180,11 +189,19 @@ test('create and delete {{pascal_case entry_type.name}}', async () => { // Wait for the entry deletion to be propagated to the other node. await dhtSync([alice, bob], alice.cells[0].cell_id[0]); + + // Bob gets the oldest delete for the {{pascal_case entry_type.name}} + const oldestDeleteFor{{pascal_case entry_type.name}} = await bob.cells[0].callZome({ + zome_name: "{{coordinator_zome_manifest.name}}", + fn_name: "get_oldest_delete_for_{{snake_case entry_type.name}}", + payload: record.signed_action.hashed.hash, + }); + assert.ok(oldestDeleteFor{{pascal_case entry_type.name}}); // Bob gets the deletions for {{pascal_case entry_type.name}} const deletesFor{{title_case entry_type.name}} = await bob.cells[0].callZome({ zome_name: "{{coordinator_zome_manifest.name}}", - fn_name: "get_deletes_for_{{snake_case entry_type.name}}", + fn_name: "get_all_deletes_for_{{snake_case entry_type.name}}", payload: record.signed_action.hashed.hash, }); assert.equal(deletesFor{{title_case entry_type.name}}.length, 1); diff --git a/templates/svelte/entry-type/tests/src/{{dna_role_name}}/{{coordinator_zome_manifest.name}}/{{kebab_case entry_type.name}}.test.ts.hbs b/templates/svelte/entry-type/tests/src/{{dna_role_name}}/{{coordinator_zome_manifest.name}}/{{kebab_case entry_type.name}}.test.ts.hbs index 42d6bceea..02e3e6e6d 100644 --- a/templates/svelte/entry-type/tests/src/{{dna_role_name}}/{{coordinator_zome_manifest.name}}/{{kebab_case entry_type.name}}.test.ts.hbs +++ b/templates/svelte/entry-type/tests/src/{{dna_role_name}}/{{coordinator_zome_manifest.name}}/{{kebab_case entry_type.name}}.test.ts.hbs @@ -144,6 +144,15 @@ test('create and update {{pascal_case entry_type.name}}', async () => { payload: updatedRecord.signed_action.hashed.hash, }); assert.deepEqual(contentUpdate, decode((readUpdatedOutput1.entry as any).Present.entry) as any); + + // Bob gets all the revisions for {{pascal_case entry_type.name}} + const revisions: Record[] = await bob.cells[0].callZome({ + zome_name: "{{coordinator_zome_manifest.name}}", + fn_name: "get_all_revisions_for_{{snake_case entry_type.name}}", + payload: originalActionHash, + }); + assert.equal(revisions.length, 3); + assert.deepEqual(contentUpdate, decode((revisions[2].entry as any).Present.entry) as any); }); }); {{/if}} @@ -181,10 +190,18 @@ test('create and delete {{pascal_case entry_type.name}}', async () => { // Wait for the entry deletion to be propagated to the other node. await dhtSync([alice, bob], alice.cells[0].cell_id[0]); + // Bob gets the oldest delete for the {{pascal_case entry_type.name}} + const oldestDeleteFor{{pascal_case entry_type.name}} = await bob.cells[0].callZome({ + zome_name: "{{coordinator_zome_manifest.name}}", + fn_name: "get_oldest_delete_for_{{snake_case entry_type.name}}", + payload: record.signed_action.hashed.hash, + }); + assert.ok(oldestDeleteFor{{pascal_case entry_type.name}}); + // Bob gets the deletions for the {{pascal_case entry_type.name}} const deletesFor{{pascal_case entry_type.name}} = await bob.cells[0].callZome({ zome_name: "{{coordinator_zome_manifest.name}}", - fn_name: "get_deletes_for_{{snake_case entry_type.name}}", + fn_name: "get_all_deletes_for_{{snake_case entry_type.name}}", payload: record.signed_action.hashed.hash, }); assert.equal(deletesFor{{pascal_case entry_type.name}}.length, 1); diff --git a/templates/vanilla/entry-type/tests/src/{{dna_role_name}}/{{coordinator_zome_manifest.name}}/{{kebab_case entry_type.name}}.test.ts.hbs b/templates/vanilla/entry-type/tests/src/{{dna_role_name}}/{{coordinator_zome_manifest.name}}/{{kebab_case entry_type.name}}.test.ts.hbs index f82cbdbe9..785ef3d9b 100644 --- a/templates/vanilla/entry-type/tests/src/{{dna_role_name}}/{{coordinator_zome_manifest.name}}/{{kebab_case entry_type.name}}.test.ts.hbs +++ b/templates/vanilla/entry-type/tests/src/{{dna_role_name}}/{{coordinator_zome_manifest.name}}/{{kebab_case entry_type.name}}.test.ts.hbs @@ -144,6 +144,15 @@ test('create and update {{pascal_case entry_type.name}}', async () => { payload: updatedRecord.signed_action.hashed.hash, }); assert.deepEqual(contentUpdate, decode((readUpdatedOutput1.entry as any).Present.entry) as any); + + // Bob gets all the revisions for {{pascal_case entry_type.name}} + const revisions: Record[] = await bob.cells[0].callZome({ + zome_name: "{{coordinator_zome_manifest.name}}", + fn_name: "get_all_revisions_for_{{snake_case entry_type.name}}", + payload: originalActionHash, + }); + assert.equal(revisions.length, 3); + assert.deepEqual(contentUpdate, decode((revisions[2].entry as any).Present.entry) as any); }); }); {{/if}} @@ -181,10 +190,18 @@ test('create and delete {{pascal_case entry_type.name}}', async () => { // Wait for the entry deletion to be propagated to the other node. await dhtSync([alice, bob], alice.cells[0].cell_id[0]); + // Bob gets the oldest delete for the {{pascal_case entry_type.name}} + const oldestDeleteFor{{pascal_case entry_type.name}} = await bob.cells[0].callZome({ + zome_name: "{{coordinator_zome_manifest.name}}", + fn_name: "get_oldest_delete_for_{{snake_case entry_type.name}}", + payload: record.signed_action.hashed.hash, + }); + assert.ok(oldestDeleteFor{{pascal_case entry_type.name}}); + // Bob gets the deletions for the {{pascal_case entry_type.name}} const deletesFor{{pascal_case entry_type.name}} = await bob.cells[0].callZome({ zome_name: "{{coordinator_zome_manifest.name}}", - fn_name: "get_deletes_for_{{snake_case entry_type.name}}", + fn_name: "get_all_deletes_for_{{snake_case entry_type.name}}", payload: record.signed_action.hashed.hash, }); assert.equal(deletesFor{{pascal_case entry_type.name}}.length, 1); diff --git a/templates/vue/entry-type/tests/src/{{dna_role_name}}/{{coordinator_zome_manifest.name}}/{{kebab_case entry_type.name}}.test.ts.hbs b/templates/vue/entry-type/tests/src/{{dna_role_name}}/{{coordinator_zome_manifest.name}}/{{kebab_case entry_type.name}}.test.ts.hbs index 42d6bceea..c6781734d 100644 --- a/templates/vue/entry-type/tests/src/{{dna_role_name}}/{{coordinator_zome_manifest.name}}/{{kebab_case entry_type.name}}.test.ts.hbs +++ b/templates/vue/entry-type/tests/src/{{dna_role_name}}/{{coordinator_zome_manifest.name}}/{{kebab_case entry_type.name}}.test.ts.hbs @@ -144,6 +144,15 @@ test('create and update {{pascal_case entry_type.name}}', async () => { payload: updatedRecord.signed_action.hashed.hash, }); assert.deepEqual(contentUpdate, decode((readUpdatedOutput1.entry as any).Present.entry) as any); + + // Bob gets all the revisions for {{pascal_case entry_type.name}} + const revisions: Record[] = await bob.cells[0].callZome({ + zome_name: "{{coordinator_zome_manifest.name}}", + fn_name: "get_all_revisions_for_{{snake_case entry_type.name}}", + payload: originalActionHash, + }); + assert.equal(revisions.length, 3); + assert.deepEqual(contentUpdate, decode((revisions[2].entry as any).Present.entry) as any); }); }); {{/if}} @@ -180,11 +189,19 @@ test('create and delete {{pascal_case entry_type.name}}', async () => { // Wait for the entry deletion to be propagated to the other node. await dhtSync([alice, bob], alice.cells[0].cell_id[0]); + + // Bob gets the oldest delete for the {{pascal_case entry_type.name}} + const oldestDeleteFor{{pascal_case entry_type.name}} = await bob.cells[0].callZome({ + zome_name: "{{coordinator_zome_manifest.name}}", + fn_name: "get_oldest_delete_for_{{snake_case entry_type.name}}", + payload: record.signed_action.hashed.hash, + }); + assert.ok(oldestDeleteFor{{pascal_case entry_type.name}}); // Bob gets the deletions for the {{pascal_case entry_type.name}} const deletesFor{{pascal_case entry_type.name}} = await bob.cells[0].callZome({ zome_name: "{{coordinator_zome_manifest.name}}", - fn_name: "get_deletes_for_{{snake_case entry_type.name}}", + fn_name: "get_all_deletes_for_{{snake_case entry_type.name}}", payload: record.signed_action.hashed.hash, }); assert.equal(deletesFor{{pascal_case entry_type.name}}.length, 1);