From 76f9ad8baedf23e04a4fcc51936f4d419cd9db75 Mon Sep 17 00:00:00 2001 From: agobrech <45268029+agobrech@users.noreply.github.com> Date: Wed, 24 Aug 2022 10:26:48 +0200 Subject: [PATCH] N8N-4126 credentials injection and testing on specific nodes (#3816) * Add credential injection and testing to Lemlist, Uproc, Supabase, Segment, Phantombuster, Mailgun and Dropcontact --- .../credentials/DropcontactApi.credentials.ts | 26 ++++++++++- .../credentials/LemlistApi.credentials.ts | 23 +++++++++- .../credentials/MailgunApi.credentials.ts | 22 ++++++++- .../PhantombusterApi.credentials.ts | 21 ++++++++- .../credentials/SegmentApi.credentials.ts | 15 ++++++- .../credentials/SupabaseApi.credentials.ts | 25 ++++++++++- .../credentials/UProcApi.credentials.ts | 26 ++++++++++- .../nodes/Dropcontact/Dropcontact.node.ts | 27 +---------- .../nodes/Dropcontact/GenericFunction.ts | 45 +------------------ .../nodes/Lemlist/GenericFunctions.ts | 9 +--- .../nodes-base/nodes/Mailgun/Mailgun.node.ts | 6 +-- .../nodes/Phantombuster/GenericFunctions.ts | 3 +- .../nodes/Segment/GenericFunctions.ts | 8 +--- .../nodes/Supabase/GenericFunctions.ts | 6 +-- .../nodes/UProc/GenericFunctions.ts | 19 +++----- 15 files changed, 167 insertions(+), 114 deletions(-) diff --git a/packages/nodes-base/credentials/DropcontactApi.credentials.ts b/packages/nodes-base/credentials/DropcontactApi.credentials.ts index 584da0347c..7f9307e8a3 100644 --- a/packages/nodes-base/credentials/DropcontactApi.credentials.ts +++ b/packages/nodes-base/credentials/DropcontactApi.credentials.ts @@ -1,4 +1,9 @@ -import { ICredentialType, NodePropertyTypes } from 'n8n-workflow'; +import { + IAuthenticateGeneric, + ICredentialTestRequest, + ICredentialType, + NodePropertyTypes, +} from 'n8n-workflow'; export class DropcontactApi implements ICredentialType { name = 'dropcontactApi'; @@ -12,4 +17,23 @@ export class DropcontactApi implements ICredentialType { default: '', }, ]; + authenticate: IAuthenticateGeneric = { + type: 'generic', + properties: { + headers: { + 'user-agent': 'n8n', + 'X-Access-Token': '={{$credentials.apiKey}}', + }, + }, + }; + test: ICredentialTestRequest = { + request: { + baseURL: 'https://api.dropcontact.io', + url: '/batch', + method: 'POST', + body: { + data: [{ email: '' }], + }, + }, + }; } diff --git a/packages/nodes-base/credentials/LemlistApi.credentials.ts b/packages/nodes-base/credentials/LemlistApi.credentials.ts index 27affe2850..99a5b31ba0 100644 --- a/packages/nodes-base/credentials/LemlistApi.credentials.ts +++ b/packages/nodes-base/credentials/LemlistApi.credentials.ts @@ -1,4 +1,10 @@ -import { ICredentialType, INodeProperties } from 'n8n-workflow'; +import { + ICredentialDataDecryptedObject, + ICredentialTestRequest, + ICredentialType, + IHttpRequestOptions, + INodeProperties, +} from 'n8n-workflow'; export class LemlistApi implements ICredentialType { name = 'lemlistApi'; @@ -12,4 +18,19 @@ export class LemlistApi implements ICredentialType { default: '', }, ]; + async authenticate( + credentials: ICredentialDataDecryptedObject, + requestOptions: IHttpRequestOptions, + ): Promise { + const encodedApiKey = Buffer.from(':' + credentials.apiKey).toString('base64'); + requestOptions.headers!['Authorization'] = `Basic ${encodedApiKey}`; + requestOptions.headers!['user-agent'] = 'n8n'; + return requestOptions; + } + test: ICredentialTestRequest = { + request: { + baseURL: 'https://api.lemlist.com/api', + url: '/campaigns', + }, + }; } diff --git a/packages/nodes-base/credentials/MailgunApi.credentials.ts b/packages/nodes-base/credentials/MailgunApi.credentials.ts index 3d5b73c15c..b2d8dc950a 100644 --- a/packages/nodes-base/credentials/MailgunApi.credentials.ts +++ b/packages/nodes-base/credentials/MailgunApi.credentials.ts @@ -1,4 +1,9 @@ -import { ICredentialType, INodeProperties } from 'n8n-workflow'; +import { + IAuthenticateGeneric, + ICredentialTestRequest, + ICredentialType, + INodeProperties, +} from 'n8n-workflow'; export class MailgunApi implements ICredentialType { name = 'mailgunApi'; @@ -35,4 +40,19 @@ export class MailgunApi implements ICredentialType { default: '', }, ]; + authenticate: IAuthenticateGeneric = { + type: 'generic', + properties: { + auth: { + username: 'api', + password: '={{$credentials.apiKey}}', + }, + }, + }; + test: ICredentialTestRequest = { + request: { + baseURL: '=https://{{$credentials.apiDomain}}/v3', + url: '/domains', + }, + }; } diff --git a/packages/nodes-base/credentials/PhantombusterApi.credentials.ts b/packages/nodes-base/credentials/PhantombusterApi.credentials.ts index 7a1ee63e7c..3fcfbf7e18 100644 --- a/packages/nodes-base/credentials/PhantombusterApi.credentials.ts +++ b/packages/nodes-base/credentials/PhantombusterApi.credentials.ts @@ -1,4 +1,9 @@ -import { ICredentialType, INodeProperties } from 'n8n-workflow'; +import { + IAuthenticateGeneric, + ICredentialTestRequest, + ICredentialType, + INodeProperties, +} from 'n8n-workflow'; export class PhantombusterApi implements ICredentialType { name = 'phantombusterApi'; @@ -12,4 +17,18 @@ export class PhantombusterApi implements ICredentialType { default: '', }, ]; + authenticate: IAuthenticateGeneric = { + type: 'generic', + properties: { + headers: { + 'X-Phantombuster-Key': '={{$credentials.apiKey}}', + }, + }, + }; + test: ICredentialTestRequest = { + request: { + baseURL: 'https://api.phantombuster.com/api/v2', + url: '/agents/fetch-all', + }, + }; } diff --git a/packages/nodes-base/credentials/SegmentApi.credentials.ts b/packages/nodes-base/credentials/SegmentApi.credentials.ts index a5b242f385..6cc84554d2 100644 --- a/packages/nodes-base/credentials/SegmentApi.credentials.ts +++ b/packages/nodes-base/credentials/SegmentApi.credentials.ts @@ -1,4 +1,9 @@ -import { ICredentialType, INodeProperties } from 'n8n-workflow'; +import { + ICredentialDataDecryptedObject, + ICredentialType, + IHttpRequestOptions, + INodeProperties, +} from 'n8n-workflow'; export class SegmentApi implements ICredentialType { name = 'segmentApi'; @@ -12,4 +17,12 @@ export class SegmentApi implements ICredentialType { default: '', }, ]; + async authenticate( + credentials: ICredentialDataDecryptedObject, + requestOptions: IHttpRequestOptions, + ): Promise { + const base64Key = Buffer.from(`${credentials.writekey}:`).toString('base64'); + requestOptions.headers!['Authorization'] = `Basic ${base64Key}`; + return requestOptions; + } } diff --git a/packages/nodes-base/credentials/SupabaseApi.credentials.ts b/packages/nodes-base/credentials/SupabaseApi.credentials.ts index 148f27fa96..e169d781b0 100644 --- a/packages/nodes-base/credentials/SupabaseApi.credentials.ts +++ b/packages/nodes-base/credentials/SupabaseApi.credentials.ts @@ -1,4 +1,9 @@ -import { ICredentialType, INodeProperties } from 'n8n-workflow'; +import { + IAuthenticateGeneric, + ICredentialTestRequest, + ICredentialType, + INodeProperties, +} from 'n8n-workflow'; export class SupabaseApi implements ICredentialType { name = 'supabaseApi'; @@ -19,4 +24,22 @@ export class SupabaseApi implements ICredentialType { default: '', }, ]; + authenticate: IAuthenticateGeneric = { + type: 'generic', + properties: { + headers: { + apikey: '={{$credentials.serviceRole}}', + Authorization: '=Bearer {{$credentials.serviceRole}}', + }, + }, + }; + test: ICredentialTestRequest = { + request: { + baseURL: '={{$credentials.host}}/rest/v1', + headers: { + Prefer: 'return=representation', + }, + url: '/', + }, + }; } diff --git a/packages/nodes-base/credentials/UProcApi.credentials.ts b/packages/nodes-base/credentials/UProcApi.credentials.ts index 0ca140f17c..f47f61ac6a 100644 --- a/packages/nodes-base/credentials/UProcApi.credentials.ts +++ b/packages/nodes-base/credentials/UProcApi.credentials.ts @@ -1,4 +1,10 @@ -import { ICredentialType, INodeProperties } from 'n8n-workflow'; +import { + ICredentialDataDecryptedObject, + ICredentialTestRequest, + ICredentialType, + IHttpRequestOptions, + INodeProperties, +} from 'n8n-workflow'; export class UProcApi implements ICredentialType { name = 'uprocApi'; @@ -19,4 +25,22 @@ export class UProcApi implements ICredentialType { default: '', }, ]; + async authenticate( + credentials: ICredentialDataDecryptedObject, + requestOptions: IHttpRequestOptions, + ): Promise { + const token = Buffer.from(`${credentials.email}:${credentials.apiKey}`).toString('base64'); + requestOptions.headers = { + ...requestOptions.headers, + Authorization: `Basic ${token}`, + }; + return requestOptions; + } + test: ICredentialTestRequest = { + request: { + baseURL: 'https://api.uproc.io/api/v2', + url: '/profile', + method: 'GET', + }, + }; } diff --git a/packages/nodes-base/nodes/Dropcontact/Dropcontact.node.ts b/packages/nodes-base/nodes/Dropcontact/Dropcontact.node.ts index bb2062d2d4..58c4241d84 100644 --- a/packages/nodes-base/nodes/Dropcontact/Dropcontact.node.ts +++ b/packages/nodes-base/nodes/Dropcontact/Dropcontact.node.ts @@ -12,7 +12,7 @@ import { NodeApiError, } from 'n8n-workflow'; -import { dropcontactApiRequest, validateCredentials } from './GenericFunction'; +import { dropcontactApiRequest } from './GenericFunction'; export class Dropcontact implements INodeType { description: INodeTypeDescription = { @@ -32,7 +32,6 @@ export class Dropcontact implements INodeType { { name: 'dropcontactApi', required: true, - testedBy: 'dropcontactApiCredentialTest', }, ], properties: [ @@ -244,30 +243,6 @@ export class Dropcontact implements INodeType { }, ], }; - - methods = { - credentialTest: { - async dropcontactApiCredentialTest( - this: ICredentialTestFunctions, - credential: ICredentialsDecrypted, - ): Promise { - try { - await validateCredentials.call(this, credential.data as ICredentialDataDecryptedObject); - } catch (error) { - return { - status: 'Error', - message: 'The API Key included in the request is invalid', - }; - } - - return { - status: 'OK', - message: 'Connection successful!', - }; - }, - }, - }; - async execute(this: IExecuteFunctions): Promise { const entryData = this.getInputData(); const resource = this.getNodeParameter('resource', 0) as string; diff --git a/packages/nodes-base/nodes/Dropcontact/GenericFunction.ts b/packages/nodes-base/nodes/Dropcontact/GenericFunction.ts index dafd841c4b..213ed3e9b0 100644 --- a/packages/nodes-base/nodes/Dropcontact/GenericFunction.ts +++ b/packages/nodes-base/nodes/Dropcontact/GenericFunction.ts @@ -1,12 +1,6 @@ import { IExecuteFunctions, IHookFunctions } from 'n8n-core'; -import { - ICredentialDataDecryptedObject, - ICredentialTestFunctions, - IDataObject, - ILoadOptionsFunctions, - NodeApiError, -} from 'n8n-workflow'; +import { IDataObject, ILoadOptionsFunctions, NodeApiError } from 'n8n-workflow'; import { OptionsWithUri } from 'request'; @@ -20,15 +14,7 @@ export async function dropcontactApiRequest( body: IDataObject, qs: IDataObject, ) { - const { apiKey } = (await this.getCredentials('dropcontactApi')) as { - apiKey: string; - }; - const options: OptionsWithUri = { - headers: { - 'user-agent': 'n8n', - 'X-Access-Token': apiKey, - }, method, uri: `https://api.dropcontact.io${endpoint}`, qs, @@ -45,35 +31,8 @@ export async function dropcontactApiRequest( } try { - return await this.helpers.request!(options); + return await this.helpers.requestWithAuthentication.call(this, 'dropcontactApi', options); } catch (error) { throw new NodeApiError(this.getNode(), error); } } - -export async function validateCredentials( - this: ICredentialTestFunctions, - decryptedCredentials: ICredentialDataDecryptedObject, - // tslint:disable-next-line:no-any -): Promise { - const credentials = decryptedCredentials; - - const { apiKey } = credentials as { - apiKey: string; - }; - - const options: OptionsWithUri = { - headers: { - 'user-agent': 'n8n', - 'X-Access-Token': apiKey, - }, - method: 'POST', - body: { - data: [{ email: '' }], - }, - uri: `https://api.dropcontact.io/batch`, - json: true, - }; - - return this.helpers.request!(options); -} diff --git a/packages/nodes-base/nodes/Lemlist/GenericFunctions.ts b/packages/nodes-base/nodes/Lemlist/GenericFunctions.ts index 5f1fed85df..c48909c4b4 100644 --- a/packages/nodes-base/nodes/Lemlist/GenericFunctions.ts +++ b/packages/nodes-base/nodes/Lemlist/GenericFunctions.ts @@ -17,16 +17,9 @@ export async function lemlistApiRequest( qs: IDataObject = {}, option: IDataObject = {}, ) { - const { apiKey } = (await this.getCredentials('lemlistApi')) as { - apiKey: string; - }; - - const encodedApiKey = Buffer.from(':' + apiKey).toString('base64'); const options: OptionsWithUri = { headers: { - 'user-agent': 'n8n', - Authorization: `Basic ${encodedApiKey}`, }, method, uri: `https://api.lemlist.com/api${endpoint}`, @@ -48,7 +41,7 @@ export async function lemlistApiRequest( } try { - return await this.helpers.request!(options); + return await this.helpers.requestWithAuthentication.call(this,'lemlistApi',options); } catch (error) { throw new NodeApiError(this.getNode(), error); } diff --git a/packages/nodes-base/nodes/Mailgun/Mailgun.node.ts b/packages/nodes-base/nodes/Mailgun/Mailgun.node.ts index 77c82dfc1f..faef4f5aae 100644 --- a/packages/nodes-base/nodes/Mailgun/Mailgun.node.ts +++ b/packages/nodes-base/nodes/Mailgun/Mailgun.node.ts @@ -173,17 +173,13 @@ export class Mailgun implements INodeType { method: 'POST', formData, uri: `https://${credentials.apiDomain}/v3/${credentials.emailDomain}/messages`, - auth: { - user: 'api', - pass: credentials.apiKey as string, - }, json: true, }; let responseData; try { - responseData = await this.helpers.request(options); + responseData = await this.helpers.requestWithAuthentication.call(this, 'mailgunApi', options); } catch (error) { throw new NodeApiError(this.getNode(), error); } diff --git a/packages/nodes-base/nodes/Phantombuster/GenericFunctions.ts b/packages/nodes-base/nodes/Phantombuster/GenericFunctions.ts index 14a333e067..bf6a1eb8a0 100644 --- a/packages/nodes-base/nodes/Phantombuster/GenericFunctions.ts +++ b/packages/nodes-base/nodes/Phantombuster/GenericFunctions.ts @@ -18,7 +18,6 @@ export async function phantombusterApiRequest( const options: OptionsWithUri = { headers: { - 'X-Phantombuster-Key': credentials.apiKey, }, method, body, @@ -31,7 +30,7 @@ export async function phantombusterApiRequest( delete options.body; } //@ts-ignore - return await this.helpers.request.call(this, options); + return await this.helpers.requestWithAuthentication.call(this, 'phantombusterApi',options); } catch (error) { throw new NodeApiError(this.getNode(), error); } diff --git a/packages/nodes-base/nodes/Segment/GenericFunctions.ts b/packages/nodes-base/nodes/Segment/GenericFunctions.ts index 1e54839f22..8d963ac853 100644 --- a/packages/nodes-base/nodes/Segment/GenericFunctions.ts +++ b/packages/nodes-base/nodes/Segment/GenericFunctions.ts @@ -17,18 +17,14 @@ export async function segmentApiRequest( | IWebhookFunctions, method: string, resource: string, - // tslint:disable-next-line:no-any - body: any = {}, + body: any = {}, // tslint:disable-line:no-any qs: IDataObject = {}, uri?: string, option: IDataObject = {}, // tslint:disable-next-line:no-any ): Promise { - const credentials = await this.getCredentials('segmentApi'); - const base64Key = Buffer.from(`${credentials.writekey}:`).toString('base64'); const options: OptionsWithUri = { headers: { - Authorization: `Basic ${base64Key}`, 'Content-Type': 'application/json', }, method, @@ -41,7 +37,7 @@ export async function segmentApiRequest( delete options.body; } try { - return await this.helpers.request!(options); + return await this.helpers.requestWithAuthentication.call(this, 'segmentApi', options); } catch (error) { throw new NodeApiError(this.getNode(), error); } diff --git a/packages/nodes-base/nodes/Supabase/GenericFunctions.ts b/packages/nodes-base/nodes/Supabase/GenericFunctions.ts index 36b4f88a35..7d98e391c1 100644 --- a/packages/nodes-base/nodes/Supabase/GenericFunctions.ts +++ b/packages/nodes-base/nodes/Supabase/GenericFunctions.ts @@ -39,8 +39,6 @@ export async function supabaseApiRequest( const options: OptionsWithUri = { headers: { - apikey: credentials.serviceRole, - Authorization: 'Bearer ' + credentials.serviceRole, Prefer: 'return=representation', }, method, @@ -56,8 +54,8 @@ export async function supabaseApiRequest( if (Object.keys(body).length === 0) { delete options.body; } - //@ts-ignore - return await this.helpers?.request(options); + return await this.helpers.requestWithAuthentication.call(this, 'supabaseApi', options); + } catch (error) { throw new NodeApiError(this.getNode(), error); } diff --git a/packages/nodes-base/nodes/UProc/GenericFunctions.ts b/packages/nodes-base/nodes/UProc/GenericFunctions.ts index 74d648d941..2082d7e79d 100644 --- a/packages/nodes-base/nodes/UProc/GenericFunctions.ts +++ b/packages/nodes-base/nodes/UProc/GenericFunctions.ts @@ -7,34 +7,27 @@ import { ILoadOptionsFunctions, } from 'n8n-core'; -import { IDataObject, NodeApiError, NodeOperationError } from 'n8n-workflow'; +import { IDataObject, IHttpRequestMethods, IHttpRequestOptions, NodeApiError } from 'n8n-workflow'; export async function uprocApiRequest( this: IHookFunctions | IExecuteFunctions | IExecuteSingleFunctions | ILoadOptionsFunctions, method: string, - // tslint:disable-next-line:no-any - body: any = {}, + body: any = {}, // tslint:disable-line:no-any qs: IDataObject = {}, uri?: string, option: IDataObject = {}, // tslint:disable-next-line:no-any ): Promise { - const credentials = await this.getCredentials('uprocApi'); - const token = Buffer.from(`${credentials.email}:${credentials.apiKey}`).toString('base64'); - const options: OptionsWithUri = { - headers: { - Authorization: `Basic ${token}`, - 'User-agent': 'n8n', - }, - method, + const options: IHttpRequestOptions = { + method: method as IHttpRequestMethods, qs, body, - uri: uri || `https://api.uproc.io/api/v2/process`, + url: 'https://api.uproc.io/api/v2/process', json: true, }; try { - return await this.helpers.request!(options); + return await this.helpers.httpRequestWithAuthentication.call(this, 'uprocApi', options); } catch (error) { throw new NodeApiError(this.getNode(), error); }