diff --git a/packages/cli/src/CredentialTypes.ts b/packages/cli/src/CredentialTypes.ts index 7f48050038..eceeb35fed 100644 --- a/packages/cli/src/CredentialTypes.ts +++ b/packages/cli/src/CredentialTypes.ts @@ -3,6 +3,7 @@ import { ICredentialTypeData, ICredentialTypes as ICredentialTypesInterface, } from 'n8n-workflow'; +import { RESPONSE_ERROR_MESSAGES } from './constants'; class CredentialTypesClass implements ICredentialTypesInterface { credentialTypes: ICredentialTypeData = {}; @@ -16,7 +17,11 @@ class CredentialTypesClass implements ICredentialTypesInterface { } getByName(credentialType: string): ICredentialType { - return this.credentialTypes[credentialType].type; + try { + return this.credentialTypes[credentialType].type; + } catch (error) { + throw new Error(`${RESPONSE_ERROR_MESSAGES.NO_CREDENTIAL}: ${credentialType}`); + } } } diff --git a/packages/cli/src/Server.ts b/packages/cli/src/Server.ts index ee99f24715..e478642809 100644 --- a/packages/cli/src/Server.ts +++ b/packages/cli/src/Server.ts @@ -1456,7 +1456,7 @@ class App { if (defaultLocale === 'en') { return nodeInfos.reduce((acc, { name, version }) => { const { description } = NodeTypes().getByNameAndVersion(name, version); - acc.push(description); + acc.push(injectCustomApiCallOption(description)); return acc; }, []); } @@ -1480,7 +1480,7 @@ class App { // ignore - no translation exists at path } - nodeTypes.push(description); + nodeTypes.push(injectCustomApiCallOption(description)); } const nodeTypes: INodeTypeDescription[] = []; @@ -3114,3 +3114,58 @@ async function getExecutionsCount( return { count, estimated: false }; } + +const CUSTOM_API_CALL_NAME = 'Custom API Call'; +const CUSTOM_API_CALL_KEY = '__CUSTOM_API_CALL__'; + +/** + * Inject a `Custom API Call` option into `resource` and `operation` + * parameters in a node that supports proxy auth. + */ +function injectCustomApiCallOption(description: INodeTypeDescription) { + if (!supportsProxyAuth(description)) return description; + + description.properties.forEach((p) => { + if ( + ['resource', 'operation'].includes(p.name) && + Array.isArray(p.options) && + p.options[p.options.length - 1].name !== CUSTOM_API_CALL_NAME + ) { + p.options.push({ + name: CUSTOM_API_CALL_NAME, + value: CUSTOM_API_CALL_KEY, + }); + } + + return p; + }); + + return description; +} + +const credentialTypes = CredentialTypes(); + +/** + * Whether any of the node's credential types may be used to + * make a request from a node other than itself. + */ +function supportsProxyAuth(description: INodeTypeDescription) { + if (!description.credentials) return false; + + return description.credentials.some(({ name }) => { + const credType = credentialTypes.getByName(name); + + if (credType.authenticate !== undefined) return true; + + return isOAuth(credType); + }); +} + +function isOAuth(credType: ICredentialType) { + return ( + Array.isArray(credType.extends) && + credType.extends.some((parentType) => + ['oAuth2Api', 'googleOAuth2Api', 'oAuth1Api'].includes(parentType), + ) + ); +} diff --git a/packages/cli/src/requests.d.ts b/packages/cli/src/requests.d.ts index 276c06346f..e373cb2ad0 100644 --- a/packages/cli/src/requests.d.ts +++ b/packages/cli/src/requests.d.ts @@ -244,9 +244,7 @@ export declare namespace OAuthRequest { namespace OAuth2Credential { type Auth = OAuth1Credential.Auth; - type Callback = AuthenticatedRequest<{}, {}, {}, { code: string; state: string }> & { - user?: User; - }; + type Callback = AuthenticatedRequest<{}, {}, {}, { code: string; state: string }>; } } diff --git a/packages/design-system/src/components/N8nNotice/Notice.vue b/packages/design-system/src/components/N8nNotice/Notice.vue index bdd7bcec24..55571d3ec5 100644 --- a/packages/design-system/src/components/N8nNotice/Notice.vue +++ b/packages/design-system/src/components/N8nNotice/Notice.vue @@ -1,24 +1,14 @@