From 37f787d7b28d91794b14063c7010b568f2240547 Mon Sep 17 00:00:00 2001 From: Omar Ajoue Date: Thu, 22 Oct 2020 12:24:35 -0300 Subject: [PATCH] :sparkles: Load node properties on demand (#1089) * :zap: Changed rest api endpoint to omit node properties by default. :zap: Created another endpoint to return all node information based on a list of names * refactor: changed endpoint to POST instead of GET * Changed properties to be optional for node description * Removed eager loading for node properties. All nodes will have their properties fetched when used in a workflow. Works when opening an already saved workflow, creating a new one from scratch or pasting JSON / URLs. * Removing unnecessary dependency --- packages/cli/src/Server.ts | 18 +++++++++++++++++- packages/editor-ui/src/Interface.ts | 1 + .../src/components/mixins/restApi.ts | 4 ++++ packages/editor-ui/src/store.ts | 8 ++++++++ packages/editor-ui/src/views/NodeView.vue | 19 +++++++++++++++++++ packages/workflow/src/Workflow.ts | 2 +- 6 files changed, 50 insertions(+), 2 deletions(-) diff --git a/packages/cli/src/Server.ts b/packages/cli/src/Server.ts index 80292da24b..7df867a034 100644 --- a/packages/cli/src/Server.ts +++ b/packages/cli/src/Server.ts @@ -715,13 +715,29 @@ class App { const allNodes = nodeTypes.getAll(); allNodes.forEach((nodeData) => { - returnData.push(nodeData.description); + // Make a copy of the object. If we don't do this, then when + // The method below is called the properties are removed for good + // This happens because nodes are returned as reference. + let nodeInfo: INodeTypeDescription = {...nodeData.description}; + if (!['true', '1'].includes(req.query.includeProperties as string)) { + delete nodeInfo.properties; + } + returnData.push(nodeInfo); }); return returnData; })); + // Returns node information baesd on namese + this.app.post(`/${this.restEndpoint}/node-types`, ResponseHelper.send(async (req: express.Request, res: express.Response): Promise => { + const nodeNames = _.get(req, 'body.nodeNames', []); + const nodeTypes = NodeTypes(); + const allNodes = nodeTypes.getAll(); + return allNodes.filter(node => nodeNames.includes(node.description.name)).map(node => node.description); + })); + + // ---------------------------------------- // Node-Types diff --git a/packages/editor-ui/src/Interface.ts b/packages/editor-ui/src/Interface.ts index bdb3fe0fa5..5e28745774 100644 --- a/packages/editor-ui/src/Interface.ts +++ b/packages/editor-ui/src/Interface.ts @@ -126,6 +126,7 @@ export interface IRestApi { makeRestApiRequest(method: string, endpoint: string, data?: any): Promise; // tslint:disable-line:no-any getSettings(): Promise; getNodeTypes(): Promise; + getNodesInformation(nodeList: string[]): Promise; getNodeParameterOptions(nodeType: string, methodName: string, currentNodeParameters: INodeParameters, credentials?: INodeCredentials): Promise; removeTestWebhook(workflowId: string): Promise; runWorkflow(runData: IStartRunData): Promise; diff --git a/packages/editor-ui/src/components/mixins/restApi.ts b/packages/editor-ui/src/components/mixins/restApi.ts index bca3fff338..8b1b6f2567 100644 --- a/packages/editor-ui/src/components/mixins/restApi.ts +++ b/packages/editor-ui/src/components/mixins/restApi.ts @@ -152,6 +152,10 @@ export const restApi = Vue.extend({ return self.restApi().makeRestApiRequest('GET', `/node-types`); }, + getNodesInformation: (nodeList: string[]): Promise => { + return self.restApi().makeRestApiRequest('POST', `/node-types`, {nodeNames: nodeList}); + }, + // Returns all the parameter options from the server getNodeParameterOptions: (nodeType: string, methodName: string, currentNodeParameters: INodeParameters, credentials?: INodeCredentials): Promise => { const sendData = { diff --git a/packages/editor-ui/src/store.ts b/packages/editor-ui/src/store.ts index 8aabbdee02..ba5ca7265c 100644 --- a/packages/editor-ui/src/store.ts +++ b/packages/editor-ui/src/store.ts @@ -562,6 +562,14 @@ export const store = new Vuex.Store({ Vue.set(state.workflow, 'settings', {}); } }, + + updateNodeTypes (state, nodeTypes: INodeTypeDescription[]) { + const updatedNodeNames = nodeTypes.map(node => node.name) as string[]; + const oldNodesNotChanged = state.nodeTypes.filter(node => !updatedNodeNames.includes(node.name)); + const updatedNodes = [...oldNodesNotChanged, ...nodeTypes]; + Vue.set(state, 'nodeTypes', updatedNodes); + state.nodeTypes = updatedNodes; + }, }, getters: { diff --git a/packages/editor-ui/src/views/NodeView.vue b/packages/editor-ui/src/views/NodeView.vue index c0e6094ba5..bdd61e17ca 100644 --- a/packages/editor-ui/src/views/NodeView.vue +++ b/packages/editor-ui/src/views/NodeView.vue @@ -1563,6 +1563,11 @@ export default mixins( return; } + // Before proceeding we must check if all nodes contain the `properties` attribute. + // Nodes are loaded without this information so we must make sure that all nodes + // being added have this information. + await this.loadNodesProperties(nodes.map(node => node.type)); + // Add the node to the node-list let nodeType: INodeTypeDescription | null; let foundNodeIssues: INodeIssues | null; @@ -1673,6 +1678,9 @@ export default mixins( let oldName: string; let newName: string; const createNodes: INode[] = []; + + await this.loadNodesProperties(data.nodes.map(node => node.type)); + data.nodes.forEach(node => { if (nodeTypesCount[node.type] !== undefined) { if (nodeTypesCount[node.type].exist >= nodeTypesCount[node.type].max) { @@ -1896,6 +1904,17 @@ export default mixins( const credentials = await this.restApi().getAllCredentials(); this.$store.commit('setCredentials', credentials); }, + async loadNodesProperties(nodeNames: string[]): Promise { + const allNodes = this.$store.getters.allNodeTypes; + const nodesToBeFetched = allNodes.filter((node: INodeTypeDescription) => nodeNames.includes(node.name) && !node.hasOwnProperty('properties')).map((node: INodeTypeDescription) => node.name) as string[]; + if (nodesToBeFetched.length > 0) { + // Only call API if node information is actually missing + this.startLoading(); + const nodeInfo = await this.restApi().getNodesInformation(nodesToBeFetched); + this.$store.commit('updateNodeTypes', nodeInfo); + this.stopLoading(); + } + }, }, async mounted () { diff --git a/packages/workflow/src/Workflow.ts b/packages/workflow/src/Workflow.ts index fb91226b52..6386786a80 100644 --- a/packages/workflow/src/Workflow.ts +++ b/packages/workflow/src/Workflow.ts @@ -215,7 +215,7 @@ export class Workflow { typeUnknown: true, }; } else { - nodeIssues = NodeHelpers.getNodeParametersIssues(nodeType.description.properties, node); + nodeIssues = NodeHelpers.getNodeParametersIssues(nodeType.description.properties!, node); } if (nodeIssues !== null) {