diff --git a/packages/nodes-base/nodes/ConvertKit/ConvertKit.node.ts b/packages/nodes-base/nodes/ConvertKit/ConvertKit.node.ts index 71f58a4172..8c9870639e 100644 --- a/packages/nodes-base/nodes/ConvertKit/ConvertKit.node.ts +++ b/packages/nodes-base/nodes/ConvertKit/ConvertKit.node.ts @@ -1,5 +1,6 @@ import { IExecuteFunctions, + ILoadOptionsFunctions, } from 'n8n-core'; import { @@ -7,6 +8,7 @@ import { INodeExecutionData, INodeTypeDescription, INodeType, + INodePropertyOptions, } from 'n8n-workflow'; import { @@ -14,9 +16,9 @@ import { } from './GenericFunctions'; import { - fieldOperations, - fieldFields, -} from './FieldDescription'; + customFieldOperations, + customFieldFields, +} from './CustomFieldDescription'; import { formOperations, @@ -33,6 +35,11 @@ import { tagFields, } from './TagDescription'; +import { + tagSubscriberOperations, + tagSubscriberFields, +} from './TagSubscriberDescription'; + export class ConvertKit implements INodeType { description: INodeTypeDescription = { displayName: 'ConvertKit', @@ -61,8 +68,8 @@ export class ConvertKit implements INodeType { type: 'options', options: [ { - name: 'Field', - value: 'field', + name: 'Custom Field', + value: 'customField', }, { name: 'Form', @@ -76,15 +83,19 @@ export class ConvertKit implements INodeType { name: 'Tag', value: 'tag', }, + { + name: 'Tag Subscriber', + value: 'tagSubscriber', + }, ], - default: 'field', + default: 'customField', description: 'The resource to operate on.' }, //-------------------- // Field Description //-------------------- - ...fieldOperations, - ...fieldFields, + ...customFieldOperations, + ...customFieldFields, //-------------------- // FormDescription //-------------------- @@ -100,187 +111,373 @@ export class ConvertKit implements INodeType { //-------------------- ...tagOperations, ...tagFields, + //-------------------- + // Tag Subscriber Description + //-------------------- + ...tagSubscriberOperations, + ...tagSubscriberFields, ], }; + methods = { + loadOptions: { + // Get all the tags to display them to user so that he can + // select them easily + async getTags(this: ILoadOptionsFunctions): Promise { + const returnData: INodePropertyOptions[] = []; + const { tags } = await convertKitApiRequest.call(this, 'GET', '/tags'); + for (const tag of tags) { + const tagName = tag.name; + const tagId = tag.id; + returnData.push({ + name: tagName, + value: tagId, + }); + } + + return returnData; + }, + // Get all the forms to display them to user so that he can + // select them easily + async getForms(this: ILoadOptionsFunctions): Promise { + const returnData: INodePropertyOptions[] = []; + const { forms } = await convertKitApiRequest.call(this, 'GET', '/forms'); + for (const form of forms) { + const formName = form.name; + const formId = form.id; + returnData.push({ + name: formName, + value: formId, + }); + } + + return returnData; + }, + + // Get all the sequences to display them to user so that he can + // select them easily + async getSequences(this: ILoadOptionsFunctions): Promise { + const returnData: INodePropertyOptions[] = []; + const { courses } = await convertKitApiRequest.call(this, 'GET', '/sequences'); + for (const course of courses) { + const courseName = course.name; + const courseId = course.id; + returnData.push({ + name: courseName, + value: courseId, + }); + } + + return returnData; + }, + } + }; + async execute(this: IExecuteFunctions): Promise { const items = this.getInputData(); const returnData: IDataObject[] = []; - let method = ''; - let endpoint = ''; const qs: IDataObject = {}; let responseData; const resource = this.getNodeParameter('resource', 0) as string; const operation = this.getNodeParameter('operation', 0) as string; - const fullOperation = `${resource}/${operation}`; - for (let i = 0; i < items.length; i++) { - //-------------------- - // Field Operations - //-------------------- - if(resource === 'field') { - //--------- - // Update - //--------- - if(operation === 'update') { - qs.label = this.getNodeParameter('label', i) as string; + + if (resource === 'customField') { + if (operation === 'create') { + + const label = this.getNodeParameter('label', i) as string; + + responseData = await convertKitApiRequest.call(this, 'POST', '/custom_fields', { label }, qs); + } + if (operation === 'delete') { const id = this.getNodeParameter('id', i) as string; - method = 'PUT'; - endpoint = `/custom_fields/${id}`; - //--------- - // Get All - //--------- - } else if(operation === 'getAll') { - method = 'GET'; - endpoint = '/custom_fields'; - //--------- - // Create - //--------- - } else if(operation === 'create') { - qs.label = this.getNodeParameter('label', i) as string; + responseData = await convertKitApiRequest.call(this, 'DELETE', `/custom_fields/${id}`); + } + if (operation === 'get') { - method = 'POST'; - endpoint = '/custom_fields'; - //--------- - // Delete - //--------- - } else if(operation === 'delete') { const id = this.getNodeParameter('id', i) as string; - method = 'DELETE'; - endpoint = `/custom_fields/${id}`; - } else { - throw new Error(`The operation "${operation}" is not known!`); + responseData = await convertKitApiRequest.call(this, 'GET', `/custom_fields/${id}`); } - //-------------------------------------------- - // Form, Sequence, and Tag Operations - //-------------------------------------------- - } else if(['form', 'sequence', 'tag'].includes(resource)) { - //----------------- - // Add Subscriber - //----------------- - if(operation === 'addSubscriber') { - qs.email= this.getNodeParameter('email', i) as string; - const id = this.getNodeParameter('id', i); + if (operation === 'getAll') { - const additionalParams = this.getNodeParameter('additionalFields', 0) as IDataObject; + const returnAll = this.getNodeParameter('returnAll', i) as boolean; - if(additionalParams.firstName) { - qs.first_name = additionalParams.firstName; + responseData = await convertKitApiRequest.call(this, 'GET', `/custom_fields`); + + responseData = responseData.custom_fields; + + if (!returnAll) { + + const limit = this.getNodeParameter('limit', i) as number; + + responseData = responseData.slice(0, limit); } - - if(additionalParams.fields !== undefined) { - const fields = {} as IDataObject; - const fieldsParams = additionalParams.fields as IDataObject; - const field = fieldsParams?.field as IDataObject[]; - - for(let j = 0; j < field.length; j++) { - const key = field[j].key as string; - const value = field[j].value as string; - - fields[key] = value; - } - - qs.fields = fields; - } - - if(resource === 'form') { - method = 'POST'; - endpoint = `/forms/${id}/subscribe`; - } else if(resource === 'sequence') { - method = 'POST'; - endpoint = `/sequences/${id}/subscribe`; - } else if(resource === 'tag') { - method = 'POST'; - endpoint = `/tags/${id}/subscribe`; - } - //----------------- - // Get All - //----------------- - } else if(operation === 'getAll') { - method = 'GET'; - if(resource === 'form') { - endpoint = '/forms'; - } else if(resource === 'tag') { - endpoint = '/tags'; - } else if(resource === 'sequence') { - endpoint = '/sequences'; - } - //-------------------- - // Get Subscriptions - //-------------------- - } else if(operation === 'getSubscriptions') { - const id = this.getNodeParameter('id', i); - const additionalParams = this.getNodeParameter('additionalFields', 0) as IDataObject; - if(additionalParams.subscriberState) { - qs.subscriber_state = additionalParams.subscriberState; - } - - method = 'GET'; - if(resource === 'form') { - endpoint = `/forms/${id}/subscriptions`; - } else if(resource === 'tag') { - endpoint = `/tags/${id}/subscriptions`; - } else if(resource === 'sequence') { - endpoint = `/sequences/${id}/subscriptions`; - } - //------------ - // Create Tag - //------------ - } else if(operation === 'create') { - const name = this.getNodeParameter('name', i); - qs.tag = { name, }; - - method = 'POST'; - endpoint = '/tags'; - //------------ - // Remove Tag - //------------ - } else if(operation === 'removeSubscriber') { - const id = this.getNodeParameter('id', i); - - qs.email = this.getNodeParameter('email', i); - - method = 'POST'; - endpoint = `/tags/${id}/unsubscribe`; - } else { - throw new Error(`The operation "${operation}" is not known!`); } - } else { - throw new Error(`The resource "${resource}" is not known!`); + if (operation === 'update') { + + const id = this.getNodeParameter('id', i) as string; + + const label = this.getNodeParameter('label', i) as string; + + responseData = await convertKitApiRequest.call(this, 'PUT', `/custom_fields/${id}`, { label }); + + responseData = { success: true }; + } } - responseData = await convertKitApiRequest.call(this, method, endpoint, {}, qs); + if (resource === 'form') { + if (operation === 'addSubscriber') { - if(fullOperation === 'field/getAll') { - responseData = responseData.custom_fields; - } else if(['form/addSubscriber', 'tag/addSubscriber', 'sequence/addSubscriber'].includes(fullOperation)) { - responseData = responseData.subscription; - } else if(fullOperation === 'form/getAll') { - responseData = responseData.forms; - } else if(['form/getSubscriptions', 'tag/getSubscriptions'].includes(fullOperation)) { - responseData = responseData.subscriptions; - } else if(fullOperation === 'tag/getAll') { - responseData = responseData.tags; - } else if(fullOperation === 'sequence/getAll') { - responseData = responseData.courses; + const email = this.getNodeParameter('email', i) as string; + + const formId = this.getNodeParameter('id', i) as string; + + const additionalFields = this.getNodeParameter('additionalFields', i) as IDataObject; + + const body: IDataObject = { + email, + }; + + if (additionalFields.firstName) { + body.first_name = additionalFields.firstName as string; + } + + if (additionalFields.tags) { + body.tags = additionalFields.tags as string[]; + } + + if (additionalFields.fieldsUi) { + const fieldValues = (additionalFields.fieldsUi as IDataObject).fieldsValues as IDataObject[]; + if (fieldValues) { + body.fields = {}; + for (const fieldValue of fieldValues) { + //@ts-ignore + body.fields[fieldValue.key] = fieldValue.value; + } + } + } + + const { subscription } = await convertKitApiRequest.call(this, 'POST', `/forms/${formId}/subscribe`, body); + + responseData = subscription; + } + if (operation === 'getAll') { + + const returnAll = this.getNodeParameter('returnAll', i) as boolean; + + responseData = await convertKitApiRequest.call(this, 'GET', `/forms`); + + responseData = responseData.forms; + + if (!returnAll) { + + const limit = this.getNodeParameter('limit', i) as number; + + responseData = responseData.slice(0, limit); + } + } + if (operation === 'getSubscriptions') { + + const formId = this.getNodeParameter('id', i) as string; + + const returnAll = this.getNodeParameter('returnAll', i) as boolean; + + const additionalFields = this.getNodeParameter('additionalFields', i) as IDataObject; + + if (additionalFields.subscriberState) { + qs.subscriber_state = additionalFields.subscriberState as string; + } + + responseData = await convertKitApiRequest.call(this, 'GET', `/forms/${formId}/subscriptions`, {}, qs); + + responseData = responseData.subscriptions; + + if (!returnAll) { + + const limit = this.getNodeParameter('limit', i) as number; + + responseData = responseData.slice(0, limit); + } + } + } + + if (resource === 'sequence') { + if (operation === 'addSubscriber') { + + const email = this.getNodeParameter('email', i) as string; + + const sequenceId = this.getNodeParameter('id', i) as string; + + const additionalFields = this.getNodeParameter('additionalFields', i) as IDataObject; + + const body: IDataObject = { + email, + }; + + if (additionalFields.firstName) { + body.first_name = additionalFields.firstName as string; + } + + if (additionalFields.tags) { + body.tags = additionalFields.tags as string[]; + } + + if (additionalFields.fieldsUi) { + const fieldValues = (additionalFields.fieldsUi as IDataObject).fieldsValues as IDataObject[]; + if (fieldValues) { + body.fields = {}; + for (const fieldValue of fieldValues) { + //@ts-ignore + body.fields[fieldValue.key] = fieldValue.value; + } + } + } + + const { subscription } = await convertKitApiRequest.call(this, 'POST', `/sequences/${sequenceId}/subscribe`, body); + + responseData = subscription; + } + if (operation === 'getAll') { + + const returnAll = this.getNodeParameter('returnAll', i) as boolean; + + responseData = await convertKitApiRequest.call(this, 'GET', `/sequences`); + + responseData = responseData.courses; + + if (!returnAll) { + + const limit = this.getNodeParameter('limit', i) as number; + + responseData = responseData.slice(0, limit); + } + } + if (operation === 'getSubscriptions') { + + const sequenceId = this.getNodeParameter('id', i) as string; + + const returnAll = this.getNodeParameter('returnAll', i) as boolean; + + const additionalFields = this.getNodeParameter('additionalFields', i) as IDataObject; + + if (additionalFields.subscriberState) { + qs.subscriber_state = additionalFields.subscriberState as string; + } + + responseData = await convertKitApiRequest.call(this, 'GET', `/sequences/${sequenceId}/subscriptions`, {}, qs); + + responseData = responseData.subscriptions; + + if (!returnAll) { + + const limit = this.getNodeParameter('limit', i) as number; + + responseData = responseData.slice(0, limit); + } + } + } + + if (resource === 'tag') { + if (operation === 'create') { + + const names = ((this.getNodeParameter('name', i) as string).split(',') as string[]).map((e) => ({ name: e })); + + const body: IDataObject = { + tag: names + }; + + responseData = await convertKitApiRequest.call(this, 'POST', '/tags', body); + } + + if (operation === 'getAll') { + + const returnAll = this.getNodeParameter('returnAll', i) as boolean; + + responseData = await convertKitApiRequest.call(this, 'GET', `/tags`); + + responseData = responseData.tags; + + if (!returnAll) { + + const limit = this.getNodeParameter('limit', i) as number; + + responseData = responseData.slice(0, limit); + } + } + } + + if (resource === 'tagSubscriber') { + + if (operation === 'add') { + + const tagId = this.getNodeParameter('tagId', i) as string; + + const email = this.getNodeParameter('email', i) as string; + + const additionalFields = this.getNodeParameter('additionalFields', i) as IDataObject; + + const body: IDataObject = { + email, + }; + + if (additionalFields.firstName) { + body.first_name = additionalFields.firstName as string; + } + + if (additionalFields.fieldsUi) { + const fieldValues = (additionalFields.fieldsUi as IDataObject).fieldsValues as IDataObject[]; + if (fieldValues) { + body.fields = {}; + for (const fieldValue of fieldValues) { + //@ts-ignore + body.fields[fieldValue.key] = fieldValue.value; + } + } + } + + const { subscription } = await convertKitApiRequest.call(this, 'POST', `/tags/${tagId}/subscribe`, body); + + responseData = subscription; + } + + if (operation === 'getAll') { + + const tagId = this.getNodeParameter('tagId', i) as string; + + const returnAll = this.getNodeParameter('returnAll', i) as boolean; + + responseData = await convertKitApiRequest.call(this, 'GET', `/tags/${tagId}/subscriptions`); + + responseData = responseData.subscriptions; + + if (!returnAll) { + + const limit = this.getNodeParameter('limit', i) as number; + + responseData = responseData.slice(0, limit); + } + } + + if (operation === 'delete') { + + const tagId = this.getNodeParameter('tagId', i) as string; + + const email = this.getNodeParameter('email', i) as string; + + responseData= await convertKitApiRequest.call(this, 'POST', `/tags/${tagId}>/unsubscribe`, { email }); + } } if (Array.isArray(responseData)) { returnData.push.apply(returnData, responseData as IDataObject[]); } else if (responseData !== undefined) { returnData.push(responseData as IDataObject); - } else { - if(method === 'GET') { - returnData.push( { } ); - } else { - returnData.push( { success: true } ); - } } } diff --git a/packages/nodes-base/nodes/ConvertKit/FieldDescription.ts b/packages/nodes-base/nodes/ConvertKit/CustomFieldDescription.ts similarity index 52% rename from packages/nodes-base/nodes/ConvertKit/FieldDescription.ts rename to packages/nodes-base/nodes/ConvertKit/CustomFieldDescription.ts index 8966e60efa..a8541c6493 100644 --- a/packages/nodes-base/nodes/ConvertKit/FieldDescription.ts +++ b/packages/nodes-base/nodes/ConvertKit/CustomFieldDescription.ts @@ -1,8 +1,8 @@ import { - INodeProperties + INodeProperties, } from 'n8n-workflow'; -export const fieldOperations = [ +export const customFieldOperations = [ { displayName: 'Operation', name: 'operation', @@ -10,7 +10,7 @@ export const fieldOperations = [ displayOptions: { show: { resource: [ - 'field', + 'customField', ], }, }, @@ -18,22 +18,22 @@ export const fieldOperations = [ { name: 'Create', value: 'create', - description: 'Create a field.', + description: 'Create a field', }, { name: 'Delete', value: 'delete', - description: 'Delete a field.', + description: 'Delete a field', }, { name: 'Get All', value: 'getAll', - description: `List all of your account's custom fields.`, + description: 'Get all fields', }, { name: 'Update', value: 'update', - description: 'Update a field.', + description: 'Update a field', }, ], default: 'update', @@ -41,7 +41,7 @@ export const fieldOperations = [ }, ] as INodeProperties[]; -export const fieldFields = [ +export const customFieldFields = [ { displayName: 'Field ID', name: 'id', @@ -50,7 +50,7 @@ export const fieldFields = [ displayOptions: { show: { resource: [ - 'field', + 'customField', ], operation: [ 'update', @@ -69,7 +69,7 @@ export const fieldFields = [ displayOptions: { show: { resource: [ - 'field', + 'customField', ], operation: [ 'update', @@ -80,4 +80,45 @@ export const fieldFields = [ default: '', description: 'The label of the custom field.', }, + { + displayName: 'Return All', + name: 'returnAll', + type: 'boolean', + displayOptions: { + show: { + operation: [ + 'getAll', + ], + resource: [ + 'customField', + ], + }, + }, + default: false, + description: 'If all results should be returned or only up to a given limit.', + }, + { + displayName: 'Limit', + name: 'limit', + type: 'number', + displayOptions: { + show: { + operation: [ + 'getAll', + ], + resource: [ + 'customField', + ], + returnAll: [ + false, + ], + }, + }, + typeOptions: { + minValue: 1, + maxValue: 500, + }, + default: 100, + description: 'How many results to return.', + }, ] as INodeProperties[]; diff --git a/packages/nodes-base/nodes/ConvertKit/FormDescription.ts b/packages/nodes-base/nodes/ConvertKit/FormDescription.ts index 3e266d18a5..f6e1a6f3e5 100644 --- a/packages/nodes-base/nodes/ConvertKit/FormDescription.ts +++ b/packages/nodes-base/nodes/ConvertKit/FormDescription.ts @@ -18,17 +18,17 @@ export const formOperations = [ { name: 'Add Subscriber', value: 'addSubscriber', - description: 'Add a subscriber.', + description: 'Add a subscriber', }, { name: 'Get All', value: 'getAll', - description: 'Get a list of all the forms for your account.', + description: 'Get all forms', }, { name: 'Get Subscriptions', value: 'getSubscriptions', - description: 'List subscriptions to a form including subscriber data.', + description: 'List subscriptions to a form including subscriber data', }, ], default: 'addSubscriber', @@ -38,7 +38,7 @@ export const formOperations = [ export const formFields = [ { - displayName: 'Email Address', + displayName: 'Email', name: 'email', type: 'string', required: true, @@ -58,7 +58,10 @@ export const formFields = [ { displayName: 'Form ID', name: 'id', - type: 'string', + type: 'options', + typeOptions: { + loadOptionsMethod: 'getForms', + }, required: true, displayOptions: { show: { @@ -91,16 +94,9 @@ export const formFields = [ }, }, options: [ - { - displayName: 'First Name', - name: 'firstName', - type: 'string', - default: '', - description: `The subscriber's first name.`, - }, { displayName: 'Custom Fields', - name: 'fields', + name: 'fieldsUi', placeholder: 'Add Custom Field', description: 'Object of key/value pairs for custom fields (the custom field must exist before you can use it here).', type: 'fixedCollection', @@ -110,7 +106,7 @@ export const formFields = [ default: {}, options: [ { - name: 'field', + name: 'fieldsValues', displayName: 'Custom Field', values: [ { @@ -133,8 +129,58 @@ export const formFields = [ }, ], }, + { + displayName: 'First Name', + name: 'firstName', + type: 'string', + default: '', + description: `The subscriber's first name.`, + }, ], }, + { + displayName: 'Return All', + name: 'returnAll', + type: 'boolean', + displayOptions: { + show: { + operation: [ + 'getAll', + 'getSubscriptions', + ], + resource: [ + 'form', + ], + }, + }, + default: false, + description: 'If all results should be returned or only up to a given limit.', + }, + { + displayName: 'Limit', + name: 'limit', + type: 'number', + displayOptions: { + show: { + operation: [ + 'getAll', + 'getSubscriptions', + ], + resource: [ + 'form', + ], + returnAll: [ + false, + ], + }, + }, + typeOptions: { + minValue: 1, + maxValue: 500, + }, + default: 100, + description: 'How many results to return.', + }, { displayName: 'Additional Fields', name: 'additionalFields', diff --git a/packages/nodes-base/nodes/ConvertKit/GenericFunctions.ts b/packages/nodes-base/nodes/ConvertKit/GenericFunctions.ts index 525aed043a..4844045e35 100644 --- a/packages/nodes-base/nodes/ConvertKit/GenericFunctions.ts +++ b/packages/nodes-base/nodes/ConvertKit/GenericFunctions.ts @@ -15,7 +15,9 @@ import { export async function convertKitApiRequest(this: IExecuteFunctions | IExecuteSingleFunctions | ILoadOptionsFunctions | IHookFunctions, method: string, endpoint: string, body: any = {}, qs: IDataObject = {}, uri?: string, option: IDataObject = {}): Promise { // tslint:disable-line:no-any - const credentials = this.getCredentials('convertKitApi'); + + const credentials = this.getCredentials('convertKitApi'); + if (credentials === undefined) { throw new Error('No credentials got returned!'); } @@ -30,15 +32,29 @@ export async function convertKitApiRequest(this: IExecuteFunctions | IExecuteSin uri: uri ||`https://api.convertkit.com/v3${endpoint}`, json: true, }; + options = Object.assign({}, options, option); + if (Object.keys(options.body).length === 0) { delete options.body; } + + console.log(options); + try { + qs.api_secret = credentials.apiSecret; return await this.helpers.request!(options); + } catch (error) { - throw new Error(`ConvertKit error response: ${error.message}`); + + let errorMessage = error; + + if (error.response && error.response.body && error.response.body.message) { + errorMessage = error.response.body.message; + } + + throw new Error(`ConvertKit error response: ${errorMessage}`); } } diff --git a/packages/nodes-base/nodes/ConvertKit/SequenceDescription.ts b/packages/nodes-base/nodes/ConvertKit/SequenceDescription.ts index 1b82b741fa..493b713cda 100644 --- a/packages/nodes-base/nodes/ConvertKit/SequenceDescription.ts +++ b/packages/nodes-base/nodes/ConvertKit/SequenceDescription.ts @@ -1,5 +1,5 @@ import { - INodeProperties + INodeProperties, } from 'n8n-workflow'; export const sequenceOperations = [ @@ -23,7 +23,7 @@ export const sequenceOperations = [ { name: 'Get All', value: 'getAll', - description: 'Returns a list of sequences for the account.', + description: 'Get all sequences.', }, { name: 'Get Subscriptions', @@ -38,7 +38,7 @@ export const sequenceOperations = [ export const sequenceFields = [ { - displayName: 'Email Address', + displayName: 'Email', name: 'email', type: 'string', required: true, @@ -58,7 +58,10 @@ export const sequenceFields = [ { displayName: 'Sequence ID', name: 'id', - type: 'string', + type: 'options', + typeOptions: { + loadOptionsMethod: 'getSequences', + }, required: true, displayOptions: { show: { @@ -74,6 +77,49 @@ export const sequenceFields = [ default: '', description: 'Sequence ID.', }, + { + displayName: 'Return All', + name: 'returnAll', + type: 'boolean', + displayOptions: { + show: { + operation: [ + 'getAll', + 'getSubscriptions', + ], + resource: [ + 'sequence', + ], + }, + }, + default: false, + description: 'If all results should be returned or only up to a given limit.', + }, + { + displayName: 'Limit', + name: 'limit', + type: 'number', + displayOptions: { + show: { + operation: [ + 'getAll', + 'getSubscriptions', + ], + resource: [ + 'sequence', + ], + returnAll: [ + false, + ], + }, + }, + typeOptions: { + minValue: 1, + maxValue: 500, + }, + default: 100, + description: 'How many results to return.', + }, { displayName: 'Additional Fields', name: 'additionalFields', @@ -91,16 +137,9 @@ export const sequenceFields = [ }, }, options: [ - { - displayName: 'First Name', - name: 'firstName', - type: 'string', - default: '', - description: `The subscriber's first name.`, - }, { displayName: 'Custom Fields', - name: 'fields', + name: 'fieldsUi', placeholder: 'Add Custom Field', description: 'Object of key/value pairs for custom fields (the custom field must exist before you can use it here).', type: 'fixedCollection', @@ -110,7 +149,7 @@ export const sequenceFields = [ default: {}, options: [ { - name: 'field', + name: 'fieldsValues', displayName: 'Custom Field', values: [ { @@ -133,6 +172,23 @@ export const sequenceFields = [ }, ], }, + { + displayName: 'First Name', + name: 'firstName', + type: 'string', + default: '', + description: `The subscriber's first name.`, + }, + { + displayName: 'Tag IDs', + name: 'tags', + type: 'multiOptions', + typeOptions: { + loadOptionsMethod: 'getTags', + }, + default: [], + description: 'Tags', + }, ], }, { diff --git a/packages/nodes-base/nodes/ConvertKit/TagDescription.ts b/packages/nodes-base/nodes/ConvertKit/TagDescription.ts index a1a718ba3c..b0d18adcfc 100644 --- a/packages/nodes-base/nodes/ConvertKit/TagDescription.ts +++ b/packages/nodes-base/nodes/ConvertKit/TagDescription.ts @@ -1,5 +1,5 @@ import { - INodeProperties + INodeProperties, } from 'n8n-workflow'; export const tagOperations = [ @@ -18,27 +18,12 @@ export const tagOperations = [ { name: 'Create', value: 'create', - description: 'Create a tag.', + description: 'Create a tag', }, { name: 'Get All', value: 'getAll', - description: 'Returns a list of tags for the account.', - }, - { - name: 'Get Subscriptions', - value: 'getSubscriptions', - description: 'List subscriptions to a tag including subscriber data.', - }, - { - name: 'Remove Subscriber', - value: 'removeSubscriber', - description: 'Remove a tag from a subscriber.', - }, - { - name: 'Add Subscriber', - value: 'addSubscriber', - description: 'Add a tag to a subscriber.', + description: 'Get all tags', }, ], default: 'create', @@ -63,142 +48,47 @@ export const tagFields = [ }, }, default: '', - description: 'Tag name.', + description: 'Tag name, multiple can be added separated by comma', }, { - displayName: 'Email Address', - name: 'email', - type: 'string', - required: true, + displayName: 'Return All', + name: 'returnAll', + type: 'boolean', displayOptions: { show: { + operation: [ + 'getAll', + ], resource: [ 'tag', ], - operation: [ - 'addSubscriber', - 'removeSubscriber', - ], }, }, - default: '', - description: 'Subscriber email address.', + default: false, + description: 'If all results should be returned or only up to a given limit.', }, { - displayName: 'Tag ID', - name: 'id', - type: 'string', - required: true, + displayName: 'Limit', + name: 'limit', + type: 'number', displayOptions: { show: { + operation: [ + 'getAll', + ], resource: [ 'tag', ], - operation: [ - 'addSubscriber', - 'removeSubscriber', - 'getSubscriptions', + returnAll: [ + false, ], }, }, - default: '', - description: 'Tag ID.', - }, - { - displayName: 'Additional Fields', - name: 'additionalFields', - type: 'collection', - placeholder: 'Add Field', - default: {}, - displayOptions: { - show: { - resource: [ - 'tag', - ], - operation: [ - 'addSubscriber', - ], - }, + typeOptions: { + minValue: 1, + maxValue: 500, }, - options: [ - { - displayName: 'First Name', - name: 'firstName', - type: 'string', - default: '', - description: 'Subscriber first name.', - }, - { - displayName: 'Custom Fields', - name: 'fields', - placeholder: 'Add Custom Field', - description: 'Object of key/value pairs for custom fields (the custom field must exist before you can use it here).', - type: 'fixedCollection', - typeOptions: { - multipleValues: true, - }, - default: {}, - options: [ - { - name: 'field', - displayName: 'Custom Field', - values: [ - { - displayName: 'Field Key', - name: 'key', - type: 'string', - default: '', - placeholder: 'last_name', - description: `The field's key.`, - }, - { - displayName: 'Field Value', - name: 'value', - type: 'string', - default: '', - placeholder: 'Doe', - description: 'Value of the field.', - }, - ], - }, - ], - }, - ], - }, - { - displayName: 'Additional Fields', - name: 'additionalFields', - type: 'collection', - placeholder: 'Add Field', - default: {}, - displayOptions: { - show: { - resource: [ - 'tag', - ], - operation: [ - 'getSubscriptions', - ], - }, - }, - options: [ - { - displayName: 'Subscriber State', - name: 'subscriberState', - type: 'options', - options: [ - { - name: 'Active', - value: 'active', - }, - { - name: 'Cancelled', - value: 'cancelled', - }, - ], - default: 'active', - }, - ], - description: 'Receive only active subscribers or cancelled subscribers.', + default: 100, + description: 'How many results to return.', }, ] as INodeProperties[]; diff --git a/packages/nodes-base/nodes/ConvertKit/TagSubscriberDescription.ts b/packages/nodes-base/nodes/ConvertKit/TagSubscriberDescription.ts new file mode 100644 index 0000000000..f625dfed83 --- /dev/null +++ b/packages/nodes-base/nodes/ConvertKit/TagSubscriberDescription.ts @@ -0,0 +1,219 @@ +import { + INodeProperties, +} from 'n8n-workflow'; + +export const tagSubscriberOperations = [ + { + displayName: 'Operation', + name: 'operation', + type: 'options', + displayOptions: { + show: { + resource: [ + 'tagSubscriber', + ], + }, + }, + options: [ + { + name: 'Add', + value: 'add', + description: 'Add a tag to a subscriber', + }, + { + name: 'Get All', + value: 'getAll', + description: 'List subscriptions to a tag including subscriber data', + }, + { + name: 'Delete', + value: 'delete', + description: 'Delete a tag from a subscriber', + }, + ], + default: 'create', + description: 'The operation to perform.', + }, +] as INodeProperties[]; + +export const tagSubscriberFields = [ + { + displayName: 'Tag ID', + name: 'tagId', + type: 'options', + typeOptions: { + loadOptionsMethod: 'getTags', + }, + required: true, + displayOptions: { + show: { + resource: [ + 'tagSubscriber', + ], + operation: [ + 'add', + 'getAll', + 'delete', + ], + }, + }, + default: '', + }, + { + displayName: 'Email', + name: 'email', + type: 'string', + required: true, + displayOptions: { + show: { + resource: [ + 'tagSubscriber', + ], + operation: [ + 'add', + 'delete', + ], + }, + }, + default: '', + description: 'Subscriber email address.', + }, + { + displayName: 'Additional Fields', + name: 'additionalFields', + type: 'collection', + placeholder: 'Add Field', + default: {}, + displayOptions: { + show: { + resource: [ + 'tagSubscriber', + ], + operation: [ + 'add', + ], + }, + }, + options: [ + { + displayName: 'Custom Fields', + name: 'fields', + placeholder: 'Add Custom Field', + description: 'Object of key/value pairs for custom fields (the custom field must exist before you can use it here).', + type: 'fixedCollection', + typeOptions: { + multipleValues: true, + }, + default: {}, + options: [ + { + name: 'field', + displayName: 'Custom Field', + values: [ + { + displayName: 'Field Key', + name: 'key', + type: 'string', + default: '', + placeholder: 'last_name', + description: `The field's key.`, + }, + { + displayName: 'Field Value', + name: 'value', + type: 'string', + default: '', + placeholder: 'Doe', + description: 'Value of the field.', + }, + ], + }, + ], + }, + { + displayName: 'First Name', + name: 'firstName', + type: 'string', + default: '', + description: 'Subscriber first name.', + }, + ], + }, + { + displayName: 'Return All', + name: 'returnAll', + type: 'boolean', + displayOptions: { + show: { + operation: [ + 'getAll', + ], + resource: [ + 'tagSubscriber', + ], + }, + }, + default: false, + description: 'If all results should be returned or only up to a given limit.', + }, + { + displayName: 'Limit', + name: 'limit', + type: 'number', + displayOptions: { + show: { + operation: [ + 'getAll', + ], + resource: [ + 'tagSubscriber', + ], + returnAll: [ + false, + ], + }, + }, + typeOptions: { + minValue: 1, + maxValue: 500, + }, + default: 100, + description: 'How many results to return.', + }, + { + displayName: 'Additional Fields', + name: 'additionalFields', + type: 'collection', + placeholder: 'Add Field', + default: {}, + displayOptions: { + show: { + resource: [ + 'tagSubscriber', + ], + operation: [ + 'getAll', + ], + }, + }, + options: [ + { + displayName: 'Subscriber State', + name: 'subscriberState', + type: 'options', + options: [ + { + name: 'Active', + value: 'active', + }, + { + name: 'Cancelled', + value: 'cancelled', + }, + ], + default: 'active', + }, + ], + description: 'Receive only active subscribers or cancelled subscribers.', + }, +] as INodeProperties[];