diff --git a/packages/nodes-base/credentials/TapfiliateApi.credentials.ts b/packages/nodes-base/credentials/TapfiliateApi.credentials.ts
new file mode 100644
index 0000000000..d52df982a1
--- /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 0000000000..c32d2bc876
--- /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 0000000000..d65d38794d
--- /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 0000000000..2eea6eac51
--- /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 0000000000..b2f6b3d396
--- /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 0000000000..ef25358307
--- /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 0000000000..7662b67f70
--- /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 2ae7d0f5e9..f3bbf24220 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",