From 70e26681a483181ff2c8daa7378724f4ef721cf3 Mon Sep 17 00:00:00 2001 From: Ricardo Espinoza Date: Thu, 23 Jan 2020 12:27:28 -0500 Subject: [PATCH 1/4] :sparkles: segment node --- .../credentials/SegmentApi.credentials.ts | 17 + .../nodes/Segment/GenericFunctions.ts | 36 + .../nodes/Segment/IdentifyDescription.ts | 508 ++++++++ .../nodes/Segment/IdentifyInterface.ts | 10 + .../nodes-base/nodes/Segment/Segment.node.ts | 784 +++++++++++ .../nodes/Segment/TrackDescription.ts | 1155 +++++++++++++++++ .../nodes/Segment/TrackInterface.ts | 13 + packages/nodes-base/nodes/Segment/segment.png | Bin 0 -> 1144 bytes packages/nodes-base/package.json | 4 + 9 files changed, 2527 insertions(+) create mode 100644 packages/nodes-base/credentials/SegmentApi.credentials.ts create mode 100644 packages/nodes-base/nodes/Segment/GenericFunctions.ts create mode 100644 packages/nodes-base/nodes/Segment/IdentifyDescription.ts create mode 100644 packages/nodes-base/nodes/Segment/IdentifyInterface.ts create mode 100644 packages/nodes-base/nodes/Segment/Segment.node.ts create mode 100644 packages/nodes-base/nodes/Segment/TrackDescription.ts create mode 100644 packages/nodes-base/nodes/Segment/TrackInterface.ts create mode 100644 packages/nodes-base/nodes/Segment/segment.png diff --git a/packages/nodes-base/credentials/SegmentApi.credentials.ts b/packages/nodes-base/credentials/SegmentApi.credentials.ts new file mode 100644 index 0000000000..45858d3277 --- /dev/null +++ b/packages/nodes-base/credentials/SegmentApi.credentials.ts @@ -0,0 +1,17 @@ +import { + ICredentialType, + NodePropertyTypes, +} from 'n8n-workflow'; + +export class SegmentApi implements ICredentialType { + name = 'segmentApi'; + displayName = 'Segment API'; + properties = [ + { + displayName: 'Write Key', + name: 'writekey', + type: 'string' as NodePropertyTypes, + default: '', + }, + ]; +} diff --git a/packages/nodes-base/nodes/Segment/GenericFunctions.ts b/packages/nodes-base/nodes/Segment/GenericFunctions.ts new file mode 100644 index 0000000000..bd3ba6b877 --- /dev/null +++ b/packages/nodes-base/nodes/Segment/GenericFunctions.ts @@ -0,0 +1,36 @@ +import { OptionsWithUri } from 'request'; +import { + IExecuteFunctions, + IExecuteSingleFunctions, + IHookFunctions, + ILoadOptionsFunctions, + IWebhookFunctions, +} from 'n8n-core'; +import { IDataObject } from 'n8n-workflow'; + +export async function segmentApiRequest(this: IHookFunctions | IExecuteFunctions | IExecuteSingleFunctions | ILoadOptionsFunctions | IWebhookFunctions, method: string, resource: string, body: any = {}, qs: IDataObject = {}, uri?: string, option: IDataObject = {}): Promise { // tslint:disable-line:no-any + const credentials = this.getCredentials('segmentApi'); + if (credentials === undefined) { + throw new Error('No credentials got returned!'); + } + const base64Key = Buffer.from(`${credentials.writekey}:`).toString('base64'); + const options: OptionsWithUri = { + headers: { + Authorization: `Basic ${base64Key}`, + 'Content-Type': 'application/json', + }, + method, + qs, + body, + uri: uri ||`https://api.segment.io/v1${resource}`, + json: true + }; + if (!Object.keys(body).length) { + delete options.body; + } + try { + return await this.helpers.request!(options); + } catch (error) { + throw new Error('Segment Error: ' + error); + } +} diff --git a/packages/nodes-base/nodes/Segment/IdentifyDescription.ts b/packages/nodes-base/nodes/Segment/IdentifyDescription.ts new file mode 100644 index 0000000000..750a4d03d4 --- /dev/null +++ b/packages/nodes-base/nodes/Segment/IdentifyDescription.ts @@ -0,0 +1,508 @@ +import { INodeProperties } from 'n8n-workflow'; + +export const identifyOperations = [ + { + displayName: 'Operation', + name: 'operation', + type: 'options', + displayOptions: { + show: { + resource: [ + 'identify', + ], + }, + }, + options: [ + { + name: 'Create', + value: 'create', + description: 'Create an identity', + }, + ], + default: 'create', + description: 'The operation to perform.', + }, +] as INodeProperties[]; + +export const identifyFields = [ + +/* -------------------------------------------------------------------------- */ +/* identify:create */ +/* -------------------------------------------------------------------------- */ + { + displayName: 'User ID', + name: 'userId', + type: 'string', + default: '', + displayOptions: { + show: { + resource: [ + 'identify', + ], + operation: [ + 'create', + ], + }, + }, + required: false, + }, + { + displayName: 'Traits', + name: 'traits', + placeholder: 'Add Trait', + type: 'fixedCollection', + typeOptions: { + multipleValues: false, + }, + displayOptions: { + show: { + resource: [ + 'identify', + ], + operation: [ + 'create', + ], + }, + }, + default: {}, + options: [ + { + name: 'traitsUi', + displayName: 'Trait', + values: [ + { + displayName: 'Email', + name: 'emaiL', + type: 'string', + default: '', + description: 'Email address of a user', + }, + { + displayName: 'First Name', + name: 'firstname', + type: 'string', + default: '', + description: 'First name of a user', + }, + { + displayName: 'Last Name', + name: 'lastname', + type: 'string', + default: '', + description: 'Last name of a user', + }, + { + displayName: 'Gender', + name: 'gender', + type: 'string', + default: '', + description: 'Gender of a user', + }, + { + displayName: 'Phone', + name: 'phone', + type: 'string', + default: '', + description: 'Phone number of a user', + }, + { + displayName: 'Username', + name: 'username', + type: 'string', + default: '', + description: 'User’s username', + }, + { + displayName: 'Website', + name: 'website', + type: 'string', + default: '', + description: 'Website of a user', + }, + { + displayName: 'Age', + name: 'age', + type: 'number', + default: 1, + description: 'Age of a user', + }, + { + displayName: 'Avatar', + name: 'avatar', + type: 'string', + default: '', + description: 'URL to an avatar image for the user', + }, + { + displayName: 'Birthday', + name: 'birthday', + type: 'dateTime', + default: '', + description: 'User’s birthday', + }, + { + displayName: 'Created At', + name: 'createdAt', + type: 'dateTime', + default: '', + description: 'Date the user’s account was first created', + }, + { + displayName: 'Description', + name: 'description', + type: 'string', + typeOptions: { + alwaysOpenEditWindow: true, + }, + default: '', + description: 'Description of the user', + }, + { + displayName: 'ID', + name: 'id', + type: 'string', + default: '', + description: 'Unique ID in your database for a user', + }, + { + displayName: 'Company', + name: 'company', + placeholder: 'Add Company', + type: 'fixedCollection', + typeOptions: { + multipleValues: false, + }, + default: {}, + options: [ + { + name: 'companyUi', + displayName: 'Company', + values: [ + { + displayName: 'ID', + name: 'id', + type: 'string', + default: '', + }, + { + displayName: 'Name', + name: 'name', + type: 'string', + default: '', + }, + { + displayName: 'Industry', + name: 'industry', + type: 'string', + default: '', + }, + { + displayName: 'Employee Count', + name: 'employeeCount', + type: 'number', + default: 1, + }, + { + displayName: 'Plan', + name: 'plan', + type: 'string', + default: '', + }, + ] + }, + ], + }, + { + displayName: 'Address', + name: 'address', + placeholder: 'Add Address', + type: 'fixedCollection', + typeOptions: { + multipleValues: false, + }, + default: {}, + options: [ + { + name: 'addressUi', + displayName: 'Address', + values: [ + { + displayName: 'Street', + name: 'street', + type: 'string', + default: '', + }, + { + displayName: 'City', + name: 'city', + type: 'string', + default: '', + }, + { + displayName: 'State', + name: 'state', + type: 'string', + default: '', + }, + { + displayName: 'Postal Code', + name: 'postalCode', + type: 'string', + default: '', + }, + { + displayName: 'Country', + name: 'country', + type: 'string', + default: '', + }, + ] + }, + ], + }, + ] + }, + ], + }, + { + displayName: 'Context', + name: 'context', + placeholder: 'Add Context', + type: 'fixedCollection', + typeOptions: { + multipleValues: false, + }, + displayOptions: { + show: { + resource: [ + 'identify', + ], + operation: [ + 'create', + ], + }, + }, + default: {}, + options: [ + { + name: 'contextUi', + displayName: 'Context', + values: [ + { + displayName: 'Active', + name: 'active', + type: 'boolean', + default: '', + description: 'Whether a user is active', + }, + { + displayName: 'IP', + name: 'ip', + type: 'string', + default: '', + description: 'Current user’s IP address.', + }, + { + displayName: 'Locale', + name: 'locate', + type: 'string', + default: '', + description: 'Locale string for the current user, for example en-US.', + }, + { + displayName: 'Page', + name: 'page', + type: 'string', + default: '', + description: 'Dictionary of information about the current page in the browser, containing hash, path, referrer, search, title and url', + }, + { + displayName: 'Timezone', + name: 'timezone', + type: 'string', + default: '', + description: 'Timezones are sent as tzdata strings to add user timezone information which might be stripped from the timestamp, for example America/New_York', + }, + { + displayName: 'App', + name: 'app', + placeholder: 'Add App', + type: 'fixedCollection', + typeOptions: { + multipleValues: false, + }, + default: {}, + options: [ + { + name: 'appUi', + displayName: 'App', + values: [ + { + displayName: 'Name', + name: 'name', + type: 'string', + default: '', + }, + { + displayName: 'Version', + name: 'version', + type: 'string', + default: '', + }, + { + displayName: 'Build', + name: 'build', + type: 'string', + default: '', + }, + ] + }, + ], + }, + { + displayName: 'Campaign', + name: 'campaign', + placeholder: 'Campaign App', + type: 'fixedCollection', + typeOptions: { + multipleValues: false, + }, + default: {}, + options: [ + { + name: 'campaignUi', + displayName: 'Campaign', + values: [ + { + displayName: 'Name', + name: 'name', + type: 'string', + default: '', + }, + { + displayName: 'Source', + name: 'source', + type: 'string', + default: '', + }, + { + displayName: 'Medium', + name: 'medium', + type: 'string', + default: '', + }, + { + displayName: 'Term', + name: 'term', + type: 'string', + default: '', + }, + { + displayName: 'Content', + name: 'content', + type: 'string', + default: '', + }, + ] + }, + ], + }, + { + displayName: 'Device', + name: 'device', + placeholder: 'Add Device', + type: 'fixedCollection', + typeOptions: { + multipleValues: false, + }, + default: {}, + options: [ + { + name: 'deviceUi', + displayName: 'Device', + values: [ + { + displayName: 'ID', + name: 'id', + type: 'string', + default: '', + }, + { + displayName: 'Manufacturer', + name: 'manufacturer', + type: 'string', + default: '', + }, + { + displayName: 'Model', + name: 'model', + type: 'string', + default: '', + }, + { + displayName: 'Name', + name: 'name', + type: 'string', + default: '', + }, + { + displayName: 'Type', + name: 'type', + type: 'string', + default: '', + }, + { + displayName: 'Version', + name: 'version', + type: 'string', + default: '', + }, + ], + }, + ], + }, + ] + }, + ], + }, + { + displayName: 'Integration', + name: 'integrations', + placeholder: 'Add Integration', + type: 'fixedCollection', + typeOptions: { + multipleValues: false, + }, + displayOptions: { + show: { + resource: [ + 'identify', + ], + operation: [ + 'create', + ], + }, + }, + default: {}, + options: [ + { + name: 'integrationsUi', + displayName: 'Integration', + values: [ + { + displayName: 'All', + name: 'all', + type: 'boolean', + default: true, + }, + { + displayName: 'Salesforce', + name: 'salesforce', + type: 'boolean', + default: false, + }, + ], + }, + ], + }, +] as INodeProperties[]; diff --git a/packages/nodes-base/nodes/Segment/IdentifyInterface.ts b/packages/nodes-base/nodes/Segment/IdentifyInterface.ts new file mode 100644 index 0000000000..ae3d1e4c7b --- /dev/null +++ b/packages/nodes-base/nodes/Segment/IdentifyInterface.ts @@ -0,0 +1,10 @@ +import { IDataObject } from "n8n-workflow"; + +export interface IIdentify { + userId?: string; + anonymousId?: string; + traits?: IDataObject; + context?: IDataObject; + integrations?: IDataObject; + timestamp?: string; +} diff --git a/packages/nodes-base/nodes/Segment/Segment.node.ts b/packages/nodes-base/nodes/Segment/Segment.node.ts new file mode 100644 index 0000000000..f34f1c266b --- /dev/null +++ b/packages/nodes-base/nodes/Segment/Segment.node.ts @@ -0,0 +1,784 @@ +import { + IExecuteFunctions, +} from 'n8n-core'; +import { + IDataObject, + INodeExecutionData, + INodeType, + INodeTypeDescription, +} from 'n8n-workflow'; +import { + segmentApiRequest, +} from './GenericFunctions'; +import { + identifyFields, + identifyOperations, +} from './IdentifyDescription'; +import { + IIdentify, +} from './IdentifyInterface'; +import { + trackOperations, + trackFields, +} from './TrackDescription'; +import { ITrack } from './TrackInterface'; +import * as uuid from 'uuid/v4'; + +export class Segment implements INodeType { + description: INodeTypeDescription = { + displayName: 'Segment', + name: 'segment', + icon: 'file:segment.png', + group: ['output'], + version: 1, + subtitle: '={{$parameter["operation"] + ":" + $parameter["resource"]}}', + description: 'Consume Segment API', + defaults: { + name: 'Segment', + color: '#6ebb99', + }, + inputs: ['main'], + outputs: ['main'], + credentials: [ + { + name: 'segmentApi', + required: true, + } + ], + properties: [ + { + displayName: 'Resource', + name: 'resource', + type: 'options', + options: [ + { + name: 'Identify', + value: 'identify', + description: 'Identify lets you tie a user to their actions.' + }, + { + name: 'Track', + value: 'track', + description: 'Track lets you record events', + }, + ], + default: 'identify', + description: 'Resource to consume.', + }, + ...identifyOperations, + ...trackOperations, + ...identifyFields, + ...trackFields, + ], + }; + + async execute(this: IExecuteFunctions): Promise { + const items = this.getInputData(); + const returnData: IDataObject[] = []; + const length = items.length as unknown as number; + const qs: IDataObject = {}; + let responseData; + const resource = this.getNodeParameter('resource', 0) as string; + const operation = this.getNodeParameter('operation', 0) as string; + for (let i = 0; i < length; i++) { + if (resource === 'identify') { + //https://segment.com/docs/connections/sources/catalog/libraries/server/http-api/#identify + if (operation === 'create') { + const userId = this.getNodeParameter('userId', i) as string; + const traits = (this.getNodeParameter('traits', i) as IDataObject).traitsUi as IDataObject; + const context = (this.getNodeParameter('context', i) as IDataObject).contextUi as IDataObject; + const integrations = (this.getNodeParameter('integrations', i) as IDataObject).integrationsUi as IDataObject; + const body: IIdentify = { + traits: { + company: {}, + address: {}, + }, + context: { + app: {}, + campaign: {}, + device: {}, + }, + integrations: {}, + }; + if (userId) { + body.userId = userId as string; + } else { + body.anonymousId = uuid(); + } + if (traits) { + if (traits.email) { + body.traits!.email = traits.email as string; + } + if (traits.firstname) { + body.traits!.firstname = traits.firstname as string; + } + if (traits.lastname) { + body.traits!.lastname = traits.lastname as string; + } + if (traits.gender) { + body.traits!.gender = traits.gender as string; + } + if (traits.phone) { + body.traits!.phone = traits.phone as string; + } + if (traits.username) { + body.traits!.username = traits.username as string; + } + if (traits.website) { + body.traits!.website = traits.website as string; + } + if (traits.age) { + body.traits!.age = traits.age as number; + } + if (traits.avatar) { + body.traits!.avatar = traits.avatar as string; + } + if (traits.birthday) { + body.traits!.birthday = traits.birthday as string; + } + if (traits.createdAt) { + body.traits!.createdAt = traits.createdAt as string; + } + if (traits.description) { + body.traits!.description = traits.description as string; + } + if (traits.id) { + body.traits!.id = traits.id as string; + } + if (traits.company) { + const company = (traits.company as IDataObject).companyUi as IDataObject; + if (company) { + if (company.id) { + //@ts-ignore + body.traits.company.id = company.id as string; + } + if (company.name) { + //@ts-ignore + body.traits.company.name = company.name as string; + } + if (company.industry) { + //@ts-ignore + body.traits.company.industry = company.industry as string; + } + if (company.employeeCount) { + //@ts-ignore + body.traits.company.employeeCount = company.employeeCount as number; + } + if (company.plan) { + //@ts-ignore + body.traits.company.plan = company.plan as string; + } + } + } + if (traits.address) { + const address = (traits.address as IDataObject).addressUi as IDataObject; + if (address) { + if (address.street) { + //@ts-ignore + body.traits.address.street = address.street as string; + } + if (address.city) { + //@ts-ignore + body.traits.address.city = address.city as string; + } + if (address.state) { + //@ts-ignore + body.traits.address.state = address.state as string; + } + if (address.postalCode) { + //@ts-ignore + body.traits.address.postalCode = address.postalCode as string; + } + if (address.country) { + //@ts-ignore + body.traits.address.country = address.country as string; + } + } + } + } + if (context) { + if (context.active) { + body.context!.active = context.active as boolean; + } + if (context.ip) { + body.context!.ip = context.ip as string; + } + if (context.locate) { + body.context!.locate = context.locate as string; + } + if (context.page) { + body.context!.page = context.page as string; + } + if (context.timezone) { + body.context!.timezone = context.timezone as string; + } + if (context.timezone) { + body.context!.timezone = context.timezone as string; + } + if (context.app) { + const app = (context.app as IDataObject).appUi as IDataObject; + if (app) { + if (app.name) { + //@ts-ignore + body.context.app.name = app.name as string; + } + if (app.version) { + //@ts-ignore + body.context.app.version = app.version as string; + } + if (app.build) { + //@ts-ignore + body.context.app.build = app.build as string; + } + } + } + if (context.campaign) { + const campaign = (context.campaign as IDataObject).campaignUi as IDataObject; + if (campaign) { + if (campaign.name) { + //@ts-ignore + body.context.campaign.name = campaign.name as string; + } + if (campaign.source) { + //@ts-ignore + body.context.campaign.source = campaign.source as string; + } + if (campaign.medium) { + //@ts-ignore + body.context.campaign.medium = campaign.medium as string; + } + if (campaign.term) { + //@ts-ignore + body.context.campaign.term = campaign.term as string; + } + if (campaign.content) { + //@ts-ignore + body.context.campaign.content = campaign.content as string; + } + } + } + + if (context.device) { + const device = (context.device as IDataObject).deviceUi as IDataObject; + if (device) { + if (device.id) { + //@ts-ignore + body.context.device.id = device.id as string; + } + if (device.manufacturer) { + //@ts-ignore + body.context.device.manufacturer = device.manufacturer as string; + } + if (device.model) { + //@ts-ignore + body.context.device.model = device.model as string; + } + if (device.type) { + //@ts-ignore + body.context.device.type = device.type as string; + } + if (device.version) { + //@ts-ignore + body.context.device.version = device.version as string; + } + } + } + } + if (integrations) { + if (integrations.all) { + body.integrations!.all = integrations.all as boolean; + } + if (integrations.salesforce) { + body.integrations!.salesforce = integrations.salesforce as boolean; + } + } + responseData = await segmentApiRequest.call(this, 'POST', '/identify', body); + } + } + if (resource === 'track') { + //https://segment.com/docs/connections/sources/catalog/libraries/server/http-api/#track + if (operation === 'event') { + const userId = this.getNodeParameter('userId', i) as string; + const event = this.getNodeParameter('event', i) as string; + const traits = (this.getNodeParameter('traits', i) as IDataObject).traitsUi as IDataObject; + const context = (this.getNodeParameter('context', i) as IDataObject).contextUi as IDataObject; + const integrations = (this.getNodeParameter('integrations', i) as IDataObject).integrationsUi as IDataObject; + const properties = (this.getNodeParameter('properties', i) as IDataObject).propertiesUi as IDataObject; + const body: ITrack = { + event, + traits: { + company: {}, + address: {}, + }, + context: { + app: {}, + campaign: {}, + device: {}, + }, + integrations: {}, + properties: {}, + }; + if (userId) { + body.userId = userId as string; + } else { + body.anonymousId = uuid(); + } + if (userId) { + body.userId = userId as string; + } else { + body.anonymousId = uuid(); + } + if (traits) { + if (traits.email) { + body.traits!.email = traits.email as string; + } + if (traits.firstname) { + body.traits!.firstname = traits.firstname as string; + } + if (traits.lastname) { + body.traits!.lastname = traits.lastname as string; + } + if (traits.gender) { + body.traits!.gender = traits.gender as string; + } + if (traits.phone) { + body.traits!.phone = traits.phone as string; + } + if (traits.username) { + body.traits!.username = traits.username as string; + } + if (traits.website) { + body.traits!.website = traits.website as string; + } + if (traits.age) { + body.traits!.age = traits.age as number; + } + if (traits.avatar) { + body.traits!.avatar = traits.avatar as string; + } + if (traits.birthday) { + body.traits!.birthday = traits.birthday as string; + } + if (traits.createdAt) { + body.traits!.createdAt = traits.createdAt as string; + } + if (traits.description) { + body.traits!.description = traits.description as string; + } + if (traits.id) { + body.traits!.id = traits.id as string; + } + if (traits.company) { + const company = (traits.company as IDataObject).companyUi as IDataObject; + if (company) { + if (company.id) { + //@ts-ignore + body.traits.company.id = company.id as string; + } + if (company.name) { + //@ts-ignore + body.traits.company.name = company.name as string; + } + if (company.industry) { + //@ts-ignore + body.traits.company.industry = company.industry as string; + } + if (company.employeeCount) { + //@ts-ignore + body.traits.company.employeeCount = company.employeeCount as number; + } + if (company.plan) { + //@ts-ignore + body.traits.company.plan = company.plan as string; + } + } + } + if (traits.address) { + const address = (traits.address as IDataObject).addressUi as IDataObject; + if (address) { + if (address.street) { + //@ts-ignore + body.traits.address.street = address.street as string; + } + if (address.city) { + //@ts-ignore + body.traits.address.city = address.city as string; + } + if (address.state) { + //@ts-ignore + body.traits.address.state = address.state as string; + } + if (address.postalCode) { + //@ts-ignore + body.traits.address.postalCode = address.postalCode as string; + } + if (address.country) { + //@ts-ignore + body.traits.address.country = address.country as string; + } + } + } + } + if (context) { + if (context.active) { + body.context!.active = context.active as boolean; + } + if (context.ip) { + body.context!.ip = context.ip as string; + } + if (context.locate) { + body.context!.locate = context.locate as string; + } + if (context.page) { + body.context!.page = context.page as string; + } + if (context.timezone) { + body.context!.timezone = context.timezone as string; + } + if (context.timezone) { + body.context!.timezone = context.timezone as string; + } + if (context.app) { + const app = (context.app as IDataObject).appUi as IDataObject; + if (app) { + if (app.name) { + //@ts-ignore + body.context.app.name = app.name as string; + } + if (app.version) { + //@ts-ignore + body.context.app.version = app.version as string; + } + if (app.build) { + //@ts-ignore + body.context.app.build = app.build as string; + } + } + } + if (context.campaign) { + const campaign = (context.campaign as IDataObject).campaignUi as IDataObject; + if (campaign) { + if (campaign.name) { + //@ts-ignore + body.context.campaign.name = campaign.name as string; + } + if (campaign.source) { + //@ts-ignore + body.context.campaign.source = campaign.source as string; + } + if (campaign.medium) { + //@ts-ignore + body.context.campaign.medium = campaign.medium as string; + } + if (campaign.term) { + //@ts-ignore + body.context.campaign.term = campaign.term as string; + } + if (campaign.content) { + //@ts-ignore + body.context.campaign.content = campaign.content as string; + } + } + } + + if (context.device) { + const device = (context.device as IDataObject).deviceUi as IDataObject; + if (device) { + if (device.id) { + //@ts-ignore + body.context.device.id = device.id as string; + } + if (device.manufacturer) { + //@ts-ignore + body.context.device.manufacturer = device.manufacturer as string; + } + if (device.model) { + //@ts-ignore + body.context.device.model = device.model as string; + } + if (device.type) { + //@ts-ignore + body.context.device.type = device.type as string; + } + if (device.version) { + //@ts-ignore + body.context.device.version = device.version as string; + } + } + } + } + if (integrations) { + if (integrations.all) { + body.integrations!.all = integrations.all as boolean; + } + if (integrations.salesforce) { + body.integrations!.salesforce = integrations.salesforce as boolean; + } + } + if (properties) { + if (properties.revenue) { + body.properties!.revenue = properties.revenue as number; + } + if (properties.currency) { + body.properties!.currency = properties.currency as string; + } + if (properties.value) { + body.properties!.value = properties.value as string; + } + } + responseData = await segmentApiRequest.call(this, 'POST', '/track', body); + } + //https://segment.com/docs/connections/sources/catalog/libraries/server/http-api/#page + if (operation === 'page') { + const userId = this.getNodeParameter('userId', i) as string; + const event = this.getNodeParameter('event', i) as string; + const traits = (this.getNodeParameter('traits', i) as IDataObject).traitsUi as IDataObject; + const context = (this.getNodeParameter('context', i) as IDataObject).contextUi as IDataObject; + const integrations = (this.getNodeParameter('integrations', i) as IDataObject).integrationsUi as IDataObject; + const properties = (this.getNodeParameter('properties', i) as IDataObject).propertiesUi as IDataObject; + const body: ITrack = { + event, + traits: { + company: {}, + address: {}, + }, + context: { + app: {}, + campaign: {}, + device: {}, + }, + integrations: {}, + properties: {}, + }; + if (userId) { + body.userId = userId as string; + } else { + body.anonymousId = uuid(); + } + if (userId) { + body.userId = userId as string; + } else { + body.anonymousId = uuid(); + } + if (traits) { + if (traits.email) { + body.traits!.email = traits.email as string; + } + if (traits.firstname) { + body.traits!.firstname = traits.firstname as string; + } + if (traits.lastname) { + body.traits!.lastname = traits.lastname as string; + } + if (traits.gender) { + body.traits!.gender = traits.gender as string; + } + if (traits.phone) { + body.traits!.phone = traits.phone as string; + } + if (traits.username) { + body.traits!.username = traits.username as string; + } + if (traits.website) { + body.traits!.website = traits.website as string; + } + if (traits.age) { + body.traits!.age = traits.age as number; + } + if (traits.avatar) { + body.traits!.avatar = traits.avatar as string; + } + if (traits.birthday) { + body.traits!.birthday = traits.birthday as string; + } + if (traits.createdAt) { + body.traits!.createdAt = traits.createdAt as string; + } + if (traits.description) { + body.traits!.description = traits.description as string; + } + if (traits.id) { + body.traits!.id = traits.id as string; + } + if (traits.company) { + const company = (traits.company as IDataObject).companyUi as IDataObject; + if (company) { + if (company.id) { + //@ts-ignore + body.traits.company.id = company.id as string; + } + if (company.name) { + //@ts-ignore + body.traits.company.name = company.name as string; + } + if (company.industry) { + //@ts-ignore + body.traits.company.industry = company.industry as string; + } + if (company.employeeCount) { + //@ts-ignore + body.traits.company.employeeCount = company.employeeCount as number; + } + if (company.plan) { + //@ts-ignore + body.traits.company.plan = company.plan as string; + } + } + } + if (traits.address) { + const address = (traits.address as IDataObject).addressUi as IDataObject; + if (address) { + if (address.street) { + //@ts-ignore + body.traits.address.street = address.street as string; + } + if (address.city) { + //@ts-ignore + body.traits.address.city = address.city as string; + } + if (address.state) { + //@ts-ignore + body.traits.address.state = address.state as string; + } + if (address.postalCode) { + //@ts-ignore + body.traits.address.postalCode = address.postalCode as string; + } + if (address.country) { + //@ts-ignore + body.traits.address.country = address.country as string; + } + } + } + } + if (context) { + if (context.active) { + body.context!.active = context.active as boolean; + } + if (context.ip) { + body.context!.ip = context.ip as string; + } + if (context.locate) { + body.context!.locate = context.locate as string; + } + if (context.page) { + body.context!.page = context.page as string; + } + if (context.timezone) { + body.context!.timezone = context.timezone as string; + } + if (context.timezone) { + body.context!.timezone = context.timezone as string; + } + if (context.app) { + const app = (context.app as IDataObject).appUi as IDataObject; + if (app) { + if (app.name) { + //@ts-ignore + body.context.app.name = app.name as string; + } + if (app.version) { + //@ts-ignore + body.context.app.version = app.version as string; + } + if (app.build) { + //@ts-ignore + body.context.app.build = app.build as string; + } + } + } + if (context.campaign) { + const campaign = (context.campaign as IDataObject).campaignUi as IDataObject; + if (campaign) { + if (campaign.name) { + //@ts-ignore + body.context.campaign.name = campaign.name as string; + } + if (campaign.source) { + //@ts-ignore + body.context.campaign.source = campaign.source as string; + } + if (campaign.medium) { + //@ts-ignore + body.context.campaign.medium = campaign.medium as string; + } + if (campaign.term) { + //@ts-ignore + body.context.campaign.term = campaign.term as string; + } + if (campaign.content) { + //@ts-ignore + body.context.campaign.content = campaign.content as string; + } + } + } + + if (context.device) { + const device = (context.device as IDataObject).deviceUi as IDataObject; + if (device) { + if (device.id) { + //@ts-ignore + body.context.device.id = device.id as string; + } + if (device.manufacturer) { + //@ts-ignore + body.context.device.manufacturer = device.manufacturer as string; + } + if (device.model) { + //@ts-ignore + body.context.device.model = device.model as string; + } + if (device.type) { + //@ts-ignore + body.context.device.type = device.type as string; + } + if (device.version) { + //@ts-ignore + body.context.device.version = device.version as string; + } + } + } + } + if (integrations) { + if (integrations.all) { + body.integrations!.all = integrations.all as boolean; + } + if (integrations.salesforce) { + body.integrations!.salesforce = integrations.salesforce as boolean; + } + } + if (properties) { + if (properties.name) { + body.properties!.name = properties.name as number; + } + if (properties.path) { + body.properties!.path = properties.path as string; + } + if (properties.referrer) { + body.properties!.referrer = properties.referrer as string; + } + if (properties.search) { + body.properties!.search = properties.search as string; + } + if (properties.title) { + body.properties!.title = properties.title as string; + } + if (properties.url) { + body.properties!.url = properties.url as string; + } + if (properties.keywords) { + body.properties!.keywords = properties.keywords as string; + } + } + responseData = await segmentApiRequest.call(this, 'POST', '/page', body); + } + } + if (Array.isArray(responseData)) { + returnData.push.apply(returnData, responseData as IDataObject[]); + } else { + returnData.push(responseData as IDataObject); + } + } + return [this.helpers.returnJsonArray(returnData)]; + } +} diff --git a/packages/nodes-base/nodes/Segment/TrackDescription.ts b/packages/nodes-base/nodes/Segment/TrackDescription.ts new file mode 100644 index 0000000000..cd03f9064b --- /dev/null +++ b/packages/nodes-base/nodes/Segment/TrackDescription.ts @@ -0,0 +1,1155 @@ +import { INodeProperties } from 'n8n-workflow'; + +export const trackOperations = [ + { + displayName: 'Operation', + name: 'operation', + type: 'options', + displayOptions: { + show: { + resource: [ + 'track', + ], + }, + }, + options: [ + { + name: 'Event', + value: 'event', + description: 'lets you record the actions your users perform.Every action triggers what we call an “event”, which can also have associated properties.', + }, + { + name: 'Page', + value: 'page', + description: ' lets you record page views on your website, along with optional extra information about the page being viewed.', + }, + ], + default: 'event', + description: 'The operation to perform.', + }, +] as INodeProperties[]; + +export const trackFields = [ + +/* -------------------------------------------------------------------------- */ +/* track:event */ +/* -------------------------------------------------------------------------- */ + { + displayName: 'User ID', + name: 'userId', + type: 'string', + default: '', + displayOptions: { + show: { + resource: [ + 'track', + ], + operation: [ + 'event', + ], + }, + }, + required: false, + }, + { + displayName: 'Event', + name: 'event', + type: 'string', + default: '', + displayOptions: { + show: { + resource: [ + 'track', + ], + operation: [ + 'event', + ], + }, + }, + description: 'Name of the action that a user has performed.', + required: true, + }, + { + displayName: 'Traits', + name: 'traits', + placeholder: 'Add Trait', + type: 'fixedCollection', + typeOptions: { + multipleValues: false, + }, + displayOptions: { + show: { + resource: [ + 'track', + ], + operation: [ + 'event', + ], + }, + }, + default: {}, + options: [ + { + name: 'traitsUi', + displayName: 'Trait', + values: [ + { + displayName: 'Email', + name: 'emaiL', + type: 'string', + default: '', + description: 'Email address of a user', + }, + { + displayName: 'First Name', + name: 'firstname', + type: 'string', + default: '', + description: 'First name of a user', + }, + { + displayName: 'Last Name', + name: 'lastname', + type: 'string', + default: '', + description: 'Last name of a user', + }, + { + displayName: 'Gender', + name: 'gender', + type: 'string', + default: '', + description: 'Gender of a user', + }, + { + displayName: 'Phone', + name: 'phone', + type: 'string', + default: '', + description: 'Phone number of a user', + }, + { + displayName: 'Username', + name: 'username', + type: 'string', + default: '', + description: 'User’s username', + }, + { + displayName: 'Website', + name: 'website', + type: 'string', + default: '', + description: 'Website of a user', + }, + { + displayName: 'Age', + name: 'age', + type: 'number', + default: 1, + description: 'Age of a user', + }, + { + displayName: 'Avatar', + name: 'avatar', + type: 'string', + default: '', + description: 'URL to an avatar image for the user', + }, + { + displayName: 'Birthday', + name: 'birthday', + type: 'dateTime', + default: '', + description: 'User’s birthday', + }, + { + displayName: 'eventd At', + name: 'eventdAt', + type: 'dateTime', + default: '', + description: 'Date the user’s account was first eventd', + }, + { + displayName: 'Description', + name: 'description', + type: 'string', + typeOptions: { + alwaysOpenEditWindow: true, + }, + default: '', + description: 'Description of the user', + }, + { + displayName: 'ID', + name: 'id', + type: 'string', + default: '', + description: 'Unique ID in your database for a user', + }, + { + displayName: 'Company', + name: 'company', + placeholder: 'Add Company', + type: 'fixedCollection', + typeOptions: { + multipleValues: false, + }, + default: {}, + options: [ + { + name: 'companyUi', + displayName: 'Company', + values: [ + { + displayName: 'ID', + name: 'id', + type: 'string', + default: '', + }, + { + displayName: 'Name', + name: 'name', + type: 'string', + default: '', + }, + { + displayName: 'Industry', + name: 'industry', + type: 'string', + default: '', + }, + { + displayName: 'Employee Count', + name: 'employeeCount', + type: 'number', + default: 1, + }, + { + displayName: 'Plan', + name: 'plan', + type: 'string', + default: '', + }, + ] + }, + ], + }, + { + displayName: 'Address', + name: 'address', + placeholder: 'Add Address', + type: 'fixedCollection', + typeOptions: { + multipleValues: false, + }, + default: {}, + options: [ + { + name: 'addressUi', + displayName: 'Address', + values: [ + { + displayName: 'Street', + name: 'street', + type: 'string', + default: '', + }, + { + displayName: 'City', + name: 'city', + type: 'string', + default: '', + }, + { + displayName: 'State', + name: 'state', + type: 'string', + default: '', + }, + { + displayName: 'Postal Code', + name: 'postalCode', + type: 'string', + default: '', + }, + { + displayName: 'Country', + name: 'country', + type: 'string', + default: '', + }, + ] + }, + ], + }, + ] + }, + ], + }, + { + displayName: 'Context', + name: 'context', + placeholder: 'Add Context', + type: 'fixedCollection', + typeOptions: { + multipleValues: false, + }, + displayOptions: { + show: { + resource: [ + 'track', + ], + operation: [ + 'event', + ], + }, + }, + default: {}, + options: [ + { + name: 'contextUi', + displayName: 'Context', + values: [ + { + displayName: 'Active', + name: 'active', + type: 'boolean', + default: '', + description: 'Whether a user is active', + }, + { + displayName: 'IP', + name: 'ip', + type: 'string', + default: '', + description: 'Current user’s IP address.', + }, + { + displayName: 'Locale', + name: 'locate', + type: 'string', + default: '', + description: 'Locale string for the current user, for example en-US.', + }, + { + displayName: 'Page', + name: 'page', + type: 'string', + default: '', + description: 'Dictionary of information about the current page in the browser, containing hash, path, referrer, search, title and url', + }, + { + displayName: 'Timezone', + name: 'timezone', + type: 'string', + default: '', + description: 'Timezones are sent as tzdata strings to add user timezone information which might be stripped from the timestamp, for example America/New_York', + }, + { + displayName: 'App', + name: 'app', + placeholder: 'Add App', + type: 'fixedCollection', + typeOptions: { + multipleValues: false, + }, + default: {}, + options: [ + { + name: 'appUi', + displayName: 'App', + values: [ + { + displayName: 'Name', + name: 'name', + type: 'string', + default: '', + }, + { + displayName: 'Version', + name: 'version', + type: 'string', + default: '', + }, + { + displayName: 'Build', + name: 'build', + type: 'string', + default: '', + }, + ] + }, + ], + }, + { + displayName: 'Campaign', + name: 'campaign', + placeholder: 'Campaign App', + type: 'fixedCollection', + typeOptions: { + multipleValues: false, + }, + default: {}, + options: [ + { + name: 'campaignUi', + displayName: 'Campaign', + values: [ + { + displayName: 'Name', + name: 'name', + type: 'string', + default: '', + }, + { + displayName: 'Source', + name: 'source', + type: 'string', + default: '', + }, + { + displayName: 'Medium', + name: 'medium', + type: 'string', + default: '', + }, + { + displayName: 'Term', + name: 'term', + type: 'string', + default: '', + }, + { + displayName: 'Content', + name: 'content', + type: 'string', + default: '', + }, + ] + }, + ], + }, + { + displayName: 'Device', + name: 'device', + placeholder: 'Add Device', + type: 'fixedCollection', + typeOptions: { + multipleValues: false, + }, + default: {}, + options: [ + { + name: 'deviceUi', + displayName: 'Device', + values: [ + { + displayName: 'ID', + name: 'id', + type: 'string', + default: '', + }, + { + displayName: 'Manufacturer', + name: 'manufacturer', + type: 'string', + default: '', + }, + { + displayName: 'Model', + name: 'model', + type: 'string', + default: '', + }, + { + displayName: 'Name', + name: 'name', + type: 'string', + default: '', + }, + { + displayName: 'Type', + name: 'type', + type: 'string', + default: '', + }, + { + displayName: 'Version', + name: 'version', + type: 'string', + default: '', + }, + ], + }, + ], + }, + ] + }, + ], + }, + { + displayName: 'Integration', + name: 'integrations', + placeholder: 'Add Integration', + type: 'fixedCollection', + typeOptions: { + multipleValues: false, + }, + displayOptions: { + show: { + resource: [ + 'track', + ], + operation: [ + 'event', + ], + }, + }, + default: {}, + options: [ + { + name: 'integrationsUi', + displayName: 'Integration', + values: [ + { + displayName: 'All', + name: 'all', + type: 'boolean', + default: true, + }, + { + displayName: 'Salesforce', + name: 'salesforce', + type: 'boolean', + default: false, + }, + ], + }, + ], + }, + { + displayName: 'Properties', + name: 'properties', + placeholder: 'Add Properties', + type: 'fixedCollection', + typeOptions: { + multipleValues: false, + }, + displayOptions: { + show: { + resource: [ + 'track', + ], + operation: [ + 'event', + ], + }, + }, + default: {}, + options: [ + { + name: 'propertiesUi', + displayName: 'Properties', + values: [ + { + displayName: 'Revenue', + name: 'revenue', + type: 'number', + typeOptions: { + numberPrecision: 2, + }, + default: 1, + description: 'Amount of revenue an event resulted in. This should be a decimal value, so a shirt worth $19.99 would result in a revenue of 19.99.' + }, + { + displayName: 'Currency', + name: 'currency', + type: 'string', + default: '', + description: 'Currency of the revenue an event resulted in

This should be sent in the ISO 4127 format. If this is not set, we assume the revenue to be in US dollars.

' + }, + { + displayName: 'Value', + name: 'value', + type: 'number', + default: '', + description: 'An abstract “value” to associate with an event. This is typically used in situations where the event doesn’t generate real-dollar revenue, but has an intrinsic value to a marketing team, like newsletter signups.' + }, + ], + }, + ], + }, +/* -------------------------------------------------------------------------- */ +/* track:page */ +/* -------------------------------------------------------------------------- */ + { + displayName: 'User ID', + name: 'userId', + type: 'string', + default: '', + displayOptions: { + show: { + resource: [ + 'track', + ], + operation: [ + 'page', + ], + }, + }, + }, + { + displayName: 'Name', + name: 'name', + type: 'string', + default: '', + displayOptions: { + show: { + resource: [ + 'track', + ], + operation: [ + 'page', + ], + }, + }, + description: 'Name of the page For example, most sites have a “Signup” page that can be useful to tag, so you can see users as they move through your funnel', + }, + { + displayName: 'Traits', + name: 'traits', + placeholder: 'Add Trait', + type: 'fixedCollection', + typeOptions: { + multipleValues: false, + }, + displayOptions: { + show: { + resource: [ + 'track', + ], + operation: [ + 'page', + ], + }, + }, + default: {}, + options: [ + { + name: 'traitsUi', + displayName: 'Trait', + values: [ + { + displayName: 'Email', + name: 'emaiL', + type: 'string', + default: '', + description: 'Email address of a user', + }, + { + displayName: 'First Name', + name: 'firstname', + type: 'string', + default: '', + description: 'First name of a user', + }, + { + displayName: 'Last Name', + name: 'lastname', + type: 'string', + default: '', + description: 'Last name of a user', + }, + { + displayName: 'Gender', + name: 'gender', + type: 'string', + default: '', + description: 'Gender of a user', + }, + { + displayName: 'Phone', + name: 'phone', + type: 'string', + default: '', + description: 'Phone number of a user', + }, + { + displayName: 'Username', + name: 'username', + type: 'string', + default: '', + description: 'User’s username', + }, + { + displayName: 'Website', + name: 'website', + type: 'string', + default: '', + description: 'Website of a user', + }, + { + displayName: 'Age', + name: 'age', + type: 'number', + default: 1, + description: 'Age of a user', + }, + { + displayName: 'Avatar', + name: 'avatar', + type: 'string', + default: '', + description: 'URL to an avatar image for the user', + }, + { + displayName: 'Birthday', + name: 'birthday', + type: 'dateTime', + default: '', + description: 'User’s birthday', + }, + { + displayName: 'eventd At', + name: 'eventdAt', + type: 'dateTime', + default: '', + description: 'Date the user’s account was first eventd', + }, + { + displayName: 'Description', + name: 'description', + type: 'string', + typeOptions: { + alwaysOpenEditWindow: true, + }, + default: '', + description: 'Description of the user', + }, + { + displayName: 'ID', + name: 'id', + type: 'string', + default: '', + description: 'Unique ID in your database for a user', + }, + { + displayName: 'Company', + name: 'company', + placeholder: 'Add Company', + type: 'fixedCollection', + typeOptions: { + multipleValues: false, + }, + default: {}, + options: [ + { + name: 'companyUi', + displayName: 'Company', + values: [ + { + displayName: 'ID', + name: 'id', + type: 'string', + default: '', + }, + { + displayName: 'Name', + name: 'name', + type: 'string', + default: '', + }, + { + displayName: 'Industry', + name: 'industry', + type: 'string', + default: '', + }, + { + displayName: 'Employee Count', + name: 'employeeCount', + type: 'number', + default: 1, + }, + { + displayName: 'Plan', + name: 'plan', + type: 'string', + default: '', + }, + ] + }, + ], + }, + { + displayName: 'Address', + name: 'address', + placeholder: 'Add Address', + type: 'fixedCollection', + typeOptions: { + multipleValues: false, + }, + default: {}, + options: [ + { + name: 'addressUi', + displayName: 'Address', + values: [ + { + displayName: 'Street', + name: 'street', + type: 'string', + default: '', + }, + { + displayName: 'City', + name: 'city', + type: 'string', + default: '', + }, + { + displayName: 'State', + name: 'state', + type: 'string', + default: '', + }, + { + displayName: 'Postal Code', + name: 'postalCode', + type: 'string', + default: '', + }, + { + displayName: 'Country', + name: 'country', + type: 'string', + default: '', + }, + ] + }, + ], + }, + ] + }, + ], + }, + { + displayName: 'Context', + name: 'context', + placeholder: 'Add Context', + type: 'fixedCollection', + typeOptions: { + multipleValues: false, + }, + displayOptions: { + show: { + resource: [ + 'track', + ], + operation: [ + 'page', + ], + }, + }, + default: {}, + options: [ + { + name: 'contextUi', + displayName: 'Context', + values: [ + { + displayName: 'Active', + name: 'active', + type: 'boolean', + default: '', + description: 'Whether a user is active', + }, + { + displayName: 'IP', + name: 'ip', + type: 'string', + default: '', + description: 'Current user’s IP address.', + }, + { + displayName: 'Locale', + name: 'locate', + type: 'string', + default: '', + description: 'Locale string for the current user, for example en-US.', + }, + { + displayName: 'Page', + name: 'page', + type: 'string', + default: '', + description: 'Dictionary of information about the current page in the browser, containing hash, path, referrer, search, title and url', + }, + { + displayName: 'Timezone', + name: 'timezone', + type: 'string', + default: '', + description: 'Timezones are sent as tzdata strings to add user timezone information which might be stripped from the timestamp, for example America/New_York', + }, + { + displayName: 'App', + name: 'app', + placeholder: 'Add App', + type: 'fixedCollection', + typeOptions: { + multipleValues: false, + }, + default: {}, + options: [ + { + name: 'appUi', + displayName: 'App', + values: [ + { + displayName: 'Name', + name: 'name', + type: 'string', + default: '', + }, + { + displayName: 'Version', + name: 'version', + type: 'string', + default: '', + }, + { + displayName: 'Build', + name: 'build', + type: 'string', + default: '', + }, + ] + }, + ], + }, + { + displayName: 'Campaign', + name: 'campaign', + placeholder: 'Campaign App', + type: 'fixedCollection', + typeOptions: { + multipleValues: false, + }, + default: {}, + options: [ + { + name: 'campaignUi', + displayName: 'Campaign', + values: [ + { + displayName: 'Name', + name: 'name', + type: 'string', + default: '', + }, + { + displayName: 'Source', + name: 'source', + type: 'string', + default: '', + }, + { + displayName: 'Medium', + name: 'medium', + type: 'string', + default: '', + }, + { + displayName: 'Term', + name: 'term', + type: 'string', + default: '', + }, + { + displayName: 'Content', + name: 'content', + type: 'string', + default: '', + }, + ] + }, + ], + }, + { + displayName: 'Device', + name: 'device', + placeholder: 'Add Device', + type: 'fixedCollection', + typeOptions: { + multipleValues: false, + }, + default: {}, + options: [ + { + name: 'deviceUi', + displayName: 'Device', + values: [ + { + displayName: 'ID', + name: 'id', + type: 'string', + default: '', + }, + { + displayName: 'Manufacturer', + name: 'manufacturer', + type: 'string', + default: '', + }, + { + displayName: 'Model', + name: 'model', + type: 'string', + default: '', + }, + { + displayName: 'Name', + name: 'name', + type: 'string', + default: '', + }, + { + displayName: 'Type', + name: 'type', + type: 'string', + default: '', + }, + { + displayName: 'Version', + name: 'version', + type: 'string', + default: '', + }, + ], + }, + ], + }, + ] + }, + ], + }, + { + displayName: 'Integration', + name: 'integrations', + placeholder: 'Add Integration', + type: 'fixedCollection', + typeOptions: { + multipleValues: false, + }, + displayOptions: { + show: { + resource: [ + 'track', + ], + operation: [ + 'page', + ], + }, + }, + default: {}, + options: [ + { + name: 'integrationsUi', + displayName: 'Integration', + values: [ + { + displayName: 'All', + name: 'all', + type: 'boolean', + default: true, + }, + { + displayName: 'Salesforce', + name: 'salesforce', + type: 'boolean', + default: false, + }, + ], + }, + ], + }, + { + displayName: 'Properties', + name: 'properties', + placeholder: 'Add Properties', + type: 'fixedCollection', + typeOptions: { + multipleValues: false, + }, + displayOptions: { + show: { + resource: [ + 'track', + ], + operation: [ + 'page', + ], + }, + }, + default: {}, + options: [ + { + name: 'propertiesUi', + displayName: 'Properties', + values: [ + { + displayName: 'Name', + name: 'name', + type: 'string', + default: '', + description: 'Name of the page. This is reserved for future use.' + }, + { + displayName: 'Path', + name: 'path', + type: 'string', + default: '', + description: 'Path portion of the URL of the page. Equivalent to canonical path which defaults to location.pathname from the DOM API.' + }, + { + displayName: 'Referrer', + name: 'referrer', + type: 'string', + default: '', + description: 'Full URL of the previous page. Equivalent to document.referrer from the DOM API.' + }, + { + displayName: 'Search', + name: 'search', + type: 'string', + default: '', + description: 'Query string portion of the URL of the page. Equivalent to location.search from the DOM API.' + }, + { + displayName: 'Title', + name: 'title', + type: 'string', + default: '', + description: 'Title of the page. Equivalent to document.title from the DOM API.' + }, + { + displayName: 'URL', + name: 'url', + type: 'string', + default: '', + description: 'Full URL of the page. First we look for the canonical url. If the canonical url is not provided, we use location.href from the DOM API.' + }, + { + displayName: 'Keywords', + name: 'keywords', + type: 'string', + default: '', + description: 'A list/array of keywords describing the content of the page. The keywords would most likely be the same as, or similar to, the keywords you would find in an html meta tag for SEO purposes.' + }, + ], + }, + ], + }, +] as INodeProperties[]; diff --git a/packages/nodes-base/nodes/Segment/TrackInterface.ts b/packages/nodes-base/nodes/Segment/TrackInterface.ts new file mode 100644 index 0000000000..85ce0924e5 --- /dev/null +++ b/packages/nodes-base/nodes/Segment/TrackInterface.ts @@ -0,0 +1,13 @@ +import { IDataObject } from "n8n-workflow"; + +export interface ITrack { + event?: string; + userId?: string; + name?: string; + anonymousId?: string; + traits?: IDataObject; + context?: IDataObject; + timestamp?: string; + properties?: IDataObject; + integrations?: IDataObject; +} diff --git a/packages/nodes-base/nodes/Segment/segment.png b/packages/nodes-base/nodes/Segment/segment.png new file mode 100644 index 0000000000000000000000000000000000000000..5dbfcaf09dc9a98928bee9f20849f28b7d9113df GIT binary patch literal 1144 zcmV-;1c&>HP)cb{$Bwr$(CZQJj)XaAX5Z%#UG+9s3fnfVRcCOKblbaMl| zQEF}-8w-W;J*Hw0uHywhLBNi;cz}~wfj+2#R0s<&bD@GUQ31no5Kr(K!bNX!1&h%J zG0YY;724te-a>eg9p9QIXi{{;B?vG1goZ{3jffUF3E^eukkH7m=`a`XA$(vM0wr~^ zAr~qk8$vx9G!T_=%8gs{4)$XKMxX~eqALbsIyU1HUPJuh4T?$X69<#<1RwDbBM{va zVMFmH9zr;Sk2r-!CymrbB(h4__&5w9aS9m%tWy4X4(yV!Pf^KAVH0R{<_-)%u$2P0 z(U`0sM&~EqOHzHWfX*o*p*71k$ia%nJg9fk1Kpm^5tsS0w|WbrJ+L`GGK*aXJG>!Y&9E zSIroBtOx$2dsj9gvSQ%IlI1D~x>)L#X4??VhEVYwC6piCgD{ED=%OQVdfbLkaS*YT zALU0=G&6y^I@i!T!yyUWAAwdHY$U^ZN!S-CY^AV4=%aCU*CB$H0vE!4N!Sl)V5P8e zumwV5JEB`9Y&eX7UE{sYT2=`gg0^@EAu$i(-RawYLLbyaG!ujk#SeG|A#oL1T-|Xg zT!8q)M=U^WgWvV+g4YldZ_&cl9Y>eUIvn`Lz&CiO;3I^D9m8RBb?4df3_`^Ug!nLM z7^>nBgcC~;)18B4!XpS33lZ$2hAA(m;U$C<8<0fLv$9OmkvG+7OtK;wf^-Cpj^MXK0000< KMNUMnLSTYtR{A{v literal 0 HcmV?d00001 diff --git a/packages/nodes-base/package.json b/packages/nodes-base/package.json index 9cd7f397c5..392bf61714 100644 --- a/packages/nodes-base/package.json +++ b/packages/nodes-base/package.json @@ -74,6 +74,7 @@ "dist/credentials/Smtp.credentials.js", "dist/credentials/StripeApi.credentials.js", "dist/credentials/SalesmateApi.credentials.js", + "dist/credentials/SegmentApi.credentials.js", "dist/credentials/TelegramApi.credentials.js", "dist/credentials/TodoistApi.credentials.js", "dist/credentials/TrelloApi.credentials.js", @@ -172,6 +173,7 @@ "dist/nodes/Stripe/StripeTrigger.node.js", "dist/nodes/Switch.node.js", "dist/nodes/Salesmate/Salesmate.node.js", + "dist/nodes/Segment/Segment.node.js", "dist/nodes/Telegram/Telegram.node.js", "dist/nodes/Telegram/TelegramTrigger.node.js", "dist/nodes/Todoist/Todoist.node.js", @@ -206,6 +208,7 @@ "@types/nodemailer": "^4.6.5", "@types/redis": "^2.8.11", "@types/request-promise-native": "~1.0.15", + "@types/uuid": "^3.4.6", "@types/xml2js": "^0.4.3", "gulp": "^4.0.0", "jest": "^24.9.0", @@ -237,6 +240,7 @@ "redis": "^2.8.0", "rhea": "^1.0.11", "rss-parser": "^3.7.0", + "uuid": "^3.4.0", "vm2": "^3.6.10", "xlsx": "^0.14.3", "xml2js": "^0.4.22" From 62134f639b0b9d0b3614439d7dd1ba7bca097938 Mon Sep 17 00:00:00 2001 From: Jan Oberhauser Date: Thu, 23 Jan 2020 15:57:34 -0800 Subject: [PATCH 2/4] :bug: Fix issue with deleting fixedCollection with multipleValues=false --- packages/editor-ui/src/components/NodeSettings.vue | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/packages/editor-ui/src/components/NodeSettings.vue b/packages/editor-ui/src/components/NodeSettings.vue index 4189d78c35..ca3cfde88d 100644 --- a/packages/editor-ui/src/components/NodeSettings.vue +++ b/packages/editor-ui/src/components/NodeSettings.vue @@ -57,7 +57,7 @@ import ParameterInputFull from '@/components/ParameterInputFull.vue'; import ParameterInputList from '@/components/ParameterInputList.vue'; import NodeCredentials from '@/components/NodeCredentials.vue'; import NodeWebhooks from '@/components/NodeWebhooks.vue'; -import { get, set } from 'lodash'; +import { get, set, unset } from 'lodash'; import { genericHelpers } from '@/components/mixins/genericHelpers'; import { nodeHelpers } from '@/components/mixins/nodeHelpers'; @@ -369,8 +369,11 @@ export default mixins( Vue.set(nodeParameters as object, path, data); } } else { - // For everything else - set(nodeParameters as object, parameterPath, newValue); + if (newValue === undefined) { + unset(nodeParameters as object, parameterPath); + } else { + set(nodeParameters as object, parameterPath, newValue); + } } // Get the parameters with the now new defaults according to the From de098f7da4bc5e8fd73b8e0210b9c8f15a39a97e Mon Sep 17 00:00:00 2001 From: Jan Oberhauser Date: Thu, 23 Jan 2020 16:05:27 -0800 Subject: [PATCH 3/4] :zap: Fix issue that parameter inputs on lower levels are very small --- packages/editor-ui/src/components/ParameterInputFull.vue | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/packages/editor-ui/src/components/ParameterInputFull.vue b/packages/editor-ui/src/components/ParameterInputFull.vue index c7ce18c98a..4d488faf92 100644 --- a/packages/editor-ui/src/components/ParameterInputFull.vue +++ b/packages/editor-ui/src/components/ParameterInputFull.vue @@ -30,6 +30,9 @@ export default Vue }, computed: { isMultiLineParameter () { + if (this.level > 4) { + return true; + } const rows = this.getArgument('rows'); if (rows !== undefined && rows > 1) { return true; @@ -37,6 +40,9 @@ export default Vue return false; }, + level (): number { + return this.path.split('.').length; + }, }, props: [ 'displayOptions', From 828962deebd96cf735e0efd1f5f153c285725445 Mon Sep 17 00:00:00 2001 From: Jan Oberhauser Date: Thu, 23 Jan 2020 16:40:39 -0800 Subject: [PATCH 4/4] :zap: Some minor fixes on Segment-Node --- .../nodes/Segment/IdentifyDescription.ts | 2 +- .../nodes-base/nodes/Segment/Segment.node.ts | 10 ---------- .../nodes-base/nodes/Segment/TrackDescription.ts | 16 ++++++++-------- 3 files changed, 9 insertions(+), 19 deletions(-) diff --git a/packages/nodes-base/nodes/Segment/IdentifyDescription.ts b/packages/nodes-base/nodes/Segment/IdentifyDescription.ts index 750a4d03d4..f80fb2bc39 100644 --- a/packages/nodes-base/nodes/Segment/IdentifyDescription.ts +++ b/packages/nodes-base/nodes/Segment/IdentifyDescription.ts @@ -72,7 +72,7 @@ export const identifyFields = [ values: [ { displayName: 'Email', - name: 'emaiL', + name: 'email', type: 'string', default: '', description: 'Email address of a user', diff --git a/packages/nodes-base/nodes/Segment/Segment.node.ts b/packages/nodes-base/nodes/Segment/Segment.node.ts index f34f1c266b..bd281366b0 100644 --- a/packages/nodes-base/nodes/Segment/Segment.node.ts +++ b/packages/nodes-base/nodes/Segment/Segment.node.ts @@ -323,11 +323,6 @@ export class Segment implements INodeType { } else { body.anonymousId = uuid(); } - if (userId) { - body.userId = userId as string; - } else { - body.anonymousId = uuid(); - } if (traits) { if (traits.email) { body.traits!.email = traits.email as string; @@ -555,11 +550,6 @@ export class Segment implements INodeType { } else { body.anonymousId = uuid(); } - if (userId) { - body.userId = userId as string; - } else { - body.anonymousId = uuid(); - } if (traits) { if (traits.email) { body.traits!.email = traits.email as string; diff --git a/packages/nodes-base/nodes/Segment/TrackDescription.ts b/packages/nodes-base/nodes/Segment/TrackDescription.ts index cd03f9064b..8077f412df 100644 --- a/packages/nodes-base/nodes/Segment/TrackDescription.ts +++ b/packages/nodes-base/nodes/Segment/TrackDescription.ts @@ -95,7 +95,7 @@ export const trackFields = [ values: [ { displayName: 'Email', - name: 'emaiL', + name: 'email', type: 'string', default: '', description: 'Email address of a user', @@ -164,11 +164,11 @@ export const trackFields = [ description: 'User’s birthday', }, { - displayName: 'eventd At', - name: 'eventdAt', + displayName: 'Created At', + name: 'createdAt', type: 'dateTime', default: '', - description: 'Date the user’s account was first eventd', + description: 'Date the user’s account was first created at', }, { displayName: 'Description', @@ -642,7 +642,7 @@ export const trackFields = [ values: [ { displayName: 'Email', - name: 'emaiL', + name: 'email', type: 'string', default: '', description: 'Email address of a user', @@ -711,11 +711,11 @@ export const trackFields = [ description: 'User’s birthday', }, { - displayName: 'eventd At', - name: 'eventdAt', + displayName: 'Created At', + name: 'createdAt', type: 'dateTime', default: '', - description: 'Date the user’s account was first eventd', + description: 'Date the user’s account was first created at', }, { displayName: 'Description',