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:
|
* Options:
|
||||||
* - generateIds - whether to replace all node ids
|
* - generateIds - whether to replace all node ids
|
||||||
@ -1776,119 +1803,131 @@ RED.nodes = (function() {
|
|||||||
* - id:copy - import with new id
|
* - id:copy - import with new id
|
||||||
* - id:replace - import over the top of existing
|
* - id:replace - import over the top of existing
|
||||||
*/
|
*/
|
||||||
function importNodes(newNodesObj,options) { // createNewIds,createMissingWorkspace) {
|
function importNodes(newNodes, options) {
|
||||||
const defOpts = { generateIds: false, addFlow: false, markChanged: false, reimport: false, importMap: {} }
|
const defaultOptions = { generateIds: false, addFlow: false, markChanged: false, reimport: false, importMap: {} };
|
||||||
options = Object.assign({}, defOpts, options)
|
options = Object.assign({}, defaultOptions, options);
|
||||||
options.importMap = options.importMap || {}
|
|
||||||
const createNewIds = options.generateIds;
|
const createNewIds = options.generateIds;
|
||||||
const reimport = (!createNewIds && !!options.reimport)
|
|
||||||
const createMissingWorkspace = options.addFlow;
|
const createMissingWorkspace = options.addFlow;
|
||||||
var i;
|
const reimport = (!createNewIds && !!options.reimport);
|
||||||
var n;
|
|
||||||
var newNodes;
|
// Checks and converts the flow into an Array if necessary
|
||||||
var nodeZmap = {};
|
if (typeof newNodes === "string") {
|
||||||
var recoveryWorkspace;
|
if (newNodes === "") { return; }
|
||||||
if (typeof newNodesObj === "string") {
|
|
||||||
if (newNodesObj === "") {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
try {
|
try {
|
||||||
newNodes = JSON.parse(newNodesObj);
|
newNodes = JSON.parse(newNodes);
|
||||||
} catch(err) {
|
} catch(err) {
|
||||||
var e = new Error(RED._("clipboard.invalidFlow",{message:err.message}));
|
const error = new Error(RED._("clipboard.invalidFlow", { message: err.message }));
|
||||||
e.code = "NODE_RED";
|
error.code = "NODE_RED";
|
||||||
throw e;
|
throw error;
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
newNodes = newNodesObj;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!Array.isArray(newNodes)) {
|
if (!Array.isArray(newNodes)) {
|
||||||
newNodes = [newNodes];
|
newNodes = [newNodes];
|
||||||
}
|
}
|
||||||
|
|
||||||
// Scan for any duplicate nodes and remove them. This is a temporary
|
const seenIds = new Set();
|
||||||
// fix to help resolve corrupted flows caused by 0.20.0 where multiple
|
const existingNodes = [];
|
||||||
|
const nodesToReplace = [];
|
||||||
|
// Checks if the imported flow contains duplicates or existing nodes.
|
||||||
|
newNodes = newNodes.filter(function(node) {
|
||||||
|
const id = node.id;
|
||||||
|
|
||||||
|
// 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.
|
// copies of the flow would get loaded at the same time.
|
||||||
// If the user hit deploy they would have saved those duplicates.
|
// NOTE: Generally it is the last occurrence which is kept but not here.
|
||||||
var seenIds = {};
|
if (seenIds.has(id)) { return false; }
|
||||||
var existingNodes = [];
|
seenIds.add(id);
|
||||||
var nodesToReplace = [];
|
|
||||||
|
|
||||||
newNodes = newNodes.filter(function(n) {
|
// Checks if the node already exists - the user will choose between copying the node or replacing it
|
||||||
var id = n.id;
|
if (!createNewIds) {
|
||||||
if (seenIds[n.id]) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
seenIds[n.id] = true;
|
|
||||||
|
|
||||||
if (!options.generateIds) {
|
|
||||||
if (!options.importMap[id]) {
|
if (!options.importMap[id]) {
|
||||||
// No conflict resolution for this node
|
// No conflict resolution for this node
|
||||||
var existing = allNodes.getNode(id) || configNodes[id] || workspaces[id] || subflows[id] || groups[id] || junctions[id];
|
const existingNode = allNodes.getNode(id) || configNodes[id] || workspaces[id] || subflows[id] || groups[id] || junctions[id];
|
||||||
if (existing) {
|
if (existingNode) {
|
||||||
existingNodes.push({existing:existing, imported:n});
|
existingNodes.push({ existing: existingNode, imported: node });
|
||||||
}
|
}
|
||||||
} else if (options.importMap[id] === "replace") {
|
} else if (options.importMap[id] === "replace") {
|
||||||
nodesToReplace.push(n);
|
nodesToReplace.push(node);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
})
|
});
|
||||||
|
|
||||||
if (existingNodes.length > 0) {
|
// If some nodes already exists, ask the user for each node to choose between copying it or replacing it
|
||||||
var errorMessage = RED._("clipboard.importDuplicate",{count:existingNodes.length});
|
if (existingNodes.length) {
|
||||||
var nodeList = $("<ul>");
|
const errorMessage = RED._("clipboard.importDuplicate",{ count: existingNodes.length });
|
||||||
var existingNodesCount = Math.min(5,existingNodes.length);
|
const maxItemCount = 5; // Max 5 items in the list
|
||||||
for (var i=0;i<existingNodesCount;i++) {
|
const nodeList = $("<ul>");
|
||||||
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);
|
|
||||||
|
|
||||||
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.code = "import_conflict";
|
||||||
existingNodesError.importConfig = identifyImportConflicts(newNodes);
|
existingNodesError.importConfig = identifyImportConflicts(newNodes);
|
||||||
throw existingNodesError;
|
throw existingNodesError;
|
||||||
}
|
}
|
||||||
var removedNodes;
|
|
||||||
if (nodesToReplace.length > 0) {
|
// TODO: check the z of the subflow instance and check _that_ if it exists
|
||||||
var replaceResult = replaceNodes(nodesToReplace);
|
// TODO: Handled activeWorkspace = 0
|
||||||
removedNodes = replaceResult.removedNodes;
|
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) {
|
if (!initialLoad) {
|
||||||
isInitialLoad = true;
|
isInitialLoad = true;
|
||||||
initialLoad = JSON.parse(JSON.stringify(newNodes));
|
initialLoad = JSON.parse(JSON.stringify(newNodes));
|
||||||
}
|
}
|
||||||
var unknownTypes = [];
|
|
||||||
for (i=0;i<newNodes.length;i++) {
|
const unknownTypes = identifyUnknowType(newNodes, { emitNotification: isInitialLoad });
|
||||||
n = newNodes[i];
|
|
||||||
var id = n.id;
|
const nodeZmap = {};
|
||||||
// TODO: remove workspace in next release+1
|
let recoveryWorkspace;
|
||||||
if (n.type != "workspace" &&
|
for (let node of newNodes) {
|
||||||
n.type != "tab" &&
|
if (node.z) {
|
||||||
n.type != "subflow" &&
|
nodeZmap[node.z] = nodeZmap[node.z] || [];
|
||||||
n.type != "group" &&
|
nodeZmap[node.z].push(node);
|
||||||
n.type != 'junction' &&
|
} else if (isInitialLoad && node.hasOwnProperty('x') && node.hasOwnProperty('y') && !node.z) {
|
||||||
!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) {
|
|
||||||
// Hit the rare issue where node z values get set to 0.
|
// Hit the rare issue where node z values get set to 0.
|
||||||
// Repair the flow - but we really need to track that down.
|
// Repair the flow - but we really need to track that down.
|
||||||
if (!recoveryWorkspace) {
|
if (!recoveryWorkspace) {
|
||||||
@ -1899,49 +1938,20 @@ RED.nodes = (function() {
|
|||||||
label: RED._("clipboard.recoveredNodes"),
|
label: RED._("clipboard.recoveredNodes"),
|
||||||
info: RED._("clipboard.recoveredNodesInfo"),
|
info: RED._("clipboard.recoveredNodesInfo"),
|
||||||
env: []
|
env: []
|
||||||
}
|
};
|
||||||
addWorkspace(recoveryWorkspace);
|
addWorkspace(recoveryWorkspace);
|
||||||
RED.workspaces.add(recoveryWorkspace);
|
RED.workspaces.add(recoveryWorkspace);
|
||||||
nodeZmap[recoveryWorkspace.id] = [];
|
nodeZmap[recoveryWorkspace.id] = [];
|
||||||
}
|
}
|
||||||
n.z = recoveryWorkspace.id;
|
node.z = recoveryWorkspace.id;
|
||||||
nodeZmap[recoveryWorkspace.id].push(n);
|
nodeZmap[recoveryWorkspace.id].push(node);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
|
||||||
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();
|
var i;
|
||||||
//TODO: check the z of the subflow instance and check _that_ if it exists
|
var n;
|
||||||
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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
var new_workspaces = [];
|
var new_workspaces = [];
|
||||||
var workspace_map = {};
|
var workspace_map = {};
|
||||||
|
Loading…
x
Reference in New Issue
Block a user