From f370569ccc5176b6096e8cf59639ea1377fd16d2 Mon Sep 17 00:00:00 2001 From: Ricardo Espinoza Date: Fri, 5 Feb 2021 03:25:41 -0500 Subject: [PATCH] :sparkles: Add Tapfiliate Node (#1399) * :sparkles: Tapfiliate Node * :zap: Improvements * :zap: Minor improvements to Tapfiliate Node Co-authored-by: Jan Oberhauser --- .../credentials/TapfiliateApi.credentials.ts | 19 + .../nodes/Tapfiliate/AffiliateDescription.ts | 331 ++++++++++++++++ .../AffiliateMetadataDescription.ts | 196 ++++++++++ .../nodes/Tapfiliate/GenericFunctions.ts | 78 ++++ .../Tapfiliate/ProgramAffiliateDescription.ts | 359 ++++++++++++++++++ .../nodes/Tapfiliate/Tapfiliate.node.ts | 280 ++++++++++++++ .../nodes/Tapfiliate/tapfiliate.svg | 1 + packages/nodes-base/package.json | 2 + 8 files changed, 1266 insertions(+) create mode 100644 packages/nodes-base/credentials/TapfiliateApi.credentials.ts create mode 100644 packages/nodes-base/nodes/Tapfiliate/AffiliateDescription.ts create mode 100644 packages/nodes-base/nodes/Tapfiliate/AffiliateMetadataDescription.ts create mode 100644 packages/nodes-base/nodes/Tapfiliate/GenericFunctions.ts create mode 100644 packages/nodes-base/nodes/Tapfiliate/ProgramAffiliateDescription.ts create mode 100644 packages/nodes-base/nodes/Tapfiliate/Tapfiliate.node.ts create mode 100644 packages/nodes-base/nodes/Tapfiliate/tapfiliate.svg diff --git a/packages/nodes-base/credentials/TapfiliateApi.credentials.ts b/packages/nodes-base/credentials/TapfiliateApi.credentials.ts new file mode 100644 index 0000000000000..d52df982a1ed7 --- /dev/null +++ b/packages/nodes-base/credentials/TapfiliateApi.credentials.ts @@ -0,0 +1,19 @@ +import { + ICredentialType, + NodePropertyTypes, +} from 'n8n-workflow'; + +export class TapfiliateApi implements ICredentialType { + name = 'tapfiliateApi'; + displayName = 'Tapfiliate API'; + documentationUrl = 'tapfiliate'; + properties = [ + { + displayName: 'API Key', + name: 'apiKey', + required: true, + type: 'string' as NodePropertyTypes, + default: '', + }, + ]; +} diff --git a/packages/nodes-base/nodes/Tapfiliate/AffiliateDescription.ts b/packages/nodes-base/nodes/Tapfiliate/AffiliateDescription.ts new file mode 100644 index 0000000000000..c32d2bc876163 --- /dev/null +++ b/packages/nodes-base/nodes/Tapfiliate/AffiliateDescription.ts @@ -0,0 +1,331 @@ +import { + INodeProperties, +} from 'n8n-workflow'; + +export const affiliateOperations = [ + { + displayName: 'Operation', + name: 'operation', + type: 'options', + displayOptions: { + show: { + resource: [ + 'affiliate', + ], + }, + }, + options: [ + { + name: 'Create', + value: 'create', + description: 'Create an affiliate', + }, + { + name: 'Delete', + value: 'delete', + description: 'Delete an affiliate', + }, + { + name: 'Get', + value: 'get', + description: 'Get an affiliate by ID', + }, + { + name: 'Get All', + value: 'getAll', + description: 'Get all affiliates', + }, + ], + default: 'create', + description: 'The operation to perform.', + }, +] as INodeProperties[]; + +export const affiliateFields = [ + /* -------------------------------------------------------------------------- */ + /* affiliate:create */ + /* -------------------------------------------------------------------------- */ + { + displayName: 'Email', + name: 'email', + type: 'string', + required: true, + default: '', + displayOptions: { + show: { + operation: [ + 'create', + ], + resource: [ + 'affiliate', + ], + }, + }, + description: `The affiliate’s email.`, + }, + { + displayName: 'First Name', + name: 'firstname', + type: 'string', + required: true, + displayOptions: { + show: { + operation: [ + 'create', + ], + resource: [ + 'affiliate', + ], + }, + }, + default: '', + description: `The affiliate’s firstname.`, + }, + { + displayName: 'Last Name', + name: 'lastname', + type: 'string', + required: true, + displayOptions: { + show: { + operation: [ + 'create', + ], + resource: [ + 'affiliate', + ], + }, + }, + default: '', + description: `The affiliate’s lastname.`, + }, + { + displayName: 'Additional Fields', + name: 'additionalFields', + type: 'collection', + placeholder: 'Add Field', + default: {}, + displayOptions: { + show: { + resource: [ + 'affiliate', + ], + operation: [ + 'create', + ], + }, + }, + options: [ + { + displayName: 'Address', + name: 'addressUi', + placeholder: 'Address', + type: 'fixedCollection', + typeOptions: { + multipleValues: false, + }, + default: {}, + options: [ + { + name: 'addressValues', + displayName: 'Address', + values: [ + { + displayName: 'Line 1', + name: 'address', + type: 'string', + default: '', + }, + { + displayName: 'Line 2', + name: 'address_two', + type: 'string', + default: '', + }, + { + displayName: 'Postal Code', + name: 'postal_code', + type: 'string', + default: '', + }, + { + displayName: 'City', + name: 'city', + type: 'string', + default: '', + }, + { + displayName: 'State', + name: 'state', + type: 'string', + default: '', + }, + { + displayName: 'Country Code', + name: 'country', + type: 'string', + default: '', + description: `The country’s ISO_3166-1 code. Codes.`, + }, + ], + }, + ], + }, + { + displayName: 'Company Name', + name: 'companyName', + type: 'string', + default: '', + description: `The affiliate’s company data,`, + }, + ], + }, + + /* -------------------------------------------------------------------------- */ + /* affiliate:delete */ + /* -------------------------------------------------------------------------- */ + { + displayName: 'Affiliate ID', + name: 'affiliateId', + required: true, + type: 'string', + displayOptions: { + show: { + resource: [ + 'affiliate', + ], + operation: [ + 'delete', + ], + }, + }, + description: 'The ID of the affiliate.', + }, + + /* -------------------------------------------------------------------------- */ + /* affiliate:get */ + /* -------------------------------------------------------------------------- */ + { + displayName: 'Affiliate ID', + name: 'affiliateId', + type: 'string', + required: true, + displayOptions: { + show: { + resource: [ + 'affiliate', + ], + operation: [ + 'get', + ], + }, + }, + description: 'The ID of the affiliate.', + }, + + /* -------------------------------------------------------------------------- */ + /* affiliate:getAll */ + /* -------------------------------------------------------------------------- */ + { + displayName: 'Return All', + name: 'returnAll', + type: 'boolean', + displayOptions: { + show: { + resource: [ + 'affiliate', + ], + operation: [ + 'getAll', + ], + }, + }, + default: false, + description: 'If set to true, all the results will be returned.', + }, + { + displayName: 'Limit', + name: 'limit', + type: 'number', + displayOptions: { + show: { + resource: [ + 'affiliate', + ], + operation: [ + 'getAll', + ], + returnAll: [ + false, + ], + }, + }, + typeOptions: { + minValue: 1, + maxValue: 1000, + }, + default: 100, + description: 'How many results to return.', + }, + { + displayName: 'Filters', + name: 'filters', + type: 'collection', + placeholder: 'Add Field', + default: {}, + displayOptions: { + show: { + resource: [ + 'affiliate', + ], + operation: [ + 'getAll', + ], + }, + }, + options: [ + { + displayName: 'Affiliate Group ID', + name: 'affiliate_group_id', + type: 'string', + default: '', + description: 'Retrieves affiliates for a certain affiliate group.', + }, + { + displayName: 'Click ID', + name: 'click_id', + type: 'string', + default: '', + description: 'Click ID.', + }, + { + displayName: 'Email', + name: 'email', + type: 'string', + default: '', + description: 'An email address,', + }, + { + displayName: 'Parent ID', + name: 'parentId', + type: 'string', + default: '', + description: 'Retrieves children for a certain parent affiliate.', + }, + { + displayName: 'Referral Code', + name: 'referral_code', + type: 'string', + default: '', + description: 'An affiliate’s referral code. This corresponds to the value of ref= in their referral link.', + }, + { + displayName: 'Source ID', + name: 'source_id', + type: 'string', + default: '', + description: 'The Source ID.', + }, + ], + }, +] as INodeProperties[]; diff --git a/packages/nodes-base/nodes/Tapfiliate/AffiliateMetadataDescription.ts b/packages/nodes-base/nodes/Tapfiliate/AffiliateMetadataDescription.ts new file mode 100644 index 0000000000000..d65d38794d5c8 --- /dev/null +++ b/packages/nodes-base/nodes/Tapfiliate/AffiliateMetadataDescription.ts @@ -0,0 +1,196 @@ +import { + INodeProperties, +} from 'n8n-workflow'; + +export const affiliateMetadataOperations = [ + { + displayName: 'Operation', + name: 'operation', + type: 'options', + displayOptions: { + show: { + resource: [ + 'affiliateMetadata', + ], + }, + }, + options: [ + { + name: 'Add', + value: 'add', + description: `Add metadata to affiliate`, + }, + { + name: 'Remove', + value: 'remove', + description: `Remove metadata from affiliate`, + }, + { + name: 'Update', + value: 'update', + description: `Update affiliate's metadata`, + }, + ], + default: 'add', + description: 'The operation to perform.', + }, +] as INodeProperties[]; + +export const affiliateMetadataFields = [ + /* -------------------------------------------------------------------------- */ + /* affiliateMetadata:add */ + /* -------------------------------------------------------------------------- */ + { + displayName: 'Affiliate ID', + name: 'affiliateId', + type: 'string', + required: true, + displayOptions: { + show: { + resource: [ + 'affiliateMetadata', + ], + operation: [ + 'add', + ], + }, + }, + description: 'The ID of the affiliate.', + }, + { + displayName: 'Metadata', + name: 'metadataUi', + placeholder: 'Add Metadata', + type: 'fixedCollection', + displayOptions: { + show: { + resource: [ + 'affiliateMetadata', + ], + operation: [ + 'add', + ], + }, + }, + default: '', + typeOptions: { + multipleValues: true, + }, + description: 'Meta data', + options: [ + { + name: 'metadataValues', + displayName: 'Metadata', + values: [ + { + displayName: 'Key', + name: 'key', + type: 'string', + default: '', + description: 'Name of the metadata key to add.', + }, + { + displayName: 'Value', + name: 'value', + type: 'string', + default: '', + description: 'Value to set for the metadata key.', + }, + ], + }, + ], + }, + + /* -------------------------------------------------------------------------- */ + /* ffiliateMetadata:remove */ + /* -------------------------------------------------------------------------- */ + { + displayName: 'Affiliate ID', + name: 'affiliateId', + type: 'string', + required: true, + displayOptions: { + show: { + resource: [ + 'affiliateMetadata', + ], + operation: [ + 'remove', + ], + }, + }, + description: 'The ID of the affiliate.', + }, + { + displayName: 'Key', + name: 'key', + type: 'string', + displayOptions: { + show: { + resource: [ + 'affiliateMetadata', + ], + operation: [ + 'remove', + ], + }, + }, + default: '', + description: 'Name of the metadata key to remove.', + }, + + /* -------------------------------------------------------------------------- */ + /* affiliateMetadata:update */ + /* -------------------------------------------------------------------------- */ + { + displayName: 'Affiliate ID', + name: 'affiliateId', + type: 'string', + required: true, + displayOptions: { + show: { + resource: [ + 'affiliateMetadata', + ], + operation: [ + 'update', + ], + }, + }, + description: 'The ID of the affiliate.', + }, + { + displayName: 'Key', + name: 'key', + type: 'string', + displayOptions: { + show: { + resource: [ + 'affiliateMetadata', + ], + operation: [ + 'update', + ], + }, + }, + default: '', + description: 'Name of the metadata key to update.', + }, + { + displayName: 'Value', + name: 'value', + type: 'string', + displayOptions: { + show: { + resource: [ + 'affiliateMetadata', + ], + operation: [ + 'update', + ], + }, + }, + default: '', + description: 'Value to set for the metadata key.', + }, +] as INodeProperties[]; diff --git a/packages/nodes-base/nodes/Tapfiliate/GenericFunctions.ts b/packages/nodes-base/nodes/Tapfiliate/GenericFunctions.ts new file mode 100644 index 0000000000000..2eea6eac513e0 --- /dev/null +++ b/packages/nodes-base/nodes/Tapfiliate/GenericFunctions.ts @@ -0,0 +1,78 @@ +import { + OptionsWithUri, +} from 'request'; + +import { + IExecuteFunctions, + IExecuteSingleFunctions, + IHookFunctions, + ILoadOptionsFunctions, +} from 'n8n-core'; + +import { + IDataObject, +} from 'n8n-workflow'; + +export async function tapfiliateApiRequest(this: IHookFunctions | IExecuteFunctions | IExecuteSingleFunctions | ILoadOptionsFunctions, method: string, endpoint: string, body: any = {}, qs: IDataObject = {}, uri?: string | undefined, option: IDataObject = {}): Promise { // tslint:disable-line:no-any + const credentials = this.getCredentials('tapfiliateApi') as IDataObject; + + const options: OptionsWithUri = { + headers: { + 'Api-Key': credentials.apiKey, + }, + method, + qs, + body, + uri: uri || `https://api.tapfiliate.com/1.6${endpoint}`, + json: true, + }; + + if (Object.keys(body).length === 0) { + delete options.body; + } + + if (Object.keys(option).length !== 0) { + Object.assign(options, option); + } + try { + return await this.helpers.request!(options); + } catch (error) { + if (error.statusCode === 404) { + throw new Error( + `Tapfiliate error response [${error.statusCode}]: Not Found`, + ); + } + + if (error.response && error.response.body && error.response.body.errors) { + + let errors = error.response.body.errors; + + errors = errors.map((e: IDataObject) => e.message); + // Try to return the error prettier + throw new Error( + `Tapfiliate error response [${error.statusCode}]: ${errors.join('|')}`, + ); + } + throw error; + } +} + +export async function tapfiliateApiRequestAllItems(this: IExecuteFunctions | ILoadOptionsFunctions, method: string, endpoint: string, body: any = {}, query: IDataObject = {}): Promise { // tslint:disable-line:no-any + + const returnData: IDataObject[] = []; + + let responseData; + + query.page = 1; + + do { + responseData = await tapfiliateApiRequest.call(this, method, endpoint, body, query, '', { resolveWithFullResponse: true }); + returnData.push.apply(returnData, responseData.body); + query.page++; + + } while ( + responseData.headers.link.includes('next') + ); + + return returnData; +} diff --git a/packages/nodes-base/nodes/Tapfiliate/ProgramAffiliateDescription.ts b/packages/nodes-base/nodes/Tapfiliate/ProgramAffiliateDescription.ts new file mode 100644 index 0000000000000..b2f6b3d396cc3 --- /dev/null +++ b/packages/nodes-base/nodes/Tapfiliate/ProgramAffiliateDescription.ts @@ -0,0 +1,359 @@ +import { + INodeProperties, +} from 'n8n-workflow'; + +export const programAffiliateOperations = [ + { + displayName: 'Operation', + name: 'operation', + type: 'options', + displayOptions: { + show: { + resource: [ + 'programAffiliate', + ], + }, + }, + options: [ + { + name: 'Add', + value: 'add', + description: 'Add affiliate to program', + }, + { + name: 'Approve', + value: 'approve', + description: 'Approve an affiliate for a program', + }, + { + name: 'Disapprove', + value: 'disapprove', + description: 'Disapprove an affiliate', + }, + { + name: 'Get', + value: 'get', + description: 'Get an affiliate in a program', + }, + { + name: 'Get All', + value: 'getAll', + description: 'Get all affiliates in program', + }, + ], + default: 'add', + description: 'The operation to perform.', + }, +] as INodeProperties[]; + +export const programAffiliateFields = [ + /* -------------------------------------------------------------------------- */ + /* programAffiliate:add */ + /* -------------------------------------------------------------------------- */ + { + displayName: 'Program ID', + name: 'programId', + type: 'options', + required: true, + typeOptions: { + loadOptionsMethod: 'getPrograms', + }, + default: '', + displayOptions: { + show: { + operation: [ + 'add', + ], + resource: [ + 'programAffiliate', + ], + }, + }, + description: `The ID of the Program to add the affiliate to. This ID can be found as part of the URL when viewing the program on the platform.`, + }, + { + displayName: 'Affiliate ID', + name: 'affiliateId', + type: 'string', + required: true, + displayOptions: { + show: { + resource: [ + 'programAffiliate', + ], + operation: [ + 'add', + ], + }, + }, + description: 'The ID of the affiliate.', + }, + { + displayName: 'Additional Fields', + name: 'additionalFields', + type: 'collection', + placeholder: 'Add Field', + default: {}, + displayOptions: { + show: { + resource: [ + 'programAffiliate', + ], + operation: [ + 'add', + ], + }, + }, + options: [ + { + displayName: 'Approved', + name: 'approved', + type: 'boolean', + default: true, + description: `An optional approval status.`, + }, + { + displayName: 'Coupon', + name: 'coupon', + type: 'string', + default: '', + description: 'An optional coupon for this affiliate.', + }, + ], + }, + + /* -------------------------------------------------------------------------- */ + /* programAffiliate:approve */ + /* -------------------------------------------------------------------------- */ + { + displayName: 'Program ID', + name: 'programId', + type: 'options', + typeOptions: { + loadOptionsMethod: 'getPrograms', + }, + default: '', + displayOptions: { + show: { + operation: [ + 'approve', + ], + resource: [ + 'programAffiliate', + ], + }, + }, + description: `The ID of the Program to add the affiliate to. This ID can be found as part of the URL when viewing the program on the platform.`, + }, + { + displayName: 'Affiliate ID', + name: 'affiliateId', + type: 'string', + displayOptions: { + show: { + resource: [ + 'programAffiliate', + ], + operation: [ + 'approve', + ], + }, + }, + description: 'The ID of the affiliate.', + }, + + /* -------------------------------------------------------------------------- */ + /* programAffiliate:disapprove */ + /* -------------------------------------------------------------------------- */ + { + displayName: 'Program ID', + name: 'programId', + type: 'options', + typeOptions: { + loadOptionsMethod: 'getPrograms', + }, + default: '', + displayOptions: { + show: { + operation: [ + 'disapprove', + ], + resource: [ + 'programAffiliate', + ], + }, + }, + description: `The ID of the Program to add the affiliate to. This ID can be found as part of the URL when viewing the program on the platform.`, + }, + { + displayName: 'Affiliate ID', + name: 'affiliateId', + type: 'string', + displayOptions: { + show: { + resource: [ + 'programAffiliate', + ], + operation: [ + 'disapprove', + ], + }, + }, + description: 'The ID of the affiliate.', + }, + + /* -------------------------------------------------------------------------- */ + /* affiliate:get */ + /* -------------------------------------------------------------------------- */ + { + displayName: 'Program ID', + name: 'programId', + type: 'options', + required: true, + typeOptions: { + loadOptionsMethod: 'getPrograms', + }, + default: '', + displayOptions: { + show: { + operation: [ + 'get', + ], + resource: [ + 'programAffiliate', + ], + }, + }, + description: `The ID of the Program to add the affiliate to. This ID can be found as part of the URL when viewing the program on the platform.`, + }, + { + displayName: 'Affiliate ID', + name: 'affiliateId', + type: 'string', + required: true, + displayOptions: { + show: { + resource: [ + 'programAffiliate', + ], + operation: [ + 'get', + ], + }, + }, + description: 'The ID of the affiliate.', + }, + + /* -------------------------------------------------------------------------- */ + /* programAffiliate:getAll */ + /* -------------------------------------------------------------------------- */ + { + displayName: 'Program ID', + name: 'programId', + type: 'options', + typeOptions: { + loadOptionsMethod: 'getPrograms', + }, + required: true, + default: '', + displayOptions: { + show: { + operation: [ + 'getAll', + ], + resource: [ + 'programAffiliate', + ], + }, + }, + description: `The ID of the Program to add the affiliate to. This ID can be found as part of the URL when viewing the program on the platform.`, + }, + { + displayName: 'Return All', + name: 'returnAll', + type: 'boolean', + displayOptions: { + show: { + resource: [ + 'programAffiliate', + ], + operation: [ + 'getAll', + ], + }, + }, + default: false, + description: 'If set to true, all the results will be returned.', + }, + { + displayName: 'Limit', + name: 'limit', + type: 'number', + displayOptions: { + show: { + resource: [ + 'programAffiliate', + ], + operation: [ + 'getAll', + ], + returnAll: [ + false, + ], + }, + }, + typeOptions: { + minValue: 1, + maxValue: 1000, + }, + default: 100, + description: 'How many results to return.', + }, + { + displayName: 'Filters', + name: 'filters', + type: 'collection', + placeholder: 'Add Field', + default: {}, + displayOptions: { + show: { + resource: [ + 'programAffiliate', + ], + operation: [ + 'getAll', + ], + }, + }, + options: [ + { + displayName: 'Affiliate Group ID', + name: 'affiliate_group_id', + type: 'string', + default: '', + description: 'Retrieves affiliates for a certain affiliate group.', + }, + { + displayName: 'Email', + name: 'email', + type: 'string', + default: '', + description: 'An email address.', + }, + { + displayName: 'Parent ID', + name: 'parentId', + type: 'string', + default: '', + description: 'Retrieves children for a certain parent affiliate.', + }, + { + displayName: 'Source ID', + name: 'source_id', + type: 'string', + default: '', + description: 'Source ID.', + }, + ], + }, +] as INodeProperties[]; diff --git a/packages/nodes-base/nodes/Tapfiliate/Tapfiliate.node.ts b/packages/nodes-base/nodes/Tapfiliate/Tapfiliate.node.ts new file mode 100644 index 0000000000000..ef2535830728c --- /dev/null +++ b/packages/nodes-base/nodes/Tapfiliate/Tapfiliate.node.ts @@ -0,0 +1,280 @@ +import { + IExecuteFunctions, +} from 'n8n-core'; + +import { + IDataObject, + ILoadOptionsFunctions, + INodeExecutionData, + INodePropertyOptions, + INodeType, + INodeTypeDescription +} from 'n8n-workflow'; + +import { + affiliateFields, + affiliateOperations +} from './AffiliateDescription'; + +import { + affiliateMetadataFields, + affiliateMetadataOperations, +} from './AffiliateMetadataDescription'; + +import { + programAffiliateFields, + programAffiliateOperations, +} from './ProgramAffiliateDescription'; + +import { + tapfiliateApiRequest, + tapfiliateApiRequestAllItems, +} from './GenericFunctions'; + +export class Tapfiliate implements INodeType { + description: INodeTypeDescription = { + displayName: 'Tapfiliate', + name: 'tapfiliate', + icon: 'file:tapfiliate.svg', + group: ['transform'], + version: 1, + subtitle: '={{$parameter["operation"] + ":" + $parameter["resource"]}}', + description: 'Consume Tapfiliate API', + defaults: { + name: 'Tapfiliate', + color: '#4a8de8', + }, + inputs: ['main'], + outputs: ['main'], + credentials: [ + { + name: 'tapfiliateApi', + required: true, + }, + ], + properties: [ + { + displayName: 'Resource', + name: 'resource', + type: 'options', + options: [ + { + name: 'Affiliate', + value: 'affiliate', + }, + { + name: 'Affiliate Metadata', + value: 'affiliateMetadata', + }, + { + name: 'Program Affiliate', + value: 'programAffiliate', + }, + ], + default: 'affiliate', + required: true, + description: 'Resource to consume', + }, + ...affiliateOperations, + ...affiliateFields, + ...affiliateMetadataOperations, + ...affiliateMetadataFields, + ...programAffiliateOperations, + ...programAffiliateFields, + ], + }; + + methods = { + loadOptions: { + // Get custom fields to display to user so that they can select them easily + async getPrograms(this: ILoadOptionsFunctions,): Promise { + const returnData: INodePropertyOptions[] = []; + const programs = await tapfiliateApiRequestAllItems.call(this, 'GET', '/programs/'); + for (const program of programs) { + returnData.push({ + name: program.title, + value: program.id, + }); + } + return returnData; + }, + }, + }; + + async execute(this: IExecuteFunctions): Promise { + const items = this.getInputData(); + const length = (items.length as unknown) as number; + const qs: IDataObject = {}; + let responseData; + const returnData: IDataObject[] = []; + const resource = this.getNodeParameter('resource', 0) as string; + const operation = this.getNodeParameter('operation', 0) as string; + if (resource === 'affiliate') { + if (operation === 'create') { + //https://tapfiliate.com/docs/rest/#affiliates-affiliates-collection-post + for (let i = 0; i < length; i++) { + const firstname = this.getNodeParameter('firstname', i) as string; + const lastname = this.getNodeParameter('lastname', i) as string; + const email = this.getNodeParameter('email', i) as string; + const additionalFields = this.getNodeParameter('additionalFields', i) as IDataObject; + const body: IDataObject = { + firstname, + lastname, + email, + }; + Object.assign(body, additionalFields); + + if (body.addressUi) { + body.address = (body.addressUi as IDataObject).addressValues as IDataObject; + delete body.addressUi; + if ((body.address as IDataObject).country) { + (body.address as IDataObject).country = { + code: (body.address as IDataObject).country, + }; + } + } + + if (body.companyName) { + body.company = { + name: body.companyName, + }; + delete body.companyName; + } + responseData = await tapfiliateApiRequest.call(this, 'POST', '/affiliates/', body); + returnData.push(responseData); + } + } + if (operation === 'delete') { + //https://tapfiliate.com/docs/rest/#affiliates-affiliate-delete + for (let i = 0; i < length; i++) { + const affiliateId = this.getNodeParameter('affiliateId', i) as string; + responseData = await tapfiliateApiRequest.call(this, 'DELETE', `/affiliates/${affiliateId}/`); + returnData.push({ success: true }); + } + } + if (operation === 'get') { + //https://tapfiliate.com/docs/rest/#affiliates-affiliate-get + for (let i = 0; i < length; i++) { + const affiliateId = this.getNodeParameter('affiliateId', i) as string; + responseData = await tapfiliateApiRequest.call(this, 'GET', `/affiliates/${affiliateId}/`); + returnData.push(responseData); + } + } + if (operation === 'getAll') { + //https://tapfiliate.com/docs/rest/#affiliates-affiliates-collection-get + for (let i = 0; i < length; i++) { + const returnAll = this.getNodeParameter('returnAll', i) as boolean; + const filters = this.getNodeParameter('filters', i) as IDataObject; + Object.assign(qs, filters); + if (returnAll) { + responseData = await tapfiliateApiRequestAllItems.call(this, 'GET', `/affiliates/`, {}, qs); + } else { + const limit = this.getNodeParameter('limit', i) as number; + responseData = await tapfiliateApiRequest.call(this, 'GET', `/affiliates/`, {}, qs); + responseData = responseData.splice(0, limit); + } + returnData.push.apply(returnData, responseData); + } + } + } + if (resource === 'affiliateMetadata') { + if (operation === 'add') { + //https://tapfiliate.com/docs/rest/#affiliates-meta-data-key-put + for (let i = 0; i < length; i++) { + const affiliateId = this.getNodeParameter('affiliateId', i) as string; + const metadata = (this.getNodeParameter('metadataUi', i) as IDataObject || {}).metadataValues as IDataObject[] || []; + if (metadata.length === 0) { + throw new Error('Metadata cannot be empty.'); + } + for (const { key, value } of metadata) { + await tapfiliateApiRequest.call(this, 'PUT', `/affiliates/${affiliateId}/meta-data/${key}/`, { value }); + } + returnData.push({ success: true }); + } + } + if (operation === 'remove') { + //https://tapfiliate.com/docs/rest/#affiliates-meta-data-key-delete + for (let i = 0; i < length; i++) { + const affiliateId = this.getNodeParameter('affiliateId', i) as string; + const key = this.getNodeParameter('key', i) as string; + responseData = await tapfiliateApiRequest.call(this, 'DELETE', `/affiliates/${affiliateId}/meta-data/${key}/`); + returnData.push({ success: true }); + } + } + if (operation === 'update') { + //https://tapfiliate.com/docs/rest/#affiliates-notes-collection-get + for (let i = 0; i < length; i++) { + const affiliateId = this.getNodeParameter('affiliateId', i) as string; + const key = this.getNodeParameter('key', i) as string; + const value = this.getNodeParameter('value', i) as string; + responseData = await tapfiliateApiRequest.call(this, 'PUT', `/affiliates/${affiliateId}/meta-data/`, { [key]: value }); + returnData.push(responseData); + } + } + } + if (resource === 'programAffiliate') { + if (operation === 'add') { + //https://tapfiliate.com/docs/rest/#programs-program-affiliates-collection-post + for (let i = 0; i < length; i++) { + const programId = this.getNodeParameter('programId', i) as string; + const affiliateId = this.getNodeParameter('affiliateId', i) as string; + const additionalFields = this.getNodeParameter('additionalFields', i) as IDataObject; + const body: IDataObject = { + affiliate: { + id: affiliateId, + }, + }; + Object.assign(body, additionalFields); + + responseData = await tapfiliateApiRequest.call(this, 'POST', `/programs/${programId}/affiliates/`, body); + returnData.push(responseData); + } + } + if (operation === 'approve') { + //https://tapfiliate.com/docs/rest/#programs-approve-an-affiliate-for-a-program-put + for (let i = 0; i < length; i++) { + const programId = this.getNodeParameter('programId', i) as string; + const affiliateId = this.getNodeParameter('affiliateId', i) as string; + responseData = await tapfiliateApiRequest.call(this, 'PUT', `/programs/${programId}/affiliates/${affiliateId}/approved/`); + returnData.push(responseData); + } + } + if (operation === 'disapprove') { + //https://tapfiliate.com/docs/rest/#programs-approve-an-affiliate-for-a-program-delete + for (let i = 0; i < length; i++) { + const programId = this.getNodeParameter('programId', i) as string; + const affiliateId = this.getNodeParameter('affiliateId', i) as string; + responseData = await tapfiliateApiRequest.call(this, 'DELETE', `/programs/${programId}/affiliates/${affiliateId}/approved/`); + returnData.push(responseData); + } + } + if (operation === 'get') { + //https://tapfiliate.com/docs/rest/#programs-affiliate-in-program-get + for (let i = 0; i < length; i++) { + const programId = this.getNodeParameter('programId', i) as string; + const affiliateId = this.getNodeParameter('affiliateId', i) as string; + responseData = await tapfiliateApiRequest.call(this, 'GET', `/programs/${programId}/affiliates/${affiliateId}/`); + returnData.push(responseData); + } + } + if (operation === 'getAll') { + //https://tapfiliate.com/docs/rest/#programs-program-affiliates-collection-get + for (let i = 0; i < length; i++) { + const programId = this.getNodeParameter('programId', i) as string; + const returnAll = this.getNodeParameter('returnAll', i) as boolean; + const filters = this.getNodeParameter('filters', i) as IDataObject; + Object.assign(qs, filters); + if (returnAll) { + responseData = await tapfiliateApiRequestAllItems.call(this, 'GET', `/programs/${programId}/affiliates/`, {}, qs); + } else { + const limit = this.getNodeParameter('limit', i) as number; + responseData = await tapfiliateApiRequest.call(this, 'GET', `/programs/${programId}/affiliates/`, {}, qs); + responseData = responseData.splice(0, limit); + } + returnData.push.apply(returnData, responseData); + } + } + } + return [this.helpers.returnJsonArray(returnData)]; + } +} diff --git a/packages/nodes-base/nodes/Tapfiliate/tapfiliate.svg b/packages/nodes-base/nodes/Tapfiliate/tapfiliate.svg new file mode 100644 index 0000000000000..7662b67f709fc --- /dev/null +++ b/packages/nodes-base/nodes/Tapfiliate/tapfiliate.svg @@ -0,0 +1 @@ + diff --git a/packages/nodes-base/package.json b/packages/nodes-base/package.json index 2ae7d0f5e9ba7..f3bbf24220e93 100644 --- a/packages/nodes-base/package.json +++ b/packages/nodes-base/package.json @@ -212,6 +212,7 @@ "dist/credentials/SurveyMonkeyOAuth2Api.credentials.js", "dist/credentials/TaigaCloudApi.credentials.js", "dist/credentials/TaigaServerApi.credentials.js", + "dist/credentials/TapfiliateApi.credentials.js", "dist/credentials/TelegramApi.credentials.js", "dist/credentials/TheHiveApi.credentials.js", "dist/credentials/TimescaleDb.credentials.js", @@ -462,6 +463,7 @@ "dist/nodes/SurveyMonkey/SurveyMonkeyTrigger.node.js", "dist/nodes/Taiga/Taiga.node.js", "dist/nodes/Taiga/TaigaTrigger.node.js", + "dist/nodes/Tapfiliate/Tapfiliate.node.js", "dist/nodes/Telegram/Telegram.node.js", "dist/nodes/Telegram/TelegramTrigger.node.js", "dist/nodes/TheHive/TheHive.node.js",