From d9c7228d131f7befec4a71be87870afa9a5f0d5e Mon Sep 17 00:00:00 2001 From: paolo-rechia Date: Tue, 22 Mar 2022 15:50:22 +0100 Subject: [PATCH 1/5] Implements Magento Auth API Test --- .../nodes-base/nodes/Magento/Magento2.node.ts | 50 ++++++++++++ .../Magento.node.testCredentials.test.js | 76 +++++++++++++++++++ 2 files changed, 126 insertions(+) create mode 100644 packages/nodes-base/test/nodes/Magento/Magento.node.testCredentials.test.js diff --git a/packages/nodes-base/nodes/Magento/Magento2.node.ts b/packages/nodes-base/nodes/Magento/Magento2.node.ts index 5b0440127e716..b630b36e67739 100644 --- a/packages/nodes-base/nodes/Magento/Magento2.node.ts +++ b/packages/nodes-base/nodes/Magento/Magento2.node.ts @@ -1,3 +1,8 @@ +import { + OptionsWithUri, +} from 'request'; + + import { IExecuteFunctions, } from 'n8n-core'; @@ -5,6 +10,9 @@ import { import { IDataObject, ILoadOptionsFunctions, + ICredentialsDecrypted, + ICredentialTestFunctions, + INodeCredentialTestResult, INodeExecutionData, INodePropertyOptions, INodeType, @@ -74,6 +82,7 @@ export class Magento2 implements INodeType { { name: 'magento2Api', required: true, + testedBy: 'magento2ApiTest' }, ], properties: [ @@ -114,6 +123,47 @@ export class Magento2 implements INodeType { }; methods = { + credentialTest: { + async magento2ApiTest(this: ICredentialTestFunctions, credential: ICredentialsDecrypted): Promise { + const accessToken = credential!.data!.accessToken; + const host = credential!.data!.host; + + if (!host || !accessToken) { + return { + status: 'Error', + message: 'Connection details not valid: missing credentials', + } + } + + // Calls some API to test Acess Token + const options: OptionsWithUri = { + headers: { + 'Content-Type': 'application/json', + 'Authorization': `Bearer ${accessToken}`, + }, + method: 'GET', + uri: `${host}/rest/default/V1/directory/countries`, + qs: { + recent: 0, + }, + json: true, + timeout: 5000, + }; + + try { + await this.helpers.request!(options); + } catch (error) { + return { + status: 'Error', + message: `Connection details not valid: ${error.message}`, + }; + } + return { + status: 'OK', + message: 'Authentication successful!', + }; + } + }, loadOptions: { async getCountries(this: ILoadOptionsFunctions): Promise { //https://magento.redoc.ly/2.3.7-admin/tag/directorycountries diff --git a/packages/nodes-base/test/nodes/Magento/Magento.node.testCredentials.test.js b/packages/nodes-base/test/nodes/Magento/Magento.node.testCredentials.test.js new file mode 100644 index 0000000000000..9fb30d136420f --- /dev/null +++ b/packages/nodes-base/test/nodes/Magento/Magento.node.testCredentials.test.js @@ -0,0 +1,76 @@ +const MagentoModule = require('../../../nodes/Magento/Magento2.node.ts') + +describe('testCredentials', () => { + let node = new MagentoModule.Magento2() + const mockSuccessfulResponse = {} + const mockHost = "http://hello.magento" + const expectedEndpointCall = `${mockHost}/rest/default/V1/directory/countries` + let called = [] + + let mockCredentials = { + id: 1, + name: "test", + type: "magento2Api", + nodesAccess: [], + data: { + host: mockHost, + accessToken: "ABC" + } + } + + it('Method exists', async () => { + + expect(node.methods.credentialTest.magento2ApiTest).toBeTruthy() + + }) + + function patchHelper(node, response, error) { + called = [] + function mockRequest(options) { + console.log(options) + called.push(options) + if (error) { + throw new Error("Mocked error") + } + return mockSuccessfulResponse + } + node.methods.credentialTest.helpers = {request: mockRequest} + } + + it('Calls Mocked API', async () => { + patchHelper(node, mockSuccessfulResponse) + + result = await node.methods.credentialTest.magento2ApiTest(mockCredentials) + + expect(result).toEqual({ + status: 'OK', + message: 'Authentication successful!', + }) + expect(called[0].uri).toEqual(expectedEndpointCall) + }) + + it('Calls Mocked API Raises Error', async () => { + + patchHelper(node, {}, true) + + result = await node.methods.credentialTest.magento2ApiTest(mockCredentials) + + expect(result).toEqual({ + status: 'Error', + message: 'Connection details not valid: Mocked error', + }) + }) + + it('Invalid Input', async () => { + + mockCredentials.data.accessToken = undefined + + result = await node.methods.credentialTest.magento2ApiTest(mockCredentials) + + expect(result).toEqual({ + status: 'Error', + message: 'Connection details not valid: missing credentials', + }) + }) +}) + From c37ba35dab91d5ed179de17e5984caa753ceec91 Mon Sep 17 00:00:00 2001 From: paolo-rechia Date: Thu, 31 Mar 2022 11:47:45 +0200 Subject: [PATCH 2/5] Deletes unit tests --- .../Magento.node.testCredentials.test.js | 76 ------------------- 1 file changed, 76 deletions(-) delete mode 100644 packages/nodes-base/test/nodes/Magento/Magento.node.testCredentials.test.js diff --git a/packages/nodes-base/test/nodes/Magento/Magento.node.testCredentials.test.js b/packages/nodes-base/test/nodes/Magento/Magento.node.testCredentials.test.js deleted file mode 100644 index 9fb30d136420f..0000000000000 --- a/packages/nodes-base/test/nodes/Magento/Magento.node.testCredentials.test.js +++ /dev/null @@ -1,76 +0,0 @@ -const MagentoModule = require('../../../nodes/Magento/Magento2.node.ts') - -describe('testCredentials', () => { - let node = new MagentoModule.Magento2() - const mockSuccessfulResponse = {} - const mockHost = "http://hello.magento" - const expectedEndpointCall = `${mockHost}/rest/default/V1/directory/countries` - let called = [] - - let mockCredentials = { - id: 1, - name: "test", - type: "magento2Api", - nodesAccess: [], - data: { - host: mockHost, - accessToken: "ABC" - } - } - - it('Method exists', async () => { - - expect(node.methods.credentialTest.magento2ApiTest).toBeTruthy() - - }) - - function patchHelper(node, response, error) { - called = [] - function mockRequest(options) { - console.log(options) - called.push(options) - if (error) { - throw new Error("Mocked error") - } - return mockSuccessfulResponse - } - node.methods.credentialTest.helpers = {request: mockRequest} - } - - it('Calls Mocked API', async () => { - patchHelper(node, mockSuccessfulResponse) - - result = await node.methods.credentialTest.magento2ApiTest(mockCredentials) - - expect(result).toEqual({ - status: 'OK', - message: 'Authentication successful!', - }) - expect(called[0].uri).toEqual(expectedEndpointCall) - }) - - it('Calls Mocked API Raises Error', async () => { - - patchHelper(node, {}, true) - - result = await node.methods.credentialTest.magento2ApiTest(mockCredentials) - - expect(result).toEqual({ - status: 'Error', - message: 'Connection details not valid: Mocked error', - }) - }) - - it('Invalid Input', async () => { - - mockCredentials.data.accessToken = undefined - - result = await node.methods.credentialTest.magento2ApiTest(mockCredentials) - - expect(result).toEqual({ - status: 'Error', - message: 'Connection details not valid: missing credentials', - }) - }) -}) - From 814c37548b2fddebcc3e06b418102be3d7a07371 Mon Sep 17 00:00:00 2001 From: Jonathan Bennetts Date: Mon, 4 Apr 2022 14:12:10 +0100 Subject: [PATCH 3/5] Fixed lint issues and changed the URI for the credential test --- .../nodes-base/nodes/Magento/Magento2.node.ts | 42 +++++++++---------- 1 file changed, 21 insertions(+), 21 deletions(-) diff --git a/packages/nodes-base/nodes/Magento/Magento2.node.ts b/packages/nodes-base/nodes/Magento/Magento2.node.ts index b630b36e67739..dba66922c01c5 100644 --- a/packages/nodes-base/nodes/Magento/Magento2.node.ts +++ b/packages/nodes-base/nodes/Magento/Magento2.node.ts @@ -8,11 +8,11 @@ import { } from 'n8n-core'; import { + ICredentialsDecrypted, + ICredentialTestFunctions, IDataObject, ILoadOptionsFunctions, - ICredentialsDecrypted, - ICredentialTestFunctions, - INodeCredentialTestResult, + INodeCredentialTestResult, INodeExecutionData, INodePropertyOptions, INodeType, @@ -82,7 +82,7 @@ export class Magento2 implements INodeType { { name: 'magento2Api', required: true, - testedBy: 'magento2ApiTest' + testedBy: 'magento2ApiTest', }, ], properties: [ @@ -123,26 +123,26 @@ export class Magento2 implements INodeType { }; methods = { - credentialTest: { + credentialTest: { async magento2ApiTest(this: ICredentialTestFunctions, credential: ICredentialsDecrypted): Promise { - const accessToken = credential!.data!.accessToken; - const host = credential!.data!.host; + const accessToken = credential!.data!.accessToken; + const host = credential!.data!.host; - if (!host || !accessToken) { - return { - status: 'Error', - message: 'Connection details not valid: missing credentials', - } - } + if (!host || !accessToken) { + return { + status: 'Error', + message: 'Connection details not valid: missing credentials', + }; + } - // Calls some API to test Acess Token + // Calls some API to test Acess Token const options: OptionsWithUri = { headers: { 'Content-Type': 'application/json', 'Authorization': `Bearer ${accessToken}`, }, method: 'GET', - uri: `${host}/rest/default/V1/directory/countries`, + uri: `${host}/rest/default/V1/modules`, qs: { recent: 0, }, @@ -158,12 +158,12 @@ export class Magento2 implements INodeType { message: `Connection details not valid: ${error.message}`, }; } - return { - status: 'OK', - message: 'Authentication successful!', - }; - } - }, + return { + status: 'OK', + message: 'Authentication successful!', + }; + }, + }, loadOptions: { async getCountries(this: ILoadOptionsFunctions): Promise { //https://magento.redoc.ly/2.3.7-admin/tag/directorycountries From 1c434e943447c5200f3c3018412b55d2d278e29b Mon Sep 17 00:00:00 2001 From: ricardo Date: Wed, 6 Apr 2022 16:29:22 -0400 Subject: [PATCH 4/5] :zap: Move credential verification to the credential file --- .../credentials/Magento2Api.credentials.ts | 15 +++++++ .../nodes/Magento/GenericFunctions.ts | 6 +-- .../nodes-base/nodes/Magento/Magento2.node.ts | 42 ------------------- 3 files changed, 16 insertions(+), 47 deletions(-) diff --git a/packages/nodes-base/credentials/Magento2Api.credentials.ts b/packages/nodes-base/credentials/Magento2Api.credentials.ts index 487f68abf6826..ca22c31d50176 100644 --- a/packages/nodes-base/credentials/Magento2Api.credentials.ts +++ b/packages/nodes-base/credentials/Magento2Api.credentials.ts @@ -1,5 +1,8 @@ import { + ICredentialDataDecryptedObject, + ICredentialTestRequest, ICredentialType, + IHttpRequestOptions, INodeProperties, } from 'n8n-workflow'; @@ -21,4 +24,16 @@ export class Magento2Api implements ICredentialType { default: '', }, ]; + test: ICredentialTestRequest = { + request: { + baseURL: '={{$credentials.host}}', + url: '/rest/default/V1/modules', + }, + }; + async authenticate(credentials: ICredentialDataDecryptedObject, requestOptions: IHttpRequestOptions): Promise { + requestOptions.headers = Object.assign({}, { + 'Authorization': `Bearer ${credentials.accessToken}`, + }); + return requestOptions; + } } diff --git a/packages/nodes-base/nodes/Magento/GenericFunctions.ts b/packages/nodes-base/nodes/Magento/GenericFunctions.ts index cf09bba885f72..4a7353a50759a 100644 --- a/packages/nodes-base/nodes/Magento/GenericFunctions.ts +++ b/packages/nodes-base/nodes/Magento/GenericFunctions.ts @@ -28,10 +28,6 @@ export async function magentoApiRequest(this: IWebhookFunctions | IHookFunctions const credentials = await this.getCredentials('magento2Api') as IDataObject; let options: OptionsWithUri = { - headers: { - 'Content-Type': 'application/json', - 'Authorization': `Bearer ${credentials.accessToken}`, - }, method, body, qs, @@ -45,7 +41,7 @@ export async function magentoApiRequest(this: IWebhookFunctions | IHookFunctions delete options.body; } //@ts-ignore - return await this.helpers.request.call(this, options); + return await this.helpers.requestWithAuthentication.call(this, 'magento2Api', options); } catch (error) { throw new NodeApiError(this.getNode(), error); } diff --git a/packages/nodes-base/nodes/Magento/Magento2.node.ts b/packages/nodes-base/nodes/Magento/Magento2.node.ts index dba66922c01c5..b1ac7c941096b 100644 --- a/packages/nodes-base/nodes/Magento/Magento2.node.ts +++ b/packages/nodes-base/nodes/Magento/Magento2.node.ts @@ -82,7 +82,6 @@ export class Magento2 implements INodeType { { name: 'magento2Api', required: true, - testedBy: 'magento2ApiTest', }, ], properties: [ @@ -123,47 +122,6 @@ export class Magento2 implements INodeType { }; methods = { - credentialTest: { - async magento2ApiTest(this: ICredentialTestFunctions, credential: ICredentialsDecrypted): Promise { - const accessToken = credential!.data!.accessToken; - const host = credential!.data!.host; - - if (!host || !accessToken) { - return { - status: 'Error', - message: 'Connection details not valid: missing credentials', - }; - } - - // Calls some API to test Acess Token - const options: OptionsWithUri = { - headers: { - 'Content-Type': 'application/json', - 'Authorization': `Bearer ${accessToken}`, - }, - method: 'GET', - uri: `${host}/rest/default/V1/modules`, - qs: { - recent: 0, - }, - json: true, - timeout: 5000, - }; - - try { - await this.helpers.request!(options); - } catch (error) { - return { - status: 'Error', - message: `Connection details not valid: ${error.message}`, - }; - } - return { - status: 'OK', - message: 'Authentication successful!', - }; - }, - }, loadOptions: { async getCountries(this: ILoadOptionsFunctions): Promise { //https://magento.redoc.ly/2.3.7-admin/tag/directorycountries From 50472a7ff1beb427a051d976ac920cfadd7f6638 Mon Sep 17 00:00:00 2001 From: Jan Oberhauser Date: Fri, 8 Apr 2022 12:00:26 +0200 Subject: [PATCH 5/5] :zap: Simplify code --- .../credentials/Magento2Api.credentials.ts | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/packages/nodes-base/credentials/Magento2Api.credentials.ts b/packages/nodes-base/credentials/Magento2Api.credentials.ts index ca22c31d50176..0bd4f0d72892f 100644 --- a/packages/nodes-base/credentials/Magento2Api.credentials.ts +++ b/packages/nodes-base/credentials/Magento2Api.credentials.ts @@ -1,8 +1,7 @@ import { - ICredentialDataDecryptedObject, + IAuthenticateBearer, ICredentialTestRequest, ICredentialType, - IHttpRequestOptions, INodeProperties, } from 'n8n-workflow'; @@ -30,10 +29,9 @@ export class Magento2Api implements ICredentialType { url: '/rest/default/V1/modules', }, }; - async authenticate(credentials: ICredentialDataDecryptedObject, requestOptions: IHttpRequestOptions): Promise { - requestOptions.headers = Object.assign({}, { - 'Authorization': `Bearer ${credentials.accessToken}`, - }); - return requestOptions; - } + + authenticate = { + type: 'bearer', + properties: {}, + } as IAuthenticateBearer; }