1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131
use hdk::prelude::*;
use holo_hash::ActionHashB64;
use std::fmt;
use crate::wire_record::WireRecord;
/// when sending signals, distinguish
/// between "create", "update", and "delete" actions
/// via this enum. Serializes to/from "create" | "update" | "delete"
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, SerializedBytes)]
#[serde(from = "UIEnum")]
#[serde(into = "UIEnum")]
pub enum ActionType {
Create,
Update,
Delete,
}
#[derive(Debug, Serialize, Deserialize, SerializedBytes, Clone, PartialEq)]
struct UIEnum(String);
impl From<UIEnum> for ActionType {
fn from(ui_enum: UIEnum) -> Self {
match ui_enum.0.as_str() {
"create" => Self::Create,
"update" => Self::Update,
_ => Self::Delete,
}
}
}
impl From<ActionType> for UIEnum {
fn from(action_type: ActionType) -> Self {
Self(action_type.to_string().to_lowercase())
}
}
impl fmt::Display for ActionType {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{:?}", self)
}
}
/// Grant unrestricted access for this agent to receive
/// calls to its `recv_remote_signal` endpoint via others
/// calling `remote_signal`
pub fn create_receive_signal_cap_grant() -> ExternResult<()> {
let mut functions = BTreeSet::new();
functions.insert((zome_info()?.name, "recv_remote_signal".into()));
create_cap_grant(CapGrantEntry {
tag: "".into(),
// empty access converts to unrestricted
access: ().into(),
functions: GrantedFunctions::Listed(functions),
})?;
Ok(())
}
/// Distinguishes between what data structures should be passed
/// to the UI based on different action types, like create/update/delete
/// this will be used to send these data structures as signals to the UI
/// When Create/Update, we will pass the actual new Entry
/// but when doing Delete we will naturally only pass the ActionHash
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)]
// untagged because the useful tagging is done externally on the *Signal object
// as the tag and action
#[serde(untagged)]
pub enum SignalData<T> {
Create(WireRecord<T>),
Update(WireRecord<T>),
Delete(ActionHashB64),
}
/// This will be used to send data events as signals to the UI. All
/// signals relating to the entry type will share this high level structure, creating consistency.
/// The `data` field should use the variant (Create/Update/Delete)
/// that matches the variant for `action`. So if `action` is variant [ActionType::Create](crate::signals::ActionType::Create)
#[doc = " then `data` should be `SignalData::Create`."]
/// It serializes with camelCase style replacement of underscores in object keys.
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)]
#[serde(rename_all = "camelCase")]
pub struct ActionSignal<T> {
pub entry_type: String,
pub action: ActionType,
pub data: SignalData<T>,
}
#[cfg(test)]
mod tests {
use super::create_receive_signal_cap_grant;
use ::fixt::prelude::*;
use hdk::prelude::*;
#[test]
fn test_create_receive_signal_cap_grant() {
// set up the mock hdk responses
let mut mock_hdk = MockHdkT::new();
// zome info is dynamic so
// that this is generic and usable in any zome
let zome_info = fixt!(ZomeInfo);
mock_hdk
.expect_zome_info()
.times(1)
.return_const(Ok(zome_info.clone()));
// create_cap_grant calls just `create` under the hood
let mut functions: GrantedFunctions = BTreeSet::new();
functions.insert((zome_info.name, "recv_remote_signal".into()));
let expected = CreateInput::new(
EntryDefId::CapGrant,
EntryVisibility::Private,
Entry::CapGrant(CapGrantEntry {
tag: "".into(),
// empty access converts to unrestricted
access: ().into(),
functions,
}),
ChainTopOrdering::Strict,
);
let action_hash = fixt!(ActionHash);
mock_hdk
.expect_create()
.with(mockall::predicate::eq(expected))
.times(1)
.return_const(Ok(action_hash));
set_hdk(mock_hdk);
// call the function we are testing
let result = create_receive_signal_cap_grant();
assert_eq!(result.is_ok(), true);
}
}