N8N-4126 credentials injection and testing on specific nodes (#3816)

* Add credential injection and testing to Lemlist, Uproc, Supabase, Segment, Phantombuster, Mailgun and Dropcontact
This commit is contained in:
agobrech 2022-08-24 10:26:48 +02:00 committed by GitHub
parent 5285fc1de6
commit 76f9ad8bae
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
15 changed files with 167 additions and 114 deletions

View file

@ -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: '' }],
},
},
};
}

View file

@ -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<IHttpRequestOptions> {
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',
},
};
}

View file

@ -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',
},
};
}

View file

@ -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',
},
};
}

View file

@ -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<IHttpRequestOptions> {
const base64Key = Buffer.from(`${credentials.writekey}:`).toString('base64');
requestOptions.headers!['Authorization'] = `Basic ${base64Key}`;
return requestOptions;
}
}

View file

@ -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: '/',
},
};
}

View file

@ -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<IHttpRequestOptions> {
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',
},
};
}

View file

@ -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<INodeCredentialTestResult> {
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<INodeExecutionData[][]> {
const entryData = this.getInputData();
const resource = this.getNodeParameter('resource', 0) as string;

View file

@ -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<any> {
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);
}

View file

@ -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);
}

View file

@ -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);
}

View file

@ -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);
}

View file

@ -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<any> {
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);
}

View file

@ -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);
}

View file

@ -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<any> {
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);
}