fix(editor): Fix issue that frontend breaks with unkown nodes (#7596)

Because of recent changes does the frontend break when a node is not
known.

![Screenshot from 2023-11-03
08-27-43](https://github.com/n8n-io/n8n/assets/6249596/ab340c3c-9b72-4220-b1f8-70d80f7a5522)



Github issue / Community forum post (link here to close automatically):

---------

Signed-off-by: Oleg Ivaniv <me@olegivaniv.com>
Co-authored-by: Oleg Ivaniv <me@olegivaniv.com>
This commit is contained in:
Jan Oberhauser 2023-11-06 13:44:05 +01:00 committed by GitHub
parent a994ba5e8d
commit db56a9ee37
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
3 changed files with 153 additions and 25 deletions

View file

@ -370,4 +370,40 @@ describe('Canvas Node Manipulation and Navigation', () => {
NDVDialog.actions.close();
});
});
it('should render connections correctly if unkown nodes are present', () => {
const unknownNodeName = 'Unknown node';
cy.createFixtureWorkflow('workflow-with-unknown-nodes.json', 'Unknown nodes');
WorkflowPage.getters.canvasNodeByName(`${unknownNodeName} 1`).should('exist');
WorkflowPage.getters.canvasNodeByName(`${unknownNodeName} 2`).should('exist');
WorkflowPage.actions.zoomToFit();
cy.draganddrop(
WorkflowPage.getters.getEndpointSelector('plus', `${unknownNodeName} 1`),
WorkflowPage.getters.getEndpointSelector('input', EDIT_FIELDS_SET_NODE_NAME),
);
cy.draganddrop(
WorkflowPage.getters.getEndpointSelector('plus', `${unknownNodeName} 2`),
WorkflowPage.getters.getEndpointSelector('input', `${EDIT_FIELDS_SET_NODE_NAME}1`),
);
WorkflowPage.actions.executeWorkflow();
cy.contains('Node not found').should('be.visible');
WorkflowPage.getters
.canvasNodeByName(`${unknownNodeName} 1`)
.find('[data-test-id=delete-node-button]')
.click({ force: true });
WorkflowPage.getters
.canvasNodeByName(`${unknownNodeName} 2`)
.find('[data-test-id=delete-node-button]')
.click({ force: true });
WorkflowPage.actions.executeWorkflow();
cy.contains('Node not found').should('not.exist');
});
});

View file

@ -0,0 +1,90 @@
{
"meta": {
"instanceId": "15bbf37b6a515ccc2f534cabcd8bd171ca33583ff7744b1e9420e5ce68e615bb"
},
"nodes": [
{
"parameters": {},
"id": "40720511-19b6-4421-bdb0-3fb6efef4bc5",
"name": "When clicking \"Execute Workflow\"",
"type": "n8n-nodes-base.manualTrigger",
"typeVersion": 1,
"position": [
280,
320
]
},
{
"parameters": {},
"id": "acdd1bdc-c642-4ea6-ad67-f4201b640cfa",
"name": "Unknown node 1",
"type": "n8n-nodes-base.thisNodeDoesntExist",
"typeVersion": 1,
"position": [
400,
500
]
},
{
"parameters": {},
"id": "acdd1bdc-c642-4ea6-ad67-f4201b640ffa",
"name": "Unknown node 2",
"type": "n8n-nodes-base.thisNodeDoesntExistEither",
"typeVersion": 1,
"position": [
600,
500
]
},
{
"parameters": {
"options": {}
},
"id": "fbe5163b-7474-4741-980a-e4956789be0a",
"name": "Edit Fields",
"type": "n8n-nodes-base.set",
"typeVersion": 3.2,
"position": [
500,
320
]
},
{
"parameters": {
"options": {}
},
"id": "163313b9-64ff-4ffc-b00f-09b267d8132c",
"name": "Edit Fields1",
"type": "n8n-nodes-base.set",
"typeVersion": 3.2,
"position": [
720,
320
]
}
],
"connections": {
"When clicking \"Execute Workflow\"": {
"main": [
[
{
"node": "Edit Fields",
"type": "main",
"index": 0
}
]
]
},
"Edit Fields": {
"main": [
[
{
"node": "Edit Fields1",
"type": "main",
"index": 0
}
]
]
}
}
}

View file

@ -365,37 +365,39 @@ export default defineComponent({
top: this.position[1] + 'px',
};
const workflow = this.workflowsStore.getCurrentWorkflow();
const inputs =
NodeHelpers.getNodeInputs(workflow, this.node, this.nodeType) ||
([] as Array<ConnectionTypes | INodeInputConfiguration>);
const inputTypes = NodeHelpers.getConnectionTypes(inputs);
if (this.node && this.nodeType) {
const workflow = this.workflowsStore.getCurrentWorkflow();
const inputs =
NodeHelpers.getNodeInputs(workflow, this.node, this.nodeType) ||
([] as Array<ConnectionTypes | INodeInputConfiguration>);
const inputTypes = NodeHelpers.getConnectionTypes(inputs);
const nonMainInputs = inputTypes.filter((input) => input !== NodeConnectionType.Main);
if (nonMainInputs.length) {
const requiredNonMainInputs = inputs.filter(
(input) => typeof input !== 'string' && input.required,
);
const nonMainInputs = inputTypes.filter((input) => input !== NodeConnectionType.Main);
if (nonMainInputs.length) {
const requiredNonMainInputs = inputs.filter(
(input) => typeof input !== 'string' && input.required,
);
let spacerCount = 0;
if (NODE_INSERT_SPACER_BETWEEN_INPUT_GROUPS) {
const requiredNonMainInputsCount = requiredNonMainInputs.length;
const optionalNonMainInputsCount = nonMainInputs.length - requiredNonMainInputsCount;
spacerCount = requiredNonMainInputsCount > 0 && optionalNonMainInputsCount > 0 ? 1 : 0;
let spacerCount = 0;
if (NODE_INSERT_SPACER_BETWEEN_INPUT_GROUPS) {
const requiredNonMainInputsCount = requiredNonMainInputs.length;
const optionalNonMainInputsCount = nonMainInputs.length - requiredNonMainInputsCount;
spacerCount = requiredNonMainInputsCount > 0 && optionalNonMainInputsCount > 0 ? 1 : 0;
}
styles['--configurable-node-input-count'] = nonMainInputs.length + spacerCount;
}
styles['--configurable-node-input-count'] = nonMainInputs.length + spacerCount;
const outputs =
NodeHelpers.getNodeOutputs(workflow, this.node, this.nodeType) ||
([] as Array<ConnectionTypes | INodeOutputConfiguration>);
const outputTypes = NodeHelpers.getConnectionTypes(outputs);
const mainOutputs = outputTypes.filter((output) => output === NodeConnectionType.Main);
styles['--node-main-output-count'] = mainOutputs.length;
}
const outputs =
NodeHelpers.getNodeOutputs(workflow, this.node, this.nodeType) ||
([] as Array<ConnectionTypes | INodeOutputConfiguration>);
const outputTypes = NodeHelpers.getConnectionTypes(outputs);
const mainOutputs = outputTypes.filter((output) => output === NodeConnectionType.Main);
styles['--node-main-output-count'] = mainOutputs.length;
return styles;
},
nodeClass(): object {