processcube.apptemplate
Version:
A AppTemplate for a App build with and for the ProcessCube Plattform
310 lines (264 loc) • 8.11 kB
JavaScript
/**
* Prüft, ob ein External Task Input Node mit dem angegebenen Topic bereits existiert
* @param {string} topic - External Task Topic
* @returns {object|null} Existierender Node oder null
*/
function findExistingExternalTaskByTopic(topic) {
if (!topic) {
return null;
}
var externalTaskInputNodes = RED.nodes.filterNodes({type: "externaltask-input"});
return externalTaskInputNodes.find(function(node) {
return node.topic === topic;
}) || null;
}
/**
* Sucht einen Flow anhand seines Namens
* @param {string} flowName - Name des zu suchenden Flows
* @returns {{flow: object|null, shouldCreate: boolean}} Flow-Objekt und Flag ob ein neuer Flow erstellt werden muss
*/
function findOrPrepareFlow(flowName) {
if (!flowName) {
return { flow: null, shouldCreate: false };
}
var targetFlow = null;
// Suche in Workspaces
RED.nodes.eachWorkspace(function(ws) {
if (ws.label === flowName) {
targetFlow = ws;
}
});
// Suche in Subflows falls nicht gefunden
if (!targetFlow) {
RED.nodes.eachSubflow(function(sf) {
if (sf.name === flowName) {
targetFlow = sf;
}
});
}
return {
flow: targetFlow,
shouldCreate: !targetFlow
};
}
/**
* Findet die ProcessCube Engine-Konfiguration
* @returns {object} Engine-Konfiguration
*/
function getEngineConfig() {
var engineConfigs = [];
RED.nodes.eachConfig(function(node) {
if (node.type === "processcube-engine-config") {
engineConfigs.push(node);
}
});
return engineConfigs.find(node => node.url === 'ENGINE_URL');
}
/**
* Berechnet die Y-Position für eine neue Gruppe im Flow
* @param {object} targetFlow - Der Ziel-Flow
* @param {boolean} shouldCreateFlow - Flag ob ein neuer Flow erstellt wird
* @returns {number} Y-Position für die neue Gruppe
*/
function calculateGroupYPosition(targetFlow, shouldCreateFlow) {
var yPosition = 50;
if (targetFlow && !shouldCreateFlow) {
var maxY = 0;
var groupHeight = 0;
RED.nodes.eachGroup(function(group) {
if (group.z === targetFlow.id) {
if (group.y > maxY) {
maxY = group.y;
groupHeight = group.h || 0;
}
}
});
yPosition = maxY > 0 ? maxY + groupHeight + 20 : 20;
console.log("Berechnete Y-Position für neue Gruppe:", yPosition, "MaxY war:", maxY);
}
return yPosition;
}
/**
* Erstellt die Nodes für einen External Task Worker
* @param {string} topic - External Task Topic
* @param {object} engineConfig - Engine-Konfiguration
* @param {number} yPosition - Y-Position für die Nodes
* @returns {{nodes: object[], group: object}} Erstellte Nodes und Gruppe
*/
function createExternalTaskNodes(topic, engineConfig, yPosition) {
// Output Node
var etwOutput = {
id: RED.nodes.id(),
type: "externaltask-output",
x: 726,
y: yPosition + 50
};
// Delay Node
var delay = {
id: RED.nodes.id(),
type: "delay",
x: 426,
y: yPosition + 50,
pauseType: "delay",
timeout: "5",
timeoutUnits: "seconds",
wires: [[etwOutput.id]]
};
// Input Node
var etwInput = {
id: RED.nodes.id(),
type: "externaltask-input",
x: 126,
y: yPosition + 50,
wires: [[delay.id]],
topic: topic,
engine: engineConfig.id
};
// Error Node
var etwError = {
id: RED.nodes.id(),
type: "externaltask-error",
x: 526,
y: yPosition + 100
};
// Catch Node
var catchNode = {
id: RED.nodes.id(),
type: "catch",
x: 326,
y: yPosition + 100,
scope: 'group',
wires: [[etwError.id]]
};
// Group
var group = {
id: RED.nodes.id(),
type: "group",
name: `ETW: ${topic}`,
x: 34,
y: yPosition,
nodes: [etwInput.id, delay.id, etwOutput.id, catchNode.id, etwError.id],
style: {
"label": true
}
};
return {
nodes: [etwInput, delay, etwOutput, catchNode, etwError],
group: group
};
}
/**
* Setzt die Flow-Zugehörigkeit (z-Property) für alle Nodes
* @param {object[]} nodes - Array von Nodes
* @param {object} group - Gruppen-Objekt
* @param {string} flowId - Flow-ID
*/
function assignNodesToFlow(nodes, group, flowId) {
if (!flowId) {
return;
}
group.z = flowId;
nodes.forEach(function(node) {
node.z = flowId;
});
}
/**
* Erstellt einen neuen Flow
* @param {string} flowName - Name des neuen Flows
* @returns {object} Flow-Objekt
*/
function createNewFlow(flowName) {
return {
id: RED.nodes.id(),
type: "tab",
label: flowName,
disabled: false,
info: ""
};
}
/**
* Erstellt einen neuen Flow und zeigt ihn an
* @param {object} flowObject - Das Flow-Objekt
* @returns {string} Die ID des erstellten Flows
*/
function createAndShowFlow(flowObject) {
// Importiere nur den Flow
RED.view.importNodes([flowObject], {
addFlow: true,
touchImport: true,
applyNodeDefaults: true
});
// Wechsle zum neuen Flow
RED.workspaces.show(flowObject.id);
return flowObject.id;
}
/**
* Importiert Nodes in einen existierenden Flow
* @param {object[]} nodes - Zu importierende Nodes
* @param {object} group - Gruppe
*/
function importNodesIntoFlow(nodes, group) {
var nodesToImport = [group].concat(nodes);
var importOptions = {
addFlow: false,
touchImport: true,
applyNodeDefaults: true
};
RED.view.importNodes(nodesToImport, importOptions);
// Markiere Workspace als dirty, um Deploy-Button zu aktivieren
RED.nodes.dirty(true);
}
var addExternalTask = function(params) {
'use strict';
console.log("Füge neuen ExternalTask-Node hinzu");
// Prüfe, ob External Task mit diesem Topic bereits existiert
var existingNode = findExistingExternalTaskByTopic(params.externalTaskTopic);
if (existingNode) {
console.log("External Task mit Topic bereits vorhanden:", params.externalTaskTopic);
// Verwende die fokusExternalTask Funktion
if (typeof focusExternalTask === 'function') {
focusExternalTask({externalTaskTopic: params.externalTaskTopic});
}
return;
}
// Flow-Management
var flowInfo = findOrPrepareFlow(params.flowName);
var targetFlow = flowInfo.flow;
var shouldCreateFlow = flowInfo.shouldCreate;
// Engine-Konfiguration laden
var engineConfig = getEngineConfig();
// Y-Position berechnen
var yPosition = calculateGroupYPosition(targetFlow, shouldCreateFlow);
// Nodes und Gruppe erstellen
var externalTaskSetup = createExternalTaskNodes(
params.externalTaskTopic,
engineConfig,
yPosition
);
var nodes = externalTaskSetup.nodes;
var group = externalTaskSetup.group;
// Flow-Zuweisung und Import
if (shouldCreateFlow) {
// 1. Neuen Flow erstellen
var newFlow = createNewFlow(params.flowName);
// 2. Flow anzeigen (leerer Flow)
createAndShowFlow(newFlow);
// 3. Nodes dem Flow zuweisen
assignNodesToFlow(nodes, group, newFlow.id);
// 4. Nodes in den jetzt sichtbaren Flow importieren
setTimeout(function() {
importNodesIntoFlow(nodes, group);
}, 100); // Kurze Verzögerung, damit der Flow-Wechsel abgeschlossen ist
} else {
// In existierenden Flow einfügen
var flowId = targetFlow ? targetFlow.id : null;
// Zum Ziel-Flow wechseln falls vorhanden
if (targetFlow) {
RED.workspaces.show(targetFlow.id);
}
assignNodesToFlow(nodes, group, flowId);
setTimeout(function() {
importNodesIntoFlow(nodes, group);
}, 100);
}
};