@node-red/runtime 3.1.0-beta.3 → 3.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/flows/Flow.js +81 -156
- package/lib/flows/Group.js +55 -0
- package/lib/flows/Subflow.js +20 -53
- package/lib/flows/index.js +14 -20
- package/lib/flows/util.js +478 -416
- package/lib/nodes/index.js +0 -1
- package/lib/storage/localfilesystem/projects/git/index.js +1 -1
- package/lib/storage/localfilesystem/util.js +23 -20
- package/locales/ko/runtime.json +167 -167
- package/package.json +3 -3
package/lib/flows/util.js
CHANGED
|
@@ -13,23 +13,32 @@
|
|
|
13
13
|
* See the License for the specific language governing permissions and
|
|
14
14
|
* limitations under the License.
|
|
15
15
|
**/
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
const credentials = require("../nodes/credentials");
|
|
16
|
+
const clone = require("clone");
|
|
17
|
+
const redUtil = require("@node-red/util").util;
|
|
18
|
+
const Log = require("@node-red/util").log;
|
|
19
|
+
const typeRegistry = require("@node-red/registry");
|
|
20
|
+
const subflowInstanceRE = /^subflow:(.+)$/;
|
|
22
21
|
|
|
23
22
|
let _runtime = null;
|
|
23
|
+
let envVarExcludes = {};
|
|
24
24
|
|
|
25
|
-
|
|
25
|
+
function init(runtime) {
|
|
26
|
+
_runtime = runtime;
|
|
27
|
+
envVarExcludes = {};
|
|
28
|
+
if (runtime.settings.hasOwnProperty('envVarExcludes') && Array.isArray(runtime.settings.envVarExcludes)) {
|
|
29
|
+
runtime.settings.envVarExcludes.forEach(v => envVarExcludes[v] = true);
|
|
30
|
+
}
|
|
31
|
+
}
|
|
26
32
|
|
|
27
33
|
function diffNodes(oldNode,newNode) {
|
|
28
34
|
if (oldNode == null) {
|
|
29
35
|
return true;
|
|
30
36
|
}
|
|
31
|
-
|
|
32
|
-
|
|
37
|
+
const keyFilter = p => p != 'x' && p != 'y' && p != 'wires'
|
|
38
|
+
const groupKeyFilter = p => keyFilter(p) && p != 'nodes' && p != 'style' && p != 'w' && p != 'h'
|
|
39
|
+
var oldKeys = Object.keys(oldNode).filter(oldNode.type === 'group' ? groupKeyFilter : keyFilter);
|
|
40
|
+
var newKeys = Object.keys(newNode).filter(newNode.type === 'group' ? groupKeyFilter : keyFilter);
|
|
41
|
+
|
|
33
42
|
if (oldKeys.length != newKeys.length) {
|
|
34
43
|
return true;
|
|
35
44
|
}
|
|
@@ -70,8 +79,64 @@ function mapEnvVarProperties(obj,prop,flow,config) {
|
|
|
70
79
|
}
|
|
71
80
|
}
|
|
72
81
|
|
|
82
|
+
async function evaluateEnvProperties(flow, env, credentials) {
|
|
83
|
+
const pendingEvaluations = []
|
|
84
|
+
const evaluatedEnv = {}
|
|
85
|
+
const envTypes = []
|
|
86
|
+
for (let i = 0; i < env.length; i++) {
|
|
87
|
+
let { name, value, type } = env[i]
|
|
88
|
+
if (type === "env") {
|
|
89
|
+
// Do env types last as they may include references to other env vars
|
|
90
|
+
// at this level which need to be resolved before they can be looked-up
|
|
91
|
+
envTypes.push(env[i])
|
|
92
|
+
} else if (type === "bool") {
|
|
93
|
+
value = (value === "true") || (value === true);
|
|
94
|
+
} else if (type === "cred") {
|
|
95
|
+
if (credentials.hasOwnProperty(name)) {
|
|
96
|
+
value = credentials[name];
|
|
97
|
+
}
|
|
98
|
+
} else if (type ==='jsonata') {
|
|
99
|
+
pendingEvaluations.push(new Promise((resolve, _) => {
|
|
100
|
+
redUtil.evaluateNodeProperty(value, 'jsonata', {_flow: flow}, null, (err, result) => {
|
|
101
|
+
if (!err) {
|
|
102
|
+
evaluatedEnv[name] = result
|
|
103
|
+
}
|
|
104
|
+
resolve()
|
|
105
|
+
});
|
|
106
|
+
}))
|
|
107
|
+
} else {
|
|
108
|
+
value = redUtil.evaluateNodeProperty(value, type, {_flow: flow}, null, null);
|
|
109
|
+
}
|
|
110
|
+
evaluatedEnv[name] = value
|
|
111
|
+
}
|
|
112
|
+
if (pendingEvaluations.length > 0) {
|
|
113
|
+
await Promise.all(pendingEvaluations)
|
|
114
|
+
}
|
|
115
|
+
for (let i = 0; i < envTypes.length; i++) {
|
|
116
|
+
let { name, value, type } = envTypes[i]
|
|
117
|
+
// If an env-var wants to lookup itself, delegate straight to the parent
|
|
118
|
+
// https://github.com/node-red/node-red/issues/2099
|
|
119
|
+
if (value === name) {
|
|
120
|
+
value = `$parent.${name}`
|
|
121
|
+
}
|
|
122
|
+
if (evaluatedEnv.hasOwnProperty(value)) {
|
|
123
|
+
value = evaluatedEnv[value]
|
|
124
|
+
} else {
|
|
125
|
+
value = redUtil.evaluateNodeProperty(value, type, {_flow: flow}, null, null);
|
|
126
|
+
}
|
|
127
|
+
evaluatedEnv[name] = value
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
return evaluatedEnv
|
|
131
|
+
}
|
|
73
132
|
|
|
74
|
-
|
|
133
|
+
/**
|
|
134
|
+
* Create a new instance of a node
|
|
135
|
+
* @param {Flow} flow The containing flow
|
|
136
|
+
* @param {object} config The node configuration object
|
|
137
|
+
* @return {Node} The instance of the node
|
|
138
|
+
*/
|
|
139
|
+
async function createNode(flow,config) {
|
|
75
140
|
var newNode = null;
|
|
76
141
|
var type = config.type;
|
|
77
142
|
try {
|
|
@@ -140,7 +205,7 @@ function createNode(flow,config) {
|
|
|
140
205
|
// This allows nodes inside the subflow to get ahold of each other
|
|
141
206
|
// such as a node accessing its config node
|
|
142
207
|
flow.subflowInstanceNodes[config.id] = subflow
|
|
143
|
-
subflow.start();
|
|
208
|
+
await subflow.start();
|
|
144
209
|
return subflow.node;
|
|
145
210
|
}
|
|
146
211
|
} catch(err) {
|
|
@@ -150,375 +215,343 @@ function createNode(flow,config) {
|
|
|
150
215
|
}
|
|
151
216
|
|
|
152
217
|
function parseConfig(config) {
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
if (n.type === 'subflow') {
|
|
177
|
-
flow.subflows[n.id] = n;
|
|
178
|
-
flow.subflows[n.id].configs = {};
|
|
179
|
-
flow.subflows[n.id].nodes = {};
|
|
180
|
-
flow.subflows[n.id].instances = [];
|
|
181
|
-
}
|
|
182
|
-
});
|
|
183
|
-
var linkWires = {};
|
|
184
|
-
var linkOutNodes = [];
|
|
185
|
-
config.forEach(function(n) {
|
|
186
|
-
if (n.type !== 'subflow' && n.type !== 'tab' && n.type !== 'group') {
|
|
187
|
-
var subflowDetails = subflowInstanceRE.exec(n.type);
|
|
188
|
-
|
|
189
|
-
if ( (subflowDetails && !flow.subflows[subflowDetails[1]]) || (!subflowDetails && !typeRegistry.get(n.type)) ) {
|
|
190
|
-
if (flow.missingTypes.indexOf(n.type) === -1) {
|
|
191
|
-
flow.missingTypes.push(n.type);
|
|
192
|
-
}
|
|
193
|
-
}
|
|
194
|
-
var container = null;
|
|
195
|
-
if (flow.flows[n.z]) {
|
|
196
|
-
container = flow.flows[n.z];
|
|
197
|
-
} else if (flow.subflows[n.z]) {
|
|
198
|
-
container = flow.subflows[n.z];
|
|
199
|
-
}
|
|
200
|
-
if (n.hasOwnProperty('x') && n.hasOwnProperty('y')) {
|
|
201
|
-
if (subflowDetails) {
|
|
202
|
-
var subflowType = subflowDetails[1]
|
|
203
|
-
n.subflow = subflowType;
|
|
204
|
-
if (flow.subflows[subflowType]) {
|
|
205
|
-
flow.subflows[subflowType].instances.push(n)
|
|
206
|
-
}
|
|
207
|
-
}
|
|
208
|
-
if (container) {
|
|
209
|
-
container.nodes[n.id] = n;
|
|
210
|
-
}
|
|
211
|
-
} else {
|
|
212
|
-
if (container) {
|
|
213
|
-
container.configs[n.id] = n;
|
|
214
|
-
} else {
|
|
215
|
-
flow.configs[n.id] = n;
|
|
216
|
-
flow.configs[n.id]._users = [];
|
|
217
|
-
}
|
|
218
|
-
}
|
|
219
|
-
if (n.type === 'link in' && n.links) {
|
|
220
|
-
// Ensure wires are present in corresponding link out nodes
|
|
221
|
-
n.links.forEach(function(id) {
|
|
222
|
-
linkWires[id] = linkWires[id]||{};
|
|
223
|
-
linkWires[id][n.id] = true;
|
|
224
|
-
})
|
|
225
|
-
} else if (n.type === 'link out' && n.links) {
|
|
226
|
-
linkWires[n.id] = linkWires[n.id]||{};
|
|
227
|
-
n.links.forEach(function(id) {
|
|
228
|
-
linkWires[n.id][id] = true;
|
|
229
|
-
})
|
|
230
|
-
linkOutNodes.push(n);
|
|
231
|
-
}
|
|
232
|
-
}
|
|
233
|
-
});
|
|
234
|
-
linkOutNodes.forEach(function(n) {
|
|
235
|
-
var links = linkWires[n.id];
|
|
236
|
-
var targets = Object.keys(links);
|
|
237
|
-
n.wires = [targets];
|
|
238
|
-
});
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
var addedTabs = {};
|
|
242
|
-
config.forEach(function(n) {
|
|
243
|
-
if (n.type !== 'subflow' && n.type !== 'tab' && n.type !== 'group') {
|
|
244
|
-
for (var prop in n) {
|
|
245
|
-
if (n.hasOwnProperty(prop) && prop !== 'id' && prop !== 'wires' && prop !== 'type' && prop !== '_users' && flow.configs.hasOwnProperty(n[prop])) {
|
|
246
|
-
// This property references a global config node
|
|
247
|
-
flow.configs[n[prop]]._users.push(n.id)
|
|
248
|
-
}
|
|
249
|
-
}
|
|
250
|
-
if (n.z && !flow.subflows[n.z]) {
|
|
251
|
-
|
|
252
|
-
if (!flow.flows[n.z]) {
|
|
253
|
-
flow.flows[n.z] = {type:'tab',id:n.z};
|
|
254
|
-
flow.flows[n.z].subflows = {};
|
|
255
|
-
flow.flows[n.z].configs = {};
|
|
256
|
-
flow.flows[n.z].nodes = {};
|
|
257
|
-
addedTabs[n.z] = flow.flows[n.z];
|
|
258
|
-
}
|
|
259
|
-
if (addedTabs[n.z]) {
|
|
260
|
-
if (n.hasOwnProperty('x') && n.hasOwnProperty('y')) {
|
|
261
|
-
addedTabs[n.z].nodes[n.id] = n;
|
|
262
|
-
} else {
|
|
263
|
-
addedTabs[n.z].configs[n.id] = n;
|
|
264
|
-
}
|
|
265
|
-
}
|
|
266
|
-
}
|
|
267
|
-
}
|
|
268
|
-
});
|
|
269
|
-
return flow;
|
|
270
|
-
}
|
|
218
|
+
var flow = {};
|
|
219
|
+
flow.allNodes = {};
|
|
220
|
+
flow.subflows = {};
|
|
221
|
+
flow.configs = {};
|
|
222
|
+
flow.flows = {};
|
|
223
|
+
flow.missingTypes = [];
|
|
224
|
+
|
|
225
|
+
config.forEach(function (n) {
|
|
226
|
+
flow.allNodes[n.id] = clone(n);
|
|
227
|
+
if (n.type === 'tab') {
|
|
228
|
+
flow.flows[n.id] = n;
|
|
229
|
+
flow.flows[n.id].subflows = {};
|
|
230
|
+
flow.flows[n.id].configs = {};
|
|
231
|
+
flow.flows[n.id].nodes = {};
|
|
232
|
+
flow.flows[n.id].groups = {};
|
|
233
|
+
} else if (n.type === 'subflow') {
|
|
234
|
+
flow.subflows[n.id] = n;
|
|
235
|
+
flow.subflows[n.id].configs = {};
|
|
236
|
+
flow.subflows[n.id].nodes = {};
|
|
237
|
+
flow.subflows[n.id].groups = {};
|
|
238
|
+
flow.subflows[n.id].instances = [];
|
|
239
|
+
}
|
|
240
|
+
});
|
|
271
241
|
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
const cred = (gconf ? credentials.get(gconf.id) : null) || {
|
|
282
|
-
map: {}
|
|
283
|
-
};
|
|
284
|
-
const map = cred.map;
|
|
285
|
-
|
|
286
|
-
for (let i = 0; i < env.length; i++) {
|
|
287
|
-
const item = env[i];
|
|
288
|
-
if (item.name === name) {
|
|
289
|
-
if (item.type === "cred") {
|
|
290
|
-
return {
|
|
291
|
-
name: name,
|
|
292
|
-
value: map[name],
|
|
293
|
-
type: "cred"
|
|
294
|
-
};
|
|
242
|
+
var linkWires = {};
|
|
243
|
+
var linkOutNodes = [];
|
|
244
|
+
config.forEach(function (n) {
|
|
245
|
+
if (n.type !== 'subflow' && n.type !== 'tab' && n.type !== 'group') {
|
|
246
|
+
var subflowDetails = subflowInstanceRE.exec(n.type);
|
|
247
|
+
|
|
248
|
+
if ((subflowDetails && !flow.subflows[subflowDetails[1]]) || (!subflowDetails && !typeRegistry.get(n.type))) {
|
|
249
|
+
if (flow.missingTypes.indexOf(n.type) === -1) {
|
|
250
|
+
flow.missingTypes.push(n.type);
|
|
295
251
|
}
|
|
296
|
-
return item;
|
|
297
252
|
}
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
}
|
|
302
|
-
|
|
303
|
-
module.exports = {
|
|
304
|
-
init: function(runtime) {
|
|
305
|
-
_runtime = runtime;
|
|
306
|
-
envVarExcludes = {};
|
|
307
|
-
if (runtime.settings.hasOwnProperty('envVarExcludes') && Array.isArray(runtime.settings.envVarExcludes)) {
|
|
308
|
-
runtime.settings.envVarExcludes.forEach(v => envVarExcludes[v] = true);
|
|
309
|
-
}
|
|
310
|
-
},
|
|
311
|
-
getEnvVar: function(k) {
|
|
312
|
-
if (!envVarExcludes[k]) {
|
|
313
|
-
const item = getGlobalEnv(k);
|
|
314
|
-
if (item) {
|
|
315
|
-
const val = redUtil.evaluateNodeProperty(item.value, item.type, null, null, null);
|
|
316
|
-
return val;
|
|
253
|
+
var container = null;
|
|
254
|
+
if (flow.flows[n.z]) {
|
|
255
|
+
container = flow.flows[n.z];
|
|
256
|
+
} else if (flow.subflows[n.z]) {
|
|
257
|
+
container = flow.subflows[n.z];
|
|
317
258
|
}
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
flows:{},
|
|
337
|
-
allNodes:{}
|
|
259
|
+
if (n.hasOwnProperty('x') && n.hasOwnProperty('y')) {
|
|
260
|
+
if (subflowDetails) {
|
|
261
|
+
var subflowType = subflowDetails[1]
|
|
262
|
+
n.subflow = subflowType;
|
|
263
|
+
if (flow.subflows[subflowType]) {
|
|
264
|
+
flow.subflows[subflowType].instances.push(n)
|
|
265
|
+
}
|
|
266
|
+
}
|
|
267
|
+
if (container) {
|
|
268
|
+
container.nodes[n.id] = n;
|
|
269
|
+
}
|
|
270
|
+
} else {
|
|
271
|
+
if (container) {
|
|
272
|
+
container.configs[n.id] = n;
|
|
273
|
+
} else {
|
|
274
|
+
flow.configs[n.id] = n;
|
|
275
|
+
flow.configs[n.id]._users = [];
|
|
276
|
+
}
|
|
338
277
|
}
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
278
|
+
if (n.type === 'link in' && n.links) {
|
|
279
|
+
// Ensure wires are present in corresponding link out nodes
|
|
280
|
+
n.links.forEach(function (id) {
|
|
281
|
+
linkWires[id] = linkWires[id] || {};
|
|
282
|
+
linkWires[id][n.id] = true;
|
|
283
|
+
})
|
|
284
|
+
} else if (n.type === 'link out' && n.links) {
|
|
285
|
+
linkWires[n.id] = linkWires[n.id] || {};
|
|
286
|
+
n.links.forEach(function (id) {
|
|
287
|
+
linkWires[n.id][id] = true;
|
|
288
|
+
})
|
|
289
|
+
linkOutNodes.push(n);
|
|
290
|
+
}
|
|
291
|
+
} else if (n.type === 'group') {
|
|
292
|
+
const parentContainer = flow.flows[n.z] || flow.subflows[n.z]
|
|
293
|
+
if (parentContainer) {
|
|
294
|
+
parentContainer.groups[n.id] = n
|
|
355
295
|
}
|
|
356
296
|
}
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
297
|
+
});
|
|
298
|
+
linkOutNodes.forEach(function (n) {
|
|
299
|
+
var links = linkWires[n.id];
|
|
300
|
+
var targets = Object.keys(links);
|
|
301
|
+
n.wires = [targets];
|
|
302
|
+
});
|
|
303
|
+
|
|
304
|
+
|
|
305
|
+
var addedTabs = {};
|
|
306
|
+
config.forEach(function (n) {
|
|
307
|
+
if (n.type !== 'subflow' && n.type !== 'tab' && n.type !== 'group') {
|
|
308
|
+
for (var prop in n) {
|
|
309
|
+
if (n.hasOwnProperty(prop) && prop !== 'id' && prop !== 'wires' && prop !== 'type' && prop !== '_users' && flow.configs.hasOwnProperty(n[prop])) {
|
|
310
|
+
// This property references a global config node
|
|
311
|
+
flow.configs[n[prop]]._users.push(n.id)
|
|
312
|
+
}
|
|
313
|
+
}
|
|
314
|
+
if (n.z && !flow.subflows[n.z]) {
|
|
315
|
+
|
|
316
|
+
if (!flow.flows[n.z]) {
|
|
317
|
+
flow.flows[n.z] = { type: 'tab', id: n.z };
|
|
318
|
+
flow.flows[n.z].subflows = {};
|
|
319
|
+
flow.flows[n.z].configs = {};
|
|
320
|
+
flow.flows[n.z].nodes = {};
|
|
321
|
+
addedTabs[n.z] = flow.flows[n.z];
|
|
322
|
+
}
|
|
323
|
+
if (addedTabs[n.z]) {
|
|
324
|
+
if (n.hasOwnProperty('x') && n.hasOwnProperty('y')) {
|
|
325
|
+
addedTabs[n.z].nodes[n.id] = n;
|
|
367
326
|
} else {
|
|
368
|
-
|
|
327
|
+
addedTabs[n.z].configs[n.id] = n;
|
|
369
328
|
}
|
|
370
329
|
}
|
|
371
330
|
}
|
|
372
331
|
}
|
|
332
|
+
});
|
|
333
|
+
return flow;
|
|
334
|
+
}
|
|
335
|
+
function getEnvVar(k) {
|
|
336
|
+
if (!envVarExcludes[k]) {
|
|
337
|
+
return process.env[k];
|
|
338
|
+
}
|
|
339
|
+
return undefined;
|
|
340
|
+
}
|
|
341
|
+
function diffConfigs(oldConfig, newConfig) {
|
|
342
|
+
var id;
|
|
343
|
+
var node;
|
|
344
|
+
var nn;
|
|
345
|
+
var wires;
|
|
346
|
+
var j,k;
|
|
347
|
+
|
|
348
|
+
if (!oldConfig) {
|
|
349
|
+
oldConfig = {
|
|
350
|
+
flows:{},
|
|
351
|
+
allNodes:{}
|
|
352
|
+
}
|
|
353
|
+
}
|
|
354
|
+
var changedSubflows = {};
|
|
355
|
+
|
|
356
|
+
var added = {};
|
|
357
|
+
var removed = {};
|
|
358
|
+
var changed = {};
|
|
359
|
+
var flowChanged = {};
|
|
360
|
+
var wiringChanged = {};
|
|
361
|
+
var globalConfigChanged = false;
|
|
362
|
+
var linkMap = {};
|
|
363
|
+
var allNestedGroups = []
|
|
364
|
+
|
|
365
|
+
// Look for tabs that have been removed
|
|
366
|
+
for (id in oldConfig.flows) {
|
|
367
|
+
if (oldConfig.flows.hasOwnProperty(id) && (!newConfig.flows.hasOwnProperty(id))) {
|
|
368
|
+
removed[id] = oldConfig.allNodes[id];
|
|
369
|
+
}
|
|
370
|
+
}
|
|
373
371
|
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
372
|
+
// Look for tabs that have been disabled
|
|
373
|
+
for (id in oldConfig.flows) {
|
|
374
|
+
if (oldConfig.flows.hasOwnProperty(id) && newConfig.flows.hasOwnProperty(id)) {
|
|
375
|
+
var originalState = oldConfig.flows[id].disabled||false;
|
|
376
|
+
var newState = newConfig.flows[id].disabled||false;
|
|
377
|
+
if (originalState !== newState) {
|
|
378
|
+
if (originalState) {
|
|
379
|
+
added[id] = oldConfig.allNodes[id];
|
|
380
|
+
} else {
|
|
381
|
+
removed[id] = oldConfig.allNodes[id];
|
|
382
|
+
}
|
|
383
|
+
}
|
|
384
|
+
}
|
|
385
|
+
}
|
|
386
|
+
|
|
387
|
+
for (id in oldConfig.allNodes) {
|
|
388
|
+
if (oldConfig.allNodes.hasOwnProperty(id)) {
|
|
389
|
+
node = oldConfig.allNodes[id];
|
|
390
|
+
if (node.type !== 'tab') {
|
|
391
|
+
// build the map of what this node was previously wired to
|
|
392
|
+
if (node.wires) {
|
|
393
|
+
linkMap[node.id] = linkMap[node.id] || [];
|
|
394
|
+
for (j=0;j<node.wires.length;j++) {
|
|
395
|
+
wires = node.wires[j];
|
|
396
|
+
for (k=0;k<wires.length;k++) {
|
|
397
|
+
linkMap[node.id].push(wires[k]);
|
|
398
|
+
nn = oldConfig.allNodes[wires[k]];
|
|
399
|
+
if (nn) {
|
|
400
|
+
linkMap[nn.id] = linkMap[nn.id] || [];
|
|
401
|
+
linkMap[nn.id].push(node.id);
|
|
390
402
|
}
|
|
391
403
|
}
|
|
392
404
|
}
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
405
|
+
}
|
|
406
|
+
// This node has been removed or its flow disabled
|
|
407
|
+
if (removed[node.z] || !newConfig.allNodes.hasOwnProperty(id)) {
|
|
408
|
+
removed[id] = node;
|
|
409
|
+
// Mark the container as changed
|
|
410
|
+
if (!removed[node.z] && newConfig.allNodes[removed[id].z]) {
|
|
411
|
+
changed[removed[id].z] = newConfig.allNodes[removed[id].z];
|
|
412
|
+
if (changed[removed[id].z].type === "subflow") {
|
|
413
|
+
changedSubflows[removed[id].z] = changed[removed[id].z];
|
|
414
|
+
//delete removed[id];
|
|
403
415
|
}
|
|
416
|
+
}
|
|
417
|
+
} else {
|
|
418
|
+
if (added[node.z]) {
|
|
419
|
+
added[id] = node;
|
|
404
420
|
} else {
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
421
|
+
var currentState = node.d;
|
|
422
|
+
var newState = newConfig.allNodes[id].d;
|
|
423
|
+
if (!currentState && newState) {
|
|
424
|
+
removed[id] = node;
|
|
425
|
+
}
|
|
426
|
+
// This node has a material configuration change
|
|
427
|
+
if (diffNodes(node,newConfig.allNodes[id]) || newConfig.allNodes[id].credentials) {
|
|
428
|
+
changed[id] = newConfig.allNodes[id];
|
|
429
|
+
if (changed[id].type === "subflow") {
|
|
430
|
+
changedSubflows[id] = changed[id];
|
|
412
431
|
}
|
|
413
|
-
//
|
|
414
|
-
if (
|
|
415
|
-
changed[id] = newConfig.allNodes[id];
|
|
416
|
-
if (changed[id].type === "subflow") {
|
|
417
|
-
changedSubflows[id] = changed[id];
|
|
418
|
-
|
|
419
|
-
// Mark the container as changed
|
|
420
|
-
if (newConfig.allNodes[changed[id].z]) {
|
|
421
|
-
changed[changed[id].z] = newConfig.allNodes[changed[id].z];
|
|
422
|
-
if (changed[changed[id].z].type === "subflow") {
|
|
423
|
-
changedSubflows[changed[id].z] = changed[changed[id].z];
|
|
424
|
-
delete changed[id];
|
|
425
|
-
}
|
|
432
|
+
// Mark the container as changed
|
|
433
|
+
if (newConfig.allNodes[changed[id].z]) {
|
|
434
|
+
changed[changed[id].z] = newConfig.allNodes[changed[id].z];
|
|
435
|
+
if (changed[changed[id].z].type === "subflow") {
|
|
436
|
+
changedSubflows[changed[id].z] = changed[changed[id].z];
|
|
437
|
+
delete changed[id];
|
|
426
438
|
}
|
|
427
439
|
}
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
440
|
+
if (newConfig.allNodes[id].type === 'global-config') {
|
|
441
|
+
globalConfigChanged = true
|
|
442
|
+
}
|
|
443
|
+
}
|
|
444
|
+
// This node's wiring has changed
|
|
445
|
+
if (!redUtil.compareObjects(node.wires,newConfig.allNodes[id].wires)) {
|
|
446
|
+
wiringChanged[id] = newConfig.allNodes[id];
|
|
447
|
+
// Mark the container as changed
|
|
448
|
+
if (newConfig.allNodes[wiringChanged[id].z]) {
|
|
449
|
+
changed[wiringChanged[id].z] = newConfig.allNodes[wiringChanged[id].z];
|
|
450
|
+
if (changed[wiringChanged[id].z].type === "subflow") {
|
|
451
|
+
changedSubflows[wiringChanged[id].z] = changed[wiringChanged[id].z];
|
|
452
|
+
delete wiringChanged[id];
|
|
438
453
|
}
|
|
439
454
|
}
|
|
440
455
|
}
|
|
441
456
|
}
|
|
442
457
|
}
|
|
458
|
+
} else if (!removed[id]) {
|
|
459
|
+
if (JSON.stringify(node.env) !== JSON.stringify(newConfig.allNodes[id].env)) {
|
|
460
|
+
flowChanged[id] = newConfig.allNodes[id];
|
|
461
|
+
}
|
|
443
462
|
}
|
|
444
463
|
}
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
464
|
+
}
|
|
465
|
+
// Look for added nodes
|
|
466
|
+
for (id in newConfig.allNodes) {
|
|
467
|
+
if (newConfig.allNodes.hasOwnProperty(id)) {
|
|
468
|
+
node = newConfig.allNodes[id];
|
|
469
|
+
if (node.type === 'group') {
|
|
470
|
+
if (node.g) {
|
|
471
|
+
allNestedGroups.push(node)
|
|
472
|
+
}
|
|
473
|
+
if (changed[node.id]) {
|
|
474
|
+
if (node.nodes) {
|
|
475
|
+
node.nodes.forEach(nid => {
|
|
476
|
+
if (!changed[nid]) {
|
|
477
|
+
changed[nid] = true
|
|
457
478
|
}
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
479
|
+
})
|
|
480
|
+
}
|
|
481
|
+
}
|
|
482
|
+
}
|
|
483
|
+
// build the map of what this node is now wired to
|
|
484
|
+
if (node.wires) {
|
|
485
|
+
linkMap[node.id] = linkMap[node.id] || [];
|
|
486
|
+
for (j=0;j<node.wires.length;j++) {
|
|
487
|
+
wires = node.wires[j];
|
|
488
|
+
for (k=0;k<wires.length;k++) {
|
|
489
|
+
if (linkMap[node.id].indexOf(wires[k]) === -1) {
|
|
490
|
+
linkMap[node.id].push(wires[k]);
|
|
491
|
+
}
|
|
492
|
+
nn = newConfig.allNodes[wires[k]];
|
|
493
|
+
if (nn) {
|
|
494
|
+
linkMap[nn.id] = linkMap[nn.id] || [];
|
|
495
|
+
if (linkMap[nn.id].indexOf(node.id) === -1) {
|
|
496
|
+
linkMap[nn.id].push(node.id);
|
|
464
497
|
}
|
|
465
498
|
}
|
|
466
499
|
}
|
|
467
500
|
}
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
501
|
+
}
|
|
502
|
+
// This node has been added
|
|
503
|
+
if (!oldConfig.allNodes.hasOwnProperty(id)) {
|
|
504
|
+
added[id] = node;
|
|
505
|
+
// Mark the container as changed
|
|
506
|
+
if (newConfig.allNodes[added[id].z]) {
|
|
507
|
+
changed[added[id].z] = newConfig.allNodes[added[id].z];
|
|
508
|
+
if (changed[added[id].z].type === "subflow") {
|
|
509
|
+
changedSubflows[added[id].z] = changed[added[id].z];
|
|
510
|
+
delete added[id];
|
|
478
511
|
}
|
|
479
512
|
}
|
|
480
513
|
}
|
|
481
514
|
}
|
|
515
|
+
}
|
|
482
516
|
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
}
|
|
517
|
+
var madeChange;
|
|
518
|
+
// Loop through the nodes looking for references to changed config nodes
|
|
519
|
+
// Repeat the loop if anything is marked as changed as it may need to be
|
|
520
|
+
// propagated to parent nodes.
|
|
521
|
+
// TODO: looping through all nodes every time is a bit inefficient - could be more targeted
|
|
522
|
+
do {
|
|
523
|
+
madeChange = false;
|
|
524
|
+
for (id in newConfig.allNodes) {
|
|
525
|
+
if (newConfig.allNodes.hasOwnProperty(id)) {
|
|
526
|
+
node = newConfig.allNodes[id];
|
|
527
|
+
for (var prop in node) {
|
|
528
|
+
if (node.hasOwnProperty(prop) && prop != "z" && prop != "id" && prop != "wires") {
|
|
529
|
+
// This node has a property that references a changed/removed node
|
|
530
|
+
// Assume it is a config node change and mark this node as
|
|
531
|
+
// changed.
|
|
532
|
+
|
|
533
|
+
var changeOrigin = changed[node[prop]];
|
|
534
|
+
if (changeOrigin || removed[node[prop]]) {
|
|
535
|
+
if (!changed[node.id]) {
|
|
536
|
+
if (changeOrigin &&
|
|
537
|
+
(prop === "g") &&
|
|
538
|
+
(changeOrigin.type === "group")) {
|
|
539
|
+
var oldNode = oldConfig.allNodes[node.id];
|
|
540
|
+
// ignore change of group node
|
|
541
|
+
// if group of this node not changed
|
|
542
|
+
if (oldNode &&
|
|
543
|
+
(node.g === oldNode.g)) {
|
|
544
|
+
continue;
|
|
512
545
|
}
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
546
|
+
}
|
|
547
|
+
madeChange = true;
|
|
548
|
+
changed[node.id] = node;
|
|
549
|
+
// This node exists within subflow template
|
|
550
|
+
// Mark the template as having changed
|
|
551
|
+
if (newConfig.allNodes[node.z]) {
|
|
552
|
+
changed[node.z] = newConfig.allNodes[node.z];
|
|
553
|
+
if (changed[node.z].type === "subflow") {
|
|
554
|
+
changedSubflows[node.z] = changed[node.z];
|
|
522
555
|
}
|
|
523
556
|
}
|
|
524
557
|
}
|
|
@@ -526,94 +559,123 @@ module.exports = {
|
|
|
526
559
|
}
|
|
527
560
|
}
|
|
528
561
|
}
|
|
529
|
-
}
|
|
562
|
+
}
|
|
563
|
+
} while (madeChange===true)
|
|
564
|
+
|
|
565
|
+
// Find any nodes that exist on a subflow template and remove from changed
|
|
566
|
+
// list as the parent subflow will now be marked as containing a change
|
|
567
|
+
for (id in newConfig.allNodes) {
|
|
568
|
+
if (newConfig.allNodes.hasOwnProperty(id)) {
|
|
569
|
+
node = newConfig.allNodes[id];
|
|
570
|
+
if (newConfig.allNodes[node.z] && newConfig.allNodes[node.z].type === "subflow") {
|
|
571
|
+
delete changed[node.id];
|
|
572
|
+
}
|
|
573
|
+
}
|
|
574
|
+
}
|
|
530
575
|
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
576
|
+
// Recursively mark all children of changed groups as changed
|
|
577
|
+
do {
|
|
578
|
+
madeChange = false
|
|
579
|
+
for (let i = 0; i < allNestedGroups.length; i++) {
|
|
580
|
+
const group = allNestedGroups[i]
|
|
581
|
+
if (!changed[group.id] && group.g && changed[group.g]) {
|
|
582
|
+
changed[group.id] = true
|
|
583
|
+
madeChange = true
|
|
584
|
+
}
|
|
585
|
+
if (changed[group.id] && group.nodes) {
|
|
586
|
+
group.nodes.forEach(nid => {
|
|
587
|
+
if (!changed[nid]) {
|
|
588
|
+
changed[nid] = true
|
|
589
|
+
madeChange = true
|
|
590
|
+
}
|
|
591
|
+
})
|
|
539
592
|
}
|
|
540
593
|
}
|
|
594
|
+
} while(madeChange)
|
|
541
595
|
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
}
|
|
596
|
+
// Recursively mark all instances of changed subflows as changed
|
|
597
|
+
var changedSubflowStack = Object.keys(changedSubflows);
|
|
598
|
+
while (changedSubflowStack.length > 0) {
|
|
599
|
+
var subflowId = changedSubflowStack.pop();
|
|
600
|
+
for (id in newConfig.allNodes) {
|
|
601
|
+
if (newConfig.allNodes.hasOwnProperty(id)) {
|
|
602
|
+
node = newConfig.allNodes[id];
|
|
603
|
+
if (node.type === 'subflow:'+subflowId) {
|
|
604
|
+
if (!changed[node.id]) {
|
|
605
|
+
changed[node.id] = node;
|
|
606
|
+
if (!changed[changed[node.id].z] && newConfig.allNodes[changed[node.id].z]) {
|
|
607
|
+
changed[changed[node.id].z] = newConfig.allNodes[changed[node.id].z];
|
|
608
|
+
if (newConfig.allNodes[changed[node.id].z].type === "subflow") {
|
|
609
|
+
// This subflow instance is inside a subflow. Add the
|
|
610
|
+
// containing subflow to the stack to mark
|
|
611
|
+
changedSubflowStack.push(changed[node.id].z);
|
|
612
|
+
delete changed[node.id];
|
|
560
613
|
}
|
|
561
614
|
}
|
|
562
615
|
}
|
|
563
616
|
}
|
|
564
617
|
}
|
|
565
618
|
}
|
|
619
|
+
}
|
|
566
620
|
|
|
567
|
-
var diff = {
|
|
568
|
-
added:Object.keys(added),
|
|
569
|
-
changed:Object.keys(changed),
|
|
570
|
-
removed:Object.keys(removed),
|
|
571
|
-
rewired:Object.keys(wiringChanged),
|
|
572
|
-
linked:[]
|
|
573
|
-
}
|
|
574
621
|
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
622
|
+
|
|
623
|
+
var diff = {
|
|
624
|
+
added:Object.keys(added),
|
|
625
|
+
changed:Object.keys(changed),
|
|
626
|
+
removed:Object.keys(removed),
|
|
627
|
+
rewired:Object.keys(wiringChanged),
|
|
628
|
+
linked:[],
|
|
629
|
+
flowChanged: Object.keys(flowChanged),
|
|
630
|
+
globalConfigChanged
|
|
631
|
+
}
|
|
632
|
+
|
|
633
|
+
// Traverse the links of all modified nodes to mark the connected nodes
|
|
634
|
+
var modifiedNodes = diff.added.concat(diff.changed).concat(diff.removed).concat(diff.rewired);
|
|
635
|
+
var visited = {};
|
|
636
|
+
while (modifiedNodes.length > 0) {
|
|
637
|
+
node = modifiedNodes.pop();
|
|
638
|
+
if (!visited[node]) {
|
|
639
|
+
visited[node] = true;
|
|
640
|
+
if (linkMap[node]) {
|
|
641
|
+
if (!changed[node] && !added[node] && !removed[node] && !wiringChanged[node]) {
|
|
642
|
+
diff.linked.push(node);
|
|
587
643
|
}
|
|
644
|
+
modifiedNodes = modifiedNodes.concat(linkMap[node]);
|
|
588
645
|
}
|
|
589
646
|
}
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
}
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
647
|
+
}
|
|
648
|
+
// console.log(diff);
|
|
649
|
+
// for (id in newConfig.allNodes) {
|
|
650
|
+
// if (added[id] || changed[id] || wiringChanged[id] || diff.linked.indexOf(id)!==-1) {
|
|
651
|
+
// console.log(
|
|
652
|
+
// (added[id]?"a":(changed[id]?"c":" "))+(wiringChanged[id]?"w":" ")+(diff.linked.indexOf(id)!==-1?"l":" "),
|
|
653
|
+
// newConfig.allNodes[id].type.padEnd(10),
|
|
654
|
+
// id.padEnd(16),
|
|
655
|
+
// (newConfig.allNodes[id].z||"").padEnd(16),
|
|
656
|
+
// newConfig.allNodes[id].name||newConfig.allNodes[id].label||""
|
|
657
|
+
// );
|
|
658
|
+
// }
|
|
659
|
+
// }
|
|
660
|
+
// for (id in removed) {
|
|
661
|
+
// console.log(
|
|
662
|
+
// "- "+(diff.linked.indexOf(id)!==-1?"~":" "),
|
|
663
|
+
// id,
|
|
664
|
+
// oldConfig.allNodes[id].type,
|
|
665
|
+
// oldConfig.allNodes[id].name||oldConfig.allNodes[id].label||""
|
|
666
|
+
// );
|
|
667
|
+
// }
|
|
668
|
+
|
|
669
|
+
return diff;
|
|
670
|
+
}
|
|
671
|
+
|
|
672
|
+
module.exports = {
|
|
673
|
+
init,
|
|
674
|
+
createNode,
|
|
675
|
+
parseConfig,
|
|
676
|
+
diffConfigs,
|
|
677
|
+
diffNodes,
|
|
678
|
+
getEnvVar,
|
|
679
|
+
mapEnvVarProperties,
|
|
680
|
+
evaluateEnvProperties
|
|
619
681
|
}
|