Skip to content

Commit

Permalink
return tag as well for linked nodes
Browse files Browse the repository at this point in the history
  • Loading branch information
matthme committed Nov 7, 2024
1 parent 7bdac85 commit f9c24f8
Show file tree
Hide file tree
Showing 4 changed files with 115 additions and 58 deletions.
63 changes: 41 additions & 22 deletions dnas/generic_dna/zomes/coordinator/generic_zome/src/api.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,12 @@ pub enum NodeContent {
Thing(Thing),
}

#[derive(Serialize, Deserialize, Clone, Debug)]
pub struct NodeIdAndTag {
node_id: NodeId,
tag: Option<Vec<u8>>,
}

#[derive(Serialize, Deserialize, Clone, Debug)]
pub struct LinkInput {
pub direction: LinkDirection,
Expand Down Expand Up @@ -346,22 +352,22 @@ pub fn delete_thing(input: DeleteThingInput) -> ExternResult<()> {
}

#[hdk_extern]
pub fn get_all_linked_node_ids(node_id: NodeId) -> ExternResult<Vec<NodeId>> {
let mut linked_node_ids: Vec<NodeId> = Vec::new();
pub fn get_all_linked_node_ids(node_id: NodeId) -> ExternResult<Vec<NodeIdAndTag>> {
let mut linked_node_ids: Vec<NodeIdAndTag> = Vec::new();
let linked_thing_ids = get_linked_thing_ids(node_id.clone())?;
for thing_id in linked_thing_ids {
for (thing_id, tag) in linked_thing_ids {
let node = NodeId::Thing(thing_id);
linked_node_ids.push(node);
linked_node_ids.push(NodeIdAndTag { node_id: node, tag });
}
let linked_anchors = get_linked_anchors(node_id.clone())?;
for anchor in linked_anchors {
for (anchor, tag) in linked_anchors {
let node = NodeId::Anchor(anchor);
linked_node_ids.push(node);
linked_node_ids.push(NodeIdAndTag { node_id: node, tag });
}
let linked_agents = get_linked_agents(node_id)?;
for agent in linked_agents {
for (agent, tag) in linked_agents {
let node = NodeId::Agent(agent);
linked_node_ids.push(node);
linked_node_ids.push(NodeIdAndTag { node_id: node, tag });
}
Ok(linked_node_ids)
}
Expand All @@ -375,50 +381,63 @@ pub fn get_all_linked_nodes(node_id: NodeId) -> ExternResult<Vec<NodeContent>> {
linked_nodes.push(node);
}
let linked_anchors = get_linked_anchors(node_id.clone())?;
for anchor in linked_anchors {
for (anchor, _) in linked_anchors {
let node = NodeContent::Anchor(anchor);
linked_nodes.push(node);
}
let linked_agents = get_linked_agents(node_id)?;
for agent in linked_agents {
for (agent, _) in linked_agents {
let node = NodeContent::Agent(agent);
linked_nodes.push(node);
}
Ok(linked_nodes)
}

#[hdk_extern]
pub fn get_linked_agents(node_id: NodeId) -> ExternResult<Vec<AgentPubKey>> {
pub fn get_linked_agents(node_id: NodeId) -> ExternResult<Vec<(AgentPubKey, Option<Vec<u8>>)>> {
let base = linkable_hash_from_node_id(node_id)?;
let links = get_links(GetLinksInputBuilder::try_new(base, LinkTypes::ToAgent)?.build())?;
Ok(links
.into_iter()
.map(|l| l.target.into_agent_pub_key())
.filter_map(|a| a)
.map(|l| {
(
l.target.into_agent_pub_key(),
deserialize_link_tag(l.tag.0).ok(),
)
})
.filter(|(maybe_agent, maybe_tag)| maybe_agent.is_some() && maybe_tag.is_some())
.map(|(agent, tag)| (agent.unwrap(), tag.unwrap().tag))
.collect())
}

#[hdk_extern]
pub fn get_linked_anchors(node_id: NodeId) -> ExternResult<Vec<String>> {
pub fn get_linked_anchors(node_id: NodeId) -> ExternResult<Vec<(String, Option<Vec<u8>>)>> {
let base = linkable_hash_from_node_id(node_id)?;
let links = get_links(GetLinksInputBuilder::try_new(base, LinkTypes::ToAnchor)?.build())?;
Ok(links
.into_iter()
.map(|l| deserialize_link_tag(l.tag.0).ok())
.filter_map(|c| c)
.map(|c| anchor_string_from_node_id(c.target_node_id))
.filter_map(|a| a)
.filter_map(|l| deserialize_link_tag(l.tag.0).ok())
.map(|c| (anchor_string_from_node_id(c.target_node_id), c.tag))
.filter(|(maybe_anchor, _)| maybe_anchor.is_some())
.map(|(anchor, tag)| (anchor.unwrap(), tag))
.collect())
}

/// Returns the linked thing ids together with the link tag
#[hdk_extern]
pub fn get_linked_thing_ids(node_id: NodeId) -> ExternResult<Vec<ActionHash>> {
pub fn get_linked_thing_ids(node_id: NodeId) -> ExternResult<Vec<(ActionHash, Option<Vec<u8>>)>> {
let base = linkable_hash_from_node_id(node_id)?;
let links = get_links(GetLinksInputBuilder::try_new(base, LinkTypes::ToThing)?.build())?;
Ok(links
.into_iter()
.map(|l| l.target.into_action_hash())
.filter_map(|r| r)
.map(|l| {
(
l.target.into_action_hash(),
deserialize_link_tag(l.tag.0).ok(),
)
})
.filter(|(maybe_action, maybe_tag)| maybe_action.is_some() && maybe_tag.is_some())
.map(|(action, tag)| (action.unwrap(), tag.unwrap().tag))
.collect())
}

Expand All @@ -440,7 +459,7 @@ pub fn get_linked_things(node_id: NodeId) -> ExternResult<Vec<Thing>> {
#[derive(Serialize, Deserialize, Clone, Debug)]
pub struct NodeAndLinkedIds {
pub content: NodeContent,
pub linked_node_ids: Vec<NodeId>,
pub linked_node_ids: Vec<NodeIdAndTag>,
}

#[hdk_extern]
Expand Down
87 changes: 60 additions & 27 deletions lib/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,10 @@ import {
NodeAndLinkedIds,
NodeContent,
NodeId,
NodeIdAndTag,
NodeLink,
RemoteSignalInput,
Tag,
Thing,
ThingId,
UpdateThingInput,
Expand All @@ -50,7 +52,7 @@ declare global {

export type NodeStoreContent = {
content: NodeContent;
linkedNodeIds: NodeId[];
linkedNodeIds: NodeIdAndTag[];
};

export type AsyncStatus<T> =
Expand Down Expand Up @@ -207,8 +209,8 @@ export class SimpleHolochain {
allAgentsNodeStore.subscribe((val) => {
if (val.status === "complete" && val.value.content.type === "Anchor") {
this.allAgents = val.value.linkedNodeIds
.filter((nodeId) => nodeId.type === "Agent")
.map((nodeId) => nodeId.id);
.filter((nodeIdAndTag) => nodeIdAndTag.node_id.type === "Agent")
.map((nodeIdAndTag) => nodeIdAndTag.node_id.id) as AgentPubKey[];
console.log(
"GOT AGENTS: ",
this.allAgents.map((a) => encodeHashToBase64(a))
Expand Down Expand Up @@ -283,16 +285,16 @@ export class SimpleHolochain {
case "LinksCreated": {
console.log("Got LINKS_CREATED SIGNAL!!");

signal.links.forEach(({ src, dst }) => {
signal.links.forEach(({ src, dst, tag }) => {
const nodeStore = this.nodeStore(src);
nodeStore.nodeStore.update((store) => {
if (store.status === "complete") {
const currentLinkedNodeIds = store.value.linkedNodeIds;
const nodeExists = currentLinkedNodeIds.find((nodeId) =>
areNodesEqual(dst, nodeId)
const nodeExists = currentLinkedNodeIds.find((nodeIdAndTag) =>
areNodeAndTagEqual({ node_id: dst, tag }, nodeIdAndTag)
);
if (nodeExists) return store;
currentLinkedNodeIds.push(dst);
currentLinkedNodeIds.push({ node_id: dst, tag });
store.value.linkedNodeIds = currentLinkedNodeIds;
}
return store;
Expand All @@ -308,13 +310,13 @@ export class SimpleHolochain {
break;
}
case "LinksDeleted": {
signal.links.forEach(({ src, dst }) => {
signal.links.forEach(({ src, dst, tag }) => {
const nodeStore = this.nodeStore(src);
nodeStore.nodeStore.update((store) => {
if (store.status === "complete") {
const currentLinkedNodeIds = store.value.linkedNodeIds;
store.value.linkedNodeIds = currentLinkedNodeIds.filter(
(nodeId) => !areNodesEqual(dst, nodeId)
(nodeIdAndTag) => !areNodeAndTagEqual({ node_id: dst, tag }, nodeIdAndTag)
);
}
return store;
Expand Down Expand Up @@ -431,13 +433,13 @@ export class SimpleHolochain {
});
} else {
nodeStore.nodeStore.update((store) => {
let newLinkedNodeIds: NodeId[] = [];
let newLinkedNodeIds: NodeIdAndTag[] = [];
if (store.status === "complete") {
newLinkedNodeIds = store.value.linkedNodeIds;
}
nodeAndLinkedIds.linked_node_ids.forEach((nodeId) => {
if (!containsNodeId(newLinkedNodeIds, nodeId)) {
newLinkedNodeIds.push(nodeId);
nodeAndLinkedIds.linked_node_ids.forEach((nodeIdAndTag) => {
if (!containsNodeIdAndTag(newLinkedNodeIds, nodeIdAndTag)) {
newLinkedNodeIds.push(nodeIdAndTag);
}
});
return {
Expand Down Expand Up @@ -474,13 +476,13 @@ export class SimpleHolochain {
});
} else {
nodeStore.nodeStore.update((store) => {
let newLinkedNodeIds: NodeId[] = [];
let newLinkedNodeIds: NodeIdAndTag[] = [];
if (store.status === "complete") {
newLinkedNodeIds = store.value.linkedNodeIds;
}
nodeAndLinkedIds.linked_node_ids.forEach((nodeId) => {
if (!containsNodeId(newLinkedNodeIds, nodeId)) {
newLinkedNodeIds.push(nodeId);
nodeAndLinkedIds.linked_node_ids.forEach((nodeIdAndTag) => {
if (!containsNodeIdAndTag(newLinkedNodeIds, nodeIdAndTag)) {
newLinkedNodeIds.push(nodeIdAndTag);
}
});
return {
Expand Down Expand Up @@ -517,13 +519,13 @@ export class SimpleHolochain {
});
} else {
nodeStore.nodeStore.update((store) => {
let newLinkedNodeIds: NodeId[] = [];
let newLinkedNodeIds: NodeIdAndTag[] = [];
if (store.status === "complete") {
newLinkedNodeIds = store.value.linkedNodeIds;
}
nodeAndLinkedIds.linked_node_ids.forEach((nodeId) => {
if (!containsNodeId(newLinkedNodeIds, nodeId)) {
newLinkedNodeIds.push(nodeId);
nodeAndLinkedIds.linked_node_ids.forEach((nodeIdAndTag) => {
if (!containsNodeIdAndTag(newLinkedNodeIds, nodeIdAndTag)) {
newLinkedNodeIds.push(nodeIdAndTag);
}
});
return {
Expand Down Expand Up @@ -678,7 +680,7 @@ export class SimpleHolochain {
* @param src
* @returns
*/
async getAllLinkedNodeIds(src: NodeId): Promise<NodeId[]> {
async getAllLinkedNodeIds(src: NodeId): Promise<NodeIdAndTag[]> {
return this.callZome("get_all_linked_node_ids", src);
}

Expand All @@ -698,7 +700,9 @@ export class SimpleHolochain {
* @param src
* @returns
*/
async getLinkedAgents(src: NodeId): Promise<AgentPubKey[]> {
async getLinkedAgents(
src: NodeId
): Promise<[AgentPubKey, Tag | undefined][]> {
return this.callZome("get_linked_agents", src);
}

Expand All @@ -708,7 +712,7 @@ export class SimpleHolochain {
* @param src
* @returns
*/
async getLinkedAnchors(src: NodeId): Promise<string[]> {
async getLinkedAnchors(src: NodeId): Promise<[string, Tag | undefined][]> {
return this.callZome("get_linked_anchors", src);
}

Expand All @@ -719,7 +723,7 @@ export class SimpleHolochain {
* @param src
* @returns
*/
async getLinkedThings(src: NodeId): Promise<Thing[]> {
async getLinkedThings(src: NodeId): Promise<[Thing, Tag | undefined][]> {
return this.callZome("get_linked_things", src);
}

Expand Down Expand Up @@ -822,11 +826,40 @@ function areNodesEqual(nodeId_a: NodeId, nodeId_b: NodeId): boolean {
return false;
}

function containsNodeId(arr: NodeId[], nodeId: NodeId): boolean {
function areNodeAndTagEqual(
nodeId_a: NodeIdAndTag,
nodeId_b: NodeIdAndTag
): boolean {
if (nodeId_a.node_id.type !== nodeId_b.node_id.type) return false;
if (nodeId_a.node_id.type === "Agent" && nodeId_b.node_id.type === "Agent")
return (
encodeHashToBase64(nodeId_a.node_id.id) ===
encodeHashToBase64(nodeId_b.node_id.id) && areUint8ArrayEqual(nodeId_a.tag, nodeId_b.tag)
);
if (nodeId_a.node_id.type === "Thing" && nodeId_b.node_id.type === "Thing")
return (
encodeHashToBase64(nodeId_a.node_id.id) ===
encodeHashToBase64(nodeId_b.node_id.id) && areUint8ArrayEqual(nodeId_a.tag, nodeId_b.tag)
);
if (nodeId_a.node_id.type === "Anchor" && nodeId_b.node_id.type === "Anchor")
return nodeId_a.node_id.id === nodeId_b.node_id.id && areUint8ArrayEqual(nodeId_a.tag, nodeId_b.tag);
return false;
}

function containsNodeIdAndTag(
arr: NodeIdAndTag[],
nodeIdAndTag: NodeIdAndTag
): boolean {
for (const id of arr) {
if (areNodesEqual(id, nodeId)) {
if (areNodeAndTagEqual(id, nodeIdAndTag)) {
return true;
}
}
return false;
}

function areUint8ArrayEqual(a, b) {
if (a.length != b.length) return false;
for (let i = 0; i < a.length; i++) if (a[i] != b[i]) return false;
return true;
}
20 changes: 13 additions & 7 deletions lib/src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -69,11 +69,10 @@ export type SignalKind =
link_type: string;
};


export type RemoteSignalInput = {
signal: GenericZomeSignal,
agents: AgentPubKey[],
}
signal: GenericZomeSignal;
agents: AgentPubKey[];
};

/* dprint-ignore-start */
export type EntryTypes = { type: "Thing" } & ThingEntry;
Expand Down Expand Up @@ -107,6 +106,13 @@ export type NodeId =
id: AgentPubKey;
};

export type NodeIdAndTag = {
node_id: NodeId;
tag: Tag | undefined;
};

export type Tag = Uint8Array;

export type NodeContent =
| {
type: "Anchor";
Expand Down Expand Up @@ -193,6 +199,6 @@ export type CreateOrDeleteLinksInput = {
};

export type NodeAndLinkedIds = {
content: NodeContent,
linked_node_ids: NodeId[],
}
content: NodeContent;
linked_node_ids: NodeIdAndTag[];
};
3 changes: 1 addition & 2 deletions ui/src/holochain-app.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,8 @@
import { ActionHash, AppClient, AppWebsocket, HolochainError } from "@holochain/client";
import { HolochainError } from "@holochain/client";
import { createContext, provide } from "@lit/context";
import { css, html, LitElement } from "lit";
import { customElement, property, state } from "lit/decorators.js";

import HolochainLogo from "./assets/holochainLogo.svg";
import { simpleHolochainContext } from "./contexts";
import { sharedStyles } from "./shared-styles";
import { SimpleHolochain } from "@holochain/simple-holochain";
Expand Down

0 comments on commit f9c24f8

Please sign in to comment.