diff --git a/packages/nodes-base/credentials/HubspotAppToken.credentials.ts b/packages/nodes-base/credentials/HubspotAppToken.credentials.ts index ab4f0a9503bad..4f6df9c0e1fdb 100644 --- a/packages/nodes-base/credentials/HubspotAppToken.credentials.ts +++ b/packages/nodes-base/credentials/HubspotAppToken.credentials.ts @@ -9,7 +9,7 @@ export class HubspotAppToken implements ICredentialType { documentationUrl = 'hubspot'; properties: INodeProperties[] = [ { - displayName: 'App Token', + displayName: 'APP Token', name: 'appToken', type: 'string', default: '', diff --git a/packages/nodes-base/nodes/Hubspot/GenericFunctions.ts b/packages/nodes-base/nodes/Hubspot/GenericFunctions.ts index 2a851a25b518e..64c75a16331a0 100644 --- a/packages/nodes-base/nodes/Hubspot/GenericFunctions.ts +++ b/packages/nodes-base/nodes/Hubspot/GenericFunctions.ts @@ -10,7 +10,10 @@ import { } from 'n8n-core'; import { + ICredentialDataDecryptedObject, + ICredentialTestFunctions, IDataObject, + JsonObject, NodeApiError, } from 'n8n-workflow'; @@ -59,7 +62,7 @@ export async function hubspotApiRequest(this: IHookFunctions | IExecuteFunctions return await this.helpers.requestOAuth2!.call(this, 'hubspotOAuth2Api', options, { tokenType: 'Bearer', includeCredentialsOnRefreshOnBody: true }); } } catch (error) { - throw new NodeApiError(this.getNode(), error); + throw new NodeApiError(this.getNode(), error as JsonObject); } } @@ -1969,3 +1972,33 @@ export const getAssociations = (associations: { ...(associations.ticketIds && { ticketIds: associations.ticketIds.toString().split(',') }), }; }; + +export async function validateCredentials( + this: ICredentialTestFunctions, + decryptedCredentials: ICredentialDataDecryptedObject, +): Promise { // tslint:disable-line:no-any + const credentials = decryptedCredentials; + + const { + apiKey, + appToken, + } = credentials as { + appToken: string, + apiKey: string, + }; + + const options: OptionsWithUri = { + method: 'GET', + headers: {}, + uri: `https://api.hubapi.com/deals/v1/deal/paged`, + json: true, + }; + + if (apiKey) { + options.qs = { hapikey: apiKey }; + } else { + options.headers = { Authorization: `Bearer ${appToken}` }; + } + + return await this.helpers.request(options); +} diff --git a/packages/nodes-base/nodes/Hubspot/Hubspot.node.ts b/packages/nodes-base/nodes/Hubspot/Hubspot.node.ts index 30926c6ee65e4..2d04ca540177e 100644 --- a/packages/nodes-base/nodes/Hubspot/Hubspot.node.ts +++ b/packages/nodes-base/nodes/Hubspot/Hubspot.node.ts @@ -3,12 +3,17 @@ import { } from 'n8n-core'; import { + ICredentialDataDecryptedObject, + ICredentialsDecrypted, + ICredentialTestFunctions, IDataObject, ILoadOptionsFunctions, + INodeCredentialTestResult, INodeExecutionData, INodePropertyOptions, INodeType, INodeTypeDescription, + JsonObject, NodeOperationError, } from 'n8n-workflow'; @@ -71,6 +76,9 @@ import { snakeCase, } from 'change-case'; +import { + validateCredentials +} from './GenericFunctions'; export class Hubspot implements INodeType { description: INodeTypeDescription = { displayName: 'HubSpot', @@ -89,6 +97,7 @@ export class Hubspot implements INodeType { { name: 'hubspotApi', required: true, + testedBy: 'hubspotApiTest', displayOptions: { show: { authentication: [ @@ -100,6 +109,7 @@ export class Hubspot implements INodeType { { name: 'hubspotAppToken', required: true, + testedBy: 'hubspotApiTest', displayOptions: { show: { authentication: [ @@ -131,7 +141,7 @@ export class Hubspot implements INodeType { value: 'apiKey', }, { - name: 'App Token', + name: 'APP Token', value: 'appToken', }, { @@ -204,6 +214,26 @@ export class Hubspot implements INodeType { }; methods = { + credentialTest: { + async hubspotApiTest(this: ICredentialTestFunctions, credential: ICredentialsDecrypted): Promise { + try { + await validateCredentials.call(this, credential.data as ICredentialDataDecryptedObject); + } catch (error) { + const err = error as JsonObject; + if (err.statusCode === 401) { + return { + status: 'Error', + message: `Invalid credentials`, + }; + } + } + return { + status: 'OK', + message: 'Authentication successful', + }; + }, + }, + loadOptions: { /* -------------------------------------------------------------------------- */ /* CONTACT */ @@ -938,7 +968,7 @@ export class Hubspot implements INodeType { } } catch (error) { if (this.continueOnFail()) { - returnData.push({ error: error.message }); + returnData.push({ error: (error as JsonObject).message }); } else { throw error; } @@ -2526,7 +2556,7 @@ export class Hubspot implements INodeType { } } catch (error) { if (this.continueOnFail()) { - returnData.push({ error: error.message }); + returnData.push({ error: (error as JsonObject).message }); continue; } throw error;