Add support for multiple subscriptions in Hubspot Trigger (#1358)

*  Add support for multiple subscriptions in Hubspot Trigger

*  Load object properties for the user

*  Improvements

*  Some improvements to the Hubspot-Node

Co-authored-by: Jan Oberhauser <jan.oberhauser@gmail.com>
This commit is contained in:
Ricardo Espinoza 2021-02-01 02:31:40 -05:00 committed by GitHub
parent 5549550928
commit dc98de1ab2
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
15 changed files with 2303 additions and 373 deletions

View file

@ -2,6 +2,18 @@
This list shows all the versions which include breaking changes and how to upgrade. This list shows all the versions which include breaking changes and how to upgrade.
## 0.105.0
### What changed?
In the Hubspot Trigger, now multiple events can be provided and the field `App ID` was so moved to the credentials.
### When is action necessary?
If you are using the Hubspot Trigger node.
### How to upgrade:
Open the Hubspot Trigger and set the events again. Also open the credentials `Hubspot Developer API` and set your APP ID.
## 0.104.0 ## 0.104.0
### What changed? ### What changed?

View file

@ -20,5 +20,13 @@ export class HubspotDeveloperApi implements ICredentialType {
type: 'string' as NodePropertyTypes, type: 'string' as NodePropertyTypes,
default: '', default: '',
}, },
{
displayName: 'App ID',
name: 'appId',
type: 'string' as NodePropertyTypes,
required: true,
default: '',
description: 'The App ID',
},
]; ];
} }

View file

@ -1,6 +1,6 @@
import { import {
INodeProperties, INodeProperties,
} from 'n8n-workflow'; } from 'n8n-workflow';
export const companyOperations = [ export const companyOperations = [
{ {
@ -63,9 +63,9 @@ export const companyOperations = [
export const companyFields = [ export const companyFields = [
/* -------------------------------------------------------------------------- */ /* -------------------------------------------------------------------------- */
/* company:create */ /* company:create */
/* -------------------------------------------------------------------------- */ /* -------------------------------------------------------------------------- */
{ {
displayName: 'Name', displayName: 'Name',
name: 'name', name: 'name',
@ -370,7 +370,7 @@ export const companyFields = [
description: 'The main website of the company or organization. This property is used to identify unique companies. Powered by HubSpot Insights.', description: 'The main website of the company or organization. This property is used to identify unique companies. Powered by HubSpot Insights.',
}, },
{ {
displayName: 'Year Founded', displayName: 'Year Founded',
name: 'yearFounded', name: 'yearFounded',
type: 'string', type: 'string',
default: '', default: '',
@ -378,9 +378,10 @@ export const companyFields = [
}, },
], ],
}, },
/* -------------------------------------------------------------------------- */
/* company:update */ /* -------------------------------------------------------------------------- */
/* -------------------------------------------------------------------------- */ /* company:update */
/* -------------------------------------------------------------------------- */
{ {
displayName: 'Company ID', displayName: 'Company ID',
name: 'companyId', name: 'companyId',
@ -692,7 +693,7 @@ export const companyFields = [
description: 'The main website of the company or organization. This property is used to identify unique companies. Powered by HubSpot Insights.', description: 'The main website of the company or organization. This property is used to identify unique companies. Powered by HubSpot Insights.',
}, },
{ {
displayName: 'Year Founded', displayName: 'Year Founded',
name: 'yearFounded', name: 'yearFounded',
type: 'string', type: 'string',
default: '', default: '',
@ -700,9 +701,10 @@ export const companyFields = [
}, },
], ],
}, },
/* -------------------------------------------------------------------------- */
/* company:get */ /* -------------------------------------------------------------------------- */
/* -------------------------------------------------------------------------- */ /* company:get */
/* -------------------------------------------------------------------------- */
{ {
displayName: 'Company ID', displayName: 'Company ID',
name: 'companyId', name: 'companyId',
@ -747,9 +749,10 @@ export const companyFields = [
}, },
], ],
}, },
/* -------------------------------------------------------------------------- */
/* company:getAll */ /* -------------------------------------------------------------------------- */
/* -------------------------------------------------------------------------- */ /* company:getAll */
/* -------------------------------------------------------------------------- */
{ {
displayName: 'Return All', displayName: 'Return All',
name: 'returnAll', name: 'returnAll',
@ -838,9 +841,10 @@ export const companyFields = [
}, },
], ],
}, },
/* -------------------------------------------------------------------------- */
/* company:delete */ /* -------------------------------------------------------------------------- */
/* -------------------------------------------------------------------------- */ /* company:delete */
/* -------------------------------------------------------------------------- */
{ {
displayName: 'Company ID', displayName: 'Company ID',
name: 'companyId', name: 'companyId',
@ -859,9 +863,10 @@ export const companyFields = [
default: '', default: '',
description: 'Unique identifier for a particular company', description: 'Unique identifier for a particular company',
}, },
/* -------------------------------------------------------------------------- */
/* company:getRecentlyCreated company:getRecentlyModifie */ /* -------------------------------------------------------------------------- */
/* -------------------------------------------------------------------------- */ /* company:getRecentlyCreated company:getRecentlyModifie */
/* -------------------------------------------------------------------------- */
{ {
displayName: 'Return All', displayName: 'Return All',
name: 'returnAll', name: 'returnAll',
@ -939,9 +944,10 @@ export const companyFields = [
}, },
], ],
}, },
/* -------------------------------------------------------------------------- */
/* company:searchByDomain */ /* -------------------------------------------------------------------------- */
/* -------------------------------------------------------------------------- */ /* company:searchByDomain */
/* -------------------------------------------------------------------------- */
{ {
displayName: 'Domain', displayName: 'Domain',
name: 'domain', name: 'domain',

View file

@ -1,6 +1,6 @@
import { import {
INodeProperties, INodeProperties,
} from 'n8n-workflow'; } from 'n8n-workflow';
export const contactOperations = [ export const contactOperations = [
{ {
@ -53,9 +53,9 @@ export const contactOperations = [
export const contactFields = [ export const contactFields = [
/* -------------------------------------------------------------------------- */ /* -------------------------------------------------------------------------- */
/* contact:upsert */ /* contact:upsert */
/* -------------------------------------------------------------------------- */ /* -------------------------------------------------------------------------- */
{ {
displayName: 'Email', displayName: 'Email',
name: 'email', name: 'email',
@ -121,7 +121,7 @@ export const contactFields = [
name: 'associatedCompanyId', name: 'associatedCompanyId',
type: 'options', type: 'options',
typeOptions: { typeOptions: {
loadOptionsMethod:'getCompanies' , loadOptionsMethod: 'getCompanies',
}, },
default: '', default: '',
description: 'Companies associated with the ticket', description: 'Companies associated with the ticket',
@ -501,9 +501,10 @@ export const contactFields = [
}, },
], ],
}, },
/* -------------------------------------------------------------------------- */
/* contact:get */ /* -------------------------------------------------------------------------- */
/* -------------------------------------------------------------------------- */ /* contact:get */
/* -------------------------------------------------------------------------- */
{ {
displayName: 'Contact ID', displayName: 'Contact ID',
name: 'contactId', name: 'contactId',
@ -603,9 +604,10 @@ export const contactFields = [
}, },
], ],
}, },
/* -------------------------------------------------------------------------- */
/* contact:getAll */ /* -------------------------------------------------------------------------- */
/* -------------------------------------------------------------------------- */ /* contact:getAll */
/* -------------------------------------------------------------------------- */
{ {
displayName: 'Return All', displayName: 'Return All',
name: 'returnAll', name: 'returnAll',
@ -728,9 +730,10 @@ export const contactFields = [
}, },
], ],
}, },
/* -------------------------------------------------------------------------- */
/* contact:delete */ /* -------------------------------------------------------------------------- */
/* -------------------------------------------------------------------------- */ /* contact:delete */
/* -------------------------------------------------------------------------- */
{ {
displayName: 'Contact ID', displayName: 'Contact ID',
name: 'contactId', name: 'contactId',
@ -749,9 +752,10 @@ export const contactFields = [
default: '', default: '',
description: 'Unique identifier for a particular contact', description: 'Unique identifier for a particular contact',
}, },
/* -------------------------------------------------------------------------- */
/* contact:getRecentlyCreatedUpdated */ /* -------------------------------------------------------------------------- */
/* -------------------------------------------------------------------------- */ /* contact:getRecentlyCreatedUpdated */
/* -------------------------------------------------------------------------- */
{ {
displayName: 'Return All', displayName: 'Return All',
name: 'returnAll', name: 'returnAll',
@ -875,9 +879,9 @@ export const contactFields = [
], ],
}, },
//*-------------------------------------------------------------------------- */ //*-------------------------------------------------------------------------- */
/* contact:search */ /* contact:search */
/* -------------------------------------------------------------------------- */ /* -------------------------------------------------------------------------- */
{ {
displayName: 'Return All', displayName: 'Return All',
name: 'returnAll', name: 'returnAll',

View file

@ -120,6 +120,7 @@ export const contactListFields = [
}, },
default: '', default: '',
}, },
/* -------------------------------------------------------------------------- */ /* -------------------------------------------------------------------------- */
/* contactList:remove */ /* contactList:remove */
/* -------------------------------------------------------------------------- */ /* -------------------------------------------------------------------------- */

View file

@ -1,6 +1,6 @@
import { import {
IDataObject, IDataObject,
} from 'n8n-workflow'; } from 'n8n-workflow';
export interface IAssociation { export interface IAssociation {
associatedCompanyIds?: number[]; associatedCompanyIds?: number[];

View file

@ -1,6 +1,6 @@
import { import {
INodeProperties, INodeProperties,
} from 'n8n-workflow'; } from 'n8n-workflow';
export const formOperations = [ export const formOperations = [
{ {
@ -33,9 +33,9 @@ export const formOperations = [
export const formFields = [ export const formFields = [
/* -------------------------------------------------------------------------- */ /* -------------------------------------------------------------------------- */
/* form:submit */ /* form:submit */
/* -------------------------------------------------------------------------- */ /* -------------------------------------------------------------------------- */
{ {
displayName: 'Form', displayName: 'Form',
name: 'formId', name: 'formId',
@ -301,9 +301,10 @@ export const formFields = [
}, },
], ],
}, },
/* -------------------------------------------------------------------------- */
/* form:getFields */ /* -------------------------------------------------------------------------- */
/* -------------------------------------------------------------------------- */ /* form:getFields */
/* -------------------------------------------------------------------------- */
{ {
displayName: 'Form', displayName: 'Form',
name: 'formId', name: 'formId',

View file

@ -1,6 +1,6 @@
import { import {
IDataObject, IDataObject,
} from 'n8n-workflow'; } from 'n8n-workflow';
export interface IContext { export interface IContext {
goToWebinarWebinarKey?: string; goToWebinarWebinarKey?: string;

File diff suppressed because it is too large Load diff

View file

@ -63,7 +63,7 @@ export class Hubspot implements INodeType {
description: INodeTypeDescription = { description: INodeTypeDescription = {
displayName: 'HubSpot', displayName: 'HubSpot',
name: 'hubspot', name: 'hubspot',
icon: 'file:hubspot.png', icon: 'file:hubspot.svg',
group: ['output'], group: ['output'],
version: 1, version: 1,
subtitle: '={{$parameter["operation"] + ": " + $parameter["resource"]}}', subtitle: '={{$parameter["operation"] + ": " + $parameter["resource"]}}',

View file

@ -5,27 +5,36 @@ import {
import { import {
IDataObject, IDataObject,
ILoadOptionsFunctions,
INodePropertyOptions,
INodeType, INodeType,
INodeTypeDescription, INodeTypeDescription,
IWebhookResponseData, IWebhookResponseData,
} from 'n8n-workflow'; } from 'n8n-workflow';
import { import {
companyFields,
contactFields,
dealFields,
hubspotApiRequest, hubspotApiRequest,
propertyEvents,
} from './GenericFunctions'; } from './GenericFunctions';
import { import {
createHash, createHash,
} from 'crypto'; } from 'crypto';
import {
capitalCase,
} from 'change-case';
export class HubspotTrigger implements INodeType { export class HubspotTrigger implements INodeType {
description: INodeTypeDescription = { description: INodeTypeDescription = {
displayName: 'HubSpot Trigger', displayName: 'HubSpot Trigger',
name: 'hubspotTrigger', name: 'hubspotTrigger',
icon: 'file:hubspot.png', icon: 'file:hubspot.svg',
group: ['trigger'], group: ['trigger'],
version: 1, version: 1,
subtitle: '={{($parameter["appId"]) ? $parameter["event"] : ""}}',
description: 'Starts the workflow when HubSpot events occur.', description: 'Starts the workflow when HubSpot events occur.',
defaults: { defaults: {
name: 'Hubspot Trigger', name: 'Hubspot Trigger',
@ -55,87 +64,132 @@ export class HubspotTrigger implements INodeType {
], ],
properties: [ properties: [
{ {
displayName: 'App ID', displayName: 'Events',
name: 'appId', name: 'eventsUi',
type: 'string', type: 'fixedCollection',
default: '', typeOptions: {
required: true, multipleValues: true,
description: 'App ID', },
}, placeholder: 'Add Event',
{ default: {},
displayName: 'Event',
name: 'event',
type: 'options',
options: [ options: [
{ {
name: 'contact.creation', displayName: 'Event',
value: 'contact.creation', name: 'eventValues',
description: `To get notified if any contact is created in a customer's account.`, values: [
}, {
{ displayName: 'Name',
name: 'contact.deletion', name: 'name',
value: 'contact.deletion', type: 'options',
description: `To get notified if any contact is deleted in a customer's account.`, options: [
}, {
{ name: 'Contact Created',
name: 'contact.privacyDeletion', value: 'contact.creation',
value: 'contact.privacyDeletion', description: `To get notified if any contact is created in a customer's account.`,
description: `To get notified if a contact is deleted for privacy compliance reasons. `, },
}, {
{ name: 'Contact Deleted',
name: 'contact.propertyChange', value: 'contact.deletion',
value: 'contact.propertyChange', description: `To get notified if any contact is deleted in a customer's account.`,
description: `to get notified if a specified property is changed for any contact in a customer's account. `, },
}, {
{ name: 'Contact Privacy Deleted',
name: 'company.creation', value: 'contact.privacyDeletion',
value: 'company.creation', description: `To get notified if a contact is deleted for privacy compliance reasons. `,
description: `To get notified if any company is created in a customer's account.`, },
}, {
{ name: 'Contact Property Changed',
name: 'company.deletion', value: 'contact.propertyChange',
value: 'company.deletion', description: `to get notified if a specified property is changed for any contact in a customer's account. `,
description: `To get notified if any company is deleted in a customer's account.`, },
}, {
{ name: 'Company Created',
name: 'company.propertyChange', value: 'company.creation',
value: 'company.propertyChange', description: `To get notified if any company is created in a customer's account.`,
description: `To get notified if a specified property is changed for any company in a customer's account.`, },
}, {
{ name: 'Company Deleted',
name: 'deal.creation', value: 'company.deletion',
value: 'deal.creation', description: `To get notified if any company is deleted in a customer's account.`,
description: `To get notified if any deal is created in a customer's account.`, },
}, {
{ name: 'Company Property Changed',
name: 'deal.deletion', value: 'company.propertyChange',
value: 'deal.deletion', description: `To get notified if a specified property is changed for any company in a customer's account.`,
description: `To get notified if any deal is deleted in a customer's account.`, },
}, {
{ name: 'Deal Created',
name: 'deal.propertyChange', value: 'deal.creation',
value: 'deal.propertyChange', description: `To get notified if any deal is created in a customer's account.`,
description: `To get notified if a specified property is changed for any deal in a customer's account.`, },
}, {
], name: 'Deal Deleted',
default: 'contact.creation', value: 'deal.deletion',
required: true, description: `To get notified if any deal is deleted in a customer's account.`,
}, },
{ {
displayName: 'Property', name: 'Deal Property Changed',
name: 'property', value: 'deal.propertyChange',
type: 'string', description: `To get notified if a specified property is changed for any deal in a customer's account.`,
displayOptions: { },
show: { ],
event: [ default: 'contact.creation',
'contact.propertyChange', required: true,
'company.propertyChange', },
'deal.propertyChange', {
displayName: 'Property',
name: 'property',
type: 'options',
typeOptions: {
loadOptionsMethod: 'getContactProperties',
},
displayOptions: {
show: {
name: [
'contact.propertyChange',
],
},
},
default: '',
required: true,
},
{
displayName: 'Property',
name: 'property',
type: 'options',
typeOptions: {
loadOptionsMethod: 'getCompanyProperties',
},
displayOptions: {
show: {
name: [
'company.propertyChange',
],
},
},
default: '',
required: true,
},
{
displayName: 'Property',
name: 'property',
type: 'options',
typeOptions: {
loadOptionsMethod: 'getDealProperties',
},
displayOptions: {
show: {
name: [
'deal.propertyChange',
],
},
},
default: '',
required: true,
},
], ],
}, },
}, ],
default: '',
required: true,
}, },
{ {
displayName: 'Additional Fields', displayName: 'Additional Fields',
@ -156,7 +210,62 @@ export class HubspotTrigger implements INodeType {
], ],
}, },
], ],
};
methods = {
loadOptions: {
// Get all the available contacts to display them to user so that he can
// select them easily
async getContactProperties(this: ILoadOptionsFunctions): Promise<INodePropertyOptions[]> {
const returnData: INodePropertyOptions[] = [];
for (const field of contactFields) {
returnData.push({
name: capitalCase(field.label),
value: field.id,
});
}
returnData.sort((a, b) => {
if (a.name < b.name) { return -1; }
if (a.name > b.name) { return 1; }
return 0;
});
return returnData;
},
// Get all the available companies to display them to user so that he can
// select them easily
async getCompanyProperties(this: ILoadOptionsFunctions): Promise<INodePropertyOptions[]> {
const returnData: INodePropertyOptions[] = [];
for (const field of companyFields) {
returnData.push({
name: capitalCase(field.label),
value: field.id,
});
}
returnData.sort((a, b) => {
if (a.name < b.name) { return -1; }
if (a.name > b.name) { return 1; }
return 0;
});
return returnData;
},
// Get all the available deals to display them to user so that he can
// select them easily
async getDealProperties(this: ILoadOptionsFunctions): Promise<INodePropertyOptions[]> {
const returnData: INodePropertyOptions[] = [];
for (const field of dealFields) {
returnData.push({
name: capitalCase(field.label),
value: field.id,
});
}
returnData.sort((a, b) => {
if (a.name < b.name) { return -1; }
if (a.name > b.name) { return 1; }
return 0;
});
return returnData;
},
},
}; };
// @ts-ignore (because of request) // @ts-ignore (because of request)
@ -165,80 +274,78 @@ export class HubspotTrigger implements INodeType {
async checkExists(this: IHookFunctions): Promise<boolean> { async checkExists(this: IHookFunctions): Promise<boolean> {
// Check all the webhooks which exist already if it is identical to the // Check all the webhooks which exist already if it is identical to the
// one that is supposed to get created. // one that is supposed to get created.
const app = parseInt(this.getNodeParameter('appId') as string, 10); const currentWebhookUrl = this.getNodeWebhookUrl('default') as string;
const event = this.getNodeParameter('event') as string; const { appId } = this.getCredentials('hubspotDeveloperApi') as IDataObject;
const webhookUrlUi = this.getNodeWebhookUrl('default') as string;
let endpoint = `/webhooks/v1/${app}/settings`; try {
const { webhookUrl , appId } = await hubspotApiRequest.call(this, 'GET', endpoint, {}); const { targetUrl } = await hubspotApiRequest.call(this, 'GET', `/webhooks/v3/${appId}/settings`, {});
endpoint = `/webhooks/v1/${app}/subscriptions`; if (targetUrl !== currentWebhookUrl) {
const subscriptions = await hubspotApiRequest.call(this, 'GET', endpoint, {}); throw new Error(`The APP ID ${appId} already has a target url ${targetUrl}. Delete it or use another APP ID before executing the trigger. Due to Hubspot API limitations, you can have just one trigger per APP.`);
for (const subscription of subscriptions) { }
if (webhookUrl === webhookUrlUi } catch (error) {
&& appId === app if (error.statusCode === 404) {
&& subscription.subscriptionDetails.subscriptionType === event return false;
&& subscription.enabled === true) {
return true;
} }
} }
// if the app is using the current webhook url. Delete everything and create it again with the current events
const { results: subscriptions } = await hubspotApiRequest.call(this, 'GET', `/webhooks/v3/${appId}/subscriptions`, {});
// delete all subscriptions
for (const subscription of subscriptions) {
await hubspotApiRequest.call(this, 'DELETE', `/webhooks/v3/${appId}/subscriptions/${subscription.id}`, {});
}
await hubspotApiRequest.call(this, 'DELETE', `/webhooks/v3/${appId}/settings`, {});
return false; return false;
}, },
async create(this: IHookFunctions): Promise<boolean> { async create(this: IHookFunctions): Promise<boolean> {
const webhookUrl = this.getNodeWebhookUrl('default'); const webhookUrl = this.getNodeWebhookUrl('default');
const app = this.getNodeParameter('appId') as string; const { appId } = this.getCredentials('hubspotDeveloperApi') as IDataObject;
const event = this.getNodeParameter('event') as string; const events = (this.getNodeParameter('eventsUi') as IDataObject || {}).eventValues as IDataObject[] || [];
const additionalFields = this.getNodeParameter('additionalFields') as IDataObject; const additionalFields = this.getNodeParameter('additionalFields') as IDataObject;
const propertyEvents = [ let endpoint = `/webhooks/v3/${appId}/settings`;
'contact.propertyChange',
'company.propertyChange',
'deal.propertyChange',
];
let endpoint = `/webhooks/v1/${app}/settings`;
let body: IDataObject = { let body: IDataObject = {
webhookUrl, targetUrl: webhookUrl,
maxConcurrentRequests: additionalFields.maxConcurrentRequests || 5, maxConcurrentRequests: additionalFields.maxConcurrentRequests || 5,
}; };
await hubspotApiRequest.call(this, 'PUT', endpoint, body); await hubspotApiRequest.call(this, 'PUT', endpoint, body);
endpoint = `/webhooks/v1/${app}/subscriptions`; endpoint = `/webhooks/v3/${appId}/subscriptions`;
body = {
subscriptionDetails: { if (Array.isArray(events) && events.length === 0) {
subscriptionType: event, throw new Error(`You must define at least one event`);
},
enabled: true,
};
if (propertyEvents.includes(event)) {
const property = this.getNodeParameter('property') as string;
//@ts-ignore
body.subscriptionDetails.propertyName = property;
} }
const responseData = await hubspotApiRequest.call(this, 'POST', endpoint, body); for (const event of events) {
body = {
if (responseData.id === undefined) { eventType: event.name,
// Required data is missing so was not successful active: true,
return false; };
if (propertyEvents.includes(event.name as string)) {
const property = event.property;
body.propertyName = property;
}
await hubspotApiRequest.call(this, 'POST', endpoint, body);
} }
const webhookData = this.getWorkflowStaticData('node');
webhookData.webhookId = responseData.id as string;
return true; return true;
}, },
async delete(this: IHookFunctions): Promise<boolean> { async delete(this: IHookFunctions): Promise<boolean> {
const webhookData = this.getWorkflowStaticData('node'); const { appId } = this.getCredentials('hubspotDeveloperApi') as IDataObject;
const app = this.getNodeParameter('appId') as string;
if (webhookData.webhookId !== undefined) {
const endpoint = `/webhooks/v1/${app}/subscriptions/${webhookData.webhookId}`;
const body = {}; const { results: subscriptions } = await hubspotApiRequest.call(this, 'GET', `/webhooks/v3/${appId}/subscriptions`, {});
try { for (const subscription of subscriptions) {
await hubspotApiRequest.call(this, 'DELETE', endpoint, body); await hubspotApiRequest.call(this, 'DELETE', `/webhooks/v3/${appId}/subscriptions/${subscription.id}`, {});
} catch (e) { }
return false;
} try {
// Remove from the static workflow data so that it is clear await hubspotApiRequest.call(this, 'DELETE', `/webhooks/v3/${appId}/settings`, {});
// that no webhooks are registred anymore } catch (e) {
delete webhookData.webhookId; return false;
} }
return true; return true;
}, },
@ -265,7 +372,7 @@ export class HubspotTrigger implements INodeType {
if (credentials.clientSecret !== '') { if (credentials.clientSecret !== '') {
const hash = `${credentials!.clientSecret}${JSON.stringify(bodyData)}`; const hash = `${credentials!.clientSecret}${JSON.stringify(bodyData)}`;
const signature = createHash('sha256').update(hash).digest('hex'); const signature = createHash('sha256').update(hash).digest('hex');
//@ts-ignore //@ts-ignore
if (signature !== headerData['x-hubspot-signature']) { if (signature !== headerData['x-hubspot-signature']) {
return {}; return {};

View file

@ -1,6 +1,6 @@
import { import {
INodeProperties, INodeProperties,
} from 'n8n-workflow'; } from 'n8n-workflow';
export const ticketOperations = [ export const ticketOperations = [
{ {
@ -48,9 +48,9 @@ export const ticketOperations = [
export const ticketFields = [ export const ticketFields = [
/* -------------------------------------------------------------------------- */ /* -------------------------------------------------------------------------- */
/* ticket:create */ /* ticket:create */
/* -------------------------------------------------------------------------- */ /* -------------------------------------------------------------------------- */
{ {
displayName: 'Pipeline ID', displayName: 'Pipeline ID',
name: 'pipelineId', name: 'pipelineId',
@ -70,7 +70,7 @@ export const ticketFields = [
}, },
}, },
default: '', default: '',
description: 'The ID of the pipeline the ticket is in. ', description: 'The ID of the pipeline the ticket is in.',
}, },
{ {
displayName: 'Stage ID', displayName: 'Stage ID',
@ -94,7 +94,7 @@ export const ticketFields = [
}, },
}, },
default: '', default: '',
description: 'The ID of the pipeline the ticket is in. ', description: 'The ID of the pipeline the ticket is in.',
}, },
{ {
displayName: 'Ticket Name', displayName: 'Ticket Name',
@ -112,7 +112,7 @@ export const ticketFields = [
}, },
}, },
default: '', default: '',
description: 'The ID of the pipeline the ticket is in. ', description: 'The ID of the pipeline the ticket is in.',
}, },
{ {
displayName: 'Additional Fields', displayName: 'Additional Fields',
@ -136,7 +136,7 @@ export const ticketFields = [
name: 'associatedCompanyIds', name: 'associatedCompanyIds',
type: 'multiOptions', type: 'multiOptions',
typeOptions: { typeOptions: {
loadOptionsMethod:'getCompanies' , loadOptionsMethod: 'getCompanies',
}, },
default: [], default: [],
description: 'Companies associated with the ticket', description: 'Companies associated with the ticket',
@ -146,7 +146,7 @@ export const ticketFields = [
name: 'associatedContactIds', name: 'associatedContactIds',
type: 'multiOptions', type: 'multiOptions',
typeOptions: { typeOptions: {
loadOptionsMethod:'getContacts' , loadOptionsMethod: 'getContacts',
}, },
default: [], default: [],
description: 'Contacts associated with the ticket', description: 'Contacts associated with the ticket',
@ -228,9 +228,9 @@ export const ticketFields = [
}, },
], ],
}, },
/* -------------------------------------------------------------------------- */ /* -------------------------------------------------------------------------- */
/* ticket:update */ /* ticket:update */
/* -------------------------------------------------------------------------- */ /* -------------------------------------------------------------------------- */
{ {
displayName: 'Ticket ID', displayName: 'Ticket ID',
name: 'ticketId', name: 'ticketId',
@ -247,7 +247,7 @@ export const ticketFields = [
}, },
}, },
default: '', default: '',
description: 'Unique identifier for a particular ticket', description: 'Unique identifier for a particular ticket.',
}, },
{ {
displayName: 'Update Fields', displayName: 'Update Fields',
@ -271,20 +271,20 @@ export const ticketFields = [
name: 'associatedCompanyIds', name: 'associatedCompanyIds',
type: 'multiOptions', type: 'multiOptions',
typeOptions: { typeOptions: {
loadOptionsMethod:'getCompanies' , loadOptionsMethod: 'getCompanies',
}, },
default: [], default: [],
description: 'Companies associated with the ticket', description: 'Companies associated with the ticket.',
}, },
{ {
displayName: 'Contact Ids', displayName: 'Contact Ids',
name: 'associatedContactIds', name: 'associatedContactIds',
type: 'multiOptions', type: 'multiOptions',
typeOptions: { typeOptions: {
loadOptionsMethod:'getContacts' , loadOptionsMethod: 'getContacts',
}, },
default: [], default: [],
description: 'Contact associated with the ticket', description: 'Contact associated with the ticket.',
}, },
{ {
displayName: 'Category', displayName: 'Category',
@ -294,21 +294,21 @@ export const ticketFields = [
loadOptionsMethod: 'getTicketCategories', loadOptionsMethod: 'getTicketCategories',
}, },
default: '', default: '',
description: 'Main reason customer reached out for help', description: 'Main reason customer reached out for help.',
}, },
{ {
displayName: 'Close Date', displayName: 'Close Date',
name: 'closeDate', name: 'closeDate',
type: 'dateTime', type: 'dateTime',
default: '', default: '',
description: 'The date the ticket was closed', description: 'The date the ticket was closed.',
}, },
{ {
displayName: 'Create Date', displayName: 'Create Date',
name: 'createDate', name: 'createDate',
type: 'dateTime', type: 'dateTime',
default: '', default: '',
description: 'the date the ticket was created', description: 'The date the ticket was created.',
}, },
{ {
displayName: 'Description', displayName: 'Description',
@ -328,7 +328,7 @@ export const ticketFields = [
loadOptionsMethod: 'getTicketPipelines', loadOptionsMethod: 'getTicketPipelines',
}, },
default: '', default: '',
description: 'The ID of the pipeline the ticket is in. ', description: 'The ID of the pipeline the ticket is in.',
}, },
{ {
displayName: 'Priority', displayName: 'Priority',
@ -358,7 +358,7 @@ export const ticketFields = [
loadOptionsMethod: 'getTicketSources', loadOptionsMethod: 'getTicketSources',
}, },
default: '', default: '',
description: 'Channel where ticket was originally submitted', description: 'Channel where ticket was originally submitted.',
}, },
{ {
displayName: 'Ticket Name', displayName: 'Ticket Name',
@ -380,176 +380,179 @@ export const ticketFields = [
}, },
], ],
}, },
/* -------------------------------------------------------------------------- */
/* ticket:get */ /* -------------------------------------------------------------------------- */
/* -------------------------------------------------------------------------- */ /* ticket:get */
{ /* -------------------------------------------------------------------------- */
displayName: 'Ticket ID', {
name: 'ticketId', displayName: 'Ticket ID',
type: 'string', name: 'ticketId',
required: true, type: 'string',
displayOptions: { required: true,
show: { displayOptions: {
resource: [ show: {
'ticket', resource: [
], 'ticket',
operation: [ ],
'get', operation: [
], 'get',
}, ],
},
default: '',
description: 'Unique identifier for a particular ticket',
},
{
displayName: 'Additional Fields',
name: 'additionalFields',
type: 'collection',
placeholder: 'Add Field',
default: {},
displayOptions: {
show: {
resource: [
'ticket',
],
operation: [
'get',
],
},
},
options: [
{
displayName: 'Include Deleted',
name: 'includeDeleted',
type: 'boolean',
default: false,
},
{
displayName: 'Properties',
name: 'properties',
type: 'multiOptions',
typeOptions: {
loadOptionsMethod: 'getTicketProperties',
}, },
default: [], },
description: `Used to include specific ticket properties in the results.<br/> default: '',
description: 'Unique identifier for a particular ticket',
},
{
displayName: 'Additional Fields',
name: 'additionalFields',
type: 'collection',
placeholder: 'Add Field',
default: {},
displayOptions: {
show: {
resource: [
'ticket',
],
operation: [
'get',
],
},
},
options: [
{
displayName: 'Include Deleted',
name: 'includeDeleted',
type: 'boolean',
default: false,
},
{
displayName: 'Properties',
name: 'properties',
type: 'multiOptions',
typeOptions: {
loadOptionsMethod: 'getTicketProperties',
},
default: [],
description: `Used to include specific ticket properties in the results.<br/>
By default, the results will only include ticket ID and will not include the values for any properties for your tickets.<br/> By default, the results will only include ticket ID and will not include the values for any properties for your tickets.<br/>
Including this parameter will include the data for the specified property in the results.<br/> Including this parameter will include the data for the specified property in the results.<br/>
You can include this parameter multiple times to request multiple properties separed by ,.`, You can include this parameter multiple times to request multiple properties separed by ,.`,
},
{
displayName: 'Properties With History',
name: 'propertiesWithHistory',
type: 'string',
default: '',
description: `Works similarly to properties=, but this parameter will include the history for the specified property,<br/>
instead of just including the current value. Use this parameter when you need the full history of changes to a property's value.`,
},
],
},
/* -------------------------------------------------------------------------- */
/* ticket:getAll */
/* -------------------------------------------------------------------------- */
{
displayName: 'Return All',
name: 'returnAll',
type: 'boolean',
displayOptions: {
show: {
resource: [
'ticket',
],
operation: [
'getAll',
],
},
},
default: false,
description: 'If all results should be returned or only up to a given limit.',
},
{
displayName: 'Limit',
name: 'limit',
type: 'number',
displayOptions: {
show: {
resource: [
'ticket',
],
operation: [
'getAll',
],
returnAll: [
false,
],
},
},
typeOptions: {
minValue: 1,
maxValue: 250,
},
default: 100,
description: 'How many results to return.',
},
{
displayName: 'Additional Fields',
name: 'additionalFields',
type: 'collection',
placeholder: 'Add Field',
default: {},
displayOptions: {
show: {
resource: [
'ticket',
],
operation: [
'getAll',
],
},
},
options: [
{
displayName: 'Properties',
name: 'properties',
type: 'multiOptions',
typeOptions: {
loadOptionsMethod: 'getTicketProperties',
}, },
default: [], {
description: `Used to include specific ticket properties in the results.<br/> displayName: 'Properties With History',
name: 'propertiesWithHistory',
type: 'string',
default: '',
description: `Works similarly to properties=, but this parameter will include the history for the specified property,<br/>
instead of just including the current value. Use this parameter when you need the full history of changes to a property's value.`,
},
],
},
/* -------------------------------------------------------------------------- */
/* ticket:getAll */
/* -------------------------------------------------------------------------- */
{
displayName: 'Return All',
name: 'returnAll',
type: 'boolean',
displayOptions: {
show: {
resource: [
'ticket',
],
operation: [
'getAll',
],
},
},
default: false,
description: 'If all results should be returned or only up to a given limit.',
},
{
displayName: 'Limit',
name: 'limit',
type: 'number',
displayOptions: {
show: {
resource: [
'ticket',
],
operation: [
'getAll',
],
returnAll: [
false,
],
},
},
typeOptions: {
minValue: 1,
maxValue: 250,
},
default: 100,
description: 'How many results to return.',
},
{
displayName: 'Additional Fields',
name: 'additionalFields',
type: 'collection',
placeholder: 'Add Field',
default: {},
displayOptions: {
show: {
resource: [
'ticket',
],
operation: [
'getAll',
],
},
},
options: [
{
displayName: 'Properties',
name: 'properties',
type: 'multiOptions',
typeOptions: {
loadOptionsMethod: 'getTicketProperties',
},
default: [],
description: `Used to include specific ticket properties in the results.<br/>
By default, the results will only include ticket ID and will not include the values for any properties for your tickets.<br/> By default, the results will only include ticket ID and will not include the values for any properties for your tickets.<br/>
Including this parameter will include the data for the specified property in the results.<br/> Including this parameter will include the data for the specified property in the results.<br/>
You can include this parameter multiple times to request multiple properties separed by ,.`, You can include this parameter multiple times to request multiple properties separed by ,.`,
}, },
{ {
displayName: 'Properties With History', displayName: 'Properties With History',
name: 'propertiesWithHistory', name: 'propertiesWithHistory',
type: 'string', type: 'string',
default: '', default: '',
description: `Works similarly to properties=, but this parameter will include the history for the specified property,<br/> description: `Works similarly to properties=, but this parameter will include the history for the specified property,<br/>
instead of just including the current value. Use this parameter when you need the full history of changes to a property's value.`, instead of just including the current value. Use this parameter when you need the full history of changes to a property's value.`,
}, },
], ],
}, },
/* -------------------------------------------------------------------------- */
/* ticket:delete */ /* -------------------------------------------------------------------------- */
/* -------------------------------------------------------------------------- */ /* ticket:delete */
{ /* -------------------------------------------------------------------------- */
displayName: 'Ticket ID', {
name: 'ticketId', displayName: 'Ticket ID',
type: 'string', name: 'ticketId',
required: true, type: 'string',
displayOptions: { required: true,
show: { displayOptions: {
resource: [ show: {
'ticket', resource: [
], 'ticket',
operation: [ ],
'delete', operation: [
], 'delete',
}, ],
},
},
default: '',
description: 'Unique identifier for a particular ticket',
}, },
default: '',
description: 'Unique identifier for a particular ticket',
},
] as INodeProperties[]; ] as INodeProperties[];

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1,022 B

View file

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 62.883 69.883" fill="#fff" fill-rule="evenodd" stroke="#000" stroke-linecap="round" stroke-linejoin="round"><use xlink:href="#A" x="2.442" y="2.442"/><symbol id="A" overflow="visible"><path d="M55.504 30.401a16.26 16.26 0 0 0-5.904-5.864c-1.865-1.084-3.794-1.773-5.972-2.07v-7.798c2.161-.895 3.558-3.018 3.525-5.357a5.86 5.86 0 0 0-5.859-5.889 5.91 5.91 0 0 0-5.908 5.889c0 2.393 1.27 4.434 3.452 5.357v7.754c-1.808.262-3.562.812-5.195 1.631L12.769 8.247c.146-.552.273-1.123.273-1.724C13.042 2.92 10.122 0 6.519 0A6.52 6.52 0 0 0 0 6.524c0 3.604 2.92 6.524 6.524 6.524a6.47 6.47 0 0 0 3.35-.952l1.367 1.035 18.726 13.501c-.991.908-1.914 1.943-2.651 3.105-1.494 2.368-2.407 4.971-2.407 7.813v.586c.007 1.927.354 3.838 1.025 5.645.566 1.543 1.396 2.949 2.427 4.219l-6.221 6.235c-1.841-.684-3.906-.23-5.298 1.162-.947.942-1.48 2.227-1.475 3.565s.527 2.612 1.479 3.564 2.227 1.48 3.565 1.48a5.01 5.01 0 0 0 3.565-1.48c.942-.952 1.479-2.227 1.475-3.564a5.03 5.03 0 0 0-.234-1.514l6.426-6.426a16.09 16.09 0 0 0 2.856 1.563 16.7 16.7 0 0 0 6.685 1.406h.439a15.76 15.76 0 0 0 7.627-1.929 15.77 15.77 0 0 0 5.977-5.63c1.499-2.393 2.319-5.044 2.319-7.959v-.146c0-2.866-.664-5.508-2.051-7.93zm-7.847 13.487c-1.743 1.938-3.75 3.135-6.016 3.135h-.43c-1.294 0-2.564-.356-3.799-1.011a8.79 8.79 0 0 1-3.33-3.032c-.898-1.27-1.387-2.656-1.387-4.126v-.439c0-1.445.278-2.817.977-4.111.747-1.465 1.758-2.515 3.101-3.389a7.6 7.6 0 0 1 4.297-1.294h.147c1.416 0 2.769.278 4.038.928 1.286.677 2.378 1.67 3.174 2.886a9.18 9.18 0 0 1 1.421 4.053l.034.913c0 1.987-.762 3.828-2.28 5.498z" stroke="none" fill="#f8761f" fill-rule="nonzero"/></symbol></svg>

After

Width:  |  Height:  |  Size: 1.7 KiB