diff --git a/dna/course/zomes/courses/code/src/content/entry.rs b/dna/course/zomes/courses/code/src/content/entry.rs index 41a6e02..82a10ec 100644 --- a/dna/course/zomes/courses/code/src/content/entry.rs +++ b/dna/course/zomes/courses/code/src/content/entry.rs @@ -44,18 +44,15 @@ pub fn section_entry_def() -> ValidatingEntryType { hdk::ValidationPackageDefinition::Entry }, validation: | validation_data: hdk::EntryValidationData| { - match validation_data { - EntryValidationData::Create { .. } => { - // TODO: implement validation - Ok(()) + match validation_data { + EntryValidationData::Create { entry, validation_data } => { + validation::create(entry, validation_data) }, - EntryValidationData::Modify { .. } => { - // TODO: implement validation - Ok(()) + EntryValidationData::Modify { new_entry, old_entry, old_entry_header, validation_data } => { + validation::modify(new_entry, old_entry, old_entry_header, validation_data) }, - EntryValidationData::Delete { .. } => { - // TODO: implement validation - Ok(()) + EntryValidationData::Delete { old_entry, old_entry_header, validation_data } => { + validation::delete(old_entry, old_entry_header, validation_data) } } } diff --git a/dna/course/zomes/courses/code/src/content/validation.rs b/dna/course/zomes/courses/code/src/content/validation.rs new file mode 100644 index 0000000..ea014a1 --- /dev/null +++ b/dna/course/zomes/courses/code/src/content/validation.rs @@ -0,0 +1,86 @@ +use super::{ //Ummmm above should all probably get deleted but since content is linked to section anchor I've made changes. Also I feel like super is the wrong "expression" for retrieving section anchor as its in another directory + anchor::SectionAnchor, + entry::{Content}, +}; +use crate::anchor_trait::AnchorTrait; +use crate::helper; +use hdk::holochain_core_types::chain_header::ChainHeader; +use hdk::{LinkValidationData, ValidationData}; +use holochain_entry_utils::HolochainEntry; + +pub fn create(entry: Content, validation_data: ValidationData) -> Result<(), String> { + helper::validate_only_teacher_can_do( //only teachers to create content? + &entry.teacher_address, + validation_data.sources(), + "create their courses", + )?; + helper::validate_entity_title(&entry.title, &Content::entry_type()) +} + +pub fn modify( + new_entry: Content, + old_entry: Content, + _old_entry_header: ChainHeader, + validation_data: ValidationData, +) -> Result<(), String> { + helper::validate_only_teacher_can_do( //teachers only assumption, else delete rule + &old_entry.teacher_address, + validation_data.sources(), + "modify their content", + )?; + helper::validate_entity_title(&new_entry.title, &Course::entry_type())?; + validate_no_teacher_change(old_entry, new_entry) +} + +// this fn is only needed in the current module so it's private +fn validate_no_teacher_change(old_entry: Content, new_entry: Contetnt) -> Result<(), String> { + if new_entry.teacher_address != old_entry.teacher_address { + return Err(String::from("Cannot change the teacher of the content")); + } + Ok(()) +} + +pub fn delete( + entry: Content, + _entry_header: ChainHeader, + validation_data: ValidationData, +) -> Result<(), String> { + helper::validate_only_teacher_can_do( + &entry.teacher_address, + validation_data.sources(), + "delete their content", + ) +} + +// =========================== ContentAnchor validation +/* Pointers changed to direct to the section anchor, though I reckon its an unnecessary section hence commenting it out + +pub fn anchor_create(entry: SectionAnchor, validation_data: ValidationData) -> Result<(), String> { + helper::validate_only_teacher_can_do( + &entry.teacher_address, + validation_data.sources(), + "create their content", + )?; + helper::validate_entity_title(&entry.title, &SectionAnchor::entry_type()) +} + +// NOTE: we don't accept any parameters here because we don't need them to always return an error +// because this anchor can never be modified +pub fn anchor_modify() -> Result<(), String> { + Err(String::from( + "Can't modify the CourseAnchor entry: it can only be created or deleted", + )) +} + +pub fn anchor_delete( + entry: SectionAnchor, + _entry_header: ChainHeader, + validation_data: ValidationData, +) -> Result<(), String> { + helper::validate_only_teacher_can_do( + &entry.teacher_address, + validation_data.sources(), + "delete their content", + ) +} +*/ \ No newline at end of file diff --git a/dna/course/zomes/courses/code/src/lib.rs b/dna/course/zomes/courses/code/src/lib.rs index 4bab334..0d297d2 100644 --- a/dna/course/zomes/courses/code/src/lib.rs +++ b/dna/course/zomes/courses/code/src/lib.rs @@ -1,5 +1,3 @@ -// allowing unstable feature to remove item from the vector on a crate level -#![feature(vec_remove_item)] // allowing for this Rust project to have dead code on a crate level #![allow(dead_code)] // unstable Rust feature @@ -19,14 +17,12 @@ extern crate serde_json; extern crate holochain_json_derive; use hdk::prelude::*; + use hdk_proc_macros::zome; -// Declaring Rust modules that are used in our project mod anchor_trait; -mod content; mod course; mod helper; -mod section; #[zome] mod courses { @@ -59,8 +55,6 @@ mod courses { course::entry::course_entry_def() } - // zome_fn is used to specify that this is a function available to call from our zome - // "hc_public" here means that there are no restrictions as to who can call this function #[zome_fn("hc_public")] fn create_course(title: String, timestamp: u64) -> ZomeApiResult
{ course::handlers::create(title, timestamp) @@ -70,9 +64,15 @@ mod courses { fn get_latest_course_entry( course_anchor_address: Address, ) -> ZomeApiResult> { - course::handlers::get_latest_course_entry(course_anchor_address) + let latest_course_result = course::handlers::get_latest_course(&course_anchor_address)?; + match latest_course_result { + Some((course_entry, _course_entry_address)) => { + return Ok(Some(course_entry)); + } + None => return Ok(None), + } } - + // sections_address - do I need to replicate this in the sections CRUD? Seems weird to update the course this way. #[zome_fn("hc_public")] fn update_course( title: String, @@ -102,46 +102,40 @@ mod courses { course::handlers::get_my_enrolled_courses() } - #[zome_fn("hc_public")] - fn enrol_in_course(course_anchor_address: Address) -> ZomeApiResult
{ - course::handlers::enrol_in_course(course_anchor_address) - } - - #[zome_fn("hc_public")] - fn get_all_students(course_anchor_address: Address) -> ZomeApiResult> { - course::handlers::get_students(course_anchor_address) - } - // ====================== Section definitions + // TODO: implement section entry definitions #[entry_def] - fn section_anchor_entry_definition() -> ValidatingEntryType { - section::anchor::section_anchor_def() + fn section_entry_definition() -> ValidatingEntryType { + course::entry::section_entry_def() } #[entry_def] - fn section_entry_definition() -> ValidatingEntryType { - section::entry::entry_def() + fn section_anchor_definition() -> ValidatingEntryType { + course::anchor::section_anchor_def() + } + + // TODO: implement section CRUD methods --> fished out of modules "impl" right? or handlers? + #[zome_fn("hc_public")] + fn create_section(title: String, course_anchor_address: Address, timestamp: u64, anchor_address: Address, + ) -> ZomeApiResult
{ + section::handlers::create(title, course_anchor_address, timestamp, anchor_address) } #[zome_fn("hc_public")] fn get_latest_section_entry( section_anchor_address: Address, ) -> ZomeApiResult> { - section::handlers::get_latest_section_entry(section_anchor_address) + section::handlers::get_latest_section_entry(section_anchor_address)?; } - + // this is copied off the course methods code and changed... is the sections_address part relevant here or in the above code? #[zome_fn("hc_public")] - fn create_section( + fn update_section( title: String, course_anchor_address: Address, timestamp: u64, + anchor_address: Address, ) -> ZomeApiResult
{ - section::handlers::create(title, &course_anchor_address, timestamp) - } - - #[zome_fn("hc_public")] - fn update_section(title: String, section_anchor_address: Address) -> ZomeApiResult
{ - section::handlers::update(title, §ion_anchor_address) + section::handlers::update(title, course_anchor_address, timestamp, anchor_address) } #[zome_fn("hc_public")] @@ -149,40 +143,43 @@ mod courses { section::handlers::delete(section_anchor_address) } + // ====================== Content definitions + // TODO: implement content entry definition #[entry_def] fn content_entry_definition() -> ValidatingEntryType { - content::entry::section_entry_def() + course::entry::section_entry_def() } + // TODO: implement content CRUD methods --> used section anchor addresses as content uses that anchor #[zome_fn("hc_public")] - fn create_content( - name: String, + fn create_content(name: String, section_anchor_address: Address, url: String, timestamp: u64, description: String, ) -> ZomeApiResult
{ - content::handlers::create(name, section_anchor_address, url, timestamp, description) + content::handlers::create(name, section_anchor_address, url, timestamp, description) // defined in module } #[zome_fn("hc_public")] fn get_contents(section_anchor_address: Address) -> ZomeApiResult> { - content::handlers::get_contents(§ion_anchor_address) + content::handlers::get_contents(§ion_anchor_address)?; } #[zome_fn("hc_public")] fn update_content( - content_address: Address, name: String, + section_anchor_address: Address, url: String, + timestamp: u64, description: String, ) -> ZomeApiResult
{ - content::handlers::update(content_address, name, url, description) + content::handlers::update(name, url, description, timestamp, section_anchor_address,) } #[zome_fn("hc_public")] fn delete_content(content_address: Address) -> ZomeApiResult
{ content::handlers::delete(content_address) } -} +} \ No newline at end of file diff --git a/dna/course/zomes/courses/code/src/section/anchor.rs b/dna/course/zomes/courses/code/src/section/anchor.rs index 4dac6b6..5e6a7fa 100644 --- a/dna/course/zomes/courses/code/src/section/anchor.rs +++ b/dna/course/zomes/courses/code/src/section/anchor.rs @@ -48,17 +48,16 @@ pub fn section_anchor_def() -> ValidatingEntryType { }, validation: | validation_data: hdk::EntryValidationData| { match validation_data{ - EntryValidationData::Create { .. } => { - // TODO: implement validation - Ok(()) + EntryValidationData::Create { entry, validation_data } => { + validation::anchor_create(entry, validation_data) }, + // NOTE: the symbol .. means that we're skipping unpacking parameters that we receive here + // because we won't need them EntryValidationData::Modify { .. } => { - // TODO: implement validation - Ok(()) + validation::anchor_modify() }, - EntryValidationData::Delete { .. } => { - // TODO: implement validation - Ok(()) + EntryValidationData::Delete { old_entry, old_entry_header, validation_data } => { + validation::anchor_delete(old_entry, old_entry_header, validation_data) } } }, @@ -69,8 +68,8 @@ pub fn section_anchor_def() -> ValidatingEntryType { validation_package:||{ hdk::ValidationPackageDefinition::Entry }, - validation:|_validation_data: hdk::LinkValidationData|{ - Ok(()) + validation:|validation_data: hdk::LinkValidationData|{ + validation::anchor_to_section_link(validation_data) } ), to!( diff --git a/dna/course/zomes/courses/code/src/section/entry.rs b/dna/course/zomes/courses/code/src/section/entry.rs index ebed85b..60e834b 100644 --- a/dna/course/zomes/courses/code/src/section/entry.rs +++ b/dna/course/zomes/courses/code/src/section/entry.rs @@ -45,18 +45,15 @@ pub fn entry_def() -> ValidatingEntryType { hdk::ValidationPackageDefinition::Entry }, validation: | validation_data: hdk::EntryValidationData
| { - match validation_data { - EntryValidationData::Create { .. } => { - // TODO: implement validation - Ok(()) + match validation_data { + EntryValidationData::Create { entry, validation_data } => { + validation::create(entry, validation_data) }, - EntryValidationData::Modify { .. } => { - // TODO: implement validation - Ok(()) + EntryValidationData::Modify { new_entry, old_entry, old_entry_header, validation_data } => { + validation::modify(new_entry, old_entry, old_entry_header, validation_data) }, - EntryValidationData::Delete { .. } => { - // TODO: implement validation - Ok(()) + EntryValidationData::Delete { old_entry, old_entry_header, validation_data } => { + validation::delete(old_entry, old_entry_header, validation_data) } } }, diff --git a/dna/course/zomes/courses/code/src/section/validation.rs b/dna/course/zomes/courses/code/src/section/validation.rs new file mode 100644 index 0000000..af6fc4f --- /dev/null +++ b/dna/course/zomes/courses/code/src/section/validation.rs @@ -0,0 +1,129 @@ +use super::{ + anchor::SectionAnchor, + catalog_anchor::CourseCatalogAnchor, //I feel this is probably not needed + entry::{Section, MAX_TITLE_LEN}, //MAX_Title should probably go as well, should entry though? +}; +use crate::anchor_trait::AnchorTrait; +use crate::helper; +use hdk::holochain_core_types::chain_header::ChainHeader; +use hdk::{LinkValidationData, ValidationData}; +use holochain_entry_utils::HolochainEntry; + +pub fn create(entry: Section, validation_data: ValidationData) -> Result<(), String> { + helper::validate_only_teacher_can_do( //can only teachers create sections? If so good, else probably dont need this helper + &entry.teacher_address, + validation_data.sources(), + "create course setions", + )?; + helper::validate_entity_title(&entry.title, &Section::entry_type()) //no need for title length here I guess +} + +pub fn modify( + new_entry: Section, + old_entry: Section, + _old_entry_header: ChainHeader, + validation_data: ValidationData, +) -> Result<(), String> { + helper::validate_only_teacher_can_do( //same as above, if needed cool - if not this should not be needed + &old_entry.teacher_address, + validation_data.sources(), + "modify their sections", + )?; + helper::validate_entity_title(&new_entry.title, &Section::entry_type())?; + validate_no_teacher_change(old_entry, new_entry) //if teachers matter +} + +// this fn is only needed in the current module so it's private +fn validate_no_teacher_change(old_entry: Section, new_entry: Section) -> Result<(), String> { + if new_entry.teacher_address != old_entry.teacher_address { + return Err(String::from("Cannot change the teacher of the Section")); + } + Ok(()) +} + +pub fn delete( + entry: Section, + _entry_header: ChainHeader, + validation_data: ValidationData, +) -> Result<(), String> { + helper::validate_only_teacher_can_do(//unless students can create sections in which case this rule goes + &entry.teacher_address, + validation_data.sources(), + "delete their Sections", + ) +} + +// =========================== SectionAnchor validation +pub fn anchor_create(entry: SectionAnchor, validation_data: ValidationData) -> Result<(), String> { + helper::validate_only_teacher_can_do( + &entry.teacher_address, + validation_data.sources(), + "create their sections", + )?; + helper::validate_entity_title(&entry.title, &SectionAnchor::entry_type()) //deleted max_title as theres no max entry in the anchor code.. propably no title anyway, its an anchor +} + +// NOTE: we don't accept any parameters here because we don't need them to always return an error +// because this anchor can never be modified +pub fn anchor_modify() -> Result<(), String> { + Err(String::from( + "Can't modify the SectionAnchor entry: it can only be created or deleted", + )) +} + +pub fn anchor_delete( + entry: SectionAnchor, + _entry_header: ChainHeader, + validation_data: ValidationData, +) -> Result<(), String> { + helper::validate_only_teacher_can_do(//teachers only CRUD sections assumption still + &entry.teacher_address, + validation_data.sources(), + "delete their section anchor", + ) +} + +// =========================== SectionAnchor links validation + +pub fn anchor_to_section_link(validation_data: LinkValidationData) -> Result<(), String> { + match validation_data { + hdk::LinkValidationData::LinkAdd { + link, + validation_data, + } => { + // get author of this entry + let author = validation_data.package.chain_header.provenances()[0].source(); + // get link base: entry from which the link goes + let base: SectionAnchor = hdk::utils::get_as_type(link.link.base().clone())?; + // get link target: entry to which the link goes + let target: Section = hdk::utils::get_as_type(link.link.target().clone())?; + if base.teacher_address != target.teacher_address { + // notice that we're using return and ending this statement with ; symbol + // You can do both: skip ; symbol in the last fn statement or explicitly add return to it and then leave ; as is + return Err(String::from( + "Can't link SectionAnchor to Section because their teacher addresses are different", + )); + } else if author != base.teacher_address { + return Err(String::from( + "Can't link SectionAnchor to Section because your address isn't specified as teacher address for this Section", + )); + } + Ok(()) + } + hdk::LinkValidationData::LinkRemove { + link, + validation_data, + } => { + // get author of this entry + let author = validation_data.package.chain_header.provenances()[0].source(); + // get link base: entry from which the link goes + let base: SectionAnchor = hdk::utils::get_as_type(link.link.base().clone())?; + if author != base.teacher_address { + return Err(String::from( + "Can't remove link from SectionAnchor to Section because your address isn't specified as teacher_address for this Section", + )); + } + Ok(()) + } + } +}