mirror of
https://github.com/n8n-io/n8n.git
synced 2024-12-25 20:54:07 -08:00
✨ Add Customer.io-Node (#833)
* 🚧 Descriptions, node function, generic function changes * ✅ Finished functionality, added icon * ⚡ Added new campaign operations, acommodated for 2 different authentications * ⚡ Fixed number defaults not being numbers but empty strings
This commit is contained in:
parent
0e1a4e5309
commit
e5a5e1ed11
|
@ -10,11 +10,26 @@ export class CustomerIoApi implements ICredentialType {
|
|||
documentationUrl = 'customerIo';
|
||||
properties = [
|
||||
{
|
||||
displayName: 'App API Key',
|
||||
name: 'apiKey',
|
||||
displayName: 'Tracking API Key',
|
||||
name: 'trackingApiKey',
|
||||
type: 'string' as NodePropertyTypes,
|
||||
default: '',
|
||||
description: 'Required for tracking API.',
|
||||
required: true
|
||||
},
|
||||
{
|
||||
displayName: 'Tracking Site ID',
|
||||
name: 'trackingSiteId',
|
||||
type: 'string' as NodePropertyTypes,
|
||||
default: '',
|
||||
description: 'Required for tracking API.'
|
||||
},
|
||||
{
|
||||
displayName: 'App API Key',
|
||||
name: 'appApiKey',
|
||||
type: 'string' as NodePropertyTypes,
|
||||
default: '',
|
||||
description: 'Required for App API.'
|
||||
},
|
||||
|
||||
];
|
||||
}
|
||||
|
|
199
packages/nodes-base/nodes/CustomerIo/CampaignDescription.ts
Normal file
199
packages/nodes-base/nodes/CustomerIo/CampaignDescription.ts
Normal file
|
@ -0,0 +1,199 @@
|
|||
import { INodeProperties } from 'n8n-workflow';
|
||||
|
||||
export const campaignOperations = [
|
||||
{
|
||||
displayName: 'Operation',
|
||||
name: 'operation',
|
||||
type: 'options',
|
||||
displayOptions: {
|
||||
show: {
|
||||
resource: [
|
||||
'campaign',
|
||||
],
|
||||
},
|
||||
},
|
||||
options: [
|
||||
{
|
||||
name: 'Get',
|
||||
value: 'get',
|
||||
},
|
||||
{
|
||||
name: 'Get All',
|
||||
value: 'getAll',
|
||||
},
|
||||
{
|
||||
name: 'Get Metrics',
|
||||
value: 'getMetrics',
|
||||
},
|
||||
],
|
||||
default: 'get',
|
||||
description: 'The operation to perform',
|
||||
},
|
||||
] as INodeProperties[];
|
||||
|
||||
export const campaignFields = [
|
||||
/* -------------------------------------------------------------------------- */
|
||||
/* campaign:get */
|
||||
/* -------------------------------------------------------------------------- */
|
||||
{
|
||||
displayName: 'Campaign ID',
|
||||
name: 'campaignId',
|
||||
type: 'number',
|
||||
required: true,
|
||||
default: 0,
|
||||
displayOptions: {
|
||||
show: {
|
||||
resource: [
|
||||
'campaign',
|
||||
],
|
||||
operation: [
|
||||
'get'
|
||||
]
|
||||
},
|
||||
},
|
||||
description: 'The unique identifier for the campaign',
|
||||
},
|
||||
/* -------------------------------------------------------------------------- */
|
||||
/* campaign:getMetrics */
|
||||
/* -------------------------------------------------------------------------- */
|
||||
{
|
||||
displayName: 'Campaign ID',
|
||||
name: 'campaignId',
|
||||
type: 'number',
|
||||
required: true,
|
||||
default: 0,
|
||||
displayOptions: {
|
||||
show: {
|
||||
resource: [
|
||||
'campaign',
|
||||
],
|
||||
operation: [
|
||||
'getMetrics'
|
||||
]
|
||||
},
|
||||
},
|
||||
description: 'The unique identifier for the campaign',
|
||||
},
|
||||
{
|
||||
displayName: 'Period',
|
||||
name: 'period',
|
||||
type: 'options',
|
||||
default: 'days',
|
||||
displayOptions: {
|
||||
show: {
|
||||
resource: [
|
||||
'campaign',
|
||||
],
|
||||
operation: [
|
||||
'getMetrics'
|
||||
]
|
||||
},
|
||||
},
|
||||
description: 'Specify metric period',
|
||||
options: [
|
||||
{
|
||||
name: 'Hours',
|
||||
value: 'hours'
|
||||
},
|
||||
{
|
||||
name: 'Days',
|
||||
value: 'days'
|
||||
},
|
||||
{
|
||||
name: 'Weeks',
|
||||
value: 'weeks'
|
||||
},
|
||||
{
|
||||
name: 'Months',
|
||||
value: 'months'
|
||||
},
|
||||
]
|
||||
},
|
||||
{
|
||||
displayName: 'JSON Parameters',
|
||||
name: 'jsonParameters',
|
||||
type: 'boolean',
|
||||
default: false,
|
||||
description: '',
|
||||
displayOptions: {
|
||||
show: {
|
||||
resource: [
|
||||
'campaign',
|
||||
],
|
||||
operation: [
|
||||
'getMetrics'
|
||||
],
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
displayName: 'Additional Fields',
|
||||
name: 'additionalFields',
|
||||
type: 'collection',
|
||||
placeholder: 'Add Field',
|
||||
default: {},
|
||||
displayOptions: {
|
||||
show: {
|
||||
resource: [
|
||||
'campaign',
|
||||
],
|
||||
operation: [
|
||||
'getMetrics'
|
||||
],
|
||||
jsonParameters: [
|
||||
false,
|
||||
],
|
||||
},
|
||||
},
|
||||
options: [
|
||||
{
|
||||
displayName: 'Steps',
|
||||
name: 'steps',
|
||||
type: 'number',
|
||||
default: 0,
|
||||
description: 'Integer specifying how many steps to return. Defaults to the maximum number of timeperiods available, or 12 when using the months period. Maximum timeperiods available are 24 hours, 45 days, 12 weeks and 120 months',
|
||||
typeOptions: {
|
||||
minValue: 0,
|
||||
maxValue: 120
|
||||
}
|
||||
},
|
||||
{
|
||||
displayName: 'Type',
|
||||
name: 'type',
|
||||
type: 'options',
|
||||
default: 'empty',
|
||||
description: 'Specify metric type',
|
||||
options: [
|
||||
{
|
||||
name: 'Empty',
|
||||
value: 'empty'
|
||||
},
|
||||
{
|
||||
name: 'Email',
|
||||
value: 'email'
|
||||
},
|
||||
{
|
||||
name: 'Webhook',
|
||||
value: 'webhook'
|
||||
},
|
||||
{
|
||||
name: 'twilio',
|
||||
value: 'twilio'
|
||||
},
|
||||
{
|
||||
name: 'Urban Airship',
|
||||
value: 'urbanAirship'
|
||||
},
|
||||
{
|
||||
name: 'Slack',
|
||||
value: 'slack'
|
||||
},
|
||||
{
|
||||
name: 'Push',
|
||||
value: 'push'
|
||||
},
|
||||
]
|
||||
},
|
||||
],
|
||||
},
|
||||
] as INodeProperties[];
|
326
packages/nodes-base/nodes/CustomerIo/CustomerDescription.ts
Normal file
326
packages/nodes-base/nodes/CustomerIo/CustomerDescription.ts
Normal file
|
@ -0,0 +1,326 @@
|
|||
import { INodeProperties } from 'n8n-workflow';
|
||||
|
||||
export const customerOperations = [
|
||||
{
|
||||
displayName: 'Operation',
|
||||
name: 'operation',
|
||||
type: 'options',
|
||||
displayOptions: {
|
||||
show: {
|
||||
resource: [
|
||||
'customer',
|
||||
],
|
||||
},
|
||||
},
|
||||
options: [
|
||||
{
|
||||
name: 'Create',
|
||||
value: 'create',
|
||||
description: 'Create a customer.',
|
||||
},
|
||||
{
|
||||
name: 'Delete',
|
||||
value: 'delete',
|
||||
description: 'Delete a customer.',
|
||||
},
|
||||
{
|
||||
name: 'Update',
|
||||
value: 'update',
|
||||
description: 'Update a customer.',
|
||||
},
|
||||
],
|
||||
default: 'create',
|
||||
description: 'The operation to perform.',
|
||||
},
|
||||
] as INodeProperties[];
|
||||
|
||||
export const customerFields = [
|
||||
|
||||
/* -------------------------------------------------------------------------- */
|
||||
/* customer:create/delete */
|
||||
/* -------------------------------------------------------------------------- */
|
||||
{
|
||||
displayName: 'ID',
|
||||
name: 'id',
|
||||
type: 'number',
|
||||
required: true,
|
||||
default: 0,
|
||||
displayOptions: {
|
||||
show: {
|
||||
resource: [
|
||||
'customer',
|
||||
],
|
||||
operation: [
|
||||
'create', 'delete'
|
||||
]
|
||||
},
|
||||
},
|
||||
description: 'The unique identifier for the customer.',
|
||||
},
|
||||
{
|
||||
displayName: 'Email',
|
||||
name: 'email',
|
||||
type: 'string',
|
||||
default: '',
|
||||
displayOptions: {
|
||||
show: {
|
||||
resource: [
|
||||
'customer',
|
||||
],
|
||||
operation: [
|
||||
'create'
|
||||
]
|
||||
},
|
||||
},
|
||||
description: 'The email address of the user.',
|
||||
},
|
||||
{
|
||||
displayName: 'Created at',
|
||||
name: 'createdAt',
|
||||
type: 'dateTime',
|
||||
default: '',
|
||||
displayOptions: {
|
||||
show: {
|
||||
resource: [
|
||||
'customer',
|
||||
],
|
||||
operation: [
|
||||
'create'
|
||||
]
|
||||
},
|
||||
},
|
||||
description: 'The UNIX timestamp from when the user was created.',
|
||||
},
|
||||
{
|
||||
displayName: 'JSON Parameters',
|
||||
name: 'jsonParameters',
|
||||
type: 'boolean',
|
||||
default: false,
|
||||
description: '',
|
||||
displayOptions: {
|
||||
show: {
|
||||
resource: [
|
||||
'customer',
|
||||
],
|
||||
operation: [
|
||||
'create'
|
||||
],
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
displayName: ' Additional Fields',
|
||||
name: 'additionalFieldsJson',
|
||||
type: 'json',
|
||||
typeOptions: {
|
||||
alwaysOpenEditWindow: true,
|
||||
},
|
||||
default: '',
|
||||
displayOptions: {
|
||||
show: {
|
||||
resource: [
|
||||
'customer',
|
||||
],
|
||||
operation: [
|
||||
'create'
|
||||
],
|
||||
jsonParameters: [
|
||||
true,
|
||||
],
|
||||
},
|
||||
},
|
||||
description: 'Object of values to set as described <a href="https://github.com/agilecrm/rest-api#1-companys---companies-api" target="_blank">here</a>.',
|
||||
},
|
||||
{
|
||||
displayName: 'Additional Fields',
|
||||
name: 'additionalFields',
|
||||
type: 'collection',
|
||||
placeholder: 'Add Field',
|
||||
default: {},
|
||||
displayOptions: {
|
||||
show: {
|
||||
resource: [
|
||||
'customer',
|
||||
],
|
||||
operation: [
|
||||
'create'
|
||||
],
|
||||
jsonParameters: [
|
||||
false,
|
||||
],
|
||||
},
|
||||
},
|
||||
options: [
|
||||
{
|
||||
displayName: 'Custom Properties',
|
||||
name: 'customProperties',
|
||||
type: 'fixedCollection',
|
||||
description: 'Custom Properties',
|
||||
typeOptions: {
|
||||
multipleValues: true,
|
||||
},
|
||||
options: [
|
||||
{
|
||||
displayName: 'Property',
|
||||
name: 'customProperty',
|
||||
values: [
|
||||
{
|
||||
displayName: 'Key',
|
||||
name: 'key',
|
||||
type: 'string',
|
||||
required: true,
|
||||
default: '',
|
||||
description: 'Property name.',
|
||||
placeholder: 'Plan'
|
||||
},
|
||||
|
||||
{
|
||||
displayName: 'Value',
|
||||
name: 'value',
|
||||
type: 'string',
|
||||
required: true,
|
||||
default: '',
|
||||
description: 'Property value.',
|
||||
placeholder: 'Basic'
|
||||
},
|
||||
],
|
||||
},
|
||||
]
|
||||
},
|
||||
],
|
||||
},
|
||||
|
||||
/* -------------------------------------------------------------------------- */
|
||||
/* customer:update */
|
||||
/* -------------------------------------------------------------------------- */
|
||||
{
|
||||
displayName: 'ID',
|
||||
name: 'id',
|
||||
type: 'number',
|
||||
required: true,
|
||||
default: 0,
|
||||
displayOptions: {
|
||||
show: {
|
||||
resource: [
|
||||
'customer',
|
||||
],
|
||||
operation: [
|
||||
'update'
|
||||
]
|
||||
},
|
||||
},
|
||||
description: 'The unique identifier for the customer.',
|
||||
},
|
||||
{
|
||||
displayName: 'JSON Parameters',
|
||||
name: 'jsonParameters',
|
||||
type: 'boolean',
|
||||
default: false,
|
||||
description: '',
|
||||
displayOptions: {
|
||||
show: {
|
||||
resource: [
|
||||
'customer',
|
||||
],
|
||||
operation: [
|
||||
'update'
|
||||
],
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
displayName: ' Additional Fields',
|
||||
name: 'additionalFieldsJson',
|
||||
type: 'json',
|
||||
typeOptions: {
|
||||
alwaysOpenEditWindow: true,
|
||||
},
|
||||
default: '',
|
||||
displayOptions: {
|
||||
show: {
|
||||
resource: [
|
||||
'customer',
|
||||
],
|
||||
operation: [
|
||||
'update'
|
||||
],
|
||||
jsonParameters: [
|
||||
true,
|
||||
],
|
||||
},
|
||||
},
|
||||
description: 'Object of values to set as described <a href="https://github.com/agilecrm/rest-api#1-companys---companies-api" target="_blank">here</a>.',
|
||||
},
|
||||
{
|
||||
displayName: 'Additional Fields',
|
||||
name: 'additionalFields',
|
||||
type: 'collection',
|
||||
placeholder: 'Add Field',
|
||||
default: {},
|
||||
displayOptions: {
|
||||
show: {
|
||||
resource: [
|
||||
'customer',
|
||||
],
|
||||
operation: [
|
||||
'update'
|
||||
],
|
||||
jsonParameters: [
|
||||
false,
|
||||
],
|
||||
},
|
||||
},
|
||||
options: [
|
||||
{
|
||||
displayName: 'Custom Properties',
|
||||
name: 'customProperties',
|
||||
type: 'fixedCollection',
|
||||
description: 'Custom Properties',
|
||||
typeOptions: {
|
||||
multipleValues: true,
|
||||
},
|
||||
options: [
|
||||
{
|
||||
displayName: 'Property',
|
||||
name: 'customProperty',
|
||||
values: [
|
||||
{
|
||||
displayName: 'Key',
|
||||
name: 'key',
|
||||
type: 'string',
|
||||
required: true,
|
||||
default: '',
|
||||
description: 'Property name.',
|
||||
placeholder: 'Plan'
|
||||
},
|
||||
|
||||
{
|
||||
displayName: 'Value',
|
||||
name: 'value',
|
||||
type: 'string',
|
||||
required: true,
|
||||
default: '',
|
||||
description: 'Property value.',
|
||||
placeholder: 'Basic'
|
||||
},
|
||||
],
|
||||
},
|
||||
]
|
||||
},
|
||||
{
|
||||
displayName: 'Email',
|
||||
name: 'email',
|
||||
type: 'string',
|
||||
default: '',
|
||||
description: 'The email address of the user.',
|
||||
},
|
||||
{
|
||||
displayName: 'Created at',
|
||||
name: 'createdAt',
|
||||
type: 'dateTime',
|
||||
default: '',
|
||||
description: 'The UNIX timestamp from when the user was created.',
|
||||
},
|
||||
],
|
||||
},
|
||||
] as INodeProperties[];
|
359
packages/nodes-base/nodes/CustomerIo/CustomerIo.node.ts
Normal file
359
packages/nodes-base/nodes/CustomerIo/CustomerIo.node.ts
Normal file
|
@ -0,0 +1,359 @@
|
|||
import {
|
||||
IExecuteFunctions,
|
||||
} from 'n8n-core';
|
||||
import {
|
||||
IDataObject,
|
||||
INodeTypeDescription,
|
||||
INodeExecutionData,
|
||||
INodeType,
|
||||
ILoadOptionsFunctions,
|
||||
INodePropertyOptions,
|
||||
} from 'n8n-workflow';
|
||||
import { customerIoApiRequest, validateJSON } from './GenericFunctions';
|
||||
import { campaignOperations, campaignFields } from './CampaignDescription';
|
||||
import { customerOperations, customerFields } from './CustomerDescription';
|
||||
import { eventOperations, eventFields } from './EventDescription';
|
||||
import { segmentOperations, segmentFields } from './SegmentDescription';
|
||||
import { DateTime } from '../DateTime.node';
|
||||
|
||||
|
||||
export class CustomerIo implements INodeType {
|
||||
description: INodeTypeDescription = {
|
||||
displayName: 'Customer.io',
|
||||
name: 'customerio',
|
||||
icon: 'file:customerio.png',
|
||||
group: ['output'],
|
||||
version: 1,
|
||||
subtitle: '={{$parameter["operation"] + ": " + $parameter["resource"]}}',
|
||||
description: 'Consume Customer.io API',
|
||||
defaults: {
|
||||
name: 'CustomerIo',
|
||||
color: '#ffcd00',
|
||||
},
|
||||
inputs: ['main'],
|
||||
outputs: ['main'],
|
||||
credentials: [
|
||||
{
|
||||
name: 'customerIoApi',
|
||||
required: true,
|
||||
}
|
||||
],
|
||||
properties: [
|
||||
{
|
||||
displayName: 'Resource',
|
||||
name: 'resource',
|
||||
type: 'options',
|
||||
options: [
|
||||
{
|
||||
name: 'Customer',
|
||||
value: 'customer',
|
||||
},
|
||||
{
|
||||
name: 'Event',
|
||||
value: 'event',
|
||||
},
|
||||
{
|
||||
name: 'Campaign',
|
||||
value: 'campaign',
|
||||
},
|
||||
{
|
||||
name: 'Segment',
|
||||
value: 'segment',
|
||||
},
|
||||
],
|
||||
default: 'customer',
|
||||
description: 'Resource to consume.',
|
||||
},
|
||||
// CAMPAIGN
|
||||
...campaignOperations,
|
||||
...campaignFields,
|
||||
// CUSTOMER
|
||||
...customerOperations,
|
||||
...customerFields,
|
||||
// EVENT
|
||||
...eventOperations,
|
||||
...eventFields,
|
||||
// SEGMENT
|
||||
...segmentOperations,
|
||||
...segmentFields
|
||||
],
|
||||
};
|
||||
|
||||
async execute(this: IExecuteFunctions): Promise<INodeExecutionData[][]> {
|
||||
const returnData: IDataObject[] = [];
|
||||
const items = this.getInputData();
|
||||
let responseData;
|
||||
const resource = this.getNodeParameter('resource', 0) as string;
|
||||
const operation = this.getNodeParameter('operation', 0) as string;
|
||||
const body : IDataObject = {};
|
||||
|
||||
for (let i = 0; i < items.length; i++) {
|
||||
|
||||
if (resource === 'campaign') {
|
||||
if (operation === 'get') {
|
||||
const campaignId = this.getNodeParameter('campaignId', i) as number;
|
||||
const endpoint = `/campaigns/${campaignId}`;
|
||||
|
||||
responseData = await customerIoApiRequest.call(this, 'GET', endpoint, body, 'beta');
|
||||
}
|
||||
|
||||
if (operation === 'getAll') {
|
||||
const endpoint = `/campaigns`;
|
||||
|
||||
responseData = await customerIoApiRequest.call(this, 'GET', endpoint, body, 'beta');
|
||||
responseData = responseData.campaigns;
|
||||
}
|
||||
|
||||
if (operation === 'getMetrics') {
|
||||
const campaignId = this.getNodeParameter('campaignId', i) as number;
|
||||
const jsonParameters = this.getNodeParameter('jsonParameters', i) as boolean;
|
||||
|
||||
if (jsonParameters) {
|
||||
const additionalFieldsJson = this.getNodeParameter('additionalFieldsJson', i) as string;
|
||||
|
||||
if (additionalFieldsJson !== '') {
|
||||
|
||||
if (validateJSON(additionalFieldsJson) !== undefined) {
|
||||
|
||||
Object.assign(body, JSON.parse(additionalFieldsJson));
|
||||
|
||||
} else {
|
||||
throw new Error('Additional fields must be a valid JSON');
|
||||
}
|
||||
}
|
||||
} else {
|
||||
const additionalFields = this.getNodeParameter('additionalFields', i) as IDataObject;
|
||||
const period = this.getNodeParameter('period', i) as string;
|
||||
let endpoint = `/campaigns/${campaignId}/metrics`;
|
||||
|
||||
if (period !== 'days') {
|
||||
endpoint = `${endpoint}?period=${period}`;
|
||||
}
|
||||
if (additionalFields.steps) {
|
||||
body.steps = additionalFields.steps as number;
|
||||
}
|
||||
if (additionalFields.type) {
|
||||
if (additionalFields.type === 'urbanAirship') {
|
||||
additionalFields.type = 'urban_airship';
|
||||
} else {
|
||||
body.type = additionalFields.type as string;
|
||||
}
|
||||
}
|
||||
|
||||
responseData = await customerIoApiRequest.call(this, 'GET', endpoint, body, 'beta');
|
||||
responseData = responseData.metric;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (resource === 'customer') {
|
||||
if (operation === 'create') {
|
||||
const id = this.getNodeParameter('id', i) as number;
|
||||
const email = this.getNodeParameter('email', i) as string;
|
||||
const createdAt = this.getNodeParameter('createdAt', i) as string;
|
||||
const jsonParameters = this.getNodeParameter('jsonParameters', i) as boolean;
|
||||
|
||||
body.email = email;
|
||||
body.created_at = new Date(createdAt).getTime() / 1000;
|
||||
|
||||
if (jsonParameters) {
|
||||
const additionalFieldsJson = this.getNodeParameter('additionalFieldsJson', i) as string;
|
||||
|
||||
if (additionalFieldsJson !== '') {
|
||||
|
||||
if (validateJSON(additionalFieldsJson) !== undefined) {
|
||||
|
||||
Object.assign(body, JSON.parse(additionalFieldsJson));
|
||||
|
||||
} else {
|
||||
throw new Error('Additional fields must be a valid JSON');
|
||||
}
|
||||
}
|
||||
} else {
|
||||
const additionalFields = this.getNodeParameter('additionalFields', i) as IDataObject;
|
||||
|
||||
if (additionalFields.customProperties) {
|
||||
const data : any = {};
|
||||
//@ts-ignore
|
||||
additionalFields.customProperties.customProperty.map(property => {
|
||||
data[property.key] = property.value;
|
||||
});
|
||||
|
||||
body.data = data;
|
||||
}
|
||||
}
|
||||
|
||||
const endpoint = `/customers/${id}`;
|
||||
|
||||
responseData = await customerIoApiRequest.call(this, 'PUT', endpoint, body, 'tracking');
|
||||
}
|
||||
|
||||
if (operation === 'update') {
|
||||
const id = this.getNodeParameter('id', i) as number;
|
||||
const jsonParameters = this.getNodeParameter('jsonParameters', i) as boolean;
|
||||
|
||||
if (jsonParameters) {
|
||||
const additionalFieldsJson = this.getNodeParameter('additionalFieldsJson', i) as string;
|
||||
|
||||
if (additionalFieldsJson !== '') {
|
||||
|
||||
if (validateJSON(additionalFieldsJson) !== undefined) {
|
||||
|
||||
Object.assign(body, JSON.parse(additionalFieldsJson));
|
||||
|
||||
} else {
|
||||
throw new Error('Additional fields must be a valid JSON');
|
||||
}
|
||||
}
|
||||
} else {
|
||||
const additionalFields = this.getNodeParameter('additionalFields', i) as IDataObject;
|
||||
|
||||
if (additionalFields.customProperties) {
|
||||
const data : any = {};
|
||||
//@ts-ignore
|
||||
additionalFields.customProperties.customProperty.map(property => {
|
||||
data[property.key] = property.value;
|
||||
});
|
||||
|
||||
body.data = data;
|
||||
}
|
||||
|
||||
if (additionalFields.email) {
|
||||
body.email = additionalFields.email as string;
|
||||
}
|
||||
|
||||
if (additionalFields.createdAt) {
|
||||
body.created_at = new Date(additionalFields.createdAt as string).getTime() / 1000;
|
||||
}
|
||||
}
|
||||
|
||||
const endpoint = `/customers/${id}`;
|
||||
|
||||
responseData = await customerIoApiRequest.call(this, 'PUT', endpoint, body, 'tracking');
|
||||
}
|
||||
|
||||
if (operation === 'delete') {
|
||||
const id = this.getNodeParameter('id', i) as number;
|
||||
|
||||
body.id = id;
|
||||
|
||||
const endpoint = `/customers/${id}`;
|
||||
|
||||
responseData = await customerIoApiRequest.call(this, 'DELETE', endpoint, body, 'tracking');
|
||||
}
|
||||
}
|
||||
|
||||
if (resource === 'event') {
|
||||
if (operation === 'track') {
|
||||
const id = this.getNodeParameter('id', i) as number;
|
||||
const name = this.getNodeParameter('name', i) as string;
|
||||
const jsonParameters = this.getNodeParameter('jsonParameters', i) as boolean;
|
||||
|
||||
body.name = name;
|
||||
|
||||
if (jsonParameters) {
|
||||
const additionalFieldsJson = this.getNodeParameter('additionalFieldsJson', i) as string;
|
||||
|
||||
if (additionalFieldsJson !== '') {
|
||||
|
||||
if (validateJSON(additionalFieldsJson) !== undefined) {
|
||||
|
||||
Object.assign(body, JSON.parse(additionalFieldsJson));
|
||||
|
||||
} else {
|
||||
throw new Error('Additional fields must be a valid JSON');
|
||||
}
|
||||
}
|
||||
} else {
|
||||
const additionalFields = this.getNodeParameter('additionalFields', i) as IDataObject;
|
||||
const data : any = {};
|
||||
|
||||
if (additionalFields.customAttributes) {
|
||||
//@ts-ignore
|
||||
additionalFields.customAttributes.customAttribute.map(property => {
|
||||
data[property.key] = property.value;
|
||||
});
|
||||
}
|
||||
|
||||
if (additionalFields.type) {
|
||||
data.type = additionalFields.type as string;
|
||||
}
|
||||
|
||||
body.data = data;
|
||||
}
|
||||
|
||||
const endpoint = `/customers/${id}/events`;
|
||||
|
||||
responseData = await customerIoApiRequest.call(this, 'POST', endpoint, body, 'tracking');
|
||||
}
|
||||
|
||||
if (operation === 'trackAnonymous') {
|
||||
const name = this.getNodeParameter('name', i) as string;
|
||||
const jsonParameters = this.getNodeParameter('jsonParameters', i) as boolean;
|
||||
|
||||
body.name = name;
|
||||
|
||||
if (jsonParameters) {
|
||||
const additionalFieldsJson = this.getNodeParameter('additionalFieldsJson', i) as string;
|
||||
|
||||
if (additionalFieldsJson !== '') {
|
||||
|
||||
if (validateJSON(additionalFieldsJson) !== undefined) {
|
||||
|
||||
Object.assign(body, JSON.parse(additionalFieldsJson));
|
||||
|
||||
} else {
|
||||
throw new Error('Additional fields must be a valid JSON');
|
||||
}
|
||||
}
|
||||
} else {
|
||||
const additionalFields = this.getNodeParameter('additionalFields', i) as IDataObject;
|
||||
const data : any = {};
|
||||
|
||||
if (additionalFields.customAttributes) {
|
||||
//@ts-ignore
|
||||
additionalFields.customAttributes.customAttribute.map(property => {
|
||||
data[property.key] = property.value;
|
||||
});
|
||||
}
|
||||
body.data = data;
|
||||
}
|
||||
|
||||
const endpoint = `/events`;
|
||||
responseData = await customerIoApiRequest.call(this, 'POST', endpoint, body, 'tracking');
|
||||
}
|
||||
}
|
||||
|
||||
if (resource === 'segment') {
|
||||
const id = this.getNodeParameter('id', i) as number;
|
||||
const ids = this.getNodeParameter('ids', i) as string;
|
||||
const idArray : string[] = [];
|
||||
|
||||
ids.split(',').map(id => {
|
||||
idArray.push(id);
|
||||
});
|
||||
|
||||
body.id = id;
|
||||
body.ids = idArray;
|
||||
|
||||
let endpoint = ``;
|
||||
|
||||
if (operation === 'add') {
|
||||
endpoint = `/segments/${id}/add_customers`;
|
||||
} else {
|
||||
endpoint = `/segments/${id}/remove_customers`;
|
||||
}
|
||||
|
||||
responseData = await customerIoApiRequest.call(this, 'POST', endpoint, body, 'tracking');
|
||||
}
|
||||
|
||||
if (Array.isArray(responseData)) {
|
||||
returnData.push.apply(returnData, responseData as IDataObject[]);
|
||||
} else {
|
||||
returnData.push(responseData as unknown as IDataObject);
|
||||
}
|
||||
}
|
||||
|
||||
return [this.helpers.returnJsonArray(returnData)];
|
||||
}
|
||||
}
|
|
@ -11,7 +11,7 @@ import {
|
|||
} from 'n8n-workflow';
|
||||
|
||||
import {
|
||||
apiRequest,
|
||||
customerIoApiRequest,
|
||||
eventExists,
|
||||
} from './GenericFunctions';
|
||||
|
||||
|
@ -34,7 +34,7 @@ export class CustomerIoTrigger implements INodeType {
|
|||
description: 'Starts the workflow on a Customer.io update. (Beta)',
|
||||
defaults: {
|
||||
name: 'Customer.io Trigger',
|
||||
color: '#7131ff',
|
||||
color: '#ffcd00',
|
||||
},
|
||||
inputs: [],
|
||||
outputs: ['main'],
|
||||
|
@ -237,7 +237,7 @@ export class CustomerIoTrigger implements INodeType {
|
|||
|
||||
const endpoint = '/reporting_webhooks';
|
||||
|
||||
let { reporting_webhooks: webhooks } = await apiRequest.call(this, 'GET', endpoint, {});
|
||||
let { reporting_webhooks: webhooks } = await customerIoApiRequest.call(this, 'GET', endpoint, {}, 'beta');
|
||||
|
||||
if (webhooks === null) {
|
||||
webhooks = [];
|
||||
|
@ -295,7 +295,7 @@ export class CustomerIoTrigger implements INodeType {
|
|||
events: data,
|
||||
};
|
||||
|
||||
webhook = await apiRequest.call(this, 'POST', endpoint, body);
|
||||
webhook = await customerIoApiRequest.call(this, 'POST', endpoint, body, 'beta');
|
||||
|
||||
const webhookData = this.getWorkflowStaticData('node');
|
||||
webhookData.webhookId = webhook.id as string;
|
||||
|
@ -307,7 +307,7 @@ export class CustomerIoTrigger implements INodeType {
|
|||
if (webhookData.webhookId !== undefined) {
|
||||
const endpoint = `/reporting_webhooks/${webhookData.webhookId}`;
|
||||
try {
|
||||
await apiRequest.call(this, 'DELETE', endpoint, {});
|
||||
await customerIoApiRequest.call(this, 'DELETE', endpoint, {}, 'beta');
|
||||
} catch (e) {
|
||||
return false;
|
||||
}
|
||||
|
|
296
packages/nodes-base/nodes/CustomerIo/EventDescription.ts
Normal file
296
packages/nodes-base/nodes/CustomerIo/EventDescription.ts
Normal file
|
@ -0,0 +1,296 @@
|
|||
import { INodeProperties } from 'n8n-workflow';
|
||||
|
||||
export const eventOperations = [
|
||||
{
|
||||
displayName: 'Operation',
|
||||
name: 'operation',
|
||||
type: 'options',
|
||||
displayOptions: {
|
||||
show: {
|
||||
resource: [
|
||||
'event',
|
||||
],
|
||||
},
|
||||
},
|
||||
options: [
|
||||
{
|
||||
name: 'Track',
|
||||
value: 'track',
|
||||
description: 'Track a customer event.',
|
||||
},
|
||||
{
|
||||
name: 'Track Anonymous',
|
||||
value: 'trackAnonymous',
|
||||
description: 'Track an anonymous event.',
|
||||
},
|
||||
],
|
||||
default: 'track',
|
||||
description: 'The operation to perform.',
|
||||
},
|
||||
] as INodeProperties[];
|
||||
|
||||
export const eventFields = [
|
||||
|
||||
/* -------------------------------------------------------------------------- */
|
||||
/* event:track */
|
||||
/* -------------------------------------------------------------------------- */
|
||||
{
|
||||
displayName: 'ID',
|
||||
name: 'id',
|
||||
type: 'number',
|
||||
required: true,
|
||||
default: 0,
|
||||
displayOptions: {
|
||||
show: {
|
||||
resource: [
|
||||
'event',
|
||||
],
|
||||
operation: [
|
||||
'track'
|
||||
]
|
||||
},
|
||||
},
|
||||
description: 'The unique identifier for the customer.',
|
||||
},
|
||||
{
|
||||
displayName: 'Name',
|
||||
name: 'name',
|
||||
type: 'string',
|
||||
default: '',
|
||||
displayOptions: {
|
||||
show: {
|
||||
resource: [
|
||||
'event',
|
||||
],
|
||||
operation: [
|
||||
'track'
|
||||
]
|
||||
},
|
||||
},
|
||||
description: 'Name of the event to track.',
|
||||
},
|
||||
{
|
||||
displayName: 'JSON Parameters',
|
||||
name: 'jsonParameters',
|
||||
type: 'boolean',
|
||||
default: false,
|
||||
description: '',
|
||||
displayOptions: {
|
||||
show: {
|
||||
resource: [
|
||||
'event',
|
||||
],
|
||||
operation: [
|
||||
'track'
|
||||
],
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
displayName: 'Additional Fields',
|
||||
name: 'additionalFieldsJson',
|
||||
type: 'json',
|
||||
typeOptions: {
|
||||
alwaysOpenEditWindow: true,
|
||||
},
|
||||
default: '',
|
||||
displayOptions: {
|
||||
show: {
|
||||
resource: [
|
||||
'event',
|
||||
],
|
||||
operation: [
|
||||
'track'
|
||||
],
|
||||
jsonParameters: [
|
||||
true,
|
||||
],
|
||||
},
|
||||
},
|
||||
description: 'Object of values to set as described <a href="https://customer.io/docs/api-triggered-data-format#basic-data-formatting" target="_blank">here</a>.',
|
||||
},
|
||||
{
|
||||
displayName: 'Additional Fields',
|
||||
name: 'additionalFields',
|
||||
type: 'collection',
|
||||
placeholder: 'Add Field',
|
||||
default: {},
|
||||
displayOptions: {
|
||||
show: {
|
||||
resource: [
|
||||
'event',
|
||||
],
|
||||
operation: [
|
||||
'track'
|
||||
],
|
||||
jsonParameters: [
|
||||
false
|
||||
]
|
||||
},
|
||||
},
|
||||
options: [
|
||||
{
|
||||
displayName: 'Custom Attributes',
|
||||
name: 'customAttributes',
|
||||
type: 'fixedCollection',
|
||||
description: 'Custom Properties',
|
||||
typeOptions: {
|
||||
multipleValues: true,
|
||||
},
|
||||
options: [
|
||||
{
|
||||
displayName: 'Attribute',
|
||||
name: 'customAttribute',
|
||||
values: [
|
||||
{
|
||||
displayName: 'Key',
|
||||
name: 'key',
|
||||
type: 'string',
|
||||
required: true,
|
||||
default: '',
|
||||
description: 'Attribute name.',
|
||||
placeholder: 'Price'
|
||||
},
|
||||
|
||||
{
|
||||
displayName: 'Value',
|
||||
name: 'value',
|
||||
type: 'string',
|
||||
required: true,
|
||||
default: '',
|
||||
description: 'Attribute value.',
|
||||
placeholder: '25.50'
|
||||
},
|
||||
],
|
||||
},
|
||||
]
|
||||
},
|
||||
{
|
||||
displayName: 'Type',
|
||||
name: 'type',
|
||||
type: 'string',
|
||||
default: '',
|
||||
description: 'Used to change event type. For Page View events set to "page".',
|
||||
},
|
||||
],
|
||||
},
|
||||
/* -------------------------------------------------------------------------- */
|
||||
/* event:track anonymous */
|
||||
/* -------------------------------------------------------------------------- */
|
||||
{
|
||||
displayName: 'Name',
|
||||
name: 'name',
|
||||
type: 'string',
|
||||
required: true,
|
||||
default: '',
|
||||
displayOptions: {
|
||||
show: {
|
||||
resource: [
|
||||
'event',
|
||||
],
|
||||
operation: [
|
||||
'trackAnonymous'
|
||||
]
|
||||
},
|
||||
},
|
||||
description: 'The unique identifier for the customer.',
|
||||
},
|
||||
{
|
||||
displayName: 'JSON Parameters',
|
||||
name: 'jsonParameters',
|
||||
type: 'boolean',
|
||||
default: false,
|
||||
description: '',
|
||||
displayOptions: {
|
||||
show: {
|
||||
resource: [
|
||||
'event',
|
||||
],
|
||||
operation: [
|
||||
'trackAnonymous'
|
||||
],
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
displayName: 'Additional Fields',
|
||||
name: 'additionalFieldsJson',
|
||||
type: 'json',
|
||||
typeOptions: {
|
||||
alwaysOpenEditWindow: true,
|
||||
},
|
||||
default: '',
|
||||
displayOptions: {
|
||||
show: {
|
||||
resource: [
|
||||
'event',
|
||||
],
|
||||
operation: [
|
||||
'trackAnonymous'
|
||||
],
|
||||
jsonParameters: [
|
||||
true,
|
||||
],
|
||||
},
|
||||
},
|
||||
description: 'Object of values to set as described <a href="https://customer.io/docs/api-triggered-data-format#basic-data-formatting" target="_blank">here</a>.',
|
||||
},
|
||||
{
|
||||
displayName: 'Additional Fields',
|
||||
name: 'additionalFields',
|
||||
type: 'collection',
|
||||
placeholder: 'Add Field',
|
||||
default: {},
|
||||
displayOptions: {
|
||||
show: {
|
||||
resource: [
|
||||
'event',
|
||||
],
|
||||
operation: [
|
||||
'trackAnonymous'
|
||||
],
|
||||
jsonParameters: [
|
||||
false
|
||||
]
|
||||
},
|
||||
},
|
||||
options: [
|
||||
{
|
||||
displayName: 'Custom Attributes',
|
||||
name: 'customAttributes',
|
||||
type: 'fixedCollection',
|
||||
description: 'Custom Properties',
|
||||
typeOptions: {
|
||||
multipleValues: true,
|
||||
},
|
||||
options: [
|
||||
{
|
||||
displayName: 'Attribute',
|
||||
name: 'customAttribute',
|
||||
values: [
|
||||
{
|
||||
displayName: 'Key',
|
||||
name: 'key',
|
||||
type: 'string',
|
||||
required: true,
|
||||
default: '',
|
||||
description: 'Attribute name.',
|
||||
placeholder: 'Price'
|
||||
},
|
||||
|
||||
{
|
||||
displayName: 'Value',
|
||||
name: 'value',
|
||||
type: 'string',
|
||||
required: true,
|
||||
default: '',
|
||||
description: 'Attribute value.',
|
||||
placeholder: '25.50'
|
||||
},
|
||||
],
|
||||
},
|
||||
]
|
||||
},
|
||||
],
|
||||
},
|
||||
] as INodeProperties[];
|
|
@ -16,7 +16,7 @@ import {
|
|||
get,
|
||||
} from 'lodash';
|
||||
|
||||
export async function apiRequest(this: IHookFunctions | IExecuteFunctions | ILoadOptionsFunctions, method: string, endpoint: string, body: object, query?: IDataObject): Promise<any> { // tslint:disable-line:no-any
|
||||
export async function customerIoApiRequest(this: IHookFunctions | IExecuteFunctions | ILoadOptionsFunctions, method: string, endpoint: string, body: object, baseApi? : string, query?: IDataObject): Promise<any> { // tslint:disable-line:no-any
|
||||
const credentials = this.getCredentials('customerIoApi');
|
||||
|
||||
if (credentials === undefined) {
|
||||
|
@ -28,14 +28,26 @@ export async function apiRequest(this: IHookFunctions | IExecuteFunctions | ILoa
|
|||
const options: OptionsWithUri = {
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
'Authorization': `Bearer ${credentials.apiKey}`,
|
||||
},
|
||||
method,
|
||||
body,
|
||||
qs: query,
|
||||
uri: `https://beta-api.customer.io/v1/api${endpoint}`,
|
||||
uri: '',
|
||||
json: true,
|
||||
};
|
||||
|
||||
if (baseApi === 'tracking') {
|
||||
options.uri = `https://track.customer.io/api/v1${endpoint}`;
|
||||
const basicAuthKey = Buffer.from(`${credentials.trackingSiteId}:${credentials.trackingApiKey}`).toString('base64');
|
||||
Object.assign(options.headers, {'Authorization': `Basic ${basicAuthKey}`});
|
||||
} else if (baseApi === 'api') {
|
||||
options.uri = `https://api.customer.io/v1/api${endpoint}`;
|
||||
const basicAuthKey = Buffer.from(`${credentials.trackingSiteId}:${credentials.trackingApiKey}`).toString('base64');
|
||||
Object.assign(options.headers, {'Authorization': `Basic ${basicAuthKey}`});
|
||||
} else if (baseApi === 'beta') {
|
||||
options.uri = `https://beta-api.customer.io/v1/api${endpoint}`;
|
||||
Object.assign(options.headers, {'Authorization': `Bearer ${credentials.appApiKey as string}`});
|
||||
}
|
||||
|
||||
try {
|
||||
return await this.helpers.request!(options);
|
||||
} catch (error) {
|
||||
|
@ -63,3 +75,13 @@ export function eventExists(currentEvents: string[], webhookEvents: IDataObject)
|
|||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
export function validateJSON(json: string | undefined): any { // tslint:disable-line:no-any
|
||||
let result;
|
||||
try {
|
||||
result = JSON.parse(json!);
|
||||
} catch (exception) {
|
||||
result = undefined;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
|
71
packages/nodes-base/nodes/CustomerIo/SegmentDescription.ts
Normal file
71
packages/nodes-base/nodes/CustomerIo/SegmentDescription.ts
Normal file
|
@ -0,0 +1,71 @@
|
|||
import { INodeProperties } from 'n8n-workflow';
|
||||
|
||||
export const segmentOperations = [
|
||||
{
|
||||
displayName: 'Operation',
|
||||
name: 'operation',
|
||||
type: 'options',
|
||||
displayOptions: {
|
||||
show: {
|
||||
resource: [
|
||||
'segment',
|
||||
],
|
||||
},
|
||||
},
|
||||
options: [
|
||||
{
|
||||
name: 'Add Customer',
|
||||
value: 'add',
|
||||
},
|
||||
{
|
||||
name: 'Remove Customer',
|
||||
value: 'remove',
|
||||
},
|
||||
],
|
||||
default: 'add',
|
||||
description: 'The operation to perform.',
|
||||
},
|
||||
] as INodeProperties[];
|
||||
|
||||
export const segmentFields = [
|
||||
|
||||
/* -------------------------------------------------------------------------- */
|
||||
/* segment:add */
|
||||
/* -------------------------------------------------------------------------- */
|
||||
{
|
||||
displayName: 'ID',
|
||||
name: 'id',
|
||||
type: 'number',
|
||||
required: true,
|
||||
default: 0,
|
||||
displayOptions: {
|
||||
show: {
|
||||
resource: [
|
||||
'segment',
|
||||
],
|
||||
operation: [
|
||||
'add', 'remove'
|
||||
]
|
||||
},
|
||||
},
|
||||
description: 'The unique identifier of the segment.',
|
||||
},
|
||||
{
|
||||
displayName: 'IDs',
|
||||
name: 'ids',
|
||||
type: 'string',
|
||||
required: true,
|
||||
default: '',
|
||||
displayOptions: {
|
||||
show: {
|
||||
resource: [
|
||||
'segment',
|
||||
],
|
||||
operation: [
|
||||
'add', 'remove'
|
||||
]
|
||||
},
|
||||
},
|
||||
description: 'A list of customer ids to add to the segment.',
|
||||
},
|
||||
] as INodeProperties[];
|
BIN
packages/nodes-base/nodes/CustomerIo/customerio.png
Normal file
BIN
packages/nodes-base/nodes/CustomerIo/customerio.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 2.6 KiB |
|
@ -217,6 +217,7 @@
|
|||
"dist/nodes/CrateDb/CrateDb.node.js",
|
||||
"dist/nodes/Cron.node.js",
|
||||
"dist/nodes/Crypto.node.js",
|
||||
"dist/nodes/CustomerIo/CustomerIo.node.js",
|
||||
"dist/nodes/CustomerIo/CustomerIoTrigger.node.js",
|
||||
"dist/nodes/DateTime.node.js",
|
||||
"dist/nodes/Discord/Discord.node.js",
|
||||
|
|
Loading…
Reference in a new issue