* bring back overrides * fix input output label positions * simply update label positions * refactor a bunch * update min x to show items * hide overlay on connection * only delete target connection, add maximum to push nodes out * rename const * rename const * set new insert position * fix insert behavior * update position handling * show arrow along with label * update connector * set endpoint styles * update pattern * push nodes up / down in case of if node * set position in switch * only one action at a time * add custom flow chart type * select start node by default when opening new workflow * add enter delay * fix delete bug * change connection type * add offset for if/switch/merge * fix gap * fix drag issue * implement new states * update disabled state * add selected state * make selects faster * update positioning * truncate when selected * remove offset for actions * fix icon scaling * refactor js plumb * fix looping behavior at close distance * lock version * change background to dots * update endpoints styling * increase spacing * udpate node z-index * fix output label positions * fix output label positions * reset location * add label offset * update border radius * fix height issue * fix parallaxing issue * fix zoomout issue * add success z-index * clean up js file * add package lock * fix z-index bug * update dot grid * update zoom level * set values, increase grid size * fix drop position * prevent duplicate connections * fix stub * use localstorage overrides for colors * add colors to system * revert no longer needed changes * revert no longer needed changes * add canvas colors * add canvas colors * use variable for id * force type * refactor helpers * add label constants * refactor func * refactor * fix * refactor * clean up css * refactor setzoom level * refactor * refactor * refactor func * remove scope * remove localstorage caching * clean up imports * update zero case * add delete connection * update selected state * add base type, remove straight line * add stub offset back * rename param * add label offset * update font size of items * move up label * fix error state while executing * disrespect stubs * check for errors * refactor position * clean up extra space * make entire node connectable * Revert "make entire node connectable" e304f7c5b8ff1b41268450c60ca4bc3b2ada5d4a * always show border * add border to zoom buttons * update spacing * update colors * allow connecting to entire node * fix pull conn active * two line names * apply select to all lines * increase input margin * override target pos * reset conn after pull * fix types * update orientation * fix up connectors snapping * hide arrow on pull * update overrides for connectors * change text * update pull colors * set to 1 line when selected * fix executions bug * build * refactor node component * remove comment * refactor more * remove prop * fix build issue * fix input drag bug in executions * reset offset * update select background * handle issue when endpoints are not set * fix connection aborted issue * add try catch to help show errors * wrap bind with try/catch * set default styles * reset pos despite zoom * add more checks * clean up impl * update icon * handle unknown types * hide items on init * fix importing unknown types with credentials * change opacity * push up item label * update color * update label class and colors * add to drop distance * fix z-index to match node * disable eslint * fix lasso tool selection * update background color * update waiting state * update tooltip positions * update wait node border * fix selection bug mostly * if selected, move above other nodes * add line through disabled nodes * remove node color option * move label above connection * success color for line through * update options index * hide waiting icon when disabled * fix gmail icon * refactor icons * clear execution data on disable/delete * fix selected node * fix executing behavior * optional __meta * set grid size * remove default color * remove node color * add comments * comments * add comments * remove empty space * update comment * refactor uuids * fix type issue * Revert "fix type issue" 9523b34f9604f75253ae0631f29fc27267a99d78 * Revert "fix type issue" 9523b34f9604f75253ae0631f29fc27267a99d78 * Revert "refactor uuids" 07f6848065cb9a98475fddb8330846106f9e70ad * fix build issues * refactor * update uuid * child nodes * skip nodes behind when pushing in loop * shift output icon for switch node * don't show output if waiting * waiting on init * build * change to bezier * revert connector change * add bezier type * fix snapping * clean up impl * refactor func * make const * rename type * refactor to simplify * Revert "refactor to simplify" 2db0ed504c752c33de975370d86a83a04ffcda14 * enable flowchart mode * clean up flowchart type * refactor type * merge types * configure curviness * set in localstorage * fix straight line arrow bug * show arrow when pulling * refactor / simplify * fix target gap in bezier * refactor target gap * add comments * add comment * fix dragging connections * fix bug when moving connection * update comment * rename file * update values * update minor * update straight line box * clean up conn types * clean up z-indexes * move color filters to node icon * update background color * update to use grid size value * fix endpoint offsets * set yspan range lower * remove overlays when moving conn * prevent unwanted connections * fix messed up connections * remove console log * clear execution issues on workflow run * update corner radius * fix drag/delete bug * increase offset * update disabled state * address comments * refactor * refactor func * ⚡ Add full license text to N8nCustomConnectorType.js Co-authored-by: Jan Oberhauser <jan.oberhauser@gmail.com>
392 lines
12 KiB
TypeScript
392 lines
12 KiB
TypeScript
import {
|
|
PLACEHOLDER_FILLED_AT_EXECUTION_TIME,
|
|
} from '@/constants';
|
|
|
|
import {
|
|
IBinaryKeyData,
|
|
ICredentialType,
|
|
INodeCredentialDescription,
|
|
NodeHelpers,
|
|
INodeCredentialsDetails,
|
|
INodeExecutionData,
|
|
INodeIssues,
|
|
INodeIssueData,
|
|
INodeIssueObjectProperty,
|
|
INodeParameters,
|
|
INodeProperties,
|
|
INodeTypeDescription,
|
|
IRunData,
|
|
IRunExecutionData,
|
|
ITaskDataConnections,
|
|
INode,
|
|
INodePropertyOptions,
|
|
} from 'n8n-workflow';
|
|
|
|
import {
|
|
ICredentialsResponse,
|
|
INodeUi,
|
|
} from '../../Interface';
|
|
|
|
import { restApi } from '@/components/mixins/restApi';
|
|
|
|
import { get } from 'lodash';
|
|
|
|
import mixins from 'vue-typed-mixins';
|
|
|
|
export const nodeHelpers = mixins(
|
|
restApi,
|
|
)
|
|
.extend({
|
|
methods: {
|
|
|
|
// Returns the parameter value
|
|
getParameterValue (nodeValues: INodeParameters, parameterName: string, path: string) {
|
|
return get(
|
|
nodeValues,
|
|
path ? path + '.' + parameterName : parameterName,
|
|
);
|
|
},
|
|
|
|
// Returns if the given parameter should be displayed or not
|
|
displayParameter (nodeValues: INodeParameters, parameter: INodeProperties | INodeCredentialDescription, path: string) {
|
|
return NodeHelpers.displayParameterPath(nodeValues, parameter, path);
|
|
},
|
|
|
|
// Returns all the issues of the node
|
|
getNodeIssues (nodeType: INodeTypeDescription | null, node: INodeUi, ignoreIssues?: string[]): INodeIssues | null {
|
|
let nodeIssues: INodeIssues | null = null;
|
|
ignoreIssues = ignoreIssues || [];
|
|
|
|
if (node.disabled === true) {
|
|
// Ignore issues on disabled nodes
|
|
return null;
|
|
}
|
|
|
|
if (nodeType === null) {
|
|
// Node type is not known
|
|
if (!ignoreIssues.includes('typeUnknown')) {
|
|
nodeIssues = {
|
|
typeUnknown: true,
|
|
};
|
|
}
|
|
} else {
|
|
// Node type is known
|
|
|
|
// Add potential parameter issues
|
|
if (!ignoreIssues.includes('parameters')) {
|
|
nodeIssues = NodeHelpers.getNodeParametersIssues(nodeType.properties, node);
|
|
}
|
|
|
|
if (!ignoreIssues.includes('credentials')) {
|
|
// Add potential credential issues
|
|
const nodeCredentialIssues = this.getNodeCredentialIssues(node, nodeType);
|
|
if (nodeIssues === null) {
|
|
nodeIssues = nodeCredentialIssues;
|
|
} else {
|
|
NodeHelpers.mergeIssues(nodeIssues, nodeCredentialIssues);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (this.hasNodeExecutionIssues(node) === true && !ignoreIssues.includes('execution')) {
|
|
if (nodeIssues === null) {
|
|
nodeIssues = {};
|
|
}
|
|
nodeIssues.execution = true;
|
|
}
|
|
|
|
return nodeIssues;
|
|
},
|
|
|
|
// Set the status on all the nodes which produced an error so that it can be
|
|
// displayed in the node-view
|
|
hasNodeExecutionIssues (node: INodeUi): boolean {
|
|
const workflowResultData: IRunData = this.$store.getters.getWorkflowRunData;
|
|
|
|
if (workflowResultData === null || !workflowResultData.hasOwnProperty(node.name)) {
|
|
return false;
|
|
}
|
|
|
|
for (const taskData of workflowResultData[node.name]) {
|
|
if (taskData.error !== undefined) {
|
|
return true;
|
|
}
|
|
}
|
|
|
|
return false;
|
|
},
|
|
|
|
// Updates the execution issues.
|
|
updateNodesExecutionIssues () {
|
|
const nodes = this.$store.getters.allNodes;
|
|
|
|
for (const node of nodes) {
|
|
this.$store.commit('setNodeIssue', {
|
|
node: node.name,
|
|
type: 'execution',
|
|
value: this.hasNodeExecutionIssues(node) ? true : null,
|
|
});
|
|
}
|
|
},
|
|
|
|
// Updates the credential-issues of the node
|
|
updateNodeCredentialIssues(node: INodeUi): void {
|
|
const fullNodeIssues: INodeIssues | null = this.getNodeCredentialIssues(node);
|
|
|
|
let newIssues: INodeIssueObjectProperty | null = null;
|
|
if (fullNodeIssues !== null) {
|
|
newIssues = fullNodeIssues.credentials!;
|
|
}
|
|
|
|
this.$store.commit('setNodeIssue', {
|
|
node: node.name,
|
|
type: 'credentials',
|
|
value: newIssues,
|
|
} as INodeIssueData);
|
|
},
|
|
|
|
// Updates the parameter-issues of the node
|
|
updateNodeParameterIssues(node: INodeUi, nodeType?: INodeTypeDescription): void {
|
|
if (nodeType === undefined) {
|
|
nodeType = this.$store.getters.nodeType(node.type);
|
|
}
|
|
|
|
if (nodeType === null) {
|
|
// Could not find nodeType so can not update issues
|
|
return;
|
|
}
|
|
|
|
// All data got updated everywhere so update now the issues
|
|
const fullNodeIssues: INodeIssues | null = NodeHelpers.getNodeParametersIssues(nodeType!.properties, node);
|
|
|
|
let newIssues: INodeIssueObjectProperty | null = null;
|
|
if (fullNodeIssues !== null) {
|
|
newIssues = fullNodeIssues.parameters!;
|
|
}
|
|
|
|
this.$store.commit('setNodeIssue', {
|
|
node: node.name,
|
|
type: 'parameters',
|
|
value: newIssues,
|
|
} as INodeIssueData);
|
|
},
|
|
|
|
// Returns all the credential-issues of the node
|
|
getNodeCredentialIssues (node: INodeUi, nodeType?: INodeTypeDescription): INodeIssues | null {
|
|
if (node.disabled === true) {
|
|
// Node is disabled
|
|
return null;
|
|
}
|
|
|
|
if (nodeType === undefined) {
|
|
nodeType = this.$store.getters.nodeType(node.type);
|
|
}
|
|
|
|
if (nodeType === null || nodeType!.credentials === undefined) {
|
|
// Node does not need any credentials or nodeType could not be found
|
|
return null;
|
|
}
|
|
|
|
if (nodeType!.credentials === undefined) {
|
|
// No credentials defined for node type
|
|
return null;
|
|
}
|
|
|
|
const foundIssues: INodeIssueObjectProperty = {};
|
|
|
|
let userCredentials: ICredentialsResponse[] | null;
|
|
let credentialType: ICredentialType | null;
|
|
let credentialDisplayName: string;
|
|
let selectedCredentials: INodeCredentialsDetails;
|
|
for (const credentialTypeDescription of nodeType!.credentials!) {
|
|
// Check if credentials should be displayed else ignore
|
|
if (this.displayParameter(node.parameters, credentialTypeDescription, '') !== true) {
|
|
continue;
|
|
}
|
|
|
|
// Get the display name of the credential type
|
|
credentialType = this.$store.getters['credentials/getCredentialTypeByName'](credentialTypeDescription.name);
|
|
if (credentialType === null) {
|
|
credentialDisplayName = credentialTypeDescription.name;
|
|
} else {
|
|
credentialDisplayName = credentialType.displayName;
|
|
}
|
|
|
|
if (node.credentials === undefined || node.credentials[credentialTypeDescription.name] === undefined) {
|
|
// Credentials are not set
|
|
if (credentialTypeDescription.required === true) {
|
|
foundIssues[credentialTypeDescription.name] = [`Credentials for "${credentialDisplayName}" are not set.`];
|
|
}
|
|
} else {
|
|
// If they are set check if the value is valid
|
|
selectedCredentials = node.credentials[credentialTypeDescription.name] as INodeCredentialsDetails;
|
|
if (typeof selectedCredentials === 'string') {
|
|
selectedCredentials = {
|
|
id: null,
|
|
name: selectedCredentials,
|
|
};
|
|
}
|
|
|
|
userCredentials = this.$store.getters['credentials/getCredentialsByType'](credentialTypeDescription.name);
|
|
|
|
if (userCredentials === null) {
|
|
userCredentials = [];
|
|
}
|
|
|
|
if (selectedCredentials.id) {
|
|
const idMatch = userCredentials.find((credentialData) => credentialData.id === selectedCredentials.id);
|
|
if (idMatch) {
|
|
continue;
|
|
}
|
|
}
|
|
|
|
const nameMatches = userCredentials.filter((credentialData) => credentialData.name === selectedCredentials.name);
|
|
if (nameMatches.length > 1) {
|
|
foundIssues[credentialTypeDescription.name] = [`Credentials with name "${selectedCredentials.name}" exist for "${credentialDisplayName}"`, "Credentials are not clearly identified. Please select the correct credentials."];
|
|
continue;
|
|
}
|
|
|
|
if (nameMatches.length === 0) {
|
|
foundIssues[credentialTypeDescription.name] = [`Credentials with name "${selectedCredentials.name}" do not exist for "${credentialDisplayName}".`, "You can create credentials with the exact name and then they get auto-selected on refresh."];
|
|
}
|
|
}
|
|
}
|
|
|
|
// TODO: Could later check also if the node has access to the credentials
|
|
if (Object.keys(foundIssues).length === 0) {
|
|
return null;
|
|
}
|
|
|
|
return {
|
|
credentials: foundIssues,
|
|
};
|
|
},
|
|
|
|
// Updates the node credential issues
|
|
updateNodesCredentialsIssues () {
|
|
const nodes = this.$store.getters.allNodes;
|
|
let issues: INodeIssues | null;
|
|
|
|
for (const node of nodes) {
|
|
issues = this.getNodeCredentialIssues(node);
|
|
|
|
this.$store.commit('setNodeIssue', {
|
|
node: node.name,
|
|
type: 'credentials',
|
|
value: issues === null ? null : issues.credentials,
|
|
});
|
|
}
|
|
},
|
|
|
|
getNodeInputData (node: INodeUi | null, runIndex = 0, outputIndex = 0): INodeExecutionData[] {
|
|
if (node === null) {
|
|
return [];
|
|
}
|
|
|
|
if (this.$store.getters.getWorkflowExecution === null) {
|
|
return [];
|
|
}
|
|
const executionData: IRunExecutionData = this.$store.getters.getWorkflowExecution.data;
|
|
const runData = executionData.resultData.runData;
|
|
|
|
if (runData === null || runData[node.name] === undefined ||
|
|
!runData[node.name][runIndex].data ||
|
|
runData[node.name][runIndex].data === undefined
|
|
) {
|
|
return [];
|
|
}
|
|
|
|
return this.getMainInputData(runData[node.name][runIndex].data!, outputIndex);
|
|
},
|
|
|
|
// Returns the data of the main input
|
|
getMainInputData (connectionsData: ITaskDataConnections, outputIndex: number): INodeExecutionData[] {
|
|
if (!connectionsData || !connectionsData.hasOwnProperty('main') || connectionsData.main === undefined || connectionsData.main.length < outputIndex || connectionsData.main[outputIndex] === null) {
|
|
return [];
|
|
}
|
|
return connectionsData.main[outputIndex] as INodeExecutionData[];
|
|
},
|
|
|
|
// Returns all the binary data of all the entries
|
|
getBinaryData (workflowRunData: IRunData | null, node: string | null, runIndex: number, outputIndex: number): IBinaryKeyData[] {
|
|
if (node === null) {
|
|
return [];
|
|
}
|
|
|
|
const runData: IRunData | null = workflowRunData;
|
|
|
|
if (runData === null || !runData[node] || !runData[node][runIndex] ||
|
|
!runData[node][runIndex].data
|
|
) {
|
|
return [];
|
|
}
|
|
|
|
const inputData = this.getMainInputData(runData[node][runIndex].data!, outputIndex);
|
|
|
|
const returnData: IBinaryKeyData[] = [];
|
|
for (let i = 0; i < inputData.length; i++) {
|
|
if (inputData[i].hasOwnProperty('binary') && inputData[i].binary !== undefined) {
|
|
returnData.push(inputData[i].binary!);
|
|
}
|
|
}
|
|
|
|
return returnData;
|
|
},
|
|
|
|
disableNodes(nodes: INodeUi[]) {
|
|
for (const node of nodes) {
|
|
// Toggle disabled flag
|
|
const updateInformation = {
|
|
name: node.name,
|
|
properties: {
|
|
disabled: !node.disabled,
|
|
},
|
|
};
|
|
|
|
this.$store.commit('updateNodeProperties', updateInformation);
|
|
this.$store.commit('clearNodeExecutionData', node.name);
|
|
this.updateNodeParameterIssues(node);
|
|
this.updateNodeCredentialIssues(node);
|
|
}
|
|
},
|
|
// @ts-ignore
|
|
getNodeSubtitle (data, nodeType, workflow): string | undefined {
|
|
if (data.notesInFlow) {
|
|
return data.notes;
|
|
}
|
|
|
|
if (nodeType !== null && nodeType.subtitle !== undefined) {
|
|
return workflow.expression.getSimpleParameterValue(data as INode, nodeType.subtitle, 'internal', PLACEHOLDER_FILLED_AT_EXECUTION_TIME) as string | undefined;
|
|
}
|
|
|
|
if (data.parameters.operation !== undefined) {
|
|
const operation = data.parameters.operation as string;
|
|
if (nodeType === null) {
|
|
return operation;
|
|
}
|
|
|
|
const operationData:INodeProperties = nodeType.properties.find((property: INodeProperties) => {
|
|
return property.name === 'operation';
|
|
});
|
|
if (operationData === undefined) {
|
|
return operation;
|
|
}
|
|
|
|
if (operationData.options === undefined) {
|
|
return operation;
|
|
}
|
|
|
|
const optionData = operationData.options.find((option) => {
|
|
return (option as INodePropertyOptions).value === data.parameters.operation;
|
|
});
|
|
if (optionData === undefined) {
|
|
return operation;
|
|
}
|
|
|
|
return optionData.name;
|
|
}
|
|
return undefined;
|
|
},
|
|
},
|
|
});
|