From f36af0fcfdbd26c43d3fd6bdae02f01ad503b1b6 Mon Sep 17 00:00:00 2001 From: Ricardo Espinoza Date: Wed, 23 Dec 2020 02:19:10 -0500 Subject: [PATCH] :zap: Add user list resource to Iterable Node (#1252) * Add user list resource * :zap: Now lists are loaded automatically --- .../nodes/Iterable/Iterable.node.ts | 102 ++++++++- .../nodes/Iterable/UserDescription.ts | 22 +- .../nodes/Iterable/UserListDescription.ts | 208 ++++++++++++++++++ 3 files changed, 320 insertions(+), 12 deletions(-) create mode 100644 packages/nodes-base/nodes/Iterable/UserListDescription.ts diff --git a/packages/nodes-base/nodes/Iterable/Iterable.node.ts b/packages/nodes-base/nodes/Iterable/Iterable.node.ts index 4d04be5685..46c1f47ad6 100644 --- a/packages/nodes-base/nodes/Iterable/Iterable.node.ts +++ b/packages/nodes-base/nodes/Iterable/Iterable.node.ts @@ -4,7 +4,9 @@ import { import { IDataObject, + ILoadOptionsFunctions, INodeExecutionData, + INodePropertyOptions, INodeType, INodeTypeDescription, } from 'n8n-workflow'; @@ -23,6 +25,11 @@ import { userOperations, } from './UserDescription'; +import { + userListFields, + userListOperations, +} from './UserListDescription'; + import * as moment from 'moment-timezone'; export class Iterable implements INodeType { @@ -60,6 +67,10 @@ export class Iterable implements INodeType { name: 'User', value: 'user', }, + { + name: 'User List', + value: 'userList', + }, ], default: 'user', description: 'The resource to operate on.', @@ -68,9 +79,28 @@ export class Iterable implements INodeType { ...eventFields, ...userOperations, ...userFields, + ...userListOperations, + ...userListFields, ], }; + methods = { + loadOptions: { + // Get all the lists available channels + async getLists(this: ILoadOptionsFunctions): Promise { + const { lists } = await iterableApiRequest.call(this, 'GET', '/lists'); + const returnData: INodePropertyOptions[] = []; + for (const list of lists) { + returnData.push({ + name: list.name, + value: list.id, + }); + } + return returnData; + }, + }, + }; + async execute(this: IExecuteFunctions): Promise { const items = this.getInputData(); const returnData: IDataObject[] = []; @@ -181,7 +211,7 @@ export class Iterable implements INodeType { if (by === 'email') { const email = this.getNodeParameter('email', i) as string; - endpoint = `/users/${email}`; + endpoint = `/users/${email}`; } else { const userId = this.getNodeParameter('userId', i) as string; endpoint = `/users/byUserId/${userId}`; @@ -212,7 +242,7 @@ export class Iterable implements INodeType { if (by === 'email') { const email = this.getNodeParameter('email', i) as string; - endpoint = `/users/getByEmail`; + endpoint = `/users/getByEmail`; qs.email = email; } else { const userId = this.getNodeParameter('userId', i) as string; @@ -234,6 +264,74 @@ export class Iterable implements INodeType { } } } + + if (resource === 'userList') { + if (operation === 'add') { + //https://api.iterable.com/api/docs#lists_subscribe + const listId = this.getNodeParameter('listId', 0) as string; + + const identifier = this.getNodeParameter('identifier', 0) as string; + + const body: IDataObject = { + listId: parseInt(listId, 10), + subscribers: [], + }; + + const subscribers: IDataObject[] = []; + + for (let i = 0; i < length; i++) { + + const value = this.getNodeParameter('value', i) as string; + + if (identifier === 'email') { + subscribers.push({ email: value }); + } else { + subscribers.push({ userId: value }); + } + } + + body.subscribers = subscribers; + + responseData = await iterableApiRequest.call(this, 'POST', '/lists/subscribe', body); + + returnData.push(responseData); + } + + if (operation === 'remove') { + //https://api.iterable.com/api/docs#lists_unsubscribe + const listId = this.getNodeParameter('listId', 0) as string; + + const identifier = this.getNodeParameter('identifier', 0) as string; + + const additionalFields = this.getNodeParameter('additionalFields', 0) as IDataObject; + + const body: IDataObject = { + listId: parseInt(listId, 10), + subscribers: [], + campaignId: additionalFields.campaignId as number, + channelUnsubscribe: additionalFields.channelUnsubscribe as boolean, + }; + + const subscribers: IDataObject[] = []; + + for (let i = 0; i < length; i++) { + + const value = this.getNodeParameter('value', i) as string; + + if (identifier === 'email') { + subscribers.push({ email: value }); + } else { + subscribers.push({ userId: value }); + } + } + + body.subscribers = subscribers; + + responseData = await iterableApiRequest.call(this, 'POST', '/lists/unsubscribe', body); + + returnData.push(responseData); + } + } return [this.helpers.returnJsonArray(returnData)]; } } diff --git a/packages/nodes-base/nodes/Iterable/UserDescription.ts b/packages/nodes-base/nodes/Iterable/UserDescription.ts index ad12844068..776dc4c9f2 100644 --- a/packages/nodes-base/nodes/Iterable/UserDescription.ts +++ b/packages/nodes-base/nodes/Iterable/UserDescription.ts @@ -1,6 +1,6 @@ import { INodeProperties, - } from 'n8n-workflow'; +} from 'n8n-workflow'; export const userOperations = [ { @@ -38,9 +38,9 @@ export const userOperations = [ export const userFields = [ -/* -------------------------------------------------------------------------- */ -/* user:upsert */ -/* -------------------------------------------------------------------------- */ + /* -------------------------------------------------------------------------- */ + /* user:upsert */ + /* -------------------------------------------------------------------------- */ { displayName: 'Identifier', name: 'identifier', @@ -167,9 +167,10 @@ export const userFields = [ }, ], }, -/* -------------------------------------------------------------------------- */ -/* user:delete */ -/* -------------------------------------------------------------------------- */ + + /* -------------------------------------------------------------------------- */ + /* user:delete */ + /* -------------------------------------------------------------------------- */ { displayName: 'By', name: 'by', @@ -240,9 +241,10 @@ export const userFields = [ default: '', description: 'Email for a particular user', }, -/* -------------------------------------------------------------------------- */ -/* user:get */ -/* -------------------------------------------------------------------------- */ + + /* -------------------------------------------------------------------------- */ + /* user:get */ + /* -------------------------------------------------------------------------- */ { displayName: 'By', name: 'by', diff --git a/packages/nodes-base/nodes/Iterable/UserListDescription.ts b/packages/nodes-base/nodes/Iterable/UserListDescription.ts new file mode 100644 index 0000000000..cb8e0f6dfb --- /dev/null +++ b/packages/nodes-base/nodes/Iterable/UserListDescription.ts @@ -0,0 +1,208 @@ +import { + INodeProperties, +} from 'n8n-workflow'; + +export const userListOperations = [ + { + displayName: 'Operation', + name: 'operation', + type: 'options', + displayOptions: { + show: { + resource: [ + 'userList', + ], + }, + }, + options: [ + { + name: 'Add', + value: 'add', + description: 'Add user to list', + }, + { + name: 'Remove', + value: 'remove', + description: 'Remove a user from a list', + }, + ], + default: 'add', + description: 'The operation to perform.', + }, +] as INodeProperties[]; + +export const userListFields = [ + + /* -------------------------------------------------------------------------- */ + /* userList:add */ + /* -------------------------------------------------------------------------- */ + { + displayName: 'List ID', + name: 'listId', + type: 'options', + typeOptions: { + loadOptionsMethod: 'getLists', + }, + required: true, + displayOptions: { + show: { + resource: [ + 'userList', + ], + operation: [ + 'add', + ], + }, + }, + default: '', + description: 'Identifier to be used', + }, + { + displayName: 'Identifier', + name: 'identifier', + type: 'options', + required: true, + options: [ + { + name: 'Email', + value: 'email', + }, + { + name: 'User ID', + value: 'userId', + }, + ], + displayOptions: { + show: { + resource: [ + 'userList', + ], + operation: [ + 'add', + ], + }, + }, + default: '', + description: 'Identifier to be used', + }, + { + displayName: 'Value', + name: 'value', + type: 'string', + required: true, + displayOptions: { + show: { + resource: [ + 'userList', + ], + operation: [ + 'add', + ], + }, + }, + default: '', + }, + + /* -------------------------------------------------------------------------- */ + /* userList:remove */ + /* -------------------------------------------------------------------------- */ + { + displayName: 'List ID', + name: 'listId', + type: 'options', + typeOptions: { + loadOptionsMethod: 'getLists', + }, + required: true, + displayOptions: { + show: { + resource: [ + 'userList', + ], + operation: [ + 'remove', + ], + }, + }, + default: '', + description: 'Identifier to be used', + }, + { + displayName: 'Identifier', + name: 'identifier', + type: 'options', + required: true, + options: [ + { + name: 'Email', + value: 'email', + }, + { + name: 'User ID', + value: 'userId', + }, + ], + displayOptions: { + show: { + resource: [ + 'userList', + ], + operation: [ + 'remove', + ], + }, + }, + default: '', + description: 'Identifier to be used', + }, + { + displayName: 'Value', + name: 'value', + type: 'string', + required: true, + displayOptions: { + show: { + resource: [ + 'userList', + ], + operation: [ + 'remove', + ], + }, + }, + default: '', + }, + { + displayName: 'Additional Fields', + name: 'additionalFields', + type: 'collection', + placeholder: 'Add Field', + default: {}, + displayOptions: { + show: { + resource: [ + 'userList', + ], + operation: [ + 'remove', + ], + }, + }, + options: [ + { + displayName: 'Campaign ID', + name: 'campaignId', + type: 'number', + default: 0, + description: 'Attribute unsubscribe to a campaign', + }, + { + displayName: 'Channel Unsubscribe', + name: 'channelUnsubscribe', + type: 'boolean', + default: false, + description: `Unsubscribe email from list's associated channel - essentially a global unsubscribe`, + }, + ], + }, +] as INodeProperties[];