3
3
* SPDX-License-Identifier: Apache-2.0
4
4
*/
5
5
6
+ import { FormikValues } from 'formik' ;
6
7
import {
7
8
WorkspaceFlowState ,
8
9
ReactFlowComponent ,
@@ -17,9 +18,10 @@ import {
17
18
componentDataToFormik ,
18
19
ReactFlowEdge ,
19
20
CreateIndexNode ,
21
+ TemplateFlow ,
22
+ TemplateEdge ,
20
23
} from '../../../../common' ;
21
24
22
- // TODO: improve to make more generic
23
25
/**
24
26
* Given a ReactFlow workspace flow with fully populated input values,
25
27
* generate a backend-compatible set of sub-workflows.
@@ -28,22 +30,62 @@ import {
28
30
export function toTemplateFlows (
29
31
workspaceFlow : WorkspaceFlowState
30
32
) : TemplateFlows {
31
- const curNodes = workspaceFlow . nodes ;
33
+ const { ingestNodes, ingestEdges } = getIngestNodesAndEdges (
34
+ workspaceFlow . nodes ,
35
+ workspaceFlow . edges
36
+ ) ;
37
+ const provisionFlow = toProvisionTemplateFlow ( ingestNodes , ingestEdges ) ;
38
+
39
+ // TODO: support beyond provision
40
+ return {
41
+ provision : provisionFlow ,
42
+ } ;
43
+ }
44
+
45
+ function getIngestNodesAndEdges (
46
+ allNodes : ReactFlowComponent [ ] ,
47
+ allEdges : ReactFlowEdge [ ]
48
+ ) : { ingestNodes : ReactFlowComponent [ ] ; ingestEdges : ReactFlowEdge [ ] } {
49
+ const ingestParentId = allNodes . find (
50
+ ( node ) => node . type === NODE_CATEGORY . INGEST_GROUP
51
+ ) ?. id as string ;
52
+ const ingestNodes = allNodes . filter (
53
+ ( node ) => node . parentNode === ingestParentId
54
+ ) ;
55
+ const ingestIds = ingestNodes . map ( ( node ) => node . id ) ;
56
+ const ingestEdges = allEdges . filter (
57
+ ( edge ) => ingestIds . includes ( edge . source ) || ingestIds . includes ( edge . target )
58
+ ) ;
59
+ return {
60
+ ingestNodes,
61
+ ingestEdges,
62
+ } ;
63
+ }
64
+
65
+ // Generates the end-to-end provision subflow, if applicable
66
+ function toProvisionTemplateFlow (
67
+ nodes : ReactFlowComponent [ ] ,
68
+ edges : ReactFlowEdge [ ]
69
+ ) : TemplateFlow {
32
70
const prevNodes = [ ] as ReactFlowComponent [ ] ;
33
71
const templateNodes = [ ] as TemplateNode [ ] ;
34
- curNodes . forEach ( ( node ) => {
35
- const templateNode = toTemplateNode ( node , prevNodes , workspaceFlow . edges ) ;
72
+ const templateEdges = [ ] as TemplateEdge [ ] ;
73
+ nodes . forEach ( ( node ) => {
74
+ const templateNode = toTemplateNode ( node , prevNodes , edges ) ;
75
+ // it may be undefined if the node is not convertible for some reason
36
76
if ( templateNode ) {
37
77
templateNodes . push ( templateNode ) ;
38
78
prevNodes . push ( node ) ;
39
79
}
40
80
} ) ;
41
81
42
- console . log ( 'final template nodes: ' , templateNodes ) ;
82
+ edges . forEach ( ( edge ) => {
83
+ templateEdges . push ( toTemplateEdge ( edge ) ) ;
84
+ } ) ;
85
+
43
86
return {
44
- provision : {
45
- nodes : templateNodes ,
46
- } ,
87
+ nodes : templateNodes ,
88
+ edges : templateEdges ,
47
89
} ;
48
90
}
49
91
@@ -52,17 +94,20 @@ function toTemplateNode(
52
94
prevNodes : ReactFlowComponent [ ] ,
53
95
edges : ReactFlowEdge [ ]
54
96
) : TemplateNode | undefined {
55
- if ( flowNode . type === NODE_CATEGORY . CUSTOM ) {
56
- if ( flowNode . data . baseClasses ?. includes ( COMPONENT_CLASS . ML_TRANSFORMER ) ) {
57
- return toIngestPipelineNode ( flowNode ) ;
58
- } else if ( flowNode . data . baseClasses ?. includes ( COMPONENT_CLASS . INDEXER ) ) {
59
- return toIndexerNode ( flowNode , prevNodes , edges ) ;
60
- }
61
- } else {
62
- return undefined ;
97
+ if ( flowNode . data . baseClasses ?. includes ( COMPONENT_CLASS . ML_TRANSFORMER ) ) {
98
+ return toIngestPipelineNode ( flowNode ) ;
99
+ } else if ( flowNode . data . baseClasses ?. includes ( COMPONENT_CLASS . INDEXER ) ) {
100
+ return toIndexerNode ( flowNode , prevNodes , edges ) ;
63
101
}
64
102
}
65
103
104
+ function toTemplateEdge ( flowEdge : ReactFlowEdge ) : TemplateEdge {
105
+ return {
106
+ source : flowEdge . source ,
107
+ dest : flowEdge . target ,
108
+ } ;
109
+ }
110
+
66
111
// General fn to process all ML transform nodes. Convert into a final
67
112
// ingest pipeline with a processor specific to the final class of the node.
68
113
function toIngestPipelineNode (
@@ -77,7 +122,7 @@ function toIngestPipelineNode(
77
122
default : {
78
123
const { modelId, inputField, vectorField } = componentDataToFormik (
79
124
flowNode . data
80
- ) as { modelId : string ; inputField : string ; vectorField : string } ;
125
+ ) ;
81
126
82
127
return {
83
128
id : flowNode . data . id ,
@@ -107,8 +152,7 @@ function toIngestPipelineNode(
107
152
}
108
153
}
109
154
110
- // General fn to process all indexer nodes. Convert into a final
111
- // ingest pipeline with a processor specific to the final class of the node.
155
+ // General fn to convert an indexer node to a final CreateIndexNode template node.
112
156
function toIndexerNode (
113
157
flowNode : ReactFlowComponent ,
114
158
prevNodes : ReactFlowComponent [ ] ,
@@ -117,21 +161,19 @@ function toIndexerNode(
117
161
switch ( flowNode . data . type ) {
118
162
case COMPONENT_CLASS . KNN_INDEXER :
119
163
default : {
120
- const { indexName } = componentDataToFormik ( flowNode . data ) as {
121
- indexName : string ;
122
- } ;
164
+ const { indexName } = componentDataToFormik ( flowNode . data ) ;
123
165
// TODO: remove hardcoded logic here that is assuming each indexer node has
124
- // exactly 1 directly connected predecessor node
166
+ // exactly 1 directly connected create_ingest_pipeline predecessor node that
167
+ // contains an inputField and vectorField
125
168
const directlyConnectedNodeId = getDirectlyConnectedNodes (
126
169
flowNode ,
127
170
edges
128
171
) [ 0 ] ;
129
- const directlyConnectedNode = prevNodes . find (
130
- ( prevNode ) => prevNode . id === directlyConnectedNodeId
131
- ) as ReactFlowComponent ;
132
- const { inputField, vectorField } = componentDataToFormik (
133
- directlyConnectedNode . data
134
- ) as { inputField : string ; vectorField : string } ;
172
+ const { inputField, vectorField } = getDirectlyConnectedNodeInputs (
173
+ flowNode ,
174
+ prevNodes ,
175
+ edges
176
+ ) ;
135
177
136
178
return {
137
179
id : flowNode . data . id ,
@@ -143,7 +185,7 @@ function toIndexerNode(
143
185
index_name : indexName ,
144
186
configurations : {
145
187
settings : {
146
- default_pipeline : ' ${{create_ingest_pipeline .pipeline_id}}' ,
188
+ default_pipeline : `\ ${{${ directlyConnectedNodeId } .pipeline_id}}` ,
147
189
} ,
148
190
mappings : {
149
191
properties : {
@@ -169,6 +211,26 @@ function toIndexerNode(
169
211
}
170
212
}
171
213
214
+ // Fetch all directly connected predecessor node inputs
215
+ function getDirectlyConnectedNodeInputs (
216
+ node : ReactFlowComponent ,
217
+ prevNodes : ReactFlowComponent [ ] ,
218
+ edges : ReactFlowEdge [ ]
219
+ ) : FormikValues {
220
+ const directlyConnectedNodeIds = getDirectlyConnectedNodes ( node , edges ) ;
221
+ const directlyConnectedNodes = prevNodes . filter ( ( prevNode ) =>
222
+ directlyConnectedNodeIds . includes ( prevNode . id )
223
+ ) ;
224
+ let values = { } as FormikValues ;
225
+ directlyConnectedNodes . forEach ( ( node ) => {
226
+ values = {
227
+ ...values ,
228
+ ...componentDataToFormik ( node . data ) ,
229
+ } ;
230
+ } ) ;
231
+ return values ;
232
+ }
233
+
172
234
// Simple utility fn to fetch all direct predecessor node IDs for a given node
173
235
function getDirectlyConnectedNodes (
174
236
flowNode : ReactFlowComponent ,
0 commit comments