mirror of
https://github.com/node-red/node-red.git
synced 2025-03-01 10:36:34 +00:00
Initial commit of function importNodes
refactoring
This commit is contained in:
parent
61b12f6bbe
commit
b495c18694
@ -1764,6 +1764,33 @@ RED.nodes = (function() {
|
||||
|
||||
}
|
||||
|
||||
function identifyUnknowType(nodes, options = {}) {
|
||||
const unknownTypes = [];
|
||||
for (let node of nodes) {
|
||||
// TODO: remove workspace in next release+1
|
||||
const knowTypes = ["workspace", "tab", "subflow", "group", "junction"];
|
||||
|
||||
if (!knowTypes.includes(node.type) &&
|
||||
node.type.substring(0, 8) != "subflow:" &&
|
||||
!registry.getNodeType(node.type) &&
|
||||
!unknownTypes.includes(node.type)) {
|
||||
unknownTypes.push(node.type);
|
||||
}
|
||||
}
|
||||
|
||||
if (options.emitNotification && unknownTypes.length) {
|
||||
const typeList = $("<ul>");
|
||||
unknownTypes.forEach(function(type) {
|
||||
$("<li>").text(type).appendTo(typeList);
|
||||
});
|
||||
|
||||
RED.notify(
|
||||
"<p>" + RED._("clipboard.importUnrecognised", {count: unknownTypes.length }) + "</p>" + typeList[0].outerHTML,
|
||||
"error", false, 10000);
|
||||
}
|
||||
return unknownTypes;
|
||||
}
|
||||
|
||||
/**
|
||||
* Options:
|
||||
* - generateIds - whether to replace all node ids
|
||||
@ -1776,119 +1803,131 @@ RED.nodes = (function() {
|
||||
* - id:copy - import with new id
|
||||
* - id:replace - import over the top of existing
|
||||
*/
|
||||
function importNodes(newNodesObj,options) { // createNewIds,createMissingWorkspace) {
|
||||
const defOpts = { generateIds: false, addFlow: false, markChanged: false, reimport: false, importMap: {} }
|
||||
options = Object.assign({}, defOpts, options)
|
||||
options.importMap = options.importMap || {}
|
||||
function importNodes(newNodes, options) {
|
||||
const defaultOptions = { generateIds: false, addFlow: false, markChanged: false, reimport: false, importMap: {} };
|
||||
options = Object.assign({}, defaultOptions, options);
|
||||
|
||||
const createNewIds = options.generateIds;
|
||||
const reimport = (!createNewIds && !!options.reimport)
|
||||
const createMissingWorkspace = options.addFlow;
|
||||
var i;
|
||||
var n;
|
||||
var newNodes;
|
||||
var nodeZmap = {};
|
||||
var recoveryWorkspace;
|
||||
if (typeof newNodesObj === "string") {
|
||||
if (newNodesObj === "") {
|
||||
return;
|
||||
}
|
||||
const reimport = (!createNewIds && !!options.reimport);
|
||||
|
||||
// Checks and converts the flow into an Array if necessary
|
||||
if (typeof newNodes === "string") {
|
||||
if (newNodes === "") { return; }
|
||||
try {
|
||||
newNodes = JSON.parse(newNodesObj);
|
||||
newNodes = JSON.parse(newNodes);
|
||||
} catch(err) {
|
||||
var e = new Error(RED._("clipboard.invalidFlow",{message:err.message}));
|
||||
e.code = "NODE_RED";
|
||||
throw e;
|
||||
const error = new Error(RED._("clipboard.invalidFlow", { message: err.message }));
|
||||
error.code = "NODE_RED";
|
||||
throw error;
|
||||
}
|
||||
} else {
|
||||
newNodes = newNodesObj;
|
||||
}
|
||||
|
||||
if (!Array.isArray(newNodes)) {
|
||||
newNodes = [newNodes];
|
||||
}
|
||||
|
||||
// Scan for any duplicate nodes and remove them. This is a temporary
|
||||
// fix to help resolve corrupted flows caused by 0.20.0 where multiple
|
||||
// copies of the flow would get loaded at the same time.
|
||||
// If the user hit deploy they would have saved those duplicates.
|
||||
var seenIds = {};
|
||||
var existingNodes = [];
|
||||
var nodesToReplace = [];
|
||||
const seenIds = new Set();
|
||||
const existingNodes = [];
|
||||
const nodesToReplace = [];
|
||||
// Checks if the imported flow contains duplicates or existing nodes.
|
||||
newNodes = newNodes.filter(function(node) {
|
||||
const id = node.id;
|
||||
|
||||
newNodes = newNodes.filter(function(n) {
|
||||
var id = n.id;
|
||||
if (seenIds[n.id]) {
|
||||
return false;
|
||||
}
|
||||
seenIds[n.id] = true;
|
||||
// This is a temporary fix to help resolve corrupted flows caused by 0.20.0 where multiple
|
||||
// copies of the flow would get loaded at the same time.
|
||||
// NOTE: Generally it is the last occurrence which is kept but not here.
|
||||
if (seenIds.has(id)) { return false; }
|
||||
seenIds.add(id);
|
||||
|
||||
if (!options.generateIds) {
|
||||
// Checks if the node already exists - the user will choose between copying the node or replacing it
|
||||
if (!createNewIds) {
|
||||
if (!options.importMap[id]) {
|
||||
// No conflict resolution for this node
|
||||
var existing = allNodes.getNode(id) || configNodes[id] || workspaces[id] || subflows[id] || groups[id] || junctions[id];
|
||||
if (existing) {
|
||||
existingNodes.push({existing:existing, imported:n});
|
||||
const existingNode = allNodes.getNode(id) || configNodes[id] || workspaces[id] || subflows[id] || groups[id] || junctions[id];
|
||||
if (existingNode) {
|
||||
existingNodes.push({ existing: existingNode, imported: node });
|
||||
}
|
||||
} else if (options.importMap[id] === "replace") {
|
||||
nodesToReplace.push(n);
|
||||
nodesToReplace.push(node);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
})
|
||||
});
|
||||
|
||||
if (existingNodes.length > 0) {
|
||||
var errorMessage = RED._("clipboard.importDuplicate",{count:existingNodes.length});
|
||||
var nodeList = $("<ul>");
|
||||
var existingNodesCount = Math.min(5,existingNodes.length);
|
||||
for (var i=0;i<existingNodesCount;i++) {
|
||||
var conflict = existingNodes[i];
|
||||
$("<li>").text(
|
||||
conflict.existing.id+
|
||||
" [ "+conflict.existing.type+ ((conflict.imported.type !== conflict.existing.type)?" | "+conflict.imported.type:"")+" ]").appendTo(nodeList)
|
||||
}
|
||||
if (existingNodesCount !== existingNodes.length) {
|
||||
$("<li>").text(RED._("deploy.confirm.plusNMore",{count:existingNodes.length-existingNodesCount})).appendTo(nodeList)
|
||||
}
|
||||
var wrapper = $("<p>").append(nodeList);
|
||||
// If some nodes already exists, ask the user for each node to choose between copying it or replacing it
|
||||
if (existingNodes.length) {
|
||||
const errorMessage = RED._("clipboard.importDuplicate",{ count: existingNodes.length });
|
||||
const maxItemCount = 5; // Max 5 items in the list
|
||||
const nodeList = $("<ul>");
|
||||
|
||||
var existingNodesError = new Error(errorMessage+wrapper.html());
|
||||
let itemCount = 0;
|
||||
for (let { existing, imported } of existingNodes) {
|
||||
if (itemCount >= maxItemCount) { break; }
|
||||
const conflictType = (imported.type !== existing.type) ? " | " + imported.type : "";
|
||||
$("<li>").text(existing.id + " [ " + existing.type + conflictType + " ]").appendTo(nodeList);
|
||||
itemCount++;
|
||||
}
|
||||
|
||||
if (existingNodes.length > maxItemCount) {
|
||||
$("<li>").text(RED._("deploy.confirm.plusNMore", {count: (existingNodes.length - maxItemCount) })).appendTo(nodeList);
|
||||
}
|
||||
|
||||
const wrapper = $("<p>").append(nodeList);
|
||||
const existingNodesError = new Error(errorMessage + wrapper.html());
|
||||
existingNodesError.code = "import_conflict";
|
||||
existingNodesError.importConfig = identifyImportConflicts(newNodes);
|
||||
throw existingNodesError;
|
||||
}
|
||||
var removedNodes;
|
||||
if (nodesToReplace.length > 0) {
|
||||
var replaceResult = replaceNodes(nodesToReplace);
|
||||
removedNodes = replaceResult.removedNodes;
|
||||
|
||||
// TODO: check the z of the subflow instance and check _that_ if it exists
|
||||
// TODO: Handled activeWorkspace = 0
|
||||
let activeWorkspace = RED.workspaces.active();
|
||||
const activeSubflow = getSubflow(activeWorkspace);
|
||||
for (let node of newNodes) {
|
||||
const group = /^subflow:(.+)$/.exec(node.type);
|
||||
if (group) {
|
||||
const subflowId = group[1];
|
||||
if (activeSubflow) {
|
||||
let error;
|
||||
if (subflowId === activeSubflow.id) {
|
||||
error = new Error(RED._("notification.errors.cannotAddSubflowToItself"));
|
||||
} else if (subflowContains(subflowId, activeSubflow.id)) {
|
||||
error = new Error(RED._("notification.errors.cannotAddCircularReference"));
|
||||
}
|
||||
if (error) {
|
||||
// TODO: standardise error codes
|
||||
error.code = "NODE_RED";
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const removedNodes = [];
|
||||
// Now that the user has made his choice, replace the nodes that need to be replaced.
|
||||
if (nodesToReplace.length > 0) {
|
||||
const result = replaceNodes(nodesToReplace);
|
||||
removedNodes.concat(result.removedNodes);
|
||||
}
|
||||
|
||||
var isInitialLoad = false;
|
||||
let isInitialLoad = false;
|
||||
if (!initialLoad) {
|
||||
isInitialLoad = true;
|
||||
initialLoad = JSON.parse(JSON.stringify(newNodes));
|
||||
}
|
||||
var unknownTypes = [];
|
||||
for (i=0;i<newNodes.length;i++) {
|
||||
n = newNodes[i];
|
||||
var id = n.id;
|
||||
// TODO: remove workspace in next release+1
|
||||
if (n.type != "workspace" &&
|
||||
n.type != "tab" &&
|
||||
n.type != "subflow" &&
|
||||
n.type != "group" &&
|
||||
n.type != 'junction' &&
|
||||
!registry.getNodeType(n.type) &&
|
||||
n.type.substring(0,8) != "subflow:" &&
|
||||
unknownTypes.indexOf(n.type)==-1) {
|
||||
unknownTypes.push(n.type);
|
||||
}
|
||||
if (n.z) {
|
||||
nodeZmap[n.z] = nodeZmap[n.z] || [];
|
||||
nodeZmap[n.z].push(n);
|
||||
} else if (isInitialLoad && n.hasOwnProperty('x') && n.hasOwnProperty('y') && !n.z) {
|
||||
|
||||
const unknownTypes = identifyUnknowType(newNodes, { emitNotification: isInitialLoad });
|
||||
|
||||
const nodeZmap = {};
|
||||
let recoveryWorkspace;
|
||||
for (let node of newNodes) {
|
||||
if (node.z) {
|
||||
nodeZmap[node.z] = nodeZmap[node.z] || [];
|
||||
nodeZmap[node.z].push(node);
|
||||
} else if (isInitialLoad && node.hasOwnProperty('x') && node.hasOwnProperty('y') && !node.z) {
|
||||
// Hit the rare issue where node z values get set to 0.
|
||||
// Repair the flow - but we really need to track that down.
|
||||
if (!recoveryWorkspace) {
|
||||
@ -1899,50 +1938,21 @@ RED.nodes = (function() {
|
||||
label: RED._("clipboard.recoveredNodes"),
|
||||
info: RED._("clipboard.recoveredNodesInfo"),
|
||||
env: []
|
||||
}
|
||||
};
|
||||
addWorkspace(recoveryWorkspace);
|
||||
RED.workspaces.add(recoveryWorkspace);
|
||||
nodeZmap[recoveryWorkspace.id] = [];
|
||||
}
|
||||
n.z = recoveryWorkspace.id;
|
||||
nodeZmap[recoveryWorkspace.id].push(n);
|
||||
}
|
||||
|
||||
}
|
||||
if (!isInitialLoad && unknownTypes.length > 0) {
|
||||
var typeList = $("<ul>");
|
||||
unknownTypes.forEach(function(t) {
|
||||
$("<li>").text(t).appendTo(typeList);
|
||||
})
|
||||
typeList = typeList[0].outerHTML;
|
||||
RED.notify("<p>"+RED._("clipboard.importUnrecognised",{count:unknownTypes.length})+"</p>"+typeList,"error",false,10000);
|
||||
}
|
||||
|
||||
var activeWorkspace = RED.workspaces.active();
|
||||
//TODO: check the z of the subflow instance and check _that_ if it exists
|
||||
var activeSubflow = getSubflow(activeWorkspace);
|
||||
for (i=0;i<newNodes.length;i++) {
|
||||
var m = /^subflow:(.+)$/.exec(newNodes[i].type);
|
||||
if (m) {
|
||||
var subflowId = m[1];
|
||||
var parent = getSubflow(activeWorkspace);
|
||||
if (parent) {
|
||||
var err;
|
||||
if (subflowId === parent.id) {
|
||||
err = new Error(RED._("notification.errors.cannotAddSubflowToItself"));
|
||||
}
|
||||
if (subflowContains(subflowId,parent.id)) {
|
||||
err = new Error(RED._("notification.errors.cannotAddCircularReference"));
|
||||
}
|
||||
if (err) {
|
||||
// TODO: standardise error codes
|
||||
err.code = "NODE_RED";
|
||||
throw err;
|
||||
}
|
||||
}
|
||||
node.z = recoveryWorkspace.id;
|
||||
nodeZmap[recoveryWorkspace.id].push(node);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
var i;
|
||||
var n;
|
||||
|
||||
|
||||
var new_workspaces = [];
|
||||
var workspace_map = {};
|
||||
var new_subflows = [];
|
||||
|
Loading…
x
Reference in New Issue
Block a user