Merge pull request #5000 from node-red/fix-config-node-import

Fix config node sort order when importing
This commit is contained in:
Nick O'Leary 2024-12-20 11:04:58 +00:00 committed by GitHub
commit a0952d9a07
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194

View File

@ -2099,6 +2099,8 @@ RED.nodes = (function() {
activeWorkspace = RED.workspaces.active(); activeWorkspace = RED.workspaces.active();
} }
const pendingConfigNodes = []
const pendingConfigNodeIds = new Set()
// Find all config nodes and add them // Find all config nodes and add them
for (i=0;i<newNodes.length;i++) { for (i=0;i<newNodes.length;i++) {
n = newNodes[i]; n = newNodes[i];
@ -2158,7 +2160,8 @@ RED.nodes = (function() {
type:n.type, type:n.type,
info: n.info, info: n.info,
users:[], users:[],
_config:{} _config:{},
_configNodeReferences: new Set()
}; };
if (!n.z) { if (!n.z) {
delete configNode.z; delete configNode.z;
@ -2173,6 +2176,9 @@ RED.nodes = (function() {
if (def.defaults.hasOwnProperty(d)) { if (def.defaults.hasOwnProperty(d)) {
configNode[d] = n[d]; configNode[d] = n[d];
configNode._config[d] = JSON.stringify(n[d]); configNode._config[d] = JSON.stringify(n[d]);
if (def.defaults[d].type) {
configNode._configNodeReferences.add(n[d])
}
} }
} }
if (def.hasOwnProperty('credentials') && n.hasOwnProperty('credentials')) { if (def.hasOwnProperty('credentials') && n.hasOwnProperty('credentials')) {
@ -2189,25 +2195,54 @@ RED.nodes = (function() {
configNode.id = getID(); configNode.id = getID();
} }
node_map[n.id] = configNode; node_map[n.id] = configNode;
new_nodes.push(configNode); pendingConfigNodes.push(configNode);
pendingConfigNodeIds.add(configNode.id)
} }
} }
} }
// Config node can use another config node, must ensure that this other // We need to sort new_nodes (which only contains config nodes at this point)
// config node is added before to exists when updating the user list // to ensure they get added in the right order. If NodeA depends on NodeB, then
const configNodeFilter = function (node) { // NodeB must be added first.
let count = 0;
if (node._def?.defaults) { // Limit us to 5 full iterations of the list - this should be more than
for (const def of Object.values(node._def.defaults)) { // enough to process the list as config->config node relationships are
if (def.type) { // not very common
count++; let iterationLimit = pendingConfigNodes.length * 5
} const handledConfigNodes = new Set()
while (pendingConfigNodes.length > 0 && iterationLimit > 0) {
const node = pendingConfigNodes.shift()
let hasPending = false
// Loop through the nodes referenced by this node to see if anything
// is pending
node._configNodeReferences.forEach(id => {
if (pendingConfigNodeIds.has(id) && !handledConfigNodes.has(id)) {
// This reference is for a node we know is in this import, but
// it isn't added yet - flag as pending
hasPending = true
} }
})
if (!hasPending) {
// This node has no pending config node references - safe to add
delete node._configNodeReferences
new_nodes.push(node)
handledConfigNodes.add(node.id)
} else {
// This node has pending config node references
// Put to the back of the queue
pendingConfigNodes.push(node)
} }
return count; iterationLimit--
}; }
new_nodes.sort((a, b) => configNodeFilter(a) - configNodeFilter(b)); if (pendingConfigNodes.length > 0) {
// We exceeded the iteration count. Could be due to reference loops
// between the config nodes. At this point, just add the remaining
// nodes as-is
pendingConfigNodes.forEach(node => {
delete node._configNodeReferences
new_nodes.push(node)
})
}
// Find regular flow nodes and subflow instances // Find regular flow nodes and subflow instances
for (i=0;i<newNodes.length;i++) { for (i=0;i<newNodes.length;i++) {