Skip to content

Commit 824e01a

Browse files
authored
Merge pull request #70 from FlowTestAI/flow-node
Ability to form nested complex graphs
2 parents 30a937a + cd4819d commit 824e01a

File tree

13 files changed

+387
-231
lines changed

13 files changed

+387
-231
lines changed

src/components/molecules/flow/AddNodes.js

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,11 @@ const authNode = {
5252
type: 'authNode',
5353
};
5454

55+
const complexNode = {
56+
description: 'Helps to create nested flows',
57+
type: 'complexNode',
58+
};
59+
5560
const AddNodes = ({ collectionId }) => {
5661
// const [open, setOpen] = useState(false);
5762
// const anchorRef = useRef(null);
@@ -265,6 +270,29 @@ const AddNodes = ({ collectionId }) => {
265270
</>
266271
)}
267272
</Disclosure>
273+
{/* Complex */}
274+
<Disclosure as='div'>
275+
{({ open }) => (
276+
<>
277+
<Disclosure.Button className='flex justify-between w-full px-4 py-2 text-lg font-medium text-left border-t border-b bg-gray-50 hover:bg-gray-100 focus:outline-none focus-visible:ring'>
278+
<span>Complex</span>
279+
<ChevronUpIcon className={`${open ? 'rotate-180 transform' : ''} h-5 w-5 `} />
280+
</Disclosure.Button>
281+
<Disclosure.Panel className='px-4 pt-4 pb-2 text-sm border-l border-r'>
282+
<div
283+
key='complex'
284+
onDragStart={(event) => onDragStart(event, complexNode)}
285+
draggable
286+
cursor='move'
287+
className='py-2 border-b'
288+
>
289+
<div className='text-base font-semibold primary-text'>Complex</div>
290+
<div className='text-xs secondary-text'>{complexNode.description}</div>
291+
</div>
292+
</Disclosure.Panel>
293+
</>
294+
)}
295+
</Disclosure>
268296
</div>
269297
</>
270298
</Popover.Panel>

src/components/molecules/flow/graph/Graph.js

Lines changed: 35 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,12 @@
11
// assumption is that apis are giving json as output
22

3+
import { cloneDeep } from 'lodash';
4+
import { readFlowTestSync } from 'service/collection';
35
import useCanvasStore from 'stores/CanvasStore';
4-
import { computeAuthNode } from './compute/authnode';
5-
import { computeEvaluateNode } from './compute/evaluatenode';
6-
import { computeRequestNode } from './compute/requestnode';
6+
import authNode from './compute/authnode';
7+
import complexNode from './compute/complexNode';
8+
import evaluateNode from './compute/evaluateNode';
9+
import requestNode from './compute/requestNode';
710

811
class Graph {
912
constructor(nodes, edges, startTime, initialEnvVars, initialLogs) {
@@ -67,23 +70,21 @@ class Graph {
6770
useCanvasStore.getState().setOutputNode(node.id, prevNodeOutputData);
6871
result = {
6972
status: 'Success',
70-
node,
7173
};
7274
}
7375

7476
if (node.type === 'evaluateNode') {
75-
if (computeEvaluateNode(node, prevNodeOutputData, this.logs)) {
77+
const eNode = new evaluateNode(node.data.operator, node.data.variables, prevNodeOutputData, this.logs);
78+
if (eNode.evaluate()) {
7679
this.logs.push('Result: true');
7780
result = {
7881
status: 'Success',
79-
node,
8082
output: true,
8183
};
8284
} else {
8385
this.logs.push('Result: false');
8486
result = {
8587
status: 'Success',
86-
node,
8788
output: false,
8889
};
8990
}
@@ -98,20 +99,20 @@ class Graph {
9899
this.logs.push(`Wait for: ${delay} ms`);
99100
result = {
100101
status: 'Success',
101-
node,
102102
};
103103
}
104104

105105
if (node.type === 'authNode') {
106-
this.auth = node.data.type ? computeAuthNode(node.data, this.envVariables) : undefined;
106+
const aNode = new authNode(node.data, this.envVariables);
107+
this.auth = node.data.type ? aNode.evaluate() : undefined;
107108
result = {
108109
status: 'Success',
109-
node,
110110
};
111111
}
112112

113113
if (node.type === 'requestNode') {
114-
result = await computeRequestNode(node, prevNodeOutputData, this.envVariables, this.auth, this.logs);
114+
const rNode = new requestNode(node.data, prevNodeOutputData, this.envVariables, this.auth, this.logs);
115+
result = await rNode.evaluate();
115116
// add post response variables if any
116117
if (result.postRespVars) {
117118
this.envVariables = {
@@ -121,30 +122,47 @@ class Graph {
121122
}
122123
}
123124

125+
if (node.type === 'complexNode') {
126+
const flowData = await readFlowTestSync(node.data.pathname);
127+
if (flowData) {
128+
const cNode = new complexNode(
129+
cloneDeep(flowData.nodes),
130+
cloneDeep(flowData.edges),
131+
this.startTime,
132+
this.envVariables,
133+
this.logs,
134+
);
135+
result = await cNode.evaluate();
136+
this.envVariables = result.envVars;
137+
} else {
138+
result = {
139+
status: 'Success',
140+
};
141+
}
142+
}
143+
124144
if (this.#checkTimeout()) {
125145
throw `Timeout of ${this.timeout} ms exceeded, stopping graph run`;
126146
}
127147
} catch (err) {
128148
this.logs.push(`Flow failed at: ${JSON.stringify(node)} due to ${err}`);
129149
return {
130150
status: 'Failed',
131-
node,
132151
};
133152
}
134153

135154
if (result === undefined) {
136155
this.logs.push(`Flow failed at: ${JSON.stringify(node)}`);
137156
return {
138157
status: 'Failed',
139-
node,
140158
};
141159
} else {
142160
const connectingEdge = this.#computeConnectingEdge(node, result);
143161

144162
if (connectingEdge != undefined) {
145163
const nextNode = this.nodes.find(
146164
(node) =>
147-
['requestNode', 'outputNode', 'evaluateNode', 'delayNode', 'authNode'].includes(node.type) &&
165+
['requestNode', 'outputNode', 'evaluateNode', 'delayNode', 'authNode', 'complexNode'].includes(node.type) &&
148166
node.id === connectingEdge.target,
149167
);
150168
this.graphRunNodeOutput[node.id] = result.data ? result.data : {};
@@ -181,9 +199,9 @@ class Graph {
181199
if (connectingEdge != undefined) {
182200
const firstNode = this.nodes.find((node) => node.id === connectingEdge.target);
183201
const result = await this.#computeNode(firstNode);
184-
if (result.status == 'Failed') {
185-
console.debug('Flow failed at: ', result.node);
186-
}
202+
// if (result.status == 'Failed') {
203+
// console.debug('Flow failed at: ', result.node);
204+
// }
187205
this.logs.push('End Flowtest');
188206
return {
189207
status: result.status,
Lines changed: 21 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,25 @@
11
import { computeVariables } from './utils';
2+
import Node from './node';
23

3-
export const computeAuthNode = (auth, envVariables) => {
4-
if (auth.type === 'basic-auth') {
5-
auth.username = computeVariables(auth.username, envVariables);
6-
auth.password = computeVariables(auth.password, envVariables);
4+
class authNode extends Node {
5+
constructor(auth, envVariables) {
6+
super('authNode');
7+
(this.auth = auth), (this.envVariables = envVariables);
8+
}
9+
10+
evaluate() {
11+
console.log('Evaluating an auth node');
12+
if (this.auth.type === 'basic-auth') {
13+
this.auth.username = computeVariables(this.auth.username, this.envVariables);
14+
this.auth.password = computeVariables(this.auth.password, this.envVariables);
715

8-
return auth;
9-
} else if (auth.type === 'no-auth') {
10-
return auth;
11-
} else {
12-
throw Error(`auth type: ${auth.type} is not valid`);
16+
return this.auth;
17+
} else if (this.auth.type === 'no-auth') {
18+
return this.auth;
19+
} else {
20+
throw Error(`auth type: ${this.auth.type} is not valid`);
21+
}
1322
}
14-
};
23+
}
24+
25+
export default authNode;
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
import Graph1 from '../Graph';
2+
import Node from './node';
3+
4+
class complexNode extends Node {
5+
constructor(nodes, edges, startTime, initialEnvVars, initialLogs) {
6+
super('complexNode');
7+
this.internalGraph = new Graph1(nodes, edges, startTime, initialEnvVars, initialLogs);
8+
}
9+
10+
async evaluate() {
11+
console.log('Evaluating a complex node (nested graph):');
12+
return this.internalGraph.run();
13+
}
14+
}
15+
16+
export default complexNode;
Lines changed: 35 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1,25 +1,39 @@
11
import Operators from '../../constants/operators';
2-
import { computeNodeVariables } from './utils';
2+
import { computeNodeVariables } from '../compute/utils';
3+
import Node from './node';
34

4-
export const computeEvaluateNode = (node, prevNodeOutputData, logs) => {
5-
const evalVariables = computeNodeVariables(node.data.variables, prevNodeOutputData);
6-
const var1 = evalVariables.var1;
7-
const var2 = evalVariables.var2;
8-
9-
const operator = node.data.operator;
10-
if (operator == undefined) {
11-
throw 'Operator undefined';
5+
class evaluateNode extends Node {
6+
constructor(operator, variables, prevNodeOutputData, logs) {
7+
super('evaluateNode');
8+
this.operator = operator;
9+
this.variables = variables;
10+
this.prevNodeOutputData = prevNodeOutputData;
11+
this.logs = logs;
1212
}
13-
logs.push(
14-
`Evaluate var1: ${JSON.stringify(var1)} of type: ${typeof var1}, var2: ${JSON.stringify(var2)} of type: ${typeof var2} with operator: ${operator}`,
15-
);
16-
if (operator == Operators.isEqualTo) {
17-
return var1 === var2;
18-
} else if (operator == Operators.isNotEqualTo) {
19-
return var1 != var2;
20-
} else if (operator == Operators.isGreaterThan) {
21-
return var1 > var2;
22-
} else if (operator == Operators.isLessThan) {
23-
return var1 < var2;
13+
14+
evaluate() {
15+
console.log('Evaluating an evaluate node');
16+
const evalVariables = computeNodeVariables(this.variables, this.prevNodeOutputData);
17+
const var1 = evalVariables.var1;
18+
const var2 = evalVariables.var2;
19+
20+
const operator = this.operator;
21+
if (operator == undefined) {
22+
throw 'Operator undefined';
23+
}
24+
this.logs.push(
25+
`Evaluate var1: ${JSON.stringify(var1)} of type: ${typeof var1}, var2: ${JSON.stringify(var2)} of type: ${typeof var2} with operator: ${operator}`,
26+
);
27+
if (operator == Operators.isEqualTo) {
28+
return var1 === var2;
29+
} else if (operator == Operators.isNotEqualTo) {
30+
return var1 != var2;
31+
} else if (operator == Operators.isGreaterThan) {
32+
return var1 > var2;
33+
} else if (operator == Operators.isLessThan) {
34+
return var1 < var2;
35+
}
2436
}
25-
};
37+
}
38+
39+
export default evaluateNode;
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
class Node {
2+
constructor(type) {
3+
this.type = type;
4+
}
5+
6+
evaluate() {
7+
throw new Error('Evaluate method must be implemented by subclasses');
8+
}
9+
}
10+
11+
export default Node;

0 commit comments

Comments
 (0)