Skip to content

Commit 68cd1e7

Browse files
committed
feat: add the related task in its graph node
Signed-off-by: Jean-Baptiste Bianchi <jb.bianchi@neuroglia.io>
1 parent 311f389 commit 68cd1e7

File tree

1 file changed

+41
-39
lines changed

1 file changed

+41
-39
lines changed

src/lib/graph-builder.ts

Lines changed: 41 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -27,10 +27,6 @@ const catchReference = '/catch';
2727
const branchReference = '/fork/branches';
2828
const tryReference = '/try';
2929

30-
function exists<T>(obj: T | undefined): obj is T {
31-
return !!obj;
32-
}
33-
3430
/**
3531
* Represents a generic within a graph.
3632
* This serves as a base type for nodes, edges, and graphs.
@@ -75,6 +71,8 @@ export type GraphNode = GraphElement & {
7571
type: GraphNodeType;
7672
/** The parent graph, if any. */
7773
parent?: Graph;
74+
/** The related task */
75+
task?: Task;
7876
};
7977

8078
/**
@@ -161,13 +159,15 @@ function mapTasks(tasksList: TaskItem[] | undefined): Map<string, Task> {
161159
*
162160
* @param type The type of the graph node.
163161
* @param id Unique identifier for the graph.
162+
* @param task The related task
164163
* @param label Optional label for the graph.
165164
* @param parent Optional parent graph if this is a subgraph.
166165
* @returns A newly created Graph instance.
167166
*/
168167
function initGraph(
169168
type: GraphNodeType,
170169
id: string = rootId,
170+
task: Task | undefined = undefined,
171171
label: string | undefined = undefined,
172172
parent: Graph | undefined = undefined,
173173
): Graph {
@@ -176,6 +176,7 @@ function initGraph(
176176
label,
177177
type,
178178
parent,
179+
task,
179180
nodes: [],
180181
edges: [],
181182
};
@@ -274,7 +275,7 @@ function buildTransition(sourceNode: GraphNode | Graph, transition: TransitionIn
274275
transition.label,
275276
);
276277
} else if (transition.name === FlowDirective.Exit) {
277-
if (!exists(context.graph.exitNode)) throw new Error(`Missing exit node on graph id '${context.graph.id}'`);
278+
if (!context.graph.exitNode) throw new Error(`Missing exit node on graph id '${context.graph.id}'`);
278279
buildEdge(context.graph, context.knownEdges, exitAnchor, context.graph.exitNode, transition.label);
279280
} else if (transition.name === FlowDirective.End) {
280281
buildEdge(context.graph, context.knownEdges, exitAnchor, getEndNode(context.graph), transition.label);
@@ -336,8 +337,9 @@ function buildTaskNode(context: TaskContext): GraphNode | Graph {
336337
* @param context The context to build the graph node for
337338
* @returns A graph node for the provided context
338339
*/
339-
function buildGenericTaskNode(type: GraphNodeType, context: TaskContext): GraphNode {
340+
function buildGenericTaskNode(task: Task, type: GraphNodeType, context: TaskContext): GraphNode {
340341
const node: GraphNode = {
342+
task,
341343
type,
342344
parent: context.graph,
343345
id: context.taskReference,
@@ -355,7 +357,7 @@ function buildGenericTaskNode(type: GraphNodeType, context: TaskContext): GraphN
355357
* @returns A graph node for the provided task
356358
*/
357359
function buildCallTaskNode(task: CallTask, context: TaskContext): GraphNode {
358-
const node = buildGenericTaskNode(GraphNodeType.Call, context);
360+
const node = buildGenericTaskNode(task, GraphNodeType.Call, context);
359361
// TODO: add some details about the task?
360362
return node;
361363
}
@@ -367,15 +369,15 @@ function buildCallTaskNode(task: CallTask, context: TaskContext): GraphNode {
367369
* @returns A graph for the provided task
368370
*/
369371
function buildDoTaskNode(task: DoTask, context: TaskContext): Graph {
370-
const subgraph: Graph = initGraph(GraphNodeType.Do, context.taskReference, context.taskName, context.graph);
372+
const subgraph: Graph = initGraph(GraphNodeType.Do, context.taskReference, task, context.taskName, context.graph);
371373
const doContext: TaskContext = {
372374
...context,
373375
graph: subgraph,
374376
taskListReference: context.taskReference + doReference,
375377
taskList: mapTasks(task.do),
376378
taskName: undefined,
377379
};
378-
if (!exists(subgraph.entryNode)) throw new Error(`Missing 'entryNode' on graph id '${subgraph.id}'`);
380+
if (!subgraph.entryNode) throw new Error(`Missing 'entryNode' on graph id '${subgraph.id}'`);
379381
buildTransitions(subgraph.entryNode, doContext);
380382
buildTransitions(subgraph, context);
381383
return subgraph;
@@ -388,7 +390,7 @@ function buildDoTaskNode(task: DoTask, context: TaskContext): Graph {
388390
* @returns A graph node for the provided task
389391
*/
390392
function buildEmitTaskNode(task: EmitTask, context: TaskContext): GraphNode {
391-
const node = buildGenericTaskNode(GraphNodeType.Emit, context);
393+
const node = buildGenericTaskNode(task, GraphNodeType.Emit, context);
392394
// TODO: add some details about the task?
393395
return node;
394396
}
@@ -400,15 +402,15 @@ function buildEmitTaskNode(task: EmitTask, context: TaskContext): GraphNode {
400402
* @returns A graph for the provided task
401403
*/
402404
function buildForTaskNode(task: ForTask, context: TaskContext): Graph {
403-
const subgraph: Graph = initGraph(GraphNodeType.For, context.taskReference, context.taskName, context.graph);
405+
const subgraph: Graph = initGraph(GraphNodeType.For, context.taskReference, task, context.taskName, context.graph);
404406
const forContext: TaskContext = {
405407
...context,
406408
graph: subgraph,
407409
taskListReference: subgraph.id + forReference + doReference,
408410
taskList: mapTasks(task.do),
409411
taskName: undefined,
410412
};
411-
if (!exists(subgraph.entryNode)) throw new Error(`Missing 'entryNode' on graph id '${subgraph.id}'`);
413+
if (!subgraph.entryNode) throw new Error(`Missing 'entryNode' on graph id '${subgraph.id}'`);
412414
buildTransitions(subgraph.entryNode, forContext);
413415
buildTransitions(subgraph, context);
414416
return subgraph;
@@ -421,7 +423,7 @@ function buildForTaskNode(task: ForTask, context: TaskContext): Graph {
421423
* @returns A graph for the provided task
422424
*/
423425
function buildForkTaskNode(task: ForkTask, context: TaskContext): Graph {
424-
const subgraph: Graph = initGraph(GraphNodeType.Fork, context.taskReference, context.taskName, context.graph);
426+
const subgraph: Graph = initGraph(GraphNodeType.Fork, context.taskReference, task, context.taskName, context.graph);
425427
for (let i = 0, c = task.fork?.branches.length || 0; i < c; i++) {
426428
const branchItem = task.fork?.branches[i];
427429
if (!branchItem) continue;
@@ -435,8 +437,8 @@ function buildForkTaskNode(task: ForkTask, context: TaskContext): Graph {
435437
taskName: branchName,
436438
};
437439
const branchNode = buildTaskNode(branchContext);
438-
if (!exists(subgraph.entryNode)) throw new Error(`Missing 'entryNode' on graph id '${subgraph.id}'`);
439-
if (!exists(subgraph.exitNode)) throw new Error(`Missing 'exitNode' on graph id '${subgraph.id}'`);
440+
if (!subgraph.entryNode) throw new Error(`Missing 'entryNode' on graph id '${subgraph.id}'`);
441+
if (!subgraph.exitNode) throw new Error(`Missing 'exitNode' on graph id '${subgraph.id}'`);
440442
buildEdge(subgraph, context.knownEdges, subgraph.entryNode, (branchNode as Graph).entryNode || branchNode);
441443
buildEdge(subgraph, context.knownEdges, (branchNode as Graph).exitNode || branchNode, subgraph.exitNode);
442444
}
@@ -451,7 +453,7 @@ function buildForkTaskNode(task: ForkTask, context: TaskContext): Graph {
451453
* @returns A graph node for the provided task
452454
*/
453455
function buildListenTaskNode(task: ListenTask, context: TaskContext): GraphNode {
454-
const node = buildGenericTaskNode(GraphNodeType.Listen, context);
456+
const node = buildGenericTaskNode(task, GraphNodeType.Listen, context);
455457
// TODO: add some details about the task?
456458
return node;
457459
}
@@ -463,7 +465,7 @@ function buildListenTaskNode(task: ListenTask, context: TaskContext): GraphNode
463465
* @returns A graph node for the provided task
464466
*/
465467
function buildRaiseTaskNode(task: RaiseTask, context: TaskContext): GraphNode {
466-
const node = buildGenericTaskNode(GraphNodeType.Raise, context);
468+
const node = buildGenericTaskNode(task, GraphNodeType.Raise, context);
467469
// TODO: add some details about the task?
468470
return node;
469471
}
@@ -475,7 +477,7 @@ function buildRaiseTaskNode(task: RaiseTask, context: TaskContext): GraphNode {
475477
* @returns A graph node for the provided task
476478
*/
477479
function buildRunTaskNode(task: RunTask, context: TaskContext): GraphNode {
478-
const node = buildGenericTaskNode(GraphNodeType.Run, context);
480+
const node = buildGenericTaskNode(task, GraphNodeType.Run, context);
479481
// TODO: add some details about the task?
480482
return node;
481483
}
@@ -487,7 +489,7 @@ function buildRunTaskNode(task: RunTask, context: TaskContext): GraphNode {
487489
* @returns A graph node for the provided task
488490
*/
489491
function buildSetTaskNode(task: SetTask, context: TaskContext): GraphNode {
490-
const node = buildGenericTaskNode(GraphNodeType.Set, context);
492+
const node = buildGenericTaskNode(task, GraphNodeType.Set, context);
491493
// TODO: add some details about the task?
492494
return node;
493495
}
@@ -499,7 +501,7 @@ function buildSetTaskNode(task: SetTask, context: TaskContext): GraphNode {
499501
* @returns A graph node for the provided task
500502
*/
501503
function buildSwitchTaskNode(task: SwitchTask, context: TaskContext): GraphNode {
502-
const node: GraphNode = buildGenericTaskNode(GraphNodeType.Switch, context);
504+
const node: GraphNode = buildGenericTaskNode(task, GraphNodeType.Switch, context);
503505
let hasDefaultCase = false;
504506
task.switch?.forEach((switchItem) => {
505507
const [caseName, switchCase] = Object.entries(switchItem)[0];
@@ -524,18 +526,19 @@ function buildTryCatchTaskNode(task: TryTask, context: TaskContext): Graph {
524526
const containerSubgraph: Graph = initGraph(
525527
GraphNodeType.TryCatch,
526528
context.taskReference,
529+
task,
527530
context.taskName,
528531
context.graph,
529532
);
530533
const trySubgraph: Graph = initGraph(
531534
GraphNodeType.Try,
532535
context.taskReference + tryReference,
536+
task,
533537
context.taskName + ' (try)',
534538
containerSubgraph,
535539
);
536-
if (!exists(containerSubgraph.entryNode))
537-
throw new Error(`Missing 'entryNode' on graph id '${containerSubgraph.id}'`);
538-
if (!exists(trySubgraph.entryNode)) throw new Error(`Missing 'entryNode' on graph id '${trySubgraph.id}'`);
540+
if (!containerSubgraph.entryNode) throw new Error(`Missing 'entryNode' on graph id '${containerSubgraph.id}'`);
541+
if (!trySubgraph.entryNode) throw new Error(`Missing 'entryNode' on graph id '${trySubgraph.id}'`);
539542
buildEdge(containerSubgraph, context.knownEdges, containerSubgraph.entryNode, trySubgraph.entryNode);
540543
const tryContext: TaskContext = {
541544
...context,
@@ -544,31 +547,31 @@ function buildTryCatchTaskNode(task: TryTask, context: TaskContext): Graph {
544547
taskList: mapTasks(task.try),
545548
taskName: undefined,
546549
};
547-
if (!exists(trySubgraph.entryNode)) throw new Error(`Missing 'entryNode' on graph id '${trySubgraph.id}'`);
550+
if (!trySubgraph.entryNode) throw new Error(`Missing 'entryNode' on graph id '${trySubgraph.id}'`);
548551
buildTransitions(trySubgraph.entryNode, tryContext);
549552
if (!task.catch?.do?.length) {
550553
const catchNode: GraphNode = {
554+
task,
551555
type: GraphNodeType.Catch,
552556
parent: containerSubgraph,
553557
id: context.taskReference + catchReference,
554558
label: context.taskName + ' (catch)',
555559
};
556560
containerSubgraph.nodes.push(catchNode);
557-
if (!exists(trySubgraph.exitNode)) throw new Error(`Missing 'exitNode' on graph id '${trySubgraph.id}'`);
558-
if (!exists(containerSubgraph.exitNode))
559-
throw new Error(`Missing 'exitNode' on graph id '${containerSubgraph.id}'`);
561+
if (!trySubgraph.exitNode) throw new Error(`Missing 'exitNode' on graph id '${trySubgraph.id}'`);
562+
if (!containerSubgraph.exitNode) throw new Error(`Missing 'exitNode' on graph id '${containerSubgraph.id}'`);
560563
buildEdge(containerSubgraph, context.knownEdges, trySubgraph.exitNode, catchNode);
561564
buildEdge(containerSubgraph, context.knownEdges, catchNode, containerSubgraph.exitNode);
562565
} else {
563566
const catchSubgraph: Graph = initGraph(
564567
GraphNodeType.Catch,
565568
context.taskReference + catchReference + doReference,
569+
task,
566570
context.taskName + ' (catch)',
567571
containerSubgraph,
568572
);
569-
if (!exists(trySubgraph.exitNode)) throw new Error(`Missing 'exitNode' on graph id '${trySubgraph.id}'`);
570-
if (!exists(catchSubgraph.entryNode))
571-
throw new Error(`Missing 'entryNode' on graph id '${catchSubgraph.entryNode}'`);
573+
if (!trySubgraph.exitNode) throw new Error(`Missing 'exitNode' on graph id '${trySubgraph.id}'`);
574+
if (!catchSubgraph.entryNode) throw new Error(`Missing 'entryNode' on graph id '${catchSubgraph.entryNode}'`);
572575
buildEdge(containerSubgraph, context.knownEdges, trySubgraph.exitNode, catchSubgraph.entryNode);
573576
const catchContext: TaskContext = {
574577
...context,
@@ -578,9 +581,8 @@ function buildTryCatchTaskNode(task: TryTask, context: TaskContext): Graph {
578581
taskName: undefined,
579582
};
580583
buildTransitions(catchSubgraph.entryNode, catchContext);
581-
if (!exists(catchSubgraph.exitNode)) throw new Error(`Missing 'exitNode' on graph id '${catchSubgraph.exitNode}'`);
582-
if (!exists(containerSubgraph.exitNode))
583-
throw new Error(`Missing 'exitNode' on graph id '${containerSubgraph.exitNode}'`);
584+
if (!catchSubgraph.exitNode) throw new Error(`Missing 'exitNode' on graph id '${catchSubgraph.exitNode}'`);
585+
if (!containerSubgraph.exitNode) throw new Error(`Missing 'exitNode' on graph id '${containerSubgraph.exitNode}'`);
584586
buildEdge(containerSubgraph, context.knownEdges, catchSubgraph.exitNode, containerSubgraph.exitNode);
585587
}
586588
buildTransitions(containerSubgraph, context);
@@ -594,7 +596,7 @@ function buildTryCatchTaskNode(task: TryTask, context: TaskContext): Graph {
594596
* @returns A graph node for the provided task
595597
*/
596598
function buildWaitTaskNode(task: WaitTask, context: TaskContext): GraphNode {
597-
const node = buildGenericTaskNode(GraphNodeType.Wait, context);
599+
const node = buildGenericTaskNode(task, GraphNodeType.Wait, context);
598600
// TODO: add some details about the task?
599601
return node;
600602
}
@@ -627,7 +629,7 @@ function buildEdge(graph: Graph, knownEdges: GraphEdge[], source: GraphNode, tar
627629
* Remaps edges by getting rid of routes leading to entry/exit nodes
628630
* @param edges
629631
*/
630-
const remapEdges = (edges: GraphEdge[]): GraphEdge[] => {
632+
export const remapEdges = (edges: GraphEdge[]): GraphEdge[] => {
631633
let remappedEdges = [...edges.map((e) => ({ ...e }))];
632634
const leadsToPort = (edge: GraphEdge) =>
633635
edge.targetId !== 'root-exit-node' &&
@@ -675,7 +677,7 @@ const remapEdges = (edges: GraphEdge[]): GraphEdge[] => {
675677
* @param graph The graph to flatten the edges of
676678
* @returns All the edge declared in the graph and its subgraphs
677679
*/
678-
const flattenEdges = (graph: Graph): GraphEdge[] => [
680+
export const flattenEdges = (graph: Graph): GraphEdge[] => [
679681
...(graph.edges || []),
680682
...((graph.nodes || []).filter((node) => (node as Graph).edges?.length) as Graph[]).flatMap(flattenEdges),
681683
];
@@ -685,7 +687,7 @@ const flattenEdges = (graph: Graph): GraphEdge[] => [
685687
* @param graph The graph/node to flatten the nodes of
686688
* @returns All the nodes and subnodes declared in the graph
687689
*/
688-
const flattenNodes = (node: Graph | GraphNode): Array<Graph | GraphNode> => [
690+
export const flattenNodes = (node: Graph | GraphNode): Array<Graph | GraphNode> => [
689691
{
690692
...node,
691693
entryNode: undefined,
@@ -701,7 +703,7 @@ const flattenNodes = (node: Graph | GraphNode): Array<Graph | GraphNode> => [
701703
* @param graph The target graph
702704
* @returns The modified graph
703705
*/
704-
function flattenGraph(graph: Graph): Graph {
706+
export function flattenGraph(graph: Graph): Graph {
705707
const edges = remapEdges(flattenEdges(graph));
706708
const nodes = graph.nodes
707709
.flatMap((node) => flattenNodes(node))
@@ -718,7 +720,7 @@ function flattenGraph(graph: Graph): Graph {
718720
* Constructs a graph representation based on the given workflow.
719721
*
720722
* @param workflow The workflow to be converted into a graph structure.
721-
* @param simplifyGraph A boolean indicating whether the graph should be flattened and free of internal entry/exit nodes.
723+
* @param simplifyGraph A boolean indicating whether the graph nodes & edges should be flattened and free of internal entry/exit nodes.
722724
* @returns A graph representation of the workflow.
723725
*/
724726
export function buildGraph(workflow: Workflow, simplifyGraph: boolean = false): Graph {

0 commit comments

Comments
 (0)