diff --git a/packages/cli/package.json b/packages/cli/package.json index ddeb6d019a..acec654b20 100644 --- a/packages/cli/package.json +++ b/packages/cli/package.json @@ -84,6 +84,7 @@ "@types/lodash.set": "^4.3.6", "@types/lodash.split": "^4.4.7", "@types/lodash.unionby": "^4.8.7", + "@types/lodash.uniq": "^4.5.7", "@types/lodash.uniqby": "^4.7.7", "@types/lodash.unset": "^4.5.7", "@types/parseurl": "^1.3.1", @@ -156,6 +157,7 @@ "lodash.set": "^4.3.2", "lodash.split": "^4.4.2", "lodash.unionby": "^4.8.0", + "lodash.uniq": "^4.5.0", "lodash.uniqby": "^4.7.0", "lodash.unset": "^4.5.2", "luxon": "^3.1.0", diff --git a/packages/cli/src/CredentialTypes.ts b/packages/cli/src/CredentialTypes.ts index 47c02f9a62..2b0c331b82 100644 --- a/packages/cli/src/CredentialTypes.ts +++ b/packages/cli/src/CredentialTypes.ts @@ -8,7 +8,9 @@ import type { import { RESPONSE_ERROR_MESSAGES } from './constants'; class CredentialTypesClass implements ICredentialTypes { - constructor(private nodesAndCredentials: INodesAndCredentials) {} + constructor(private nodesAndCredentials: INodesAndCredentials) { + nodesAndCredentials.credentialTypes = this; + } recognizes(type: string) { return type in this.knownCredentials || type in this.loadedCredentials; @@ -22,6 +24,22 @@ class CredentialTypesClass implements ICredentialTypes { return this.knownCredentials[type]?.nodesToTestWith ?? []; } + /** + * Returns all parent types of the given credential type + */ + getParentTypes(typeName: string): string[] { + const credentialType = this.getByName(typeName); + if (credentialType?.extends === undefined) return []; + + const types: string[] = []; + credentialType.extends.forEach((type: string) => { + types.push(type); + types.push(...this.getParentTypes(type)); + }); + + return types; + } + private getCredential(type: string): LoadedClass { const loadedCredentials = this.loadedCredentials; if (type in loadedCredentials) { diff --git a/packages/cli/src/CredentialsHelper.ts b/packages/cli/src/CredentialsHelper.ts index d7a8144e31..0771889b85 100644 --- a/packages/cli/src/CredentialsHelper.ts +++ b/packages/cli/src/CredentialsHelper.ts @@ -247,18 +247,7 @@ export class CredentialsHelper extends ICredentialsHelper { * Returns all parent types of the given credential type */ getParentTypes(typeName: string): string[] { - const credentialType = this.credentialTypes.getByName(typeName); - - if (credentialType === undefined || credentialType.extends === undefined) { - return []; - } - - let types: string[] = []; - credentialType.extends.forEach((type: string) => { - types = [...types, type, ...this.getParentTypes(type)]; - }); - - return types; + return this.credentialTypes.getParentTypes(typeName); } /** diff --git a/packages/cli/src/CredentialsOverwrites.ts b/packages/cli/src/CredentialsOverwrites.ts index e19dc5e18a..414f3eb349 100644 --- a/packages/cli/src/CredentialsOverwrites.ts +++ b/packages/cli/src/CredentialsOverwrites.ts @@ -86,8 +86,13 @@ class CredentialsOverwritesClass { return overwrites; } - private get(type: string): ICredentialDataDecryptedObject | undefined { - return this.overwriteData[type]; + private get(name: string): ICredentialDataDecryptedObject | undefined { + const parentTypes = this.credentialTypes.getParentTypes(name); + return [name, ...parentTypes] + .reverse() + .map((type) => this.overwriteData[type]) + .filter((type) => !!type) + .reduce((acc, current) => Object.assign(acc, current), {}); } getAll(): ICredentialsOverwrite { diff --git a/packages/cli/src/LoadNodesAndCredentials.ts b/packages/cli/src/LoadNodesAndCredentials.ts index bb612dd7f5..bcdbdab6a2 100644 --- a/packages/cli/src/LoadNodesAndCredentials.ts +++ b/packages/cli/src/LoadNodesAndCredentials.ts @@ -1,3 +1,4 @@ +import uniq from 'lodash.uniq'; import { CUSTOM_EXTENSION_ENV, UserSettings, @@ -8,6 +9,7 @@ import { Types, } from 'n8n-core'; import type { + ICredentialTypes, ILogger, INodesAndCredentials, KnownNodesAndCredentials, @@ -46,6 +48,8 @@ export class LoadNodesAndCredentialsClass implements INodesAndCredentials { includeNodes = config.getEnv('nodes.include'); + credentialTypes: ICredentialTypes; + logger: ILogger; async init() { @@ -68,8 +72,21 @@ export class LoadNodesAndCredentialsClass implements INodesAndCredentials { async generateTypesForFrontend() { const credentialsOverwrites = CredentialsOverwrites().getAll(); for (const credential of this.types.credentials) { + const overwrittenProperties = []; + this.credentialTypes + .getParentTypes(credential.name) + .reverse() + .map((name) => credentialsOverwrites[name]) + .forEach((overwrite) => { + if (overwrite) overwrittenProperties.push(...Object.keys(overwrite)); + }); + if (credential.name in credentialsOverwrites) { - credential.__overwrittenProperties = Object.keys(credentialsOverwrites[credential.name]); + overwrittenProperties.push(...Object.keys(credentialsOverwrites[credential.name])); + } + + if (overwrittenProperties.length) { + credential.__overwrittenProperties = uniq(overwrittenProperties); } } diff --git a/packages/cli/test/integration/shared/utils.ts b/packages/cli/test/integration/shared/utils.ts index e0cffaa29f..3c27abce8f 100644 --- a/packages/cli/test/integration/shared/utils.ts +++ b/packages/cli/test/integration/shared/utils.ts @@ -8,6 +8,7 @@ import set from 'lodash.set'; import { BinaryDataManager, UserSettings } from 'n8n-core'; import { ICredentialType, + ICredentialTypes, IDataObject, IExecuteFunctions, INode, @@ -71,6 +72,7 @@ import { eventBusRouter } from '@/eventbus/eventBusRoutes'; const loadNodesAndCredentials: INodesAndCredentials = { loaded: { nodes: {}, credentials: {} }, known: { nodes: {}, credentials: {} }, + credentialTypes: {} as ICredentialTypes, }; const mockNodeTypes = NodeTypes(loadNodesAndCredentials); @@ -160,7 +162,7 @@ export async function initTestServer({ * Pre-requisite: Mock the telemetry module before calling. */ export function initTestTelemetry() { - void InternalHooksManager.init('test-instance-id', 'test-version', mockNodeTypes); + void InternalHooksManager.init('test-instance-id', mockNodeTypes); } /** diff --git a/packages/cli/test/unit/CredentialTypes.test.ts b/packages/cli/test/unit/CredentialTypes.test.ts index 06bc84a15d..35eb0c321c 100644 --- a/packages/cli/test/unit/CredentialTypes.test.ts +++ b/packages/cli/test/unit/CredentialTypes.test.ts @@ -43,4 +43,5 @@ const mockNodesAndCredentials = (): INodesAndCredentials => ({ }, }, known: { nodes: {}, credentials: {} }, + credentialTypes: {} as ICredentialTypes, }); diff --git a/packages/cli/test/unit/CredentialsHelper.test.ts b/packages/cli/test/unit/CredentialsHelper.test.ts index f3fc8043dd..50f191deb7 100644 --- a/packages/cli/test/unit/CredentialsHelper.test.ts +++ b/packages/cli/test/unit/CredentialsHelper.test.ts @@ -2,6 +2,7 @@ import { IAuthenticateGeneric, ICredentialDataDecryptedObject, ICredentialType, + ICredentialTypes, IHttpRequestOptions, INode, INodeProperties, @@ -16,6 +17,7 @@ const TEST_ENCRYPTION_KEY = 'test'; const mockNodesAndCredentials: INodesAndCredentials = { loaded: { nodes: {}, credentials: {} }, known: { nodes: {}, credentials: {} }, + credentialTypes: {} as ICredentialTypes, }; describe('CredentialsHelper', () => { diff --git a/packages/cli/test/unit/PermissionChecker.test.ts b/packages/cli/test/unit/PermissionChecker.test.ts index e5a22dece9..56a1c23757 100644 --- a/packages/cli/test/unit/PermissionChecker.test.ts +++ b/packages/cli/test/unit/PermissionChecker.test.ts @@ -1,5 +1,11 @@ import { v4 as uuid } from 'uuid'; -import { INodeTypeData, INodeTypes, SubworkflowOperationError, Workflow } from 'n8n-workflow'; +import { + ICredentialTypes, + INodeTypeData, + INodeTypes, + SubworkflowOperationError, + Workflow, +} from 'n8n-workflow'; import config from '@/config'; import * as Db from '@/Db'; @@ -35,6 +41,7 @@ beforeAll(async () => { credentials: {}, }, known: { nodes: {}, credentials: {} }, + credentialTypes: {} as ICredentialTypes, }); credentialOwnerRole = await testDb.getCredentialOwnerRole(); diff --git a/packages/workflow/src/Interfaces.ts b/packages/workflow/src/Interfaces.ts index b0f06bdfda..c09e689a7a 100644 --- a/packages/workflow/src/Interfaces.ts +++ b/packages/workflow/src/Interfaces.ts @@ -336,6 +336,7 @@ export interface ICredentialTypes { recognizes(credentialType: string): boolean; getByName(credentialType: string): ICredentialType; getNodeTypesToTestWith(type: string): string[]; + getParentTypes(typeName: string): string[]; } // The way the credentials get saved in the database (data encrypted) @@ -1492,6 +1493,7 @@ export type LoadedNodesAndCredentials = { export interface INodesAndCredentials { known: KnownNodesAndCredentials; loaded: LoadedNodesAndCredentials; + credentialTypes: ICredentialTypes; } export interface IRun { diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index d49254175b..bee31df85e 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -122,6 +122,7 @@ importers: '@types/lodash.set': ^4.3.6 '@types/lodash.split': ^4.4.7 '@types/lodash.unionby': ^4.8.7 + '@types/lodash.uniq': ^4.5.7 '@types/lodash.uniqby': ^4.7.7 '@types/lodash.unset': ^4.5.7 '@types/parseurl': ^1.3.1 @@ -179,6 +180,7 @@ importers: lodash.set: ^4.3.2 lodash.split: ^4.4.2 lodash.unionby: ^4.8.0 + lodash.uniq: ^4.5.0 lodash.uniqby: ^4.7.0 lodash.unset: ^4.5.2 luxon: ^3.1.0 @@ -271,6 +273,7 @@ importers: lodash.set: 4.3.2 lodash.split: 4.4.2 lodash.unionby: 4.8.0 + lodash.uniq: 4.5.0 lodash.uniqby: 4.7.0 lodash.unset: 4.5.2 luxon: 3.1.1 @@ -332,6 +335,7 @@ importers: '@types/lodash.set': 4.3.7 '@types/lodash.split': 4.4.7 '@types/lodash.unionby': 4.8.7 + '@types/lodash.uniq': 4.5.7 '@types/lodash.uniqby': 4.7.7 '@types/lodash.unset': 4.5.7 '@types/parseurl': 1.3.1 @@ -5929,6 +5933,12 @@ packages: '@types/lodash': 4.14.186 dev: true + /@types/lodash.uniq/4.5.7: + resolution: {integrity: sha512-qg7DeAbdZMi6DGvCxThlJycykLLhETrJrQZ6F2KaZ+o0sNK1qRHz46lgNA+nHHjwrmA2a91DyiZTp3ey3m1rEw==} + dependencies: + '@types/lodash': 4.14.186 + dev: true + /@types/lodash.uniqby/4.7.7: resolution: {integrity: sha512-sv2g6vkCIvEUsK5/Vq17haoZaisfj2EWW8mP7QWlnKi6dByoNmeuHDDXHR7sabuDqwO4gvU7ModIL22MmnOocg==} dependencies: