mirror of
https://github.com/n8n-io/n8n.git
synced 2024-12-25 04:34:06 -08:00
✨ Load node properties on demand (#1089)
* ⚡ Changed rest api endpoint to omit node properties by default. ⚡ 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
This commit is contained in:
parent
40c2acd77b
commit
37f787d7b2
|
@ -715,13 +715,29 @@ class App {
|
||||||
const allNodes = nodeTypes.getAll();
|
const allNodes = nodeTypes.getAll();
|
||||||
|
|
||||||
allNodes.forEach((nodeData) => {
|
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;
|
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<INodeTypeDescription[]> => {
|
||||||
|
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
|
// Node-Types
|
||||||
|
|
|
@ -126,6 +126,7 @@ export interface IRestApi {
|
||||||
makeRestApiRequest(method: string, endpoint: string, data?: any): Promise<any>; // tslint:disable-line:no-any
|
makeRestApiRequest(method: string, endpoint: string, data?: any): Promise<any>; // tslint:disable-line:no-any
|
||||||
getSettings(): Promise<IN8nUISettings>;
|
getSettings(): Promise<IN8nUISettings>;
|
||||||
getNodeTypes(): Promise<INodeTypeDescription[]>;
|
getNodeTypes(): Promise<INodeTypeDescription[]>;
|
||||||
|
getNodesInformation(nodeList: string[]): Promise<INodeTypeDescription[]>;
|
||||||
getNodeParameterOptions(nodeType: string, methodName: string, currentNodeParameters: INodeParameters, credentials?: INodeCredentials): Promise<INodePropertyOptions[]>;
|
getNodeParameterOptions(nodeType: string, methodName: string, currentNodeParameters: INodeParameters, credentials?: INodeCredentials): Promise<INodePropertyOptions[]>;
|
||||||
removeTestWebhook(workflowId: string): Promise<boolean>;
|
removeTestWebhook(workflowId: string): Promise<boolean>;
|
||||||
runWorkflow(runData: IStartRunData): Promise<IExecutionPushResponse>;
|
runWorkflow(runData: IStartRunData): Promise<IExecutionPushResponse>;
|
||||||
|
|
|
@ -152,6 +152,10 @@ export const restApi = Vue.extend({
|
||||||
return self.restApi().makeRestApiRequest('GET', `/node-types`);
|
return self.restApi().makeRestApiRequest('GET', `/node-types`);
|
||||||
},
|
},
|
||||||
|
|
||||||
|
getNodesInformation: (nodeList: string[]): Promise<INodeTypeDescription[]> => {
|
||||||
|
return self.restApi().makeRestApiRequest('POST', `/node-types`, {nodeNames: nodeList});
|
||||||
|
},
|
||||||
|
|
||||||
// Returns all the parameter options from the server
|
// Returns all the parameter options from the server
|
||||||
getNodeParameterOptions: (nodeType: string, methodName: string, currentNodeParameters: INodeParameters, credentials?: INodeCredentials): Promise<INodePropertyOptions[]> => {
|
getNodeParameterOptions: (nodeType: string, methodName: string, currentNodeParameters: INodeParameters, credentials?: INodeCredentials): Promise<INodePropertyOptions[]> => {
|
||||||
const sendData = {
|
const sendData = {
|
||||||
|
|
|
@ -562,6 +562,14 @@ export const store = new Vuex.Store({
|
||||||
Vue.set(state.workflow, 'settings', {});
|
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: {
|
getters: {
|
||||||
|
|
||||||
|
|
|
@ -1563,6 +1563,11 @@ export default mixins(
|
||||||
return;
|
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
|
// Add the node to the node-list
|
||||||
let nodeType: INodeTypeDescription | null;
|
let nodeType: INodeTypeDescription | null;
|
||||||
let foundNodeIssues: INodeIssues | null;
|
let foundNodeIssues: INodeIssues | null;
|
||||||
|
@ -1673,6 +1678,9 @@ export default mixins(
|
||||||
let oldName: string;
|
let oldName: string;
|
||||||
let newName: string;
|
let newName: string;
|
||||||
const createNodes: INode[] = [];
|
const createNodes: INode[] = [];
|
||||||
|
|
||||||
|
await this.loadNodesProperties(data.nodes.map(node => node.type));
|
||||||
|
|
||||||
data.nodes.forEach(node => {
|
data.nodes.forEach(node => {
|
||||||
if (nodeTypesCount[node.type] !== undefined) {
|
if (nodeTypesCount[node.type] !== undefined) {
|
||||||
if (nodeTypesCount[node.type].exist >= nodeTypesCount[node.type].max) {
|
if (nodeTypesCount[node.type].exist >= nodeTypesCount[node.type].max) {
|
||||||
|
@ -1896,6 +1904,17 @@ export default mixins(
|
||||||
const credentials = await this.restApi().getAllCredentials();
|
const credentials = await this.restApi().getAllCredentials();
|
||||||
this.$store.commit('setCredentials', credentials);
|
this.$store.commit('setCredentials', credentials);
|
||||||
},
|
},
|
||||||
|
async loadNodesProperties(nodeNames: string[]): Promise<void> {
|
||||||
|
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 () {
|
async mounted () {
|
||||||
|
|
|
@ -215,7 +215,7 @@ export class Workflow {
|
||||||
typeUnknown: true,
|
typeUnknown: true,
|
||||||
};
|
};
|
||||||
} else {
|
} else {
|
||||||
nodeIssues = NodeHelpers.getNodeParametersIssues(nodeType.description.properties, node);
|
nodeIssues = NodeHelpers.getNodeParametersIssues(nodeType.description.properties!, node);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (nodeIssues !== null) {
|
if (nodeIssues !== null) {
|
||||||
|
|
Loading…
Reference in a new issue