use crate::{
context::Context,
dht::actions::hold_aspect::hold_aspect,
network::entry_with_header::EntryWithHeader,
nucleus::validation::{process_validation_err, validate_entry, ValidationContext},
workflows::{hold_entry::hold_content_aspect, validation_package},
};
use holochain_core_types::{
entry::Entry,
error::HolochainError,
network::entry_aspect::EntryAspect,
validation::{EntryLifecycle, ValidationData},
};
use holochain_persistence_api::cas::content::AddressableContent;
use snowflake::ProcessUniqueId;
use std::sync::Arc;
#[holochain_tracing_macros::newrelic_autotrace(HOLOCHAIN_CORE)]
#[allow(clippy::try_err)]
pub async fn hold_link_workflow(
pending_id: &ProcessUniqueId,
entry_with_header: &EntryWithHeader,
context: Arc<Context>,
) -> Result<(), HolochainError> {
let link_add = match &entry_with_header.entry {
Entry::LinkAdd(link_add) => link_add,
_ => Err(HolochainError::ErrorGeneric(
"hold_link_workflow expects entry to be an Entry::LinkAdd".to_string(),
))?,
};
let link = link_add.link().clone();
log_debug!(context, "workflow/hold_link: {:?}", link);
log_debug!(context, "workflow/hold_link: getting validation package...");
let maybe_validation_package = validation_package(&entry_with_header, context.clone())
.await
.map_err(|err| {
let message = "Could not get validation package from source! -> Add to pending...";
log_debug!(context, "workflow/hold_link: {}", message);
log_debug!(context, "workflow/hold_link: Error was: {:?}", err);
HolochainError::ValidationPending
})?;
let validation_package = maybe_validation_package.ok_or_else(|| {
let message = "Source did respond to request but did not deliver validation package! (Empty response) This is weird! Let's try this again later -> Add to pending";
log_debug!(context, "workflow/hold_link: {}", message);
HolochainError::ValidationPending
})?;
log_debug!(context, "workflow/hold_link: got validation package");
let validation_data = ValidationData {
package: validation_package,
lifecycle: EntryLifecycle::Meta,
};
log_debug!(context, "workflow/hold_link: validate...");
validate_entry(
entry_with_header.entry.clone(),
None,
validation_data,
&context,
ValidationContext::Holding,
)
.await
.map_err(|err| {
process_validation_err(
"hold_link",
context.clone(),
err,
entry_with_header.entry.address(),
)
})?;
log_debug!(
context,
"workflow/hold_link valid for: {:?}",
entry_with_header
);
let aspect = EntryAspect::LinkAdd(link_add.clone(), entry_with_header.header.clone());
hold_aspect(pending_id, aspect.clone(), context.clone()).await?;
log_debug!(
context,
"workflow/hold_link: aspect held! aspect address:{}, link_add: {:?} {:?}",
aspect.address(),
link_add,
entry_with_header.header
);
hold_content_aspect(pending_id, &entry_with_header, context.clone()).await?;
Ok(())
}
#[cfg(test)]
#[cfg(feature = "broken-tests")]
pub mod tests {
use super::*;
use crate::{nucleus::actions::tests::*, workflows::author_entry::author_entry};
use holochain_core_types::{
agent::test_agent_id, chain_header::test_chain_header, entry::test_entry_with_value,
link::link_data::LinkData,
};
#[test]
fn test_reject_invalid_link_on_hold_workflow() {
let hacked_dna = create_test_dna_with_wat("test_zome", Some(&test_wat_always_valid()));
let mut dna = create_test_dna_with_wat("test_zome", Some(&test_wat_always_invalid()));
dna.uuid = String::from("test_reject_invalid_link_on_hold_workflow");
let dna_address = dna.address();
let (_, context1) =
test_instance_with_spoofed_dna(hacked_dna, dna_address, "alice").unwrap();
let netname = Some("test_reject_invalid_link_on_remove_workflow");
let entry = test_entry_with_value("{\"stuff\":\"test entry value\"}");
let entry_address = context1
.block_on(author_entry(&entry, None, &context1, &Vec::new()))
.unwrap();
let link_add = LinkData::new_add(
&entry_address.address,
&entry_address.address,
"test-tag",
"test-link",
test_chain_header(),
test_agent_id(),
);
let link_entry = Entry::LinkAdd(link_add);
let _ = context1
.block_on(author_entry(&link_entry, None, &context1, &Vec::new()))
.unwrap();
let agent1_state = context1.state().unwrap().agent();
let header = agent1_state
.get_most_recent_header_for_entry(&link_entry)
.expect("There must be a header in the author's source chain after commit");
let entry_with_header = EntryWithHeader {
entry: link_entry,
header,
};
let result = context2.block_on(hold_link_workflow(&entry_with_header, context2.clone()));
assert!(result.is_err());
assert_eq!(
result.err().unwrap(),
HolochainError::ValidationFailed(String::from("FAIL wat")),
);
}
}