Skip to content

Commit

Permalink
WIP
Browse files Browse the repository at this point in the history
  • Loading branch information
guillemcordoba committed Oct 15, 2024
1 parent 2fa5558 commit 7601f09
Show file tree
Hide file tree
Showing 59 changed files with 3,318 additions and 2,471 deletions.
1,266 changes: 600 additions & 666 deletions Cargo.lock

Large diffs are not rendered by default.

197 changes: 4 additions & 193 deletions crates/coordinator/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,199 +11,10 @@ use hdk::prelude::*;

use hc_zome_profiles_integrity::*;

/// Creates the profile for the agent executing this call.
#[hdk_extern]
pub fn create_profile(profile: Profile) -> ExternResult<Record> {
let agent_info = agent_info()?;

let action_hash = create_entry(EntryTypes::Profile(profile.clone()))?;

let path = prefix_path(profile.nickname.clone())?;

path.ensure()?;

let agent_address = agent_info.agent_initial_pubkey;

create_link(
path.path_entry_hash()?,
agent_address.clone(),
LinkTypes::PathToAgent,
LinkTag::new(profile.nickname.to_lowercase().as_bytes().to_vec()),
)?;
create_link(
agent_address,
action_hash.clone(),
LinkTypes::AgentToProfile,
(),
)?;

let record = get(action_hash, GetOptions::default())?
.ok_or(wasm_error!(WasmErrorInner::Guest("Unreachable".into())))?;

Ok(record)
}

/// Updates the profile for the agent executing this call.
#[hdk_extern]
pub fn update_profile(profile: Profile) -> ExternResult<Record> {
let previous_profile_record = crate::get_agent_profile(agent_info()?.agent_latest_pubkey)?
.ok_or(wasm_error!(WasmErrorInner::Guest(
"I haven't created a profile yet".into(),
)))?;

let action_hash = update_entry(previous_profile_record.action_address().clone(), &profile)?;
let my_pub_key = agent_info()?.agent_latest_pubkey;

// If we have changed the nickname, remove the previous nickname link and add a new one
let previous_profile: Profile = previous_profile_record
.entry()
.to_app_option()
.map_err(|e| wasm_error!(e))?
.ok_or(wasm_error!(WasmErrorInner::Guest(
"Previous profile is malformed".to_string()
)))?;
if previous_profile.nickname.ne(&profile.nickname) {
let previous_prefix_path = prefix_path(previous_profile.nickname)?;
let links = get_links(
GetLinksInputBuilder::try_new(
previous_prefix_path.path_entry_hash()?,
LinkTypes::PathToAgent.try_into_filter()?,
)?
.build(),
)?;

for l in links {
if let Ok(pub_key) = AgentPubKey::try_from(l.target) {
if my_pub_key.eq(&pub_key) {
delete_link(l.create_link_hash)?;
}
}
}

let path = prefix_path(profile.nickname.clone())?;

path.ensure()?;

create_link(
path.path_entry_hash()?,
my_pub_key,
LinkTypes::PathToAgent,
LinkTag::new(profile.nickname.to_lowercase().as_bytes().to_vec()),
)?;
}

let record = get(action_hash, GetOptions::default())?
.ok_or(wasm_error!(WasmErrorInner::Guest("Unreachable".into())))?;

Ok(record)
}

/// From a nickname filter of at least 3 characters, returns all the agents whose nickname starts with that prefix
/// Ignores the nickname case, will return upper or lower case nicknames that match
#[hdk_extern]
pub fn search_agents(nickname_filter: String) -> ExternResult<Vec<AgentPubKey>> {
if nickname_filter.len() < 3 {
return Err(wasm_error!(WasmErrorInner::Guest(
"Cannot search with a prefix less than 3 characters".into(),
)));
}

let prefix_path = prefix_path(nickname_filter.clone())?;
let links = get_links(
GetLinksInputBuilder::try_new(
prefix_path.path_entry_hash()?,
LinkTypes::PathToAgent.try_into_filter()?,
)?
.tag_prefix(LinkTag::new(
nickname_filter.to_lowercase().as_bytes().to_vec(),
))
.build(),
)?;

let mut agents: Vec<AgentPubKey> = vec![];

for link in links {
if let Ok(pub_key) = AgentPubKey::try_from(link.target) {
agents.push(pub_key);
}
}

Ok(agents)
}

/// Returns the profile for the given agent, if they have created it.
#[hdk_extern]
pub fn get_agent_profile(agent_pub_key: AgentPubKey) -> ExternResult<Option<Record>> {
let links = get_links(
GetLinksInputBuilder::try_new(agent_pub_key, LinkTypes::AgentToProfile.try_into_filter()?)?
.build(),
)?;

if links.is_empty() {
return Ok(None);
}

let link = links[0].clone();

let profile = get_latest(link.target.into_action_hash().ok_or(wasm_error!(
WasmErrorInner::Guest("Profile link target is not of ActionHash".into())
))?)?;

Ok(Some(profile))
}

fn get_latest(action_hash: ActionHash) -> ExternResult<Record> {
let details = get_details(action_hash, GetOptions::default())?.ok_or(wasm_error!(
WasmErrorInner::Guest("Profile not found".into())
))?;

match details {
Details::Entry(_) => Err(wasm_error!(WasmErrorInner::Guest(
"Malformed details".into()
))),
Details::Record(element_details) => match element_details.updates.last() {
Some(update) => get_latest(update.action_address().clone()),
None => Ok(element_details.record),
},
}
}

/// Gets all the agents that have created a profile in this DHT.
#[hdk_extern]
pub fn get_agents_with_profile(_: ()) -> ExternResult<Vec<Link>> {
let path = Path::from("all_profiles").typed(LinkTypes::PrefixPath)?;

let children = path.children_paths()?;

let get_links_input: Vec<GetLinksInput> = children
.into_iter()
.map(|path| {
Ok(GetLinksInputBuilder::try_new(
path.path_entry_hash()?,
LinkTypes::PathToAgent.try_into_filter()?,
)?
.build())
})
.collect::<ExternResult<Vec<GetLinksInput>>>()?;

let links = HDK
.with(|h| h.borrow().get_links(get_links_input))?
.into_iter()
.flatten()
.collect::<Vec<Link>>();

Ok(links)
}

/** Helpers*/

fn prefix_path(nickname: String) -> ExternResult<TypedPath> {
// conver to lowercase for path for ease of search
let lower_nickname = nickname.to_lowercase();
let prefix: String = lower_nickname.chars().take(3).collect();

Path::from(format!("all_profiles.{}", prefix)).typed(LinkTypes::PrefixPath)
}
mod link_agent;
mod profile_claims;
mod profiles;
mod search;

#[derive(Serialize, Deserialize, Debug)]
#[serde(tag = "type")]
Expand Down
26 changes: 26 additions & 0 deletions crates/coordinator/src/link_agent.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
use hc_zome_profiles_integrity::LinkTypes;
use hdk::prelude::*;

use crate::search::get_agent_profile;

#[hdk_extern]
pub fn link_agent_with_my_profile(agent_pub_key: AgentPubKey) -> ExternResult<()> {
let my_profile_links = get_agent_profile(agent_info()?.agent_latest_pubkey)?;

if my_profile_links.len() != 1 {
return Err(wasm_error!(WasmErrorInner::Guest(format!(
"Can't fetch my profile"
))));
}

let profile_link = my_profile_links[0].clone();
let Some(profile_hash) = profile_link.target.into_action_hash() else {
return Err(wasm_error!(WasmErrorInner::Guest(format!(
"Profile link does not have an ActionHash as its target"
))));
};

create_link(agent_pub_key, profile_hash, LinkTypes::AgentToProfile, ())?;

Ok(())
}
40 changes: 40 additions & 0 deletions crates/coordinator/src/profile_claims.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
use hdk::prelude::*;

use hc_zome_profiles_integrity::*;

#[hdk_extern]
pub fn create_profile_claim(profile_claim: ProfileClaim) -> ExternResult<()> {
create_relaxed(EntryTypes::ProfileClaim(profile_claim))?;

Ok(())
}
pub fn create_relaxed(entry_type: EntryTypes) -> ExternResult<()> {
HDK.with(|h| {
let index = ScopedEntryDefIndex::try_from(&entry_type)?;
let vis = EntryVisibility::from(&entry_type);
let entry = Entry::try_from(entry_type)?;

h.borrow().create(CreateInput::new(
index,
vis,
entry,
// This is used to test many conductors thrashing creates between
// each other so we want to avoid retries that make the test take
// a long time.
ChainTopOrdering::Relaxed,
))
})?;

Ok(())
}

#[hdk_extern]
pub fn query_my_profile_claims() -> ExternResult<Vec<Record>> {
let filter = ChainQueryFilter::new()
.entry_type(UnitEntryTypes::ProfileClaim.try_into()?)
.include_entries(true)
.action_type(ActionType::Create);
let records = query(filter)?;

Ok(records)
}
Loading

0 comments on commit 7601f09

Please sign in to comment.