blob: 8f02da4864ce422c547fcc953f40b68f37b5bd10 [file]
import { DAGNode } from "../components/dashboard/Visualize/types";
import {
CodeArtifact,
DAGTemplateWithData,
NodeTemplate,
} from "../state/api/friendlyApi";
export const getNodesProducedByFunction = (
dag: DAGTemplateWithData,
codeArtifact: CodeArtifact
): NodeTemplate[] => {
// All nodes that are produced by the function
const producedByFunction = dag.nodes.filter((node) => {
return node.code_artifacts?.indexOf(codeArtifact?.id || -1) != -1; // Sloppy way to check if we have it
});
return producedByFunction;
// const intermediate = producedByFunction.filter((node) => {
// const dependencies = node.dependencies as string[];
// return (
// // if all dependencies are produced by the function
// // then its not an intermediate node
// dependencies.filter((dependency) => {
// return producedByFunction.map((i) => i.name).indexOf(dependency) != -1;
// }).length > 0
// );
// });
// // Upstream nodes are all those that are depended on by the intermediate nodes
// const upstream = producedByFunction.flatMap((node) => {
// return dag.nodes.filter((otherNode) => {
// const dependencies = node.dependencies as string[];
// return (
// producedByFunction.map((i) => i.name).indexOf(otherNode.name) == -1 &&
// dependencies.includes(otherNode.name)
// );
// });
// });
// // Downstream nodes are all those that depend on any intermediate nodes
// return { upstream, intermediate, sinks };
};
export const getDirectlyUpstreamNodes = (
dag: DAGTemplateWithData,
codeArtifact: CodeArtifact
): NodeTemplate[] => {
const nodesProducedByFunction = getNodesProducedByFunction(dag, codeArtifact);
const upstream = nodesProducedByFunction.flatMap((node) => {
return dag.nodes.filter((otherNode) => {
const dependencies = node.dependencies as string[];
return (
nodesProducedByFunction.map((i) => i.name).indexOf(otherNode.name) ==
-1 && dependencies.includes(otherNode.name)
);
});
});
return upstream;
};
export const groupMap = (nodes: DAGNode[]) => {
/**
* Groups nodes by their name
* Note that this is required as we produce all possible supersets of nodes
*/
const map = new Map<string, DAGNode[]>();
for (const node of nodes) {
const key = node.name;
if (map.has(key)) {
map.get(key)?.push(node);
} else {
map.set(key, [node]);
}
}
return map;
};
export const getAllUpstreamNodes = (
startNodes: DAGNode[],
includeSelf: boolean,
allNodes: DAGNode[]
): DAGNode[] => {
// Get a map of node names to possible implementations
const allNodeImplementations = groupMap(allNodes);
const upstream = new Set<string>(startNodes.map((n) => n.name));
const queue = [...startNodes];
while (queue.length > 0) {
const currentNode = queue.pop() as DAGNode;
for (const dependency of currentNode.nodeTemplate.dependencies || []) {
if (!upstream.has(dependency)) {
const possibleUpstreamNodes =
allNodeImplementations.get(dependency) ?? [];
possibleUpstreamNodes.forEach((n) => {
queue.push(n);
});
upstream.add(dependency);
}
}
}
const filterFunc = includeSelf
? () => true
: (n: string) => !startNodes.map((node) => node.name === n).some((i) => i);
return (
Array.from(upstream)
.filter(filterFunc)
// Get all possible implementations of the node
// If its not here (probably shouldn't happen) we just append an empty list
.flatMap((nodeName) => allNodeImplementations.get(nodeName) ?? [])
);
};
export const getAllDownstreamNodes = (
startNodes: DAGNode[],
includeSelf: boolean,
allNodes: DAGNode[]
): DAGNode[] => {
const allNodeImplementations = groupMap(allNodes);
const reverseMap = new Map<string, Set<string>>(); // Map of nodes => nodes that depend on them
const downstream = new Set<string>(startNodes.map((n) => n.name));
for (const n of allNodes) {
for (const dependency of n.nodeTemplate.dependencies || []) {
const depSet = reverseMap.get(dependency) || new Set<string>();
depSet.add(n.name);
reverseMap.set(dependency, depSet);
}
}
const queue = [...startNodes];
while (queue.length > 0) {
const currentNode = queue.pop();
if (currentNode === undefined) {
throw Error("this shouldn't happen");
}
const dependencies = reverseMap.get(currentNode.name) || new Set<string>();
for (const dependency of Array.from(dependencies)) {
if (!downstream.has(dependency)) {
downstream.add(dependency);
const possibleImplementations =
allNodeImplementations.get(dependency) || [];
possibleImplementations.forEach((n) => {
queue.push(n);
});
}
}
}
const filterFunc = includeSelf
? () => true
: (n: string) => !startNodes.map((node) => node.name === n).some((i) => i);
return Array.from(downstream.values())
.filter(filterFunc)
.flatMap((name) => allNodeImplementations.get(name) || []);
};