diff --git a/packages/nodes-base/credentials/MauticOAuth2Api.credentials.ts b/packages/nodes-base/credentials/MauticOAuth2Api.credentials.ts index 104ac8203..ea5e131de 100644 --- a/packages/nodes-base/credentials/MauticOAuth2Api.credentials.ts +++ b/packages/nodes-base/credentials/MauticOAuth2Api.credentials.ts @@ -22,14 +22,14 @@ export class MauticOAuth2Api implements ICredentialType { displayName: 'Authorization URL', name: 'authUrl', type: 'hidden', - default: '={{$self["url"]}}/oauth/v2/authorize', + default: '={{$self["url"].endsWith("/") ? $self["url"].slice(0, -1) : $self["url"]}}/oauth/v2/authorize', required: true, }, { displayName: 'Access Token URL', name: 'accessTokenUrl', type: 'hidden', - default: '={{$self["url"]}}/oauth/v2/token', + default: '={{$self["url"].endsWith("/") ? $self["url"].slice(0, -1) : $self["url"]}}/oauth/v2/token', required: true, }, { diff --git a/packages/nodes-base/nodes/Mautic/GenericFunctions.ts b/packages/nodes-base/nodes/Mautic/GenericFunctions.ts index c0ff315d9..6b68c00ff 100644 --- a/packages/nodes-base/nodes/Mautic/GenericFunctions.ts +++ b/packages/nodes-base/nodes/Mautic/GenericFunctions.ts @@ -10,7 +10,11 @@ import { } from 'n8n-core'; import { - IDataObject, JsonObject, NodeApiError, + ICredentialDataDecryptedObject, + ICredentialTestFunctions, + IDataObject, + JsonObject, + NodeApiError, } from 'n8n-workflow'; export async function mauticApiRequest(this: IHookFunctions | IExecuteFunctions | IExecuteSingleFunctions | ILoadOptionsFunctions, method: string, endpoint: string, body: any = {}, query?: IDataObject, uri?: string): Promise { // tslint:disable-line:no-any @@ -31,19 +35,21 @@ export async function mauticApiRequest(this: IHookFunctions | IExecuteFunctions if (authenticationMethod === 'credentials') { const credentials = await this.getCredentials('mauticApi') as IDataObject; + const baseUrl = credentials!.url as string; const base64Key = Buffer.from(`${credentials.username}:${credentials.password}`).toString('base64'); options.headers!.Authorization = `Basic ${base64Key}`; - options.uri = `${credentials.url}${options.uri}`; + options.uri = `${baseUrl.endsWith('/') ? baseUrl.slice(0, -1) : baseUrl}${options.uri}`; //@ts-ignore returnData = await this.helpers.request(options); } else { const credentials = await this.getCredentials('mauticOAuth2Api') as IDataObject; + const baseUrl = credentials!.url as string; - options.uri = `${credentials.url}${options.uri}`; + options.uri = `${baseUrl.endsWith('/') ? baseUrl.slice(0, -1) : baseUrl}${options.uri}`; //@ts-ignore returnData = await this.helpers.requestOAuth2.call(this, 'mauticOAuth2Api', options, { includeCredentialsOnRefreshOnBody: true }); } @@ -96,3 +102,30 @@ export function validateJSON(json: string | undefined): any { // tslint:disable- } return result; } + +export async function validateCredentials(this: ICredentialTestFunctions, decryptedCredentials: ICredentialDataDecryptedObject): Promise { // tslint:disable-line:no-any + const credentials = decryptedCredentials; + + const { + url, + username, + password, + } = credentials as { + url: string, + username: string, + password: string, + }; + + const base64Key = Buffer.from(`${username}:${password}`).toString('base64'); + + const options: OptionsWithUri = { + method: 'GET', + headers: { + Authorization: `Basic ${base64Key}`, + }, + uri: url.endsWith('/') ? `${url}api/users/self` : `${url}/api/users/self`, + json: true, + }; + + return await this.helpers.request(options); +} diff --git a/packages/nodes-base/nodes/Mautic/Mautic.node.ts b/packages/nodes-base/nodes/Mautic/Mautic.node.ts index d84dbdde0..a1ee45190 100644 --- a/packages/nodes-base/nodes/Mautic/Mautic.node.ts +++ b/packages/nodes-base/nodes/Mautic/Mautic.node.ts @@ -3,8 +3,12 @@ import { } from 'n8n-core'; import { + ICredentialDataDecryptedObject, + ICredentialsDecrypted, + ICredentialTestFunctions, IDataObject, ILoadOptionsFunctions, + INodeCredentialTestResult, INodeExecutionData, INodePropertyOptions, INodeType, @@ -17,6 +21,7 @@ import { import { mauticApiRequest, mauticApiRequestAllItems, + validateCredentials, validateJSON, } from './GenericFunctions'; @@ -80,6 +85,7 @@ export class Mautic implements INodeType { ], }, }, + testedBy: 'mauticCredentialTest', }, { name: 'mauticOAuth2Api', @@ -166,6 +172,29 @@ export class Mautic implements INodeType { }; methods = { + credentialTest: { + async mauticCredentialTest(this: ICredentialTestFunctions, credential: ICredentialsDecrypted): Promise { + try { + let responseData; + responseData = await validateCredentials.call(this, credential.data as ICredentialDataDecryptedObject); + if (responseData.id) { + return { + status: 'OK', + message: 'Authentication successful!', + }; + } + } catch (error) { + return { + status: 'Error', + message: 'Invalid credentials', + }; + } + return { + status: 'Error', + message: 'Invalid credentials', + }; + }, + }, loadOptions: { // Get all the available companies to display them to user so that he can // select them easily