🐛 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,
ICredentialsHelper,
INodeParameters,
INodeProperties,
NodeHelpers,
} 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
*
@ -71,15 +104,10 @@ export class CredentialsHelper extends ICredentialsHelper {
* @memberof CredentialsHelper
*/
applyDefaultsAndOverwrites(decryptedDataOriginal: ICredentialDataDecryptedObject, type: string): ICredentialDataDecryptedObject {
const credentialTypes = CredentialTypes();
const credentialType = credentialTypes.getByName(type);
if (credentialType === undefined) {
throw new Error(`The credential type "${type}" is not known and can so not be decrypted.`);
}
const credentialsProperties = this.getCredentialsProperties(type);
// 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) {
// The OAuth data gets removed as it is not defined specifically as a parameter

View file

@ -1,5 +1,7 @@
import {
CredentialTypes,
Db,
ICredentialsTypeData,
ITransferNodeTypes,
IWorkflowExecutionDataProcess,
IWorkflowErrorData,
@ -15,6 +17,7 @@ import {
IRun,
IRunExecutionData,
ITaskData,
IWorkflowCredentials,
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
* to execute the gives nodes

View file

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

View file

@ -37,6 +37,7 @@ import {
} from '@/Interface';
import {
NodeHelpers,
ICredentialType,
INodeProperties,
} from 'n8n-workflow';
@ -172,20 +173,6 @@ export default mixins(
},
},
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[] {
const credentialsData = this.$store.getters.credentialType(name);
@ -200,11 +187,11 @@ export default mixins(
const combineProperties = [] as INodeProperties[];
for (const credentialsTypeName of credentialsData.extends) {
const mergeCredentialProperties = this.getCredentialProperties(credentialsTypeName);
this.mergeCredentialProperties(combineProperties, mergeCredentialProperties);
NodeHelpers.mergeNodeProperties(combineProperties, mergeCredentialProperties);
}
// The properties defined on the parent credentials take presidence
this.mergeCredentialProperties(combineProperties, credentialsData.properties);
NodeHelpers.mergeNodeProperties(combineProperties, credentialsData.properties);
return combineProperties;
},
@ -215,10 +202,6 @@ export default mixins(
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
// extends and add them.
credentialData = JSON.parse(JSON.stringify(credentialData));

View file

@ -1093,3 +1093,27 @@ export function mergeIssues(destination: INodeIssues, source: INodeIssues | null
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;
}
}
}