🐛 Fix issue with credentials which extend others

This commit is contained in:
Jan Oberhauser 2020-05-16 19:05:40 +02:00
parent 97cf7da6c3
commit 95097a8bd7
5 changed files with 131 additions and 36 deletions

View file

@ -6,6 +6,7 @@ import {
ICredentialDataDecryptedObject, ICredentialDataDecryptedObject,
ICredentialsHelper, ICredentialsHelper,
INodeParameters, INodeParameters,
INodeProperties,
NodeHelpers, NodeHelpers,
} from 'n8n-workflow'; } from 'n8n-workflow';
@ -40,6 +41,38 @@ export class CredentialsHelper extends ICredentialsHelper {
} }
/**
* Returns all the properties of the credentials with the given name
*
* @param {string} type The name of the type to return credentials off
* @returns {INodeProperties[]}
* @memberof CredentialsHelper
*/
getCredentialsProperties(type: string): INodeProperties[] {
const credentialTypes = CredentialTypes();
const credentialTypeData = credentialTypes.getByName(type);
if (credentialTypeData === undefined) {
throw new Error(`The credentials of type "${type}" are not known.`);
}
if (credentialTypeData.extends === undefined) {
return credentialTypeData.properties;
}
const combineProperties = [] as INodeProperties[];
for (const credentialsTypeName of credentialTypeData.extends) {
const mergeCredentialProperties = this.getCredentialsProperties(credentialsTypeName);
NodeHelpers.mergeNodeProperties(combineProperties, mergeCredentialProperties);
}
// The properties defined on the parent credentials take presidence
NodeHelpers.mergeNodeProperties(combineProperties, credentialTypeData.properties);
return combineProperties;
}
/** /**
* Returns the decrypted credential data with applied overwrites * Returns the decrypted credential data with applied overwrites
* *
@ -71,15 +104,10 @@ export class CredentialsHelper extends ICredentialsHelper {
* @memberof CredentialsHelper * @memberof CredentialsHelper
*/ */
applyDefaultsAndOverwrites(decryptedDataOriginal: ICredentialDataDecryptedObject, type: string): ICredentialDataDecryptedObject { applyDefaultsAndOverwrites(decryptedDataOriginal: ICredentialDataDecryptedObject, type: string): ICredentialDataDecryptedObject {
const credentialTypes = CredentialTypes(); const credentialsProperties = this.getCredentialsProperties(type);
const credentialType = credentialTypes.getByName(type);
if (credentialType === undefined) {
throw new Error(`The credential type "${type}" is not known and can so not be decrypted.`);
}
// Add the default credential values // Add the default credential values
const decryptedData = NodeHelpers.getNodeParameters(credentialType.properties, decryptedDataOriginal as INodeParameters, true, false) as ICredentialDataDecryptedObject; const decryptedData = NodeHelpers.getNodeParameters(credentialsProperties, decryptedDataOriginal as INodeParameters, true, false) as ICredentialDataDecryptedObject;
if (decryptedDataOriginal.oauthTokenData !== undefined) { if (decryptedDataOriginal.oauthTokenData !== undefined) {
// The OAuth data gets removed as it is not defined specifically as a parameter // The OAuth data gets removed as it is not defined specifically as a parameter

View file

@ -1,5 +1,7 @@
import { import {
CredentialTypes,
Db, Db,
ICredentialsTypeData,
ITransferNodeTypes, ITransferNodeTypes,
IWorkflowExecutionDataProcess, IWorkflowExecutionDataProcess,
IWorkflowErrorData, IWorkflowErrorData,
@ -15,6 +17,7 @@ import {
IRun, IRun,
IRunExecutionData, IRunExecutionData,
ITaskData, ITaskData,
IWorkflowCredentials,
Workflow, Workflow,
} from 'n8n-workflow'; } from 'n8n-workflow';
@ -217,6 +220,63 @@ export function getNodeTypeData(nodes: INode[]): ITransferNodeTypes {
/**
* Returns the credentials data of the given type and its parent types
* it extends
*
* @export
* @param {string} type The credential type to return data off
* @returns {ICredentialsTypeData}
*/
export function getCredentialsDataWithParents(type: string): ICredentialsTypeData {
const credentialTypes = CredentialTypes();
const credentialType = credentialTypes.getByName(type);
const credentialTypeData: ICredentialsTypeData = {};
credentialTypeData[type] = credentialType;
if (credentialType === undefined || credentialType.extends === undefined) {
return credentialTypeData;
}
for (const typeName of credentialType.extends) {
if (credentialTypeData[typeName] !== undefined) {
continue;
}
credentialTypeData[typeName] = credentialTypes.getByName(typeName);
Object.assign(credentialTypeData, getCredentialsDataWithParents(typeName));
}
return credentialTypeData;
}
/**
* Returns all the credentialTypes which are needed to resolve
* the given workflow credentials
*
* @export
* @param {IWorkflowCredentials} credentials The credentials which have to be able to be resolved
* @returns {ICredentialsTypeData}
*/
export function getCredentialsData(credentials: IWorkflowCredentials): ICredentialsTypeData {
const credentialTypeData: ICredentialsTypeData = {};
for (const credentialType of Object.keys(credentials)) {
if (credentialTypeData[credentialType] !== undefined) {
continue;
}
Object.assign(credentialTypeData, getCredentialsDataWithParents(credentialType));
}
return credentialTypeData;
}
/** /**
* Returns the names of the NodeTypes which are are needed * Returns the names of the NodeTypes which are are needed
* to execute the gives nodes * to execute the gives nodes

View file

@ -179,8 +179,8 @@ export class WorkflowRunner {
const executionId = this.activeExecutions.add(data, subprocess); const executionId = this.activeExecutions.add(data, subprocess);
// Check if workflow contains a "executeWorkflow" Node as in this // Check if workflow contains a "executeWorkflow" Node as in this
// case we can not know which nodeTypes will be needed and so have // case we can not know which nodeTypes and credentialTypes will
// to load all of them in the workflowRunnerProcess // be needed and so have to load all of them in the workflowRunnerProcess
let loadAllNodeTypes = false; let loadAllNodeTypes = false;
for (const node of data.workflowData.nodes) { for (const node of data.workflowData.nodes) {
if (node.type === 'n8n-nodes-base.executeWorkflow') { if (node.type === 'n8n-nodes-base.executeWorkflow') {
@ -190,19 +190,19 @@ export class WorkflowRunner {
} }
let nodeTypeData: ITransferNodeTypes; let nodeTypeData: ITransferNodeTypes;
let credentialTypeData: ICredentialsTypeData;
if (loadAllNodeTypes === true) { if (loadAllNodeTypes === true) {
// Supply all nodeTypes // Supply all nodeTypes and credentialTypes
nodeTypeData = WorkflowHelpers.getAllNodeTypeData(); nodeTypeData = WorkflowHelpers.getAllNodeTypeData();
const credentialTypes = CredentialTypes();
credentialTypeData = credentialTypes.credentialTypes;
} else { } else {
// Supply only nodeTypes which the workflow needs // Supply only nodeTypes and credentialTypes which the workflow needs
nodeTypeData = WorkflowHelpers.getNodeTypeData(data.workflowData.nodes); nodeTypeData = WorkflowHelpers.getNodeTypeData(data.workflowData.nodes);
credentialTypeData = WorkflowHelpers.getCredentialsData(data.credentials);
} }
const credentialTypes = CredentialTypes();
const credentialTypeData: ICredentialsTypeData = {};
for (const credentialType of Object.keys(data.credentials)) {
credentialTypeData[credentialType] = credentialTypes.getByName(credentialType);
}
(data as unknown as IWorkflowExecutionDataProcessWithExecution).executionId = executionId; (data as unknown as IWorkflowExecutionDataProcessWithExecution).executionId = executionId;
(data as unknown as IWorkflowExecutionDataProcessWithExecution).nodeTypeData = nodeTypeData; (data as unknown as IWorkflowExecutionDataProcessWithExecution).nodeTypeData = nodeTypeData;

View file

@ -37,6 +37,7 @@ import {
} from '@/Interface'; } from '@/Interface';
import { import {
NodeHelpers,
ICredentialType, ICredentialType,
INodeProperties, INodeProperties,
} from 'n8n-workflow'; } from 'n8n-workflow';
@ -172,20 +173,6 @@ export default mixins(
}, },
}, },
methods: { methods: {
mergeCredentialProperties (mainProperties: INodeProperties[], addProperties: INodeProperties[]): void {
let existingIndex: number;
for (const property of addProperties) {
existingIndex = mainProperties.findIndex(element => element.name === property.name);
if (existingIndex === -1) {
// Property does not exist yet, so add
mainProperties.push(property);
} else {
// Property exists already, so overwrite
mainProperties[existingIndex] = property;
}
}
},
getCredentialProperties (name: string): INodeProperties[] { getCredentialProperties (name: string): INodeProperties[] {
const credentialsData = this.$store.getters.credentialType(name); const credentialsData = this.$store.getters.credentialType(name);
@ -200,11 +187,11 @@ export default mixins(
const combineProperties = [] as INodeProperties[]; const combineProperties = [] as INodeProperties[];
for (const credentialsTypeName of credentialsData.extends) { for (const credentialsTypeName of credentialsData.extends) {
const mergeCredentialProperties = this.getCredentialProperties(credentialsTypeName); const mergeCredentialProperties = this.getCredentialProperties(credentialsTypeName);
this.mergeCredentialProperties(combineProperties, mergeCredentialProperties); NodeHelpers.mergeNodeProperties(combineProperties, mergeCredentialProperties);
} }
// The properties defined on the parent credentials take presidence // The properties defined on the parent credentials take presidence
this.mergeCredentialProperties(combineProperties, credentialsData.properties); NodeHelpers.mergeNodeProperties(combineProperties, credentialsData.properties);
return combineProperties; return combineProperties;
}, },
@ -215,10 +202,6 @@ export default mixins(
return credentialData; return credentialData;
} }
// TODO: The credential-extend-resolve-logic is currently not needed in the backend
// as the whole credential data gets saved with the defaults. That logic should,
// however, probably also get improved in the future.
// Credentials extends another one. So get the properties of the one it // Credentials extends another one. So get the properties of the one it
// extends and add them. // extends and add them.
credentialData = JSON.parse(JSON.stringify(credentialData)); credentialData = JSON.parse(JSON.stringify(credentialData));

View file

@ -1093,3 +1093,27 @@ export function mergeIssues(destination: INodeIssues, source: INodeIssues | null
destination.typeUnknown = true; destination.typeUnknown = true;
} }
} }
/**
* Merges the given node properties
*
* @export
* @param {INodeProperties[]} mainProperties
* @param {INodeProperties[]} addProperties
*/
export function mergeNodeProperties(mainProperties: INodeProperties[], addProperties: INodeProperties[]): void {
let existingIndex: number;
for (const property of addProperties) {
existingIndex = mainProperties.findIndex(element => element.name === property.name);
if (existingIndex === -1) {
// Property does not exist yet, so add
mainProperties.push(property);
} else {
// Property exists already, so overwrite
mainProperties[existingIndex] = property;
}
}
}