From 1479ce47e6299fa66d9efc6281e8273119f69fc5 Mon Sep 17 00:00:00 2001 From: Ricardo Espinoza Date: Mon, 7 Sep 2020 03:04:27 -0400 Subject: [PATCH] :zap: Add group resource to Segment-Node (#934) --- .../nodes/Segment/GroupDescription.ts | 528 ++++++++++++++++++ .../nodes/Segment/IdentifyDescription.ts | 4 +- .../nodes-base/nodes/Segment/Segment.node.ts | 244 +++++++- .../nodes/Segment/TrackDescription.ts | 4 +- .../nodes/Segment/TrackInterface.ts | 8 +- 5 files changed, 782 insertions(+), 6 deletions(-) create mode 100644 packages/nodes-base/nodes/Segment/GroupDescription.ts diff --git a/packages/nodes-base/nodes/Segment/GroupDescription.ts b/packages/nodes-base/nodes/Segment/GroupDescription.ts new file mode 100644 index 0000000000..89907526bb --- /dev/null +++ b/packages/nodes-base/nodes/Segment/GroupDescription.ts @@ -0,0 +1,528 @@ +import { + INodeProperties, +} from 'n8n-workflow'; + +export const groupOperations = [ + { + displayName: 'Operation', + name: 'operation', + type: 'options', + displayOptions: { + show: { + resource: [ + 'group', + ], + }, + }, + options: [ + { + name: 'Add', + value: 'add', + description: 'Add a user to a group', + }, + ], + default: 'add', + description: 'The operation to perform.', + }, +] as INodeProperties[]; + +export const groupFields = [ + +/* -------------------------------------------------------------------------- */ +/* group:add */ +/* -------------------------------------------------------------------------- */ + { + displayName: 'User ID', + name: 'userId', + type: 'string', + default: '', + displayOptions: { + show: { + resource: [ + 'group', + ], + operation: [ + 'add', + ], + }, + }, + required: false, + }, + { + displayName: 'Group ID', + name: 'groupId', + type: 'string', + default: '', + displayOptions: { + show: { + resource: [ + 'group', + ], + operation: [ + 'add', + ], + }, + }, + description: 'A Group ID is the unique identifier which you recognize a group by in your own database', + required: true, + }, + { + displayName: 'Traits', + name: 'traits', + placeholder: 'Add Trait', + type: 'fixedCollection', + typeOptions: { + multipleValues: false, + }, + displayOptions: { + show: { + resource: [ + 'group', + ], + operation: [ + 'add', + ], + }, + }, + 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: [ + 'group', + ], + operation: [ + 'add', + ], + }, + }, + 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: [ + 'group', + ], + operation: [ + 'add', + ], + }, + }, + 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/IdentifyDescription.ts b/packages/nodes-base/nodes/Segment/IdentifyDescription.ts index f80fb2bc39..c80ffeeb69 100644 --- a/packages/nodes-base/nodes/Segment/IdentifyDescription.ts +++ b/packages/nodes-base/nodes/Segment/IdentifyDescription.ts @@ -1,4 +1,6 @@ -import { INodeProperties } from 'n8n-workflow'; +import { + INodeProperties, +} from 'n8n-workflow'; export const identifyOperations = [ { diff --git a/packages/nodes-base/nodes/Segment/Segment.node.ts b/packages/nodes-base/nodes/Segment/Segment.node.ts index bd281366b0..e8c11613a1 100644 --- a/packages/nodes-base/nodes/Segment/Segment.node.ts +++ b/packages/nodes-base/nodes/Segment/Segment.node.ts @@ -1,27 +1,41 @@ import { IExecuteFunctions, } from 'n8n-core'; + import { IDataObject, INodeExecutionData, INodeType, INodeTypeDescription, } from 'n8n-workflow'; + import { segmentApiRequest, } from './GenericFunctions'; + +import { + groupOperations, + groupFields, +} from './GroupDescription'; + import { identifyFields, identifyOperations, } from './IdentifyDescription'; + import { IIdentify, } from './IdentifyInterface'; + import { trackOperations, trackFields, } from './TrackDescription'; -import { ITrack } from './TrackInterface'; + +import { + ITrack, IGroup, +} from './TrackInterface'; + import * as uuid from 'uuid/v4'; export class Segment implements INodeType { @@ -43,7 +57,7 @@ export class Segment implements INodeType { { name: 'segmentApi', required: true, - } + }, ], properties: [ { @@ -51,10 +65,15 @@ export class Segment implements INodeType { name: 'resource', type: 'options', options: [ + { + name: 'Group', + value: 'group', + description: 'Group lets you associate an identified user with a group', + }, { name: 'Identify', value: 'identify', - description: 'Identify lets you tie a user to their actions.' + description: 'Identify lets you tie a user to their actions' }, { name: 'Track', @@ -65,6 +84,8 @@ export class Segment implements INodeType { default: 'identify', description: 'Resource to consume.', }, + ...groupOperations, + ...groupFields, ...identifyOperations, ...trackOperations, ...identifyFields, @@ -80,7 +101,224 @@ export class Segment implements INodeType { 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 === 'group') { + //https://segment.com/docs/connections/sources/catalog/libraries/server/http-api/#group + if (operation === 'add') { + const userId = this.getNodeParameter('userId', i) as string; + const groupId = this.getNodeParameter('groupId', 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: IGroup = { + groupId, + 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', '/group', body); + } + } if (resource === 'identify') { //https://segment.com/docs/connections/sources/catalog/libraries/server/http-api/#identify if (operation === 'create') { diff --git a/packages/nodes-base/nodes/Segment/TrackDescription.ts b/packages/nodes-base/nodes/Segment/TrackDescription.ts index 9eb029b517..ec67c25fcb 100644 --- a/packages/nodes-base/nodes/Segment/TrackDescription.ts +++ b/packages/nodes-base/nodes/Segment/TrackDescription.ts @@ -1,4 +1,6 @@ -import { INodeProperties } from 'n8n-workflow'; +import { + INodeProperties, +} from 'n8n-workflow'; export const trackOperations = [ { diff --git a/packages/nodes-base/nodes/Segment/TrackInterface.ts b/packages/nodes-base/nodes/Segment/TrackInterface.ts index 85ce0924e5..6fe9270012 100644 --- a/packages/nodes-base/nodes/Segment/TrackInterface.ts +++ b/packages/nodes-base/nodes/Segment/TrackInterface.ts @@ -1,4 +1,6 @@ -import { IDataObject } from "n8n-workflow"; +import { + IDataObject, +} from 'n8n-workflow'; export interface ITrack { event?: string; @@ -11,3 +13,7 @@ export interface ITrack { properties?: IDataObject; integrations?: IDataObject; } + +export interface IGroup extends ITrack{ + groupId: string; +}