diff --git a/packages/nodes-base/nodes/Hubspot/Hubspot.node.ts b/packages/nodes-base/nodes/Hubspot/Hubspot.node.ts index 307594f354..c78488e73b 100644 --- a/packages/nodes-base/nodes/Hubspot/Hubspot.node.ts +++ b/packages/nodes-base/nodes/Hubspot/Hubspot.node.ts @@ -1,2737 +1,27 @@ -import type { - IExecuteFunctions, - ICredentialDataDecryptedObject, - ICredentialsDecrypted, - ICredentialTestFunctions, - IDataObject, - ILoadOptionsFunctions, - INodeCredentialTestResult, - INodeExecutionData, - INodePropertyOptions, - INodeType, - INodeTypeDescription, - JsonObject, -} from 'n8n-workflow'; -import { NodeOperationError } from 'n8n-workflow'; - -import { - clean, - getAssociations, - getCallMetadata, - getEmailMetadata, - getMeetingMetadata, - getTaskMetadata, - hubspotApiRequest, - hubspotApiRequestAllItems, - validateCredentials, -} from './GenericFunctions'; - -import { contactFields, contactOperations } from './ContactDescription'; - -import { contactListFields, contactListOperations } from './ContactListDescription'; - -import { companyFields, companyOperations } from './CompanyDescription'; - -import { dealFields, dealOperations } from './DealDescription'; - -import { engagementFields, engagementOperations } from './EngagementDescription'; - -import { formFields, formOperations } from './FormDescription'; - -import { ticketFields, ticketOperations } from './TicketDescription'; - -import type { IForm } from './FormInterface'; - -import type { IAssociation, IDeal } from './DealInterface'; - -import { snakeCase } from 'change-case'; - -export class Hubspot implements INodeType { - description: INodeTypeDescription = { - displayName: 'HubSpot', - name: 'hubspot', - icon: 'file:hubspot.svg', - group: ['output'], - version: 1, - subtitle: '={{$parameter["operation"] + ": " + $parameter["resource"]}}', - description: 'Consume HubSpot API', - defaults: { - name: 'HubSpot', - }, - inputs: ['main'], - outputs: ['main'], - credentials: [ - { - name: 'hubspotApi', - required: true, - testedBy: 'hubspotApiTest', - displayOptions: { - show: { - authentication: ['apiKey'], - }, - }, - }, - { - name: 'hubspotAppToken', - required: true, - testedBy: 'hubspotApiTest', - displayOptions: { - show: { - authentication: ['appToken'], - }, - }, - }, - { - name: 'hubspotOAuth2Api', - required: true, - displayOptions: { - show: { - authentication: ['oAuth2'], - }, - }, - }, - ], - properties: [ - { - displayName: 'Authentication', - name: 'authentication', - type: 'options', - options: [ - { - name: 'API Key', - value: 'apiKey', - }, - { - name: 'APP Token', - value: 'appToken', - }, - { - name: 'OAuth2', - value: 'oAuth2', - }, - ], - default: 'apiKey', - }, - { - displayName: 'Resource', - name: 'resource', - type: 'options', - noDataExpression: true, - options: [ - { - name: 'Company', - value: 'company', - }, - { - name: 'Contact', - value: 'contact', - }, - { - name: 'Contact List', - value: 'contactList', - }, - { - name: 'Deal', - value: 'deal', - }, - { - name: 'Engagement', - value: 'engagement', - }, - { - name: 'Form', - value: 'form', - }, - { - name: 'Ticket', - value: 'ticket', - }, - ], - default: 'deal', - }, - // CONTACT - ...contactOperations, - ...contactFields, - // CONTACT LIST - ...contactListOperations, - ...contactListFields, - // COMPANY - ...companyOperations, - ...companyFields, - // DEAL - ...dealOperations, - ...dealFields, - // ENGAGEMENT - ...engagementOperations, - ...engagementFields, - // FORM - ...formOperations, - ...formFields, - // TICKET - ...ticketOperations, - ...ticketFields, - ], - }; - - methods = { - credentialTest: { - async hubspotApiTest( - this: ICredentialTestFunctions, - credential: ICredentialsDecrypted, - ): Promise { - try { - await validateCredentials.call(this, credential.data as ICredentialDataDecryptedObject); - } catch (error) { - const err = error as JsonObject; - if (err.statusCode === 401) { - return { - status: 'Error', - message: 'Invalid credentials', - }; - } - } - return { - status: 'OK', - message: 'Authentication successful', - }; - }, - }, - - loadOptions: { - /* -------------------------------------------------------------------------- */ - /* CONTACT */ - /* -------------------------------------------------------------------------- */ - - // Get all the contact lead statuses to display them to user so that they can - // select them easily - async getContactLeadStatuses(this: ILoadOptionsFunctions): Promise { - const returnData: INodePropertyOptions[] = []; - const endpoint = '/properties/v2/contacts/properties'; - const properties = await hubspotApiRequest.call(this, 'GET', endpoint, {}); - for (const property of properties) { - if (property.name === 'hs_lead_status') { - for (const option of property.options) { - const statusName = option.label; - const statusId = option.value; - returnData.push({ - name: statusName, - value: statusId, - }); - } - } - } - return returnData; - }, - - // Get all the contact legal basics to display them to user so that they can - // select them easily - async getContactLealBasics(this: ILoadOptionsFunctions): Promise { - const returnData: INodePropertyOptions[] = []; - const endpoint = '/properties/v2/contacts/properties'; - const properties = await hubspotApiRequest.call(this, 'GET', endpoint, {}); - for (const property of properties) { - if (property.name === 'hs_legal_basis') { - for (const option of property.options) { - const statusName = option.label; - const statusId = option.value; - returnData.push({ - name: statusName, - value: statusId, - }); - } - } - } - return returnData; - }, - - // Get all the contact lifecycle stages to display them to user so that they can - // select them easily - async getContactLifeCycleStages( - this: ILoadOptionsFunctions, - ): Promise { - const returnData: INodePropertyOptions[] = []; - const endpoint = '/properties/v2/contacts/properties'; - const properties = await hubspotApiRequest.call(this, 'GET', endpoint, {}); - for (const property of properties) { - if (property.name === 'lifecyclestage') { - for (const option of property.options) { - const stageName = option.label; - const stageId = option.value; - returnData.push({ - name: stageName, - value: stageId, - }); - } - } - } - return returnData; - }, - - // Get all the contact lifecycle stages to display them to user so that they can - // select them easily - async getContactOriginalSources( - this: ILoadOptionsFunctions, - ): Promise { - const returnData: INodePropertyOptions[] = []; - const endpoint = '/properties/v2/contacts/properties'; - const properties = await hubspotApiRequest.call(this, 'GET', endpoint, {}); - for (const property of properties) { - if (property.name === 'hs_analytics_source') { - for (const option of property.options) { - const sourceName = option.label; - const sourceId = option.value; - returnData.push({ - name: sourceName, - value: sourceId, - }); - } - } - } - return returnData; - }, - - // Get all the contact preffered languages to display them to user so that they can - // select them easily - async getContactPrefferedLanguages( - this: ILoadOptionsFunctions, - ): Promise { - const returnData: INodePropertyOptions[] = []; - const endpoint = '/properties/v2/contacts/properties'; - const properties = await hubspotApiRequest.call(this, 'GET', endpoint, {}); - for (const property of properties) { - if (property.name === 'hs_language') { - for (const option of property.options) { - const languageName = option.label; - const languageId = option.value; - returnData.push({ - name: languageName, - value: languageId, - }); - } - } - } - return returnData; - }, - - // Get all the contact preffered languages to display them to user so that they can - // select them easily - async getContactStatuses(this: ILoadOptionsFunctions): Promise { - const returnData: INodePropertyOptions[] = []; - const endpoint = '/properties/v2/contacts/properties'; - const properties = await hubspotApiRequest.call(this, 'GET', endpoint, {}); - for (const property of properties) { - if (property.name === 'hs_content_membership_status') { - for (const option of property.options) { - const languageName = option.label; - const languageId = option.value; - returnData.push({ - name: languageName, - value: languageId, - }); - } - } - } - return returnData; - }, - - // Get all the contact properties to display them to user so that they can - // select them easily - async getContactProperties(this: ILoadOptionsFunctions): Promise { - const returnData: INodePropertyOptions[] = []; - const endpoint = '/properties/v2/contacts/properties'; - const properties = await hubspotApiRequest.call(this, 'GET', endpoint, {}); - for (const property of properties) { - const propertyName = property.label; - const propertyId = property.name; - returnData.push({ - name: propertyName, - value: propertyId, - }); - } - return returnData; - }, - - // Get all the contact properties to display them to user so that they can - // select them easily - async getContactCustomProperties( - this: ILoadOptionsFunctions, - ): Promise { - const returnData: INodePropertyOptions[] = []; - const endpoint = '/properties/v2/contacts/properties'; - const properties = await hubspotApiRequest.call(this, 'GET', endpoint, {}); - for (const property of properties) { - if (property.hubspotDefined === null) { - const propertyName = property.label; - const propertyId = property.name; - returnData.push({ - name: propertyName, - value: propertyId, - }); - } - } - return returnData; - }, - - // Get all the contact number of employees options to display them to user so that they can - // select them easily - async getContactNumberOfEmployees( - this: ILoadOptionsFunctions, - ): Promise { - const returnData: INodePropertyOptions[] = []; - const endpoint = '/properties/v2/contacts/properties'; - const properties = await hubspotApiRequest.call(this, 'GET', endpoint, {}); - for (const property of properties) { - if (property.name === 'numemployees') { - for (const option of property.options) { - const optionName = option.label; - const optionId = option.value; - returnData.push({ - name: optionName, - value: optionId, - }); - } - } - } - return returnData; - }, - - /* -------------------------------------------------------------------------- */ - /* COMPANY */ - /* -------------------------------------------------------------------------- */ - - // Get all the company industries to display them to user so that they can - // select them easily - async getCompanyIndustries(this: ILoadOptionsFunctions): Promise { - const returnData: INodePropertyOptions[] = []; - const endpoint = '/properties/v2/companies/properties'; - const properties = await hubspotApiRequest.call(this, 'GET', endpoint, {}); - for (const property of properties) { - if (property.name === 'industry') { - for (const option of property.options) { - const industryName = option.label; - const industryId = option.value; - returnData.push({ - name: industryName, - value: industryId, - }); - } - } - } - return returnData; - }, - - // Get all the company lead statuses to display them to user so that they can - // select them easily - async getCompanyleadStatuses(this: ILoadOptionsFunctions): Promise { - const returnData: INodePropertyOptions[] = []; - const endpoint = '/properties/v2/companies/properties'; - const properties = await hubspotApiRequest.call(this, 'GET', endpoint, {}); - for (const property of properties) { - if (property.name === 'hs_lead_status') { - for (const option of property.options) { - const statusName = option.label; - const statusId = option.value; - returnData.push({ - name: statusName, - value: statusId, - }); - } - } - } - return returnData; - }, - - // Get all the company lifecycle stages to display them to user so that they can - // select them easily - async getCompanylifecycleStages( - this: ILoadOptionsFunctions, - ): Promise { - const returnData: INodePropertyOptions[] = []; - const endpoint = '/properties/v2/companies/properties'; - const properties = await hubspotApiRequest.call(this, 'GET', endpoint, {}); - for (const property of properties) { - if (property.name === 'lifecyclestage') { - for (const option of property.options) { - const stageName = option.label; - const stageId = option.value; - returnData.push({ - name: stageName, - value: stageId, - }); - } - } - } - return returnData; - }, - - // Get all the company types stages to display them to user so that they can - // select them easily - async getCompanyTypes(this: ILoadOptionsFunctions): Promise { - const returnData: INodePropertyOptions[] = []; - const endpoint = '/properties/v2/companies/properties'; - const properties = await hubspotApiRequest.call(this, 'GET', endpoint, {}); - for (const property of properties) { - if (property.name === 'type') { - for (const option of property.options) { - const typeName = option.label; - const typeId = option.value; - returnData.push({ - name: typeName, - value: typeId, - }); - } - } - } - return returnData; - }, - - // Get all the company types stages to display them to user so that they can - // select them easily - async getCompanyTargetAccounts(this: ILoadOptionsFunctions): Promise { - const returnData: INodePropertyOptions[] = []; - const endpoint = '/properties/v2/companies/properties'; - const properties = await hubspotApiRequest.call(this, 'GET', endpoint, {}); - for (const property of properties) { - if (property.name === 'hs_target_account') { - for (const option of property.options) { - const targetName = option.label; - const targetId = option.value; - returnData.push({ - name: targetName, - value: targetId, - }); - } - } - } - return returnData; - }, - - // Get all the company source types stages to display them to user so that they can - // select them easily - async getCompanySourceTypes(this: ILoadOptionsFunctions): Promise { - const returnData: INodePropertyOptions[] = []; - const endpoint = '/properties/v2/companies/properties'; - const properties = await hubspotApiRequest.call(this, 'GET', endpoint, {}); - for (const property of properties) { - if (property.name === 'hs_analytics_source') { - for (const option of property.options) { - const typeName = option.label; - const typeId = option.value; - returnData.push({ - name: typeName, - value: typeId, - }); - } - } - } - return returnData; - }, - - // Get all the company web technologies stages to display them to user so that they can - // select them easily - async getCompanyWebTechnologies( - this: ILoadOptionsFunctions, - ): Promise { - const returnData: INodePropertyOptions[] = []; - const endpoint = '/properties/v2/companies/properties'; - const properties = await hubspotApiRequest.call(this, 'GET', endpoint, {}); - for (const property of properties) { - if (property.name === 'web_technologies') { - for (const option of property.options) { - const technologyName = option.label; - const technologyId = option.value; - returnData.push({ - name: technologyName, - value: technologyId, - }); - } - } - } - return returnData; - }, - - // Get all the company properties to display them to user so that they can - // select them easily - async getCompanyProperties(this: ILoadOptionsFunctions): Promise { - const returnData: INodePropertyOptions[] = []; - const endpoint = '/properties/v2/companies/properties'; - const properties = await hubspotApiRequest.call(this, 'GET', endpoint, {}); - for (const property of properties) { - const propertyName = property.label; - const propertyId = property.name; - returnData.push({ - name: propertyName, - value: propertyId, - }); - } - return returnData; - }, - - // Get all the company custom properties to display them to user so that they can - // select them easily - async getCompanyCustomProperties( - this: ILoadOptionsFunctions, - ): Promise { - const returnData: INodePropertyOptions[] = []; - const endpoint = '/properties/v2/companies/properties'; - const properties = await hubspotApiRequest.call(this, 'GET', endpoint, {}); - for (const property of properties) { - if (property.hubspotDefined === null) { - const propertyName = property.label; - const propertyId = property.name; - returnData.push({ - name: propertyName, - value: propertyId, - }); - } - } - return returnData; - }, - - /* -------------------------------------------------------------------------- */ - /* DEAL */ - /* -------------------------------------------------------------------------- */ - - // Get all the groups to display them to user so that they can - // select them easily - async getDealStages(this: ILoadOptionsFunctions): Promise { - const returnData: INodePropertyOptions[] = []; - const endpoint = '/crm-pipelines/v1/pipelines/deals'; - let stages = await hubspotApiRequest.call(this, 'GET', endpoint, {}); - stages = stages.results[0].stages; - for (const stage of stages) { - const stageName = stage.label; - const stageId = stage.stageId; - returnData.push({ - name: stageName, - value: stageId, - }); - } - return returnData; - }, - - // Get all the deal types to display them to user so that they can - // select them easily - async getDealTypes(this: ILoadOptionsFunctions): Promise { - const returnData: INodePropertyOptions[] = []; - const endpoint = '/properties/v1/deals/properties/named/dealtype'; - const dealTypes = await hubspotApiRequest.call(this, 'GET', endpoint); - for (const dealType of dealTypes.options) { - const dealTypeName = dealType.label; - const dealTypeId = dealType.value; - returnData.push({ - name: dealTypeName, - value: dealTypeId, - }); - } - return returnData; - }, - - // Get all the deal properties to display them to user so that they can - // select them easily - async getDealCustomProperties(this: ILoadOptionsFunctions): Promise { - const returnData: INodePropertyOptions[] = []; - const endpoint = '/properties/v2/deals/properties'; - const properties = await hubspotApiRequest.call(this, 'GET', endpoint, {}); - for (const property of properties) { - if (property.hubspotDefined === null) { - const propertyName = property.label; - const propertyId = property.name; - returnData.push({ - name: propertyName, - value: propertyId, - }); - } - } - return returnData; - }, - // Get all the deal properties to display them to user so that they can - // select them easily - async getDealProperties(this: ILoadOptionsFunctions): Promise { - const returnData: INodePropertyOptions[] = []; - const endpoint = '/properties/v2/deals/properties'; - const properties = await hubspotApiRequest.call(this, 'GET', endpoint, {}); - for (const property of properties) { - const propertyName = property.label; - const propertyId = property.name; - returnData.push({ - name: propertyName, - value: propertyId, - }); - } - return returnData; - }, - - /* -------------------------------------------------------------------------- */ - /* FORM */ - /* -------------------------------------------------------------------------- */ - - // Get all the forms to display them to user so that they can - // select them easily - async getForms(this: ILoadOptionsFunctions): Promise { - const returnData: INodePropertyOptions[] = []; - const endpoint = '/forms/v2/forms'; - const forms = await hubspotApiRequest.call(this, 'GET', endpoint, {}, { formTypes: 'ALL' }); - for (const form of forms) { - const formName = form.name; - const formId = form.guid; - returnData.push({ - name: formName, - value: formId, - }); - } - return returnData; - }, - - // Get all the subscription types to display them to user so that they can - // select them easily - async getSubscriptionTypes(this: ILoadOptionsFunctions): Promise { - const returnData: INodePropertyOptions[] = []; - const endpoint = '/email/public/v1/subscriptions'; - const subscriptions = await hubspotApiRequestAllItems.call( - this, - 'subscriptionDefinitions', - 'GET', - endpoint, - {}, - ); - for (const subscription of subscriptions) { - const subscriptionName = subscription.name; - const subscriptionId = subscription.id; - returnData.push({ - name: subscriptionName, - value: subscriptionId, - }); - } - return returnData; - }, - - /* -------------------------------------------------------------------------- */ - /* TICKET */ - /* -------------------------------------------------------------------------- */ - - // Get all the ticket categories to display them to user so that they can - // select them easily - async getTicketCategories(this: ILoadOptionsFunctions): Promise { - const returnData: INodePropertyOptions[] = []; - const endpoint = '/properties/v2/tickets/properties'; - const properties = await hubspotApiRequest.call(this, 'GET', endpoint, {}); - for (const property of properties) { - if (property.name === 'hs_ticket_category') { - for (const option of property.options) { - const categoryName = option.label; - const categoryId = option.value; - returnData.push({ - name: categoryName, - value: categoryId, - }); - } - } - } - return returnData.sort((a, b) => (a.name < b.name ? 0 : 1)); - }, - - // Get all the ticket pipelines to display them to user so that they can - // select them easily - async getTicketPipelines(this: ILoadOptionsFunctions): Promise { - const returnData: INodePropertyOptions[] = []; - const endpoint = '/crm-pipelines/v1/pipelines/tickets'; - const { results } = await hubspotApiRequest.call(this, 'GET', endpoint, {}); - for (const pipeline of results) { - const pipelineName = pipeline.label; - const pipelineId = pipeline.pipelineId; - returnData.push({ - name: pipelineName, - value: pipelineId, - }); - } - return returnData; - }, - - // Get all the ticket resolutions to display them to user so that they can - // select them easily - async getTicketPriorities(this: ILoadOptionsFunctions): Promise { - const returnData: INodePropertyOptions[] = []; - const endpoint = '/properties/v2/tickets/properties'; - const properties = await hubspotApiRequest.call(this, 'GET', endpoint, {}); - for (const property of properties) { - if (property.name === 'hs_ticket_priority') { - for (const option of property.options) { - const priorityName = option.label; - const priorityId = option.value; - returnData.push({ - name: priorityName, - value: priorityId, - }); - } - } - } - return returnData; - }, - - // Get all the ticket properties to display them to user so that they can - // select them easily - async getTicketProperties(this: ILoadOptionsFunctions): Promise { - const returnData: INodePropertyOptions[] = []; - const endpoint = '/properties/v2/tickets/properties'; - const properties = await hubspotApiRequest.call(this, 'GET', endpoint, {}); - for (const property of properties) { - const propertyName = property.label; - const propertyId = property.name; - returnData.push({ - name: propertyName, - value: propertyId, - }); - } - return returnData; - }, - - // Get all the ticket resolutions to display them to user so that they can - // select them easily - async getTicketResolutions(this: ILoadOptionsFunctions): Promise { - const returnData: INodePropertyOptions[] = []; - const endpoint = '/properties/v2/tickets/properties'; - const properties = await hubspotApiRequest.call(this, 'GET', endpoint, {}); - for (const property of properties) { - if (property.name === 'hs_resolution') { - for (const option of property.options) { - const resolutionName = option.label; - const resolutionId = option.value; - returnData.push({ - name: resolutionName, - value: resolutionId, - }); - } - } - } - return returnData.sort((a, b) => (a.name < b.name ? 0 : 1)); - }, - - // Get all the ticket sources to display them to user so that they can - // select them easily - async getTicketSources(this: ILoadOptionsFunctions): Promise { - const returnData: INodePropertyOptions[] = []; - const endpoint = '/properties/v2/tickets/properties'; - const properties = await hubspotApiRequest.call(this, 'GET', endpoint, {}); - for (const property of properties) { - if (property.name === 'source_type') { - for (const option of property.options) { - const sourceName = option.label; - const sourceId = option.value; - returnData.push({ - name: sourceName, - value: sourceId, - }); - } - } - } - return returnData.sort((a, b) => (a.name < b.name ? 0 : 1)); - }, - - // Get all the ticket stages to display them to user so that they can - // select them easily - async getTicketStages(this: ILoadOptionsFunctions): Promise { - let currentPipelineId = this.getCurrentNodeParameter('pipelineId') as string; - if (currentPipelineId === undefined) { - currentPipelineId = this.getNodeParameter('updateFields.pipelineId', '') as string; - } - const returnData: INodePropertyOptions[] = []; - const endpoint = '/crm-pipelines/v1/pipelines/tickets'; - const { results } = await hubspotApiRequest.call(this, 'GET', endpoint, {}); - for (const pipeline of results) { - if (currentPipelineId === pipeline.pipelineId) { - for (const stage of pipeline.stages) { - const stageName = stage.label; - const stageId = stage.stageId; - returnData.push({ - name: stageName, - value: stageId, - }); - } - } - } - return returnData; - }, - - /* -------------------------------------------------------------------------- */ - /* COMMON */ - /* -------------------------------------------------------------------------- */ - - // Get all the owners to display them to user so that they can - // select them easily - async getOwners(this: ILoadOptionsFunctions): Promise { - const returnData: INodePropertyOptions[] = []; - const endpoint = '/owners/v2/owners'; - const owners = await hubspotApiRequest.call(this, 'GET', endpoint); - for (const owner of owners) { - const ownerName = owner.email; - const ownerId = owner.ownerId; - returnData.push({ - name: ownerName, - value: ownerId, - }); - } - return returnData; - }, - - // Get all the companies to display them to user so that they can - // select them easily - async getCompanies(this: ILoadOptionsFunctions): Promise { - const returnData: INodePropertyOptions[] = []; - const qs: IDataObject = { - properties: ['name'], - }; - const endpoint = '/companies/v2/companies/paged'; - const companies = await hubspotApiRequestAllItems.call( - this, - 'companies', - 'GET', - endpoint, - {}, - qs, - ); - for (const company of companies) { - const companyName = company.properties.name - ? company.properties.name.value - : company.companyId; - const companyId = company.companyId; - returnData.push({ - name: companyName, - value: companyId, - }); - } - return returnData.sort((a, b) => (a.name < b.name ? 0 : 1)); - }, - - // Get all the companies to display them to user so that they can - // select them easily - async getContacts(this: ILoadOptionsFunctions): Promise { - const returnData: INodePropertyOptions[] = []; - const endpoint = '/contacts/v1/lists/all/contacts/all'; - const contacts = await hubspotApiRequestAllItems.call(this, 'contacts', 'GET', endpoint); - - for (const contact of contacts) { - const firstName = contact.properties?.firstname?.value || ''; - const lastName = contact.properties?.lastname?.value || ''; - const contactName = `${firstName} ${lastName}`; - const contactId = contact.vid; - returnData.push({ - name: contactName, - value: contactId, - description: `Contact VID: ${contactId}`, - }); - } - return returnData.sort((a, b) => (a.name < b.name ? 0 : 1)); - }, - }, - }; - - async execute(this: IExecuteFunctions): Promise { - const items = this.getInputData(); - const returnData: INodeExecutionData[] = []; - const length = items.length; - let responseData; - const qs: IDataObject = {}; - const resource = this.getNodeParameter('resource', 0); - const operation = this.getNodeParameter('operation', 0); - - //https://legacydocs.hubspot.com/docs/methods/lists/contact-lists-overview - if (resource === 'contactList') { - try { - //https://legacydocs.hubspot.com/docs/methods/lists/add_contact_to_list - if (operation === 'add') { - const listId = this.getNodeParameter('listId', 0) as string; - const by = this.getNodeParameter('by', 0) as string; - const body: { [key: string]: [] } = { emails: [], vids: [] }; - for (let i = 0; i < length; i++) { - if (by === 'id') { - const id = this.getNodeParameter('id', i) as string; - body.vids.push(parseInt(id, 10) as never); - } else { - const email = this.getNodeParameter('email', i) as string; - body.emails.push(email as never); - } - } - responseData = await hubspotApiRequest.call( - this, - 'POST', - `/contacts/v1/lists/${listId}/add`, - body, - ); - returnData.push(responseData as INodeExecutionData); - } - //https://legacydocs.hubspot.com/docs/methods/lists/remove_contact_from_list - if (operation === 'remove') { - const listId = this.getNodeParameter('listId', 0) as string; - const body: { [key: string]: [] } = { vids: [] }; - for (let i = 0; i < length; i++) { - const id = this.getNodeParameter('id', i) as string; - body.vids.push(parseInt(id, 10) as never); - } - responseData = await hubspotApiRequest.call( - this, - 'POST', - `/contacts/v1/lists/${listId}/remove`, - body, - ); - returnData.push(responseData as INodeExecutionData); - } - } catch (error) { - if (this.continueOnFail()) { - returnData.push({ json: { error: (error as JsonObject).message } }); - } else { - throw error; - } - } - } else { - for (let i = 0; i < length; i++) { - try { - //https://developers.hubspot.com/docs/methods/contacts/create_or_update - if (resource === 'contact') { - //https://developers.hubspot.com/docs/methods/companies/create_company - if (operation === 'upsert') { - const email = this.getNodeParameter('email', i) as string; - const resolveData = this.getNodeParameter('resolveData', i); - const additionalFields = this.getNodeParameter('additionalFields', i); - const body: IDataObject[] = []; - if (additionalFields.annualRevenue) { - body.push({ - property: 'annualrevenue', - value: (additionalFields.annualRevenue as number).toString(), - }); - } - if (additionalFields.city) { - body.push({ - property: 'city', - value: additionalFields.city, - }); - } - if (additionalFields.clickedFacebookAd) { - body.push({ - property: 'hs_facebook_ad_clicked', - value: additionalFields.clickedFacebookAd, - }); - } - if (additionalFields.closeDate) { - body.push({ - property: 'closedate', - value: new Date(additionalFields.closeDate as string).getTime(), - }); - } - if (additionalFields.companyName) { - body.push({ - property: 'company', - value: additionalFields.companyName, - }); - } - if (additionalFields.companySize) { - body.push({ - property: 'company_size', - value: additionalFields.companySize, - }); - } - if (additionalFields.description) { - body.push({ - property: 'description', - value: additionalFields.description, - }); - } - if (additionalFields.contactOwner) { - body.push({ - property: 'hubspot_owner_id', - value: additionalFields.contactOwner, - }); - } - if (additionalFields.country) { - body.push({ - property: 'country', - value: additionalFields.country, - }); - } - if (additionalFields.dateOfBirth) { - body.push({ - property: 'date_of_birth', - value: additionalFields.dateOfBirth, - }); - } - if (additionalFields.degree) { - body.push({ - property: 'degree', - value: additionalFields.degree, - }); - } - if (additionalFields.facebookClickId) { - body.push({ - property: 'hs_facebook_click_id', - value: additionalFields.facebookClickId, - }); - } - if (additionalFields.faxNumber) { - body.push({ - property: 'fax', - value: additionalFields.faxNumber, - }); - } - if (additionalFields.fieldOfStudy) { - body.push({ - property: 'field_of_study', - value: additionalFields.fieldOfStudy, - }); - } - if (additionalFields.firstName) { - body.push({ - property: 'firstname', - value: additionalFields.firstName, - }); - } - if (additionalFields.gender) { - body.push({ - property: 'gender', - value: additionalFields.gender, - }); - } - if (additionalFields.googleAdClickId) { - body.push({ - property: 'hs_google_click_id', - value: additionalFields.googleAdClickId, - }); - } - if (additionalFields.graduationDate) { - body.push({ - property: 'graduation_date', - value: additionalFields.graduationDate, - }); - } - if (additionalFields.industry) { - body.push({ - property: 'industry', - value: additionalFields.industry, - }); - } - if (additionalFields.jobFunction) { - body.push({ - property: 'job_function', - value: additionalFields.jobFunction, - }); - } - if (additionalFields.jobTitle) { - body.push({ - property: 'jobtitle', - value: additionalFields.jobTitle, - }); - } - if (additionalFields.lastName) { - body.push({ - property: 'lastname', - value: additionalFields.lastName, - }); - } - if (additionalFields.leadStatus) { - body.push({ - property: 'hs_lead_status', - value: additionalFields.leadStatus, - }); - } - if (additionalFields.processingContactData) { - body.push({ - property: 'hs_legal_basis', - value: additionalFields.processingContactData, - }); - } - if (additionalFields.lifeCycleStage) { - body.push({ - property: 'lifecyclestage', - value: additionalFields.lifeCycleStage, - }); - } - if (additionalFields.maritalStatus) { - body.push({ - property: 'marital_status', - value: additionalFields.maritalStatus, - }); - } - if (additionalFields.membershipNote) { - body.push({ - property: 'hs_content_membership_notes', - value: additionalFields.membershipNote, - }); - } - if (additionalFields.message) { - body.push({ - property: 'message', - value: additionalFields.message, - }); - } - if (additionalFields.mobilePhoneNumber) { - body.push({ - property: 'mobilephone', - value: additionalFields.mobilePhoneNumber, - }); - } - if (additionalFields.numberOfEmployees) { - body.push({ - property: 'numemployees', - value: additionalFields.numberOfEmployees, - }); - } - if (additionalFields.originalSource) { - body.push({ - property: 'hs_analytics_source', - value: additionalFields.originalSource, - }); - } - if (additionalFields.phoneNumber) { - body.push({ - property: 'phone', - value: additionalFields.phoneNumber, - }); - } - if (additionalFields.postalCode) { - body.push({ - property: 'zip', - value: additionalFields.postalCode, - }); - } - if (additionalFields.prefferedLanguage) { - body.push({ - property: 'hs_language', - value: additionalFields.prefferedLanguage, - }); - } - if (additionalFields.relationshipStatus) { - body.push({ - property: 'relationship_status', - value: additionalFields.relationshipStatus, - }); - } - if (additionalFields.salutation) { - body.push({ - property: 'salutation', - value: additionalFields.salutation, - }); - } - if (additionalFields.school) { - body.push({ - property: 'school', - value: additionalFields.school, - }); - } - if (additionalFields.seniority) { - body.push({ - property: 'seniority', - value: additionalFields.seniority, - }); - } - if (additionalFields.startDate) { - body.push({ - property: 'start_date', - value: additionalFields.startDate, - }); - } - if (additionalFields.stateRegion) { - body.push({ - property: 'state', - value: additionalFields.stateRegion, - }); - } - if (additionalFields.status) { - body.push({ - property: 'hs_content_membership_status', - value: additionalFields.status, - }); - } - if (additionalFields.streetAddress) { - body.push({ - property: 'address', - value: additionalFields.streetAddress, - }); - } - if (additionalFields.twitterUsername) { - body.push({ - property: 'twitterhandle', - value: additionalFields.twitterUsername, - }); - } - if (additionalFields.websiteUrl) { - body.push({ - property: 'website', - value: additionalFields.websiteUrl, - }); - } - if (additionalFields.workEmail) { - body.push({ - property: 'work_email', - value: additionalFields.workEmail, - }); - } - - if (additionalFields.customPropertiesUi) { - const customProperties = (additionalFields.customPropertiesUi as IDataObject) - .customPropertiesValues as IDataObject[]; - - if (customProperties) { - for (const customProperty of customProperties) { - body.push({ - property: customProperty.property, - value: customProperty.value, - }); - } - } - } - - const endpoint = `/contacts/v1/contact/createOrUpdate/email/${email}`; - responseData = await hubspotApiRequest.call(this, 'POST', endpoint, { - properties: body, - }); - - if (additionalFields.associatedCompanyId) { - const companyAssociations: IDataObject[] = []; - companyAssociations.push({ - fromObjectId: responseData.vid, - toObjectId: additionalFields.associatedCompanyId, - category: 'HUBSPOT_DEFINED', - definitionId: 1, - }); - await hubspotApiRequest.call( - this, - 'PUT', - '/crm-associations/v1/associations/create-batch', - companyAssociations, - ); - } - - if (resolveData) { - const isNew = responseData.isNew; - if (additionalFields.properties) { - qs.property = additionalFields.properties as string[]; - } - responseData = await hubspotApiRequest.call( - this, - 'GET', - `/contacts/v1/contact/vid/${responseData.vid}/profile`, - {}, - qs, - ); - responseData.isNew = isNew; - } - } - //https://developers.hubspot.com/docs/methods/contacts/get_contact - if (operation === 'get') { - const contactId = this.getNodeParameter('contactId', i) as string; - const additionalFields = this.getNodeParameter('additionalFields', i); - if (additionalFields.formSubmissionMode) { - qs.formSubmissionMode = additionalFields.formSubmissionMode as string; - } - if (additionalFields.listMemberships) { - qs.showListMemberships = additionalFields.listMemberships as boolean; - } - if (additionalFields.properties) { - qs.property = additionalFields.properties as string[]; - } - if (additionalFields.propertyMode) { - qs.propertyMode = snakeCase(additionalFields.propertyMode as string); - } - const endpoint = `/contacts/v1/contact/vid/${contactId}/profile`; - responseData = await hubspotApiRequest.call(this, 'GET', endpoint, {}, qs); - } - //https://developers.hubspot.com/docs/methods/contacts/get_contacts - if (operation === 'getAll') { - const additionalFields = this.getNodeParameter('additionalFields', i); - const returnAll = this.getNodeParameter('returnAll', 0); - if (additionalFields.formSubmissionMode) { - qs.formSubmissionMode = additionalFields.formSubmissionMode as string; - } - if (additionalFields.listMemberships) { - qs.showListMemberships = additionalFields.listMemberships as boolean; - } - if (additionalFields.properties) { - qs.property = additionalFields.properties as string[]; - } - if (additionalFields.propertyMode) { - qs.propertyMode = snakeCase(additionalFields.propertyMode as string); - } - const endpoint = '/contacts/v1/lists/all/contacts/all'; - if (returnAll) { - responseData = await hubspotApiRequestAllItems.call( - this, - 'contacts', - 'GET', - endpoint, - {}, - qs, - ); - } else { - qs.count = this.getNodeParameter('limit', 0); - responseData = await hubspotApiRequest.call(this, 'GET', endpoint, {}, qs); - responseData = responseData.contacts; - } - } - //https://developers.hubspot.com/docs/methods/contacts/get_recently_created_contacts - if (operation === 'getRecentlyCreatedUpdated') { - const returnAll = this.getNodeParameter('returnAll', 0); - const filters = this.getNodeParameter('filters', i); - if (filters.formSubmissionMode) { - qs.formSubmissionMode = filters.formSubmissionMode as string; - } - if (filters.listMemberships) { - qs.showListMemberships = filters.listMemberships as boolean; - } - if (filters.properties) { - qs.property = filters.properties as string[]; - } - if (filters.propertyMode) { - qs.propertyMode = snakeCase(filters.propertyMode as string); - } - - const endpoint = '/contacts/v1/lists/recently_updated/contacts/recent'; - - if (returnAll) { - responseData = await hubspotApiRequestAllItems.call( - this, - 'contacts', - 'GET', - endpoint, - {}, - qs, - ); - } else { - qs.count = this.getNodeParameter('limit', 0); - responseData = await hubspotApiRequest.call(this, 'GET', endpoint, {}, qs); - responseData = responseData.contacts; - } - } - //https://developers.hubspot.com/docs/methods/contacts/delete_contact - if (operation === 'delete') { - const contactId = this.getNodeParameter('contactId', i) as string; - const endpoint = `/contacts/v1/contact/vid/${contactId}`; - responseData = await hubspotApiRequest.call(this, 'DELETE', endpoint); - } - //https://developers.hubspot.com/docs/api/crm/search - if (operation === 'search') { - const additionalFields = this.getNodeParameter('additionalFields', i); - const returnAll = this.getNodeParameter('returnAll', 0); - const filtersGroupsUi = this.getNodeParameter('filterGroupsUi', i) as IDataObject; - const sortBy = additionalFields.sortBy || 'createdate'; - const direction = additionalFields.direction || 'DESCENDING'; - - const body: IDataObject = { - sorts: [ - { - propertyName: sortBy, - direction, - }, - ], - }; - - if (filtersGroupsUi) { - const filterGroupValues = filtersGroupsUi.filterGroupsValues as IDataObject[]; - if (filterGroupValues) { - body.filterGroups = []; - for (const filterGroupValue of filterGroupValues) { - if (filterGroupValue.filtersUi) { - const filterValues = (filterGroupValue.filtersUi as IDataObject) - .filterValues as IDataObject[]; - if (filterValues) { - //@ts-ignore - body.filterGroups.push({ filters: filterValues }); - } - } - } - } - } - - Object.assign(body, additionalFields); - - const endpoint = '/crm/v3/objects/contacts/search'; - - if (returnAll) { - responseData = await hubspotApiRequestAllItems.call( - this, - 'results', - 'POST', - endpoint, - body, - qs, - ); - } else { - body.limit = this.getNodeParameter('limit', 0); - responseData = await hubspotApiRequest.call(this, 'POST', endpoint, body, qs); - responseData = responseData.results; - } - } - } - //https://developers.hubspot.com/docs/methods/companies/companies-overview - if (resource === 'company') { - //https://developers.hubspot.com/docs/methods/companies/create_company - if (operation === 'create') { - const name = this.getNodeParameter('name', i) as string; - const additionalFields = this.getNodeParameter('additionalFields', i); - const body: IDataObject[] = []; - body.push({ - name: 'name', - value: name, - }); - if (additionalFields.aboutUs) { - body.push({ - name: 'about_us', - value: additionalFields.aboutUs, - }); - } - if (additionalFields.annualRevenue) { - body.push({ - name: 'annualrevenue', - value: (additionalFields.annualRevenue as number).toString(), - }); - } - if (additionalFields.city) { - body.push({ - name: 'city', - value: additionalFields.city, - }); - } - if (additionalFields.closeDate) { - body.push({ - name: 'closedate', - value: new Date(additionalFields.closeDate as string).getTime(), - }); - } - if (additionalFields.companyDomainName) { - body.push({ - name: 'domain', - value: additionalFields.companyDomainName, - }); - } - if (additionalFields.companyOwner) { - body.push({ - name: 'hubspot_owner_id', - value: additionalFields.companyOwner, - }); - } - if (additionalFields.countryRegion) { - body.push({ - name: 'country', - value: additionalFields.countryRegion, - }); - } - if (additionalFields.description) { - body.push({ - name: 'description', - value: additionalFields.description, - }); - } - if (additionalFields.facebookFans) { - body.push({ - name: 'facebookfans', - value: additionalFields.facebookFans, - }); - } - if (additionalFields.googlePlusPage) { - body.push({ - name: 'googleplus_page', - value: additionalFields.googlePlusPage, - }); - } - if (additionalFields.industry) { - body.push({ - name: 'industry', - value: additionalFields.industry, - }); - } - if (additionalFields.isPublic) { - body.push({ - name: 'is_public', - value: additionalFields.isPublic, - }); - } - if (additionalFields.leadStatus) { - body.push({ - name: 'hs_lead_status', - value: additionalFields.leadStatus, - }); - } - if (additionalFields.lifecycleStatus) { - body.push({ - name: 'lifecyclestage', - value: additionalFields.lifecycleStatus, - }); - } - if (additionalFields.linkedinBio) { - body.push({ - name: 'linkedinbio', - value: additionalFields.linkedinBio, - }); - } - if (additionalFields.linkedInCompanyPage) { - body.push({ - name: 'linkedin_company_page', - value: additionalFields.linkedInCompanyPage, - }); - } - if (additionalFields.numberOfEmployees) { - body.push({ - name: 'numberofemployees', - value: additionalFields.numberOfEmployees, - }); - } - if (additionalFields.originalSourceType) { - body.push({ - name: 'hs_analytics_source', - value: additionalFields.originalSourceType, - }); - } - if (additionalFields.phoneNumber) { - body.push({ - name: 'phone', - value: additionalFields.phoneNumber, - }); - } - if (additionalFields.postalCode) { - body.push({ - name: 'zip', - value: additionalFields.postalCode, - }); - } - if (additionalFields.stateRegion) { - body.push({ - name: 'state', - value: additionalFields.stateRegion, - }); - } - if (additionalFields.streetAddress) { - body.push({ - name: 'address', - value: additionalFields.streetAddress, - }); - } - if (additionalFields.streetAddress2) { - body.push({ - name: 'address2', - value: additionalFields.streetAddress2, - }); - } - if (additionalFields.targetAccount) { - body.push({ - name: 'hs_target_account', - value: additionalFields.targetAccount, - }); - } - if (additionalFields.timezone) { - body.push({ - name: 'timezone', - value: additionalFields.timezone, - }); - } - if (additionalFields.totalMoneyRaised) { - body.push({ - name: 'total_money_raised', - value: additionalFields.totalMoneyRaised, - }); - } - if (additionalFields.twitterBio) { - body.push({ - name: 'twitterbio', - value: additionalFields.twitterBio, - }); - } - if (additionalFields.twitterFollowers) { - body.push({ - name: 'twitterfollowers', - value: additionalFields.twitterFollowers, - }); - } - if (additionalFields.twitterHandle) { - body.push({ - name: 'twitterhandle', - value: additionalFields.twitterHandle, - }); - } - if (additionalFields.type) { - body.push({ - name: 'type', - value: additionalFields.type, - }); - } - if (additionalFields.websiteUrl) { - body.push({ - name: 'website', - value: additionalFields.websiteUrl, - }); - } - if (additionalFields.webTechnologies) { - body.push({ - name: 'web_technologies', - value: additionalFields.webTechnologies, - }); - } - if (additionalFields.yearFounded) { - body.push({ - name: 'founded_year', - value: additionalFields.yearFounded, - }); - } - if (additionalFields.customPropertiesUi) { - const customProperties = (additionalFields.customPropertiesUi as IDataObject) - .customPropertiesValues as IDataObject[]; - - if (customProperties) { - for (const customProperty of customProperties) { - body.push({ - name: customProperty.property, - value: customProperty.value, - }); - } - } - } - const endpoint = '/companies/v2/companies'; - responseData = await hubspotApiRequest.call(this, 'POST', endpoint, { - properties: body, - }); - } - //https://developers.hubspot.com/docs/methods/companies/update_company - if (operation === 'update') { - const companyId = this.getNodeParameter('companyId', i) as string; - const updateFields = this.getNodeParameter('updateFields', i); - const body: IDataObject[] = []; - if (updateFields.name) { - body.push({ - name: 'name', - value: updateFields.name, - }); - } - if (updateFields.aboutUs) { - body.push({ - name: 'about_us', - value: updateFields.aboutUs, - }); - } - if (updateFields.annualRevenue) { - body.push({ - name: 'annualrevenue', - value: (updateFields.annualRevenue as number).toString(), - }); - } - if (updateFields.city) { - body.push({ - name: 'city', - value: updateFields.city, - }); - } - if (updateFields.closeDate) { - body.push({ - name: 'closedate', - value: new Date(updateFields.closeDate as string).getTime(), - }); - } - if (updateFields.companyDomainName) { - body.push({ - name: 'domain', - value: updateFields.companyDomainName, - }); - } - if (updateFields.companyOwner) { - body.push({ - name: 'hubspot_owner_id', - value: updateFields.companyOwner, - }); - } - if (updateFields.countryRegion) { - body.push({ - name: 'country', - value: updateFields.countryRegion, - }); - } - if (updateFields.description) { - body.push({ - name: 'description', - value: updateFields.description, - }); - } - if (updateFields.facebookFans) { - body.push({ - name: 'facebookfans', - value: updateFields.facebookFans, - }); - } - if (updateFields.googlePlusPage) { - body.push({ - name: 'googleplus_page', - value: updateFields.googlePlusPage, - }); - } - if (updateFields.industry) { - body.push({ - name: 'industry', - value: updateFields.industry, - }); - } - if (updateFields.isPublic) { - body.push({ - name: 'is_public', - value: updateFields.isPublic, - }); - } - if (updateFields.leadStatus) { - body.push({ - name: 'hs_lead_status', - value: updateFields.leadStatus, - }); - } - if (updateFields.lifecycleStatus) { - body.push({ - name: 'lifecyclestage', - value: updateFields.lifecycleStatus, - }); - } - if (updateFields.linkedinBio) { - body.push({ - name: 'linkedinbio', - value: updateFields.linkedinBio, - }); - } - if (updateFields.linkedInCompanyPage) { - body.push({ - name: 'linkedin_company_page', - value: updateFields.linkedInCompanyPage, - }); - } - if (updateFields.numberOfEmployees) { - body.push({ - name: 'numberofemployees', - value: updateFields.numberOfEmployees, - }); - } - if (updateFields.originalSourceType) { - body.push({ - name: 'hs_analytics_source', - value: updateFields.originalSourceType, - }); - } - if (updateFields.phoneNumber) { - body.push({ - name: 'phone', - value: updateFields.phoneNumber, - }); - } - if (updateFields.postalCode) { - body.push({ - name: 'zip', - value: updateFields.postalCode, - }); - } - if (updateFields.stateRegion) { - body.push({ - name: 'state', - value: updateFields.stateRegion, - }); - } - if (updateFields.streetAddress) { - body.push({ - name: 'address', - value: updateFields.streetAddress, - }); - } - if (updateFields.streetAddress2) { - body.push({ - name: 'address2', - value: updateFields.streetAddress2, - }); - } - if (updateFields.targetAccount) { - body.push({ - name: 'hs_target_account', - value: updateFields.targetAccount, - }); - } - if (updateFields.timezone) { - body.push({ - name: 'timezone', - value: updateFields.timezone, - }); - } - if (updateFields.totalMoneyRaised) { - body.push({ - name: 'total_money_raised', - value: updateFields.totalMoneyRaised, - }); - } - if (updateFields.twitterBio) { - body.push({ - name: 'twitterbio', - value: updateFields.twitterBio, - }); - } - if (updateFields.twitterFollowers) { - body.push({ - name: 'twitterfollowers', - value: updateFields.twitterFollowers, - }); - } - if (updateFields.twitterHandle) { - body.push({ - name: 'twitterhandle', - value: updateFields.twitterHandle, - }); - } - if (updateFields.type) { - body.push({ - name: 'type', - value: updateFields.type, - }); - } - if (updateFields.websiteUrl) { - body.push({ - name: 'website', - value: updateFields.websiteUrl, - }); - } - if (updateFields.webTechnologies) { - body.push({ - name: 'web_technologies', - value: updateFields.webTechnologies, - }); - } - if (updateFields.yearFounded) { - body.push({ - name: 'founded_year', - value: updateFields.yearFounded, - }); - } - if (updateFields.customPropertiesUi) { - const customProperties = (updateFields.customPropertiesUi as IDataObject) - .customPropertiesValues as IDataObject[]; - - if (customProperties) { - for (const customProperty of customProperties) { - body.push({ - name: customProperty.property, - value: customProperty.value, - }); - } - } - } - const endpoint = `/companies/v2/companies/${companyId}`; - responseData = await hubspotApiRequest.call(this, 'PUT', endpoint, { - properties: body, - }); - } - //https://developers.hubspot.com/docs/methods/companies/get_company - if (operation === 'get') { - const companyId = this.getNodeParameter('companyId', i) as string; - const additionalFields = this.getNodeParameter('additionalFields', i); - if (additionalFields.includeMergeAudits) { - qs.includeMergeAudits = additionalFields.includeMergeAudits as boolean; - } - const endpoint = `/companies/v2/companies/${companyId}`; - responseData = await hubspotApiRequest.call(this, 'GET', endpoint, {}, qs); - } - //https://developers.hubspot.com/docs/methods/companies/get-all-companies - if (operation === 'getAll') { - const options = this.getNodeParameter('options', i); - const returnAll = this.getNodeParameter('returnAll', 0); - if (options.includeMergeAudits) { - qs.includeMergeAudits = options.includeMergeAudits as boolean; - } - if (options.properties) { - qs.properties = options.properties as string[]; - } - if (options.propertiesWithHistory) { - qs.propertiesWithHistory = (options.propertiesWithHistory as string).split(','); - } - const endpoint = '/companies/v2/companies/paged'; - if (returnAll) { - responseData = await hubspotApiRequestAllItems.call( - this, - 'companies', - 'GET', - endpoint, - {}, - qs, - ); - } else { - qs.limit = this.getNodeParameter('limit', 0); - responseData = await hubspotApiRequest.call(this, 'GET', endpoint, {}, qs); - responseData = responseData.companies; - } - } - //https://developers.hubspot.com/docs/methods/companies/get_companies_modified - if (operation === 'getRecentlyCreated' || operation === 'getRecentlyModified') { - let endpoint; - const returnAll = this.getNodeParameter('returnAll', 0); - if (operation === 'getRecentlyCreated') { - endpoint = '/companies/v2/companies/recent/created'; - } else { - const filters = this.getNodeParameter('filters', i); - if (filters.since) { - qs.since = new Date(filters.since as string).getTime(); - } - endpoint = '/companies/v2/companies/recent/modified'; - } - if (returnAll) { - responseData = await hubspotApiRequestAllItems.call( - this, - 'results', - 'GET', - endpoint, - {}, - qs, - ); - } else { - qs.count = this.getNodeParameter('limit', 0); - responseData = await hubspotApiRequest.call(this, 'GET', endpoint, {}, qs); - responseData = responseData.results; - } - } - //https://developers.hubspot.com/docs/methods/companies/search_companies_by_domain - if (operation === 'searchByDomain') { - const domain = this.getNodeParameter('domain', i) as string; - const options = this.getNodeParameter('options', i); - const returnAll = this.getNodeParameter('returnAll', 0); - const body: IDataObject = { - requestOptions: {}, - }; - if (options.properties) { - body.requestOptions = { properties: options.properties as string[] }; - } - const endpoint = `/companies/v2/domains/${domain}/companies`; - if (returnAll) { - responseData = await hubspotApiRequestAllItems.call( - this, - 'results', - 'POST', - endpoint, - body, - ); - } else { - body.limit = this.getNodeParameter('limit', 0); - responseData = await hubspotApiRequest.call(this, 'POST', endpoint, body); - responseData = responseData.results; - } - } - //https://developers.hubspot.com/docs/methods/companies/delete_company - if (operation === 'delete') { - const companyId = this.getNodeParameter('companyId', i) as string; - const endpoint = `/companies/v2/companies/${companyId}`; - responseData = await hubspotApiRequest.call(this, 'DELETE', endpoint); - } - } - //https://developers.hubspot.com/docs/methods/deals/deals_overview - if (resource === 'deal') { - if (operation === 'create') { - const body: IDeal = {}; - body.properties = []; - const association: IAssociation = {}; - const additionalFields = this.getNodeParameter('additionalFields', i); - const stage = this.getNodeParameter('stage', i) as string; - if (stage) { - body.properties.push({ - name: 'dealstage', - value: stage, - }); - } - if (additionalFields.associatedCompany) { - association.associatedCompanyIds = additionalFields.associatedCompany as number[]; - } - if (additionalFields.associatedVids) { - association.associatedVids = additionalFields.associatedVids as number[]; - } - if (additionalFields.dealName) { - body.properties.push({ - name: 'dealname', - value: additionalFields.dealName as string, - }); - } - if (additionalFields.closeDate) { - body.properties.push({ - name: 'closedate', - value: new Date(additionalFields.closeDate as string).getTime(), - }); - } - if (additionalFields.amount) { - body.properties.push({ - name: 'amount', - value: additionalFields.amount as string, - }); - } - if (additionalFields.dealType) { - body.properties.push({ - name: 'dealtype', - value: additionalFields.dealType as string, - }); - } - if (additionalFields.pipeline) { - body.properties.push({ - name: 'pipeline', - value: additionalFields.pipeline as string, - }); - } - if (additionalFields.description) { - body.properties.push({ - name: 'description', - value: additionalFields.description as string, - }); - } - if (additionalFields.customPropertiesUi) { - const customProperties = (additionalFields.customPropertiesUi as IDataObject) - .customPropertiesValues as IDataObject[]; - if (customProperties) { - for (const customProperty of customProperties) { - body.properties.push({ - name: customProperty.property, - value: customProperty.value, - }); - } - } - } - body.associations = association; - const endpoint = '/deals/v1/deal'; - responseData = await hubspotApiRequest.call(this, 'POST', endpoint, body); - } - if (operation === 'update') { - const body: IDeal = {}; - body.properties = []; - const updateFields = this.getNodeParameter('updateFields', i); - const dealId = this.getNodeParameter('dealId', i) as string; - if (updateFields.stage) { - body.properties.push({ - name: 'dealstage', - value: updateFields.stage as string, - }); - } - if (updateFields.dealName) { - body.properties.push({ - name: 'dealname', - value: updateFields.dealName as string, - }); - } - if (updateFields.closeDate) { - body.properties.push({ - name: 'closedate', - value: new Date(updateFields.closeDate as string).getTime(), - }); - } - if (updateFields.amount) { - body.properties.push({ - name: 'amount', - value: updateFields.amount as string, - }); - } - if (updateFields.dealType) { - body.properties.push({ - name: 'dealtype', - value: updateFields.dealType as string, - }); - } - if (updateFields.pipeline) { - body.properties.push({ - name: 'pipeline', - value: updateFields.pipeline as string, - }); - } - if (updateFields.description) { - body.properties.push({ - name: 'description', - value: updateFields.description as string, - }); - } - if (updateFields.customPropertiesUi) { - const customProperties = (updateFields.customPropertiesUi as IDataObject) - .customPropertiesValues as IDataObject[]; - if (customProperties) { - for (const customProperty of customProperties) { - body.properties.push({ - name: customProperty.property, - value: customProperty.value, - }); - } - } - } - const endpoint = `/deals/v1/deal/${dealId}`; - responseData = await hubspotApiRequest.call(this, 'PUT', endpoint, body); - } - if (operation === 'get') { - const dealId = this.getNodeParameter('dealId', i) as string; - const additionalFields = this.getNodeParameter('additionalFields', i); - if (additionalFields.includePropertyVersions) { - qs.includePropertyVersions = additionalFields.includePropertyVersions as boolean; - } - const endpoint = `/deals/v1/deal/${dealId}`; - responseData = await hubspotApiRequest.call(this, 'GET', endpoint); - } - if (operation === 'getAll') { - const filters = this.getNodeParameter('filters', i); - const returnAll = this.getNodeParameter('returnAll', 0); - if (filters.includeAssociations) { - qs.includeAssociations = filters.includeAssociations as boolean; - } - if (filters.properties) { - const properties = filters.properties as string | string[]; - qs.properties = !Array.isArray(filters.properties) - ? (properties as string).split(',') - : properties; - } - if (filters.propertiesWithHistory) { - const propertiesWithHistory = filters.propertiesWithHistory as string | string[]; - qs.propertiesWithHistory = !Array.isArray(filters.propertiesWithHistory) - ? (propertiesWithHistory as string).split(',') - : propertiesWithHistory; - } - const endpoint = '/deals/v1/deal/paged'; - if (returnAll) { - responseData = await hubspotApiRequestAllItems.call( - this, - 'deals', - 'GET', - endpoint, - {}, - qs, - ); - } else { - qs.limit = this.getNodeParameter('limit', 0); - responseData = await hubspotApiRequest.call(this, 'GET', endpoint, {}, qs); - responseData = responseData.deals; - } - } - if (operation === 'getRecentlyCreated' || operation === 'getRecentlyModified') { - let endpoint; - const filters = this.getNodeParameter('filters', i); - const returnAll = this.getNodeParameter('returnAll', 0); - if (filters.since) { - qs.since = new Date(filters.since as string).getTime(); - } - if (filters.includePropertyVersions) { - qs.includePropertyVersions = filters.includePropertyVersions as boolean; - } - if (operation === 'getRecentlyCreated') { - endpoint = '/deals/v1/deal/recent/created'; - } else { - endpoint = '/deals/v1/deal/recent/modified'; - } - if (returnAll) { - responseData = await hubspotApiRequestAllItems.call( - this, - 'results', - 'GET', - endpoint, - {}, - qs, - ); - } else { - qs.count = this.getNodeParameter('limit', 0); - responseData = await hubspotApiRequest.call(this, 'GET', endpoint, {}, qs); - responseData = responseData.results; - } - } - if (operation === 'delete') { - const dealId = this.getNodeParameter('dealId', i) as string; - const endpoint = `/deals/v1/deal/${dealId}`; - responseData = await hubspotApiRequest.call(this, 'DELETE', endpoint); - } - //https://developers.hubspot.com/docs/api/crm/search - if (operation === 'search') { - const additionalFields = this.getNodeParameter('additionalFields', i); - const returnAll = this.getNodeParameter('returnAll', 0); - const filtersGroupsUi = this.getNodeParameter('filterGroupsUi', i) as IDataObject; - const sortBy = additionalFields.sortBy || 'createdate'; - const direction = additionalFields.direction || 'DESCENDING'; - - const body: IDataObject = { - sorts: [ - { - propertyName: sortBy, - direction, - }, - ], - }; - - if (filtersGroupsUi) { - const filterGroupValues = filtersGroupsUi.filterGroupsValues as IDataObject[]; - if (filterGroupValues) { - body.filterGroups = []; - for (const filterGroupValue of filterGroupValues) { - if (filterGroupValue.filtersUi) { - const filterValues = (filterGroupValue.filtersUi as IDataObject) - .filterValues as IDataObject[]; - if (filterValues) { - //@ts-ignore - body.filterGroups.push({ filters: filterValues }); - } - } - } - } - } - - Object.assign(body, additionalFields); - - const endpoint = '/crm/v3/objects/deals/search'; - - if (returnAll) { - responseData = await hubspotApiRequestAllItems.call( - this, - 'results', - 'POST', - endpoint, - body, - qs, - ); - } else { - body.limit = this.getNodeParameter('limit', 0); - responseData = await hubspotApiRequest.call(this, 'POST', endpoint, body, qs); - responseData = responseData.results; - } - } - } - if (resource === 'engagement') { - //https://legacydocs.hubspot.com/docs/methods/engagements/create_engagement - if (operation === 'create') { - const type = this.getNodeParameter('type', i) as string; - const metadata = this.getNodeParameter('metadata', i) as IDataObject; - const associations = this.getNodeParameter( - 'additionalFields.associations', - i, - {}, - ) as IDataObject; - - if (!Object.keys(metadata).length) { - throw new NodeOperationError( - this.getNode(), - 'At least one metadata field needs to set', - { itemIndex: i }, - ); - } - - const body: { - engagement: { type: string }; - metadata: IDataObject; - associations: IDataObject; - } = { - engagement: { - type: type.toUpperCase(), - }, - metadata: {}, - associations: {}, - }; - - if (type === 'email') { - body.metadata = getEmailMetadata(metadata); - } - - if (type === 'task') { - body.metadata = getTaskMetadata(metadata); - } - - if (type === 'meeting') { - body.metadata = getMeetingMetadata(metadata); - } - - if (type === 'call') { - body.metadata = getCallMetadata(metadata); - } - - //@ts-ignore - body.associations = getAssociations(associations); - - const endpoint = '/engagements/v1/engagements'; - responseData = await hubspotApiRequest.call(this, 'POST', endpoint, body); - } - //https://legacydocs.hubspot.com/docs/methods/engagements/get_engagement - if (operation === 'delete') { - const engagementId = this.getNodeParameter('engagementId', i) as string; - const endpoint = `/engagements/v1/engagements/${engagementId}`; - responseData = await hubspotApiRequest.call(this, 'DELETE', endpoint, {}, qs); - responseData = { success: true }; - } - //https://legacydocs.hubspot.com/docs/methods/engagements/get_engagement - if (operation === 'get') { - const engagementId = this.getNodeParameter('engagementId', i) as string; - const endpoint = `/engagements/v1/engagements/${engagementId}`; - responseData = await hubspotApiRequest.call(this, 'GET', endpoint, {}, qs); - } - //https://legacydocs.hubspot.com/docs/methods/engagements/get-all-engagements - if (operation === 'getAll') { - const returnAll = this.getNodeParameter('returnAll', 0); - const endpoint = '/engagements/v1/engagements/paged'; - if (returnAll) { - responseData = await hubspotApiRequestAllItems.call( - this, - 'results', - 'GET', - endpoint, - {}, - qs, - ); - } else { - qs.limit = this.getNodeParameter('limit', 0); - responseData = await hubspotApiRequest.call(this, 'GET', endpoint, {}, qs); - responseData = responseData.results; - } - } - } - //https://developers.hubspot.com/docs/methods/forms/forms_overview - if (resource === 'form') { - //https://developers.hubspot.com/docs/methods/forms/v2/get_fields - if (operation === 'getFields') { - const formId = this.getNodeParameter('formId', i) as string; - responseData = await hubspotApiRequest.call( - this, - 'GET', - `/forms/v2/fields/${formId}`, - ); - } - //https://developers.hubspot.com/docs/methods/forms/submit_form_v3 - if (operation === 'submit') { - const formId = this.getNodeParameter('formId', i) as string; - const additionalFields = this.getNodeParameter('additionalFields', i); - const context = (this.getNodeParameter('contextUi', i) as IDataObject) - .contextValue as IDataObject; - const legalConsent = (this.getNodeParameter('lengalConsentUi', i) as IDataObject) - .lengalConsentValues as IDataObject; - const legitimateInteres = (this.getNodeParameter('lengalConsentUi', i) as IDataObject) - .legitimateInterestValues as IDataObject; - const { portalId } = await hubspotApiRequest.call( - this, - 'GET', - `/forms/v2/forms/${formId}`, - ); - const body: IForm = { - formId, - portalId, - legalConsentOptions: {}, - fields: [], - }; - if (additionalFields.submittedAt) { - body.submittedAt = new Date(additionalFields.submittedAt as string).getTime(); - } - if (additionalFields.skipValidation) { - body.skipValidation = additionalFields.skipValidation as boolean; - } - const consent: IDataObject = {}; - if (legalConsent) { - if (legalConsent.consentToProcess) { - consent.consentToProcess = legalConsent.consentToProcess as boolean; - } - if (legalConsent.text) { - consent.text = legalConsent.text as string; - } - if (legalConsent.communicationsUi) { - consent.communications = (legalConsent.communicationsUi as IDataObject) - .communicationValues as IDataObject; - } - } - body.legalConsentOptions!.consent = consent; - const fields: IDataObject = items[i].json; - for (const key of Object.keys(fields)) { - body.fields?.push({ name: key, value: fields[key] }); - } - if (body.legalConsentOptions!.legitimateInterest) { - Object.assign(body, { - legalConsentOptions: { legitimateInterest: legitimateInteres }, - }); - } - if (context) { - clean(context); - Object.assign(body, { context }); - } - const uri = `https://api.hsforms.com/submissions/v3/integration/submit/${portalId}/${formId}`; - responseData = await hubspotApiRequest.call(this, 'POST', '', body, {}, uri); - } - } - //https://developers.hubspot.com/docs/methods/tickets/tickets-overview - if (resource === 'ticket') { - //https://developers.hubspot.com/docs/methods/tickets/create-ticket - if (operation === 'create') { - const additionalFields = this.getNodeParameter('additionalFields', i); - const pipelineId = this.getNodeParameter('pipelineId', i) as string; - const stageId = this.getNodeParameter('stageId', i) as string; - const ticketName = this.getNodeParameter('ticketName', i) as string; - const body: IDataObject[] = [ - { - // eslint-disable-next-line n8n-nodes-base/node-param-display-name-miscased - name: 'hs_pipeline', - value: pipelineId, - }, - { - // eslint-disable-next-line n8n-nodes-base/node-param-display-name-miscased - name: 'hs_pipeline_stage', - value: stageId, - }, - { - // eslint-disable-next-line n8n-nodes-base/node-param-display-name-miscased - name: 'subject', - value: ticketName, - }, - ]; - if (additionalFields.category) { - body.push({ - name: 'hs_ticket_category', - value: additionalFields.category as string, - }); - } - if (additionalFields.closeDate) { - body.push({ - name: 'closed_date', - value: new Date(additionalFields.closeDate as string).getTime(), - }); - } - if (additionalFields.createDate) { - body.push({ - name: 'createdate', - value: new Date(additionalFields.createDate as string).getTime(), - }); - } - if (additionalFields.description) { - body.push({ - name: 'content', - value: additionalFields.description as string, - }); - } - if (additionalFields.priority) { - body.push({ - name: 'hs_ticket_priority', - value: additionalFields.priority as string, - }); - } - if (additionalFields.resolution) { - body.push({ - name: 'hs_resolution', - value: additionalFields.resolution as string, - }); - } - if (additionalFields.source) { - body.push({ - name: 'source_type', - value: additionalFields.source as string, - }); - } - if (additionalFields.ticketOwnerId) { - body.push({ - name: 'hubspot_owner_id', - value: additionalFields.ticketOwnerId as string, - }); - } - const endpoint = '/crm-objects/v1/objects/tickets'; - responseData = await hubspotApiRequest.call(this, 'POST', endpoint, body); - - if (additionalFields.associatedCompanyIds) { - const companyAssociations: IDataObject[] = []; - for (const companyId of additionalFields.associatedCompanyIds as IDataObject[]) { - companyAssociations.push({ - fromObjectId: responseData.objectId, - toObjectId: companyId, - category: 'HUBSPOT_DEFINED', - definitionId: 26, - }); - } - await hubspotApiRequest.call( - this, - 'PUT', - '/crm-associations/v1/associations/create-batch', - companyAssociations, - ); - } - - if (additionalFields.associatedContactIds) { - const contactAssociations: IDataObject[] = []; - for (const contactId of additionalFields.associatedContactIds as IDataObject[]) { - contactAssociations.push({ - fromObjectId: responseData.objectId, - toObjectId: contactId, - category: 'HUBSPOT_DEFINED', - definitionId: 16, - }); - } - await hubspotApiRequest.call( - this, - 'PUT', - '/crm-associations/v1/associations/create-batch', - contactAssociations, - ); - } - } - //https://developers.hubspot.com/docs/methods/tickets/get_ticket_by_id - if (operation === 'get') { - const ticketId = this.getNodeParameter('ticketId', i) as string; - const additionalFields = this.getNodeParameter('additionalFields', i); - if (additionalFields.properties) { - qs.properties = additionalFields.properties as string[]; - } - if (additionalFields.propertiesWithHistory) { - qs.propertiesWithHistory = (additionalFields.propertiesWithHistory as string).split( - ',', - ); - } - if (additionalFields.includeDeleted) { - qs.includeDeleted = additionalFields.includeDeleted as boolean; - } - const endpoint = `/crm-objects/v1/objects/tickets/${ticketId}`; - responseData = await hubspotApiRequest.call(this, 'GET', endpoint, {}, qs); - } - //https://developers.hubspot.com/docs/methods/tickets/get-all-tickets - if (operation === 'getAll') { - const additionalFields = this.getNodeParameter('additionalFields', i); - const returnAll = this.getNodeParameter('returnAll', 0); - if (additionalFields.properties) { - qs.properties = additionalFields.properties as string[]; - } - if (additionalFields.propertiesWithHistory) { - qs.propertiesWithHistory = (additionalFields.propertiesWithHistory as string).split( - ',', - ); - } - const endpoint = '/crm-objects/v1/objects/tickets/paged'; - if (returnAll) { - responseData = await hubspotApiRequestAllItems.call( - this, - 'objects', - 'GET', - endpoint, - {}, - qs, - ); - } else { - qs.limit = this.getNodeParameter('limit', 0); - responseData = await hubspotApiRequestAllItems.call( - this, - 'objects', - 'GET', - endpoint, - {}, - qs, - ); - responseData = responseData.splice(0, qs.limit); - } - } - //https://developers.hubspot.com/docs/methods/tickets/delete-ticket - if (operation === 'delete') { - const ticketId = this.getNodeParameter('ticketId', i) as string; - const endpoint = `/crm-objects/v1/objects/tickets/${ticketId}`; - await hubspotApiRequest.call(this, 'DELETE', endpoint); - responseData = { success: true }; - } - //https://developers.hubspot.com/docs/methods/tickets/update-ticket - if (operation === 'update') { - const updateFields = this.getNodeParameter('updateFields', i); - const ticketId = this.getNodeParameter('ticketId', i) as string; - const body: IDataObject[] = []; - if (updateFields.pipelineId) { - body.push({ - name: 'hs_pipeline', - value: updateFields.pipelineId as string, - }); - } - if (updateFields.stageId) { - body.push({ - name: 'hs_pipeline_stage', - value: updateFields.stageId as string, - }); - } - if (updateFields.ticketName) { - body.push({ - name: 'subject', - value: updateFields.ticketName as string, - }); - } - if (updateFields.category) { - body.push({ - name: 'hs_ticket_category', - value: updateFields.category as string, - }); - } - if (updateFields.closeDate) { - body.push({ - name: 'closed_date', - value: new Date(updateFields.createDate as string).getTime(), - }); - } - if (updateFields.createDate) { - body.push({ - name: 'createdate', - value: new Date(updateFields.createDate as string).getTime(), - }); - } - if (updateFields.description) { - body.push({ - name: 'content', - value: updateFields.description as string, - }); - } - if (updateFields.priority) { - body.push({ - name: 'hs_ticket_priority', - value: updateFields.priority as string, - }); - } - if (updateFields.resolution) { - body.push({ - name: 'hs_resolution', - value: updateFields.resolution as string, - }); - } - if (updateFields.source) { - body.push({ - name: 'source_type', - value: updateFields.source as string, - }); - } - if (updateFields.ticketOwnerId) { - body.push({ - name: 'hubspot_owner_id', - value: updateFields.ticketOwnerId as string, - }); - } - const endpoint = `/crm-objects/v1/objects/tickets/${ticketId}`; - responseData = await hubspotApiRequest.call(this, 'PUT', endpoint, body); - - if (updateFields.associatedCompanyIds) { - const companyAssociations: IDataObject[] = []; - for (const companyId of updateFields.associatedCompanyIds as IDataObject[]) { - companyAssociations.push({ - fromObjectId: responseData.objectId, - toObjectId: companyId, - category: 'HUBSPOT_DEFINED', - definitionId: 26, - }); - } - await hubspotApiRequest.call( - this, - 'PUT', - '/crm-associations/v1/associations/create-batch', - companyAssociations, - ); - } - - if (updateFields.associatedContactIds) { - const contactAssociations: IDataObject[] = []; - for (const contactId of updateFields.associatedContactIds as IDataObject[]) { - contactAssociations.push({ - fromObjectId: responseData.objectId, - toObjectId: contactId, - category: 'HUBSPOT_DEFINED', - definitionId: 16, - }); - } - await hubspotApiRequest.call( - this, - 'PUT', - '/crm-associations/v1/associations/create-batch', - contactAssociations, - ); - } - } - } - const executionData = this.helpers.constructExecutionMetaData( - this.helpers.returnJsonArray(responseData as IDataObject[]), - { itemData: { item: i } }, - ); - returnData.push(...executionData); - } catch (error) { - if (this.continueOnFail()) { - returnData.push({ json: { error: (error as JsonObject).message } }); - continue; - } - throw error; - } - } - } - return this.prepareOutputData(returnData); +import type { INodeTypeBaseDescription, IVersionedNodeType } from 'n8n-workflow'; +import { VersionedNodeType } from 'n8n-workflow'; + +import { HubspotV1 } from './V1/HubspotV1.node'; + +import { HubspotV2 } from './V2/HubspotV2.node'; + +export class Hubspot extends VersionedNodeType { + constructor() { + const baseDescription: INodeTypeBaseDescription = { + displayName: 'HubSpot', + name: 'hubspot', + icon: 'file:hubspot.svg', + group: ['output'], + subtitle: '={{$parameter["operation"] + ": " + $parameter["resource"]}}', + description: 'Consume HubSpot API', + defaultVersion: 2, + }; + + const nodeVersions: IVersionedNodeType['nodeVersions'] = { + 1: new HubspotV1(baseDescription), + 2: new HubspotV2(baseDescription), + }; + + super(nodeVersions, baseDescription); } } diff --git a/packages/nodes-base/nodes/Hubspot/HubspotTrigger.node.ts b/packages/nodes-base/nodes/Hubspot/HubspotTrigger.node.ts index 31fee0fd3c..3a0ec29627 100644 --- a/packages/nodes-base/nodes/Hubspot/HubspotTrigger.node.ts +++ b/packages/nodes-base/nodes/Hubspot/HubspotTrigger.node.ts @@ -10,7 +10,7 @@ import type { } from 'n8n-workflow'; import { NodeOperationError } from 'n8n-workflow'; -import { hubspotApiRequest, propertyEvents } from './GenericFunctions'; +import { hubspotApiRequest, propertyEvents } from './V1/GenericFunctions'; import { createHash } from 'crypto'; diff --git a/packages/nodes-base/nodes/Hubspot/CompanyDescription.ts b/packages/nodes-base/nodes/Hubspot/V1/CompanyDescription.ts similarity index 99% rename from packages/nodes-base/nodes/Hubspot/CompanyDescription.ts rename to packages/nodes-base/nodes/Hubspot/V1/CompanyDescription.ts index 3c1c59d805..501f6d0f99 100644 --- a/packages/nodes-base/nodes/Hubspot/CompanyDescription.ts +++ b/packages/nodes-base/nodes/Hubspot/V1/CompanyDescription.ts @@ -995,6 +995,7 @@ export const companyFields: INodeProperties[] = [ }, }, required: true, + description: "The company's website domain to search for, like n8n.io", default: '', }, { diff --git a/packages/nodes-base/nodes/Hubspot/ContactDescription.ts b/packages/nodes-base/nodes/Hubspot/V1/ContactDescription.ts similarity index 100% rename from packages/nodes-base/nodes/Hubspot/ContactDescription.ts rename to packages/nodes-base/nodes/Hubspot/V1/ContactDescription.ts diff --git a/packages/nodes-base/nodes/Hubspot/ContactListDescription.ts b/packages/nodes-base/nodes/Hubspot/V1/ContactListDescription.ts similarity index 100% rename from packages/nodes-base/nodes/Hubspot/ContactListDescription.ts rename to packages/nodes-base/nodes/Hubspot/V1/ContactListDescription.ts diff --git a/packages/nodes-base/nodes/Hubspot/DealDescription.ts b/packages/nodes-base/nodes/Hubspot/V1/DealDescription.ts similarity index 100% rename from packages/nodes-base/nodes/Hubspot/DealDescription.ts rename to packages/nodes-base/nodes/Hubspot/V1/DealDescription.ts diff --git a/packages/nodes-base/nodes/Hubspot/DealInterface.ts b/packages/nodes-base/nodes/Hubspot/V1/DealInterface.ts similarity index 100% rename from packages/nodes-base/nodes/Hubspot/DealInterface.ts rename to packages/nodes-base/nodes/Hubspot/V1/DealInterface.ts diff --git a/packages/nodes-base/nodes/Hubspot/EngagementDescription.ts b/packages/nodes-base/nodes/Hubspot/V1/EngagementDescription.ts similarity index 100% rename from packages/nodes-base/nodes/Hubspot/EngagementDescription.ts rename to packages/nodes-base/nodes/Hubspot/V1/EngagementDescription.ts diff --git a/packages/nodes-base/nodes/Hubspot/FormDescription.ts b/packages/nodes-base/nodes/Hubspot/V1/FormDescription.ts similarity index 100% rename from packages/nodes-base/nodes/Hubspot/FormDescription.ts rename to packages/nodes-base/nodes/Hubspot/V1/FormDescription.ts diff --git a/packages/nodes-base/nodes/Hubspot/FormInterface.ts b/packages/nodes-base/nodes/Hubspot/V1/FormInterface.ts similarity index 100% rename from packages/nodes-base/nodes/Hubspot/FormInterface.ts rename to packages/nodes-base/nodes/Hubspot/V1/FormInterface.ts diff --git a/packages/nodes-base/nodes/Hubspot/V1/GenericFunctions.ts b/packages/nodes-base/nodes/Hubspot/V1/GenericFunctions.ts new file mode 100644 index 0000000000..9c279d2c92 --- /dev/null +++ b/packages/nodes-base/nodes/Hubspot/V1/GenericFunctions.ts @@ -0,0 +1,2023 @@ +import type { OptionsWithUri } from 'request'; + +import type { + IExecuteFunctions, + IExecuteSingleFunctions, + IHookFunctions, + ILoadOptionsFunctions, +} from 'n8n-core'; + +import type { + ICredentialDataDecryptedObject, + ICredentialTestFunctions, + IDataObject, + JsonObject, +} from 'n8n-workflow'; +import { NodeApiError } from 'n8n-workflow'; + +import moment from 'moment'; + +export async function hubspotApiRequest( + this: IHookFunctions | IExecuteFunctions | IExecuteSingleFunctions | ILoadOptionsFunctions, + method: string, + endpoint: string, + // tslint:disable-next-line:no-any + body: any = {}, + query: IDataObject = {}, + uri?: string, + // tslint:disable-next-line:no-any +): Promise { + let authenticationMethod = this.getNodeParameter('authentication', 0); + + if (this.getNode().type.includes('Trigger')) { + authenticationMethod = 'developerApi'; + } + + const options: OptionsWithUri = { + method, + qs: query, + headers: {}, + uri: uri || `https://api.hubapi.com${endpoint}`, + body, + json: true, + useQuerystring: true, + }; + + try { + if (authenticationMethod === 'apiKey') { + const credentials = await this.getCredentials('hubspotApi'); + + options.qs.hapikey = credentials.apiKey as string; + return await this.helpers.request(options); + } else if (authenticationMethod === 'appToken') { + const credentials = await this.getCredentials('hubspotAppToken'); + + options.headers!.Authorization = `Bearer ${credentials.appToken}`; + return await this.helpers.request(options); + } else if (authenticationMethod === 'developerApi') { + if (endpoint.includes('webhooks')) { + const credentials = await this.getCredentials('hubspotDeveloperApi'); + options.qs.hapikey = credentials.apiKey as string; + return await this.helpers.request(options); + } else { + return await this.helpers.requestOAuth2.call(this, 'hubspotDeveloperApi', options, { + tokenType: 'Bearer', + includeCredentialsOnRefreshOnBody: true, + }); + } + } else { + return await this.helpers.requestOAuth2.call(this, 'hubspotOAuth2Api', options, { + tokenType: 'Bearer', + includeCredentialsOnRefreshOnBody: true, + }); + } + } catch (error) { + throw new NodeApiError(this.getNode(), error as JsonObject); + } +} + +/** + * Make an API request to paginated hubspot endpoint + * and return all results + */ +export async function hubspotApiRequestAllItems( + this: IHookFunctions | IExecuteFunctions | ILoadOptionsFunctions, + propertyName: string, + method: string, + endpoint: string, + // tslint:disable-next-line:no-any + body: any = {}, + query: IDataObject = {}, + // tslint:disable-next-line:no-any +): Promise { + const returnData: IDataObject[] = []; + + let responseData; + + query.limit = (query.limit as number) || 250; + query.count = 100; + body.limit = body.limit || 100; + + do { + responseData = await hubspotApiRequest.call(this, method, endpoint, body, query); + query.offset = responseData.offset; + query.vidOffset = responseData['vid-offset']; + //Used by Search endpoints + if (responseData.paging) { + body.after = responseData.paging.next.after; + } + returnData.push.apply(returnData, responseData.propertyName as IDataObject[]); + //ticket:getAll endpoint does not support setting a limit, so return once the limit is reached + if (query.limit && query.limit <= returnData.length && endpoint.includes('/tickets/paged')) { + return returnData; + } + } while (responseData.hasMore || responseData['has-more'] || responseData.paging); + return returnData; +} + +// tslint:disable-next-line:no-any +export function validateJSON(json: string | undefined): any { + let result; + try { + result = JSON.parse(json!); + } catch (exception) { + result = ''; + } + return result; +} + +// tslint:disable-next-line: no-any +export function clean(obj: any) { + for (const propName in obj) { + if (obj[propName] === null || obj[propName] === undefined || obj[propName] === '') { + delete obj[propName]; + } + } + return obj; +} + +export const propertyEvents = [ + 'contact.propertyChange', + 'company.propertyChange', + 'deal.propertyChange', +]; + +export const contactFields = [ + { + id: 'company_size', + label: 'testingricardo', + }, + { + id: 'date', + label: 'Date', + }, + { + id: 'date_of_birth', + label: 'Date of birth', + }, + { + id: 'days_to_close', + label: 'Days To Close', + }, + { + id: 'degree', + label: 'Degree', + }, + { + id: 'field_of_study', + label: 'Field of study', + }, + { + id: 'first_conversion_date', + label: 'First Conversion Date', + }, + { + id: 'first_conversion_event_name', + label: 'First Conversion', + }, + { + id: 'first_deal_created_date', + label: 'First Deal Created Date', + }, + { + id: 'gender', + label: 'Gender', + }, + { + id: 'graduation_date', + label: 'Graduation date', + }, + { + id: 'hs_additional_emails', + label: 'Additional email addresses', + }, + { + id: 'hs_all_contact_vids', + label: 'All vids for a contact', + }, + { + id: 'hs_analytics_first_touch_converting_campaign', + label: 'First Touch Converting Campaign', + }, + { + id: 'hs_analytics_last_touch_converting_campaign', + label: 'Last Touch Converting Campaign', + }, + { + id: 'hs_avatar_filemanager_key', + label: 'Avatar FileManager key', + }, + { + id: 'hs_buying_role', + label: 'Buying Role', + }, + { + id: 'hs_calculated_form_submissions', + label: 'All form submissions for a contact', + }, + { + id: 'hs_calculated_merged_vids', + label: 'Merged vids with timestamps of a contact', + }, + { + id: 'hs_calculated_mobile_number', + label: 'Calculated Mobile Number in International Format', + }, + { + id: 'hs_calculated_phone_number', + label: 'Calculated Phone Number in International Format', + }, + { + id: 'hs_calculated_phone_number_area_code', + label: 'Calculated Phone Number Area Code', + }, + { + id: 'hs_calculated_phone_number_country_code', + label: 'Calculated Phone Number Country Code', + }, + { + id: 'hs_calculated_phone_number_region_code', + label: 'Calculated Phone Number Region', + }, + { + id: 'hs_content_membership_email_confirmed', + label: 'Email Confirmed', + }, + { + id: 'hs_content_membership_notes', + label: 'Membership Notes', + }, + { + id: 'hs_content_membership_registered_at', + label: 'Registered At', + }, + { + id: 'hs_content_membership_registration_domain_sent_to', + label: 'Domain to which registration email was sent', + }, + { + id: 'hs_content_membership_registration_email_sent_at', + label: 'Time registration email was sent', + }, + { + id: 'hs_content_membership_status', + label: 'Status', + }, + { + id: 'hs_conversations_visitor_email', + label: 'Conversations visitor email', + }, + { + id: 'hs_count_is_unworked', + label: 'Count of unengaged contacts', + }, + { + id: 'hs_count_is_worked', + label: 'Count of engaged contacts', + }, + { + id: 'hs_created_by_conversations', + label: 'Created By Conversations', + }, + { + id: 'hs_created_by_user_id', + label: 'Created by user ID', + }, + { + id: 'hs_createdate', + label: 'Object create date/time', + }, + { + id: 'hs_document_last_revisited', + label: 'Recent Document Revisit Date', + }, + { + id: 'hs_email_bad_address', + label: 'Invalid email address', + }, + { + id: 'hs_email_customer_quarantined_reason', + label: 'Email address quarantine reason', + }, + { + id: 'hs_email_domain', + label: 'Email Domain', + }, + { + id: 'hs_email_hard_bounce_reason', + label: 'Email hard bounce reason', + }, + { + id: 'hs_email_hard_bounce_reason_enum', + label: 'Email hard bounce reason', + }, + { + id: 'hs_email_quarantined', + label: 'Email Address Quarantined', + }, + { + id: 'hs_email_quarantined_reason', + label: 'Email address internal quarantine reason', + }, + { + id: 'hs_email_recipient_fatigue_recovery_time', + label: 'Email Address Recipient Fatigue Next Available Sending Time', + }, + { + id: 'hs_email_sends_since_last_engagement', + label: 'Sends Since Last Engagement', + }, + { + id: 'hs_emailconfirmationstatus', + label: 'Marketing email confirmation status', + }, + { + id: 'hs_facebook_ad_clicked', + label: 'Clicked Facebook ad', + }, + { + id: 'hs_facebook_click_id', + label: 'Facebook click id', + }, + { + id: 'hs_facebookid', + label: 'Facebook ID', + }, + { + id: 'hs_feedback_last_nps_follow_up', + label: 'Last NPS survey comment', + }, + { + id: 'hs_feedback_last_nps_rating', + label: 'Last NPS survey rating', + }, + { + id: 'hs_feedback_last_survey_date', + label: 'Last NPS survey date', + }, + { + id: 'hs_feedback_show_nps_web_survey', + label: 'Should be shown an NPS web survey', + }, + { + id: 'hs_first_engagement_object_id', + label: 'ID of first engagement', + }, + { + id: 'hs_google_click_id', + label: 'Google ad click id', + }, + { + id: 'hs_googleplusid', + label: 'googleplus ID', + }, + { + id: 'hs_ip_timezone', + label: 'IP Timezone', + }, + { + id: 'hs_is_contact', + label: 'Is a contact', + }, + { + id: 'hs_is_unworked', + label: 'Contact unworked', + }, + { + id: 'hs_last_sales_activity_date', + label: 'last sales activity date old', + }, + { + id: 'hs_last_sales_activity_timestamp', + label: 'Last Engagement Date', + }, + { + id: 'hs_lastmodifieddate', + label: 'Object last modified date/time', + }, + { + id: 'hs_lead_status', + label: 'Lead Status', + }, + { + id: 'hs_legal_basis', + label: "Legal basis for processing contact's data", + }, + { + id: 'hs_linkedinid', + label: 'Linkedin ID', + }, + { + id: 'hs_marketable_reason_id', + label: 'Marketing contact status source name', + }, + { + id: 'hs_marketable_reason_type', + label: 'Marketing contact status source type', + }, + { + id: 'hs_marketable_status', + label: 'Marketing contact status', + }, + { + id: 'hs_marketable_until_renewal', + label: 'Marketing contact until next update', + }, + { + id: 'hs_merged_object_ids', + label: 'Merged object IDs', + }, + { + id: 'hs_object_id', + label: 'Contact ID', + }, + { + id: 'hs_predictivecontactscore_v2', + label: 'Likelihood to close', + }, + { + id: 'hs_predictivescoringtier', + label: 'Contact priority', + }, + { + id: 'hs_sa_first_engagement_date', + label: 'Date of first engagement', + }, + { + id: 'hs_sa_first_engagement_descr', + label: 'Description of first engagement', + }, + { + id: 'hs_sa_first_engagement_object_type', + label: 'Type of first engagement', + }, + { + id: 'hs_sales_email_last_clicked', + label: 'Recent Sales Email Clicked Date', + }, + { + id: 'hs_sales_email_last_opened', + label: 'Recent Sales Email Opened Date', + }, + { + id: 'hs_searchable_calculated_international_mobile_number', + label: 'Calculated Mobile Number with country code', + }, + { + id: 'hs_searchable_calculated_international_phone_number', + label: 'Calculated Phone Number with country code', + }, + { + id: 'hs_searchable_calculated_mobile_number', + label: 'Calculated Mobile Number without country code', + }, + { + id: 'hs_searchable_calculated_phone_number', + label: 'Calculated Phone Number without country code', + }, + { + id: 'hs_sequences_is_enrolled', + label: 'Currently in Sequence', + }, + { + id: 'hs_testpurge', + label: 'testpurge', + }, + { + id: 'hs_testrollback', + label: 'testrollback', + }, + { + id: 'hs_time_between_contact_creation_and_deal_close', + label: 'Time between contact creation and deal close', + }, + { + id: 'hs_time_between_contact_creation_and_deal_creation', + label: 'Time between contact creation and deal creation', + }, + { + id: 'hs_time_to_first_engagement', + label: 'Lead response time', + }, + { + id: 'hs_time_to_move_from_lead_to_customer', + label: 'Time to move from lead to customer', + }, + { + id: 'hs_time_to_move_from_marketingqualifiedlead_to_customer', + label: 'Time to move from marketing qualified lead to customer', + }, + { + id: 'hs_time_to_move_from_opportunity_to_customer', + label: 'Time to move from opportunity to customer', + }, + { + id: 'hs_time_to_move_from_salesqualifiedlead_to_customer', + label: 'Time to move from sales qualified lead to customer', + }, + { + id: 'hs_time_to_move_from_subscriber_to_customer', + label: 'Time to move from subscriber to customer', + }, + { + id: 'hs_twitterid', + label: 'Twitter ID', + }, + { + id: 'hs_updated_by_user_id', + label: 'Updated by user ID', + }, + { + id: 'hs_user_ids_of_all_owners', + label: 'User IDs of all owners', + }, + { + id: 'hubspot_owner_assigneddate', + label: 'Owner Assigned Date', + }, + { + id: 'ip_city', + label: 'IP City', + }, + { + id: 'ip_country', + label: 'IP Country', + }, + { + id: 'ip_country_code', + label: 'IP Country Code', + }, + { + id: 'ip_latlon', + label: 'IP Latitude & Longitude', + }, + { + id: 'ip_state', + label: 'IP State/Region', + }, + { + id: 'ip_state_code', + label: 'IP State Code/Region Code', + }, + { + id: 'ip_zipcode', + label: 'IP Zipcode', + }, + { + id: 'job_function', + label: 'Job function', + }, + { + id: 'lastmodifieddate', + label: 'Last Modified Date', + }, + { + id: 'marital_status', + label: 'Marital Status', + }, + { + id: 'military_status', + label: 'Military status', + }, + { + id: 'num_associated_deals', + label: 'Associated Deals', + }, + { + id: 'num_conversion_events', + label: 'Number of Form Submissions', + }, + { + id: 'num_unique_conversion_events', + label: 'Number of Unique Forms Submitted', + }, + { + id: 'recent_conversion_date', + label: 'Recent Conversion Date', + }, + { + id: 'recent_conversion_event_name', + label: 'Recent Conversion', + }, + { + id: 'recent_deal_amount', + label: 'Recent Deal Amount', + }, + { + id: 'recent_deal_close_date', + label: 'Recent Deal Close Date', + }, + { + id: 'relationship_status', + label: 'Relationship Status', + }, + { + id: 'school', + label: 'School', + }, + { + id: 'seniority', + label: 'Seniority', + }, + { + id: 'start_date', + label: 'Start date', + }, + { + id: 'testing', + label: 'testing', + }, + { + id: 'total_revenue', + label: 'Total Revenue', + }, + { + id: 'work_email', + label: 'Work email', + }, + { + id: 'firstname', + label: 'First Name', + }, + { + id: 'hs_analytics_first_url', + label: 'First Page Seen', + }, + { + id: 'hs_email_delivered', + label: 'Marketing emails delivered', + }, + { + id: 'hs_email_optout_6871816', + label: 'Opted out of email: Marketing Information', + }, + { + id: 'hs_email_optout_8363428', + label: 'Opted out of email: One to One', + }, + { + id: 'twitterhandle', + label: 'Twitter Username', + }, + { + id: 'currentlyinworkflow', + label: 'Currently in workflow', + }, + { + id: 'followercount', + label: 'Follower Count', + }, + { + id: 'hs_analytics_last_url', + label: 'Last Page Seen', + }, + { + id: 'hs_email_open', + label: 'Marketing emails opened', + }, + { + id: 'lastname', + label: 'Last Name', + }, + { + id: 'hs_analytics_num_page_views', + label: 'Number of Pageviews', + }, + { + id: 'hs_email_click', + label: 'Marketing emails clicked', + }, + { + id: 'salutation', + label: 'Salutation', + }, + { + id: 'twitterprofilephoto', + label: 'Twitter Profile Photo', + }, + { + id: 'email', + label: 'Email', + }, + { + id: 'hs_analytics_num_visits', + label: 'Number of Sessions', + }, + { + id: 'hs_email_bounce', + label: 'Marketing emails bounced', + }, + { + id: 'hs_persona', + label: 'Persona', + }, + { + id: 'hs_social_last_engagement', + label: 'Most Recent Social Click', + }, + { + id: 'hs_analytics_num_event_completions', + label: 'Number of event completions', + }, + { + id: 'hs_email_optout', + label: 'Unsubscribed from all email', + }, + { + id: 'hs_social_twitter_clicks', + label: 'Twitter Clicks', + }, + { + id: 'mobilephone', + label: 'Mobile Phone Number', + }, + { + id: 'phone', + label: 'Phone Number', + }, + { + id: 'fax', + label: 'Fax Number', + }, + { + id: 'hs_analytics_first_timestamp', + label: 'Time First Seen', + }, + { + id: 'hs_email_last_email_name', + label: 'Last marketing email name', + }, + { + id: 'hs_email_last_send_date', + label: 'Last marketing email send date', + }, + { + id: 'hs_social_facebook_clicks', + label: 'Facebook Clicks', + }, + { + id: 'address', + label: 'Street Address', + }, + { + id: 'engagements_last_meeting_booked', + label: 'Date of last meeting booked in meetings tool', + }, + { + id: 'engagements_last_meeting_booked_campaign', + label: 'Campaign of last booking in meetings tool', + }, + { + id: 'engagements_last_meeting_booked_medium', + label: 'Medium of last booking in meetings tool', + }, + { + id: 'engagements_last_meeting_booked_source', + label: 'Source of last booking in meetings tool', + }, + { + id: 'hs_analytics_first_visit_timestamp', + label: 'Time of First Session', + }, + { + id: 'hs_email_last_open_date', + label: 'Last marketing email open date', + }, + { + id: 'hs_latest_meeting_activity', + label: 'Latest meeting activity', + }, + { + id: 'hs_sales_email_last_replied', + label: 'Recent Sales Email Replied Date', + }, + { + id: 'hs_social_linkedin_clicks', + label: 'LinkedIn Clicks', + }, + { + id: 'hubspot_owner_id', + label: 'Contact owner', + }, + { + id: 'notes_last_contacted', + label: 'Last Contacted', + }, + { + id: 'notes_last_updated', + label: 'Last Activity Date', + }, + { + id: 'notes_next_activity_date', + label: 'Next Activity Date', + }, + { + id: 'num_contacted_notes', + label: 'Number of times contacted', + }, + { + id: 'num_notes', + label: 'Number of Sales Activities', + }, + { + id: 'owneremail', + label: 'HubSpot Owner Email (legacy)', + }, + { + id: 'ownername', + label: 'HubSpot Owner Name (legacy)', + }, + { + id: 'surveymonkeyeventlastupdated', + label: 'SurveyMonkey Event Last Updated', + }, + { + id: 'webinareventlastupdated', + label: 'Webinar Event Last Updated', + }, + { + id: 'city', + label: 'City', + }, + { + id: 'hs_analytics_last_timestamp', + label: 'Time Last Seen', + }, + { + id: 'hs_email_last_click_date', + label: 'Last marketing email click date', + }, + { + id: 'hs_social_google_plus_clicks', + label: 'Google Plus Clicks', + }, + { + id: 'hubspot_team_id', + label: 'HubSpot Team', + }, + { + id: 'linkedinbio', + label: 'LinkedIn Bio', + }, + { + id: 'twitterbio', + label: 'Twitter Bio', + }, + { + id: 'hs_all_owner_ids', + label: 'All owner ids', + }, + { + id: 'hs_analytics_last_visit_timestamp', + label: 'Time of Last Session', + }, + { + id: 'hs_email_first_send_date', + label: 'First marketing email send date', + }, + { + id: 'hs_social_num_broadcast_clicks', + label: 'Broadcast Clicks', + }, + { + id: 'state', + label: 'State/Region', + }, + { + id: 'hs_all_team_ids', + label: 'All team ids', + }, + { + id: 'hs_analytics_source', + label: 'Original Source', + }, + { + id: 'hs_email_first_open_date', + label: 'First marketing email open date', + }, + { + id: 'zip', + label: 'Postal Code', + }, + { + id: 'country', + label: 'Country/Region', + }, + { + id: 'hs_all_accessible_team_ids', + label: 'All accessible team ids', + }, + { + id: 'hs_analytics_source_data_1', + label: 'Original Source Drill-Down 1', + }, + { + id: 'hs_email_first_click_date', + label: 'First marketing email click date', + }, + { + id: 'linkedinconnections', + label: 'LinkedIn Connections', + }, + { + id: 'hs_analytics_source_data_2', + label: 'Original Source Drill-Down 2', + }, + { + id: 'hs_email_is_ineligible', + label: 'Is globally ineligible', + }, + { + id: 'hs_language', + label: 'Preferred language', + }, + { + id: 'kloutscoregeneral', + label: 'Klout Score', + }, + { + id: 'hs_analytics_first_referrer', + label: 'First Referring Site', + }, + { + id: 'hs_email_first_reply_date', + label: 'First marketing email reply date', + }, + { + id: 'jobtitle', + label: 'Job Title', + }, + { + id: 'photo', + label: 'Photo', + }, + { + id: 'hs_analytics_last_referrer', + label: 'Last Referring Site', + }, + { + id: 'hs_email_last_reply_date', + label: 'Last marketing email reply date', + }, + { + id: 'message', + label: 'Message', + }, + { + id: 'closedate', + label: 'Close Date', + }, + { + id: 'hs_analytics_average_page_views', + label: 'Average Pageviews', + }, + { + id: 'hs_email_replied', + label: 'Marketing emails replied', + }, + { + id: 'hs_analytics_revenue', + label: 'Event Revenue', + }, + { + id: 'hs_lifecyclestage_lead_date', + label: 'Became a Lead Date', + }, + { + id: 'hs_lifecyclestage_marketingqualifiedlead_date', + label: 'Became a Marketing Qualified Lead Date', + }, + { + id: 'hs_lifecyclestage_opportunity_date', + label: 'Became an Opportunity Date', + }, + { + id: 'lifecyclestage', + label: 'Lifecycle Stage', + }, + { + id: 'hs_lifecyclestage_salesqualifiedlead_date', + label: 'Became a Sales Qualified Lead Date', + }, + { + id: 'createdate', + label: 'Create Date', + }, + { + id: 'hs_lifecyclestage_evangelist_date', + label: 'Became an Evangelist Date', + }, + { + id: 'hs_lifecyclestage_customer_date', + label: 'Became a Customer Date', + }, + { + id: 'hubspotscore', + label: 'HubSpot Score', + }, + { + id: 'company', + label: 'Company Name', + }, + { + id: 'hs_lifecyclestage_subscriber_date', + label: 'Became a Subscriber Date', + }, + { + id: 'hs_lifecyclestage_other_date', + label: 'Became an Other Lifecycle Date', + }, + { + id: 'website', + label: 'Website URL', + }, + { + id: 'numemployees', + label: 'Number of Employees', + }, + { + id: 'annualrevenue', + label: 'Annual Revenue', + }, + { + id: 'industry', + label: 'Industry', + }, + { + id: 'associatedcompanyid', + label: 'Associated Company ID', + }, + { + id: 'associatedcompanylastupdated', + label: 'Associated Company Last Updated', + }, + { + id: 'hs_predictivecontactscorebucket', + label: 'Lead Rating', + }, + { + id: 'hs_predictivecontactscore', + label: 'Predictive Lead Score', + }, +]; + +export const companyFields = [ + { + id: 'about_us', + label: 'About Us', + }, + { + id: 'closedate_timestamp_earliest_value_a2a17e6e', + label: 'closedate_timestamp_earliest_value_a2a17e6e', + }, + { + id: 'facebookfans', + label: 'Facebook Fans', + }, + { + id: 'first_contact_createdate_timestamp_earliest_value_78b50eea', + label: 'first_contact_createdate_timestamp_earliest_value_78b50eea', + }, + { + id: 'first_conversion_date', + label: 'First Conversion Date', + }, + { + id: 'first_conversion_date_timestamp_earliest_value_61f58f2c', + label: 'first_conversion_date_timestamp_earliest_value_61f58f2c', + }, + { + id: 'first_conversion_event_name', + label: 'First Conversion', + }, + { + id: 'first_conversion_event_name_timestamp_earliest_value_68ddae0a', + label: 'first_conversion_event_name_timestamp_earliest_value_68ddae0a', + }, + { + id: 'first_deal_created_date', + label: 'First Deal Created Date', + }, + { + id: 'founded_year', + label: 'Year Founded', + }, + { + id: 'hs_additional_domains', + label: 'Additional Domains', + }, + { + id: 'hs_analytics_first_timestamp', + label: 'Time First Seen', + }, + { + id: 'hs_analytics_first_timestamp_timestamp_earliest_value_11e3a63a', + label: 'hs_analytics_first_timestamp_timestamp_earliest_value_11e3a63a', + }, + { + id: 'hs_analytics_first_touch_converting_campaign', + label: 'First Touch Converting Campaign', + }, + { + id: 'hs_analytics_first_touch_converting_campaign_timestamp_earliest_value_4757fe10', + label: 'hs_analytics_first_touch_converting_campaign_timestamp_earliest_value_4757fe10', + }, + { + id: 'hs_analytics_first_visit_timestamp', + label: 'Time of First Session', + }, + { + id: 'hs_analytics_first_visit_timestamp_timestamp_earliest_value_accc17ae', + label: 'hs_analytics_first_visit_timestamp_timestamp_earliest_value_accc17ae', + }, + { + id: 'hs_analytics_last_timestamp', + label: 'Time Last Seen', + }, + { + id: 'hs_analytics_last_timestamp_timestamp_latest_value_4e16365a', + label: 'hs_analytics_last_timestamp_timestamp_latest_value_4e16365a', + }, + { + id: 'hs_analytics_last_touch_converting_campaign', + label: 'Last Touch Converting Campaign', + }, + { + id: 'hs_analytics_last_touch_converting_campaign_timestamp_latest_value_81a64e30', + label: 'hs_analytics_last_touch_converting_campaign_timestamp_latest_value_81a64e30', + }, + { + id: 'hs_analytics_last_visit_timestamp', + label: 'Time of Last Session', + }, + { + id: 'hs_analytics_last_visit_timestamp_timestamp_latest_value_999a0fce', + label: 'hs_analytics_last_visit_timestamp_timestamp_latest_value_999a0fce', + }, + { + id: 'hs_analytics_num_page_views', + label: 'Number of Pageviews', + }, + { + id: 'hs_analytics_num_page_views_cardinality_sum_e46e85b0', + label: 'hs_analytics_num_page_views_cardinality_sum_e46e85b0', + }, + { + id: 'hs_analytics_num_visits', + label: 'Number of Sessions', + }, + { + id: 'hs_analytics_num_visits_cardinality_sum_53d952a6', + label: 'hs_analytics_num_visits_cardinality_sum_53d952a6', + }, + { + id: 'hs_analytics_source', + label: 'Original Source Type', + }, + { + id: 'hs_analytics_source_data_1', + label: 'Original Source Data 1', + }, + { + id: 'hs_analytics_source_data_1_timestamp_earliest_value_9b2f1fa1', + label: 'hs_analytics_source_data_1_timestamp_earliest_value_9b2f1fa1', + }, + { + id: 'hs_analytics_source_data_2', + label: 'Original Source Data 2', + }, + { + id: 'hs_analytics_source_data_2_timestamp_earliest_value_9b2f9400', + label: 'hs_analytics_source_data_2_timestamp_earliest_value_9b2f9400', + }, + { + id: 'hs_analytics_source_timestamp_earliest_value_25a3a52c', + label: 'hs_analytics_source_timestamp_earliest_value_25a3a52c', + }, + { + id: 'hs_avatar_filemanager_key', + label: 'Avatar FileManager key', + }, + { + id: 'hs_created_by_user_id', + label: 'Created by user ID', + }, + { + id: 'hs_createdate', + label: 'Object create date/time', + }, + { + id: 'hs_ideal_customer_profile', + label: 'Ideal Customer Profile Tier', + }, + { + id: 'hs_is_target_account', + label: 'Target Account', + }, + { + id: 'hs_last_booked_meeting_date', + label: 'Last Booked Meeting Date', + }, + { + id: 'hs_last_logged_call_date', + label: 'Last Logged Call Date', + }, + { + id: 'hs_last_open_task_date', + label: 'Last Open Task Date', + }, + { + id: 'hs_last_sales_activity_date', + label: 'last sales activity date old', + }, + { + id: 'hs_last_sales_activity_timestamp', + label: 'Last Engagement Date', + }, + { + id: 'hs_lastmodifieddate', + label: 'Last Modified Date', + }, + { + id: 'hs_merged_object_ids', + label: 'Merged object IDs', + }, + { + id: 'hs_num_blockers', + label: 'Number of blockers', + }, + { + id: 'hs_num_contacts_with_buying_roles', + label: 'Number of contacts with a buying role', + }, + { + id: 'hs_num_decision_makers', + label: 'Number of decision makers', + }, + { + id: 'hs_num_open_deals', + label: 'Number of open deals', + }, + { + id: 'hs_object_id', + label: 'Company ID', + }, + { + id: 'hs_predictivecontactscore_v2', + label: 'Likelihood to close', + }, + { + id: 'hs_predictivecontactscore_v2_next_max_max_d4e58c1e', + label: 'hs_predictivecontactscore_v2_next_max_max_d4e58c1e', + }, + { + id: 'hs_target_account', + label: 'Target Account', + }, + { + id: 'hs_target_account_probability', + label: 'Target Account Probability', + }, + { + id: 'hs_target_account_recommendation_snooze_time', + label: 'Target Account Recommendation Snooze Time', + }, + { + id: 'hs_target_account_recommendation_state', + label: 'Target Account Recommendation State', + }, + { + id: 'hs_total_deal_value', + label: 'Total open deal value', + }, + { + id: 'hs_updated_by_user_id', + label: 'Updated by user ID', + }, + { + id: 'hs_user_ids_of_all_owners', + label: 'User IDs of all owners', + }, + { + id: 'hubspot_owner_assigneddate', + label: 'Owner Assigned Date', + }, + { + id: 'is_public', + label: 'Is Public', + }, + { + id: 'num_associated_contacts', + label: 'Associated Contacts', + }, + { + id: 'num_associated_deals', + label: 'Associated Deals', + }, + { + id: 'num_conversion_events', + label: 'Number of Form Submissions', + }, + { + id: 'num_conversion_events_cardinality_sum_d095f14b', + label: 'num_conversion_events_cardinality_sum_d095f14b', + }, + { + id: 'recent_conversion_date', + label: 'Recent Conversion Date', + }, + { + id: 'recent_conversion_date_timestamp_latest_value_72856da1', + label: 'recent_conversion_date_timestamp_latest_value_72856da1', + }, + { + id: 'recent_conversion_event_name', + label: 'Recent Conversion', + }, + { + id: 'recent_conversion_event_name_timestamp_latest_value_66c820bf', + label: 'recent_conversion_event_name_timestamp_latest_value_66c820bf', + }, + { + id: 'recent_deal_amount', + label: 'Recent Deal Amount', + }, + { + id: 'recent_deal_close_date', + label: 'Recent Deal Close Date', + }, + { + id: 'timezone', + label: 'Time Zone', + }, + { + id: 'total_money_raised', + label: 'Total Money Raised', + }, + { + id: 'total_revenue', + label: 'Total Revenue', + }, + { + id: 'name', + label: 'Name', + }, + { + id: 'owneremail', + label: 'HubSpot Owner Email', + }, + { + id: 'twitterhandle', + label: 'Twitter Handle', + }, + { + id: 'ownername', + label: 'HubSpot Owner Name', + }, + { + id: 'phone', + label: 'Phone Number', + }, + { + id: 'twitterbio', + label: 'Twitter Bio', + }, + { + id: 'twitterfollowers', + label: 'Twitter Followers', + }, + { + id: 'address', + label: 'Street Address', + }, + { + id: 'address2', + label: 'Street Address 2', + }, + { + id: 'facebook_company_page', + label: 'Facebook Company Page', + }, + { + id: 'city', + label: 'City', + }, + { + id: 'linkedin_company_page', + label: 'LinkedIn Company Page', + }, + { + id: 'linkedinbio', + label: 'LinkedIn Bio', + }, + { + id: 'state', + label: 'State/Region', + }, + { + id: 'googleplus_page', + label: 'Google Plus Page', + }, + { + id: 'engagements_last_meeting_booked', + label: 'Date of last meeting booked in meetings tool', + }, + { + id: 'engagements_last_meeting_booked_campaign', + label: 'Campaign of last booking in meetings tool', + }, + { + id: 'engagements_last_meeting_booked_medium', + label: 'Medium of last booking in meetings tool', + }, + { + id: 'engagements_last_meeting_booked_source', + label: 'Source of last booking in meetings tool', + }, + { + id: 'hs_latest_meeting_activity', + label: 'Latest meeting activity', + }, + { + id: 'hs_sales_email_last_replied', + label: 'Recent Sales Email Replied Date', + }, + { + id: 'hubspot_owner_id', + label: 'Company owner', + }, + { + id: 'notes_last_contacted', + label: 'Last Contacted', + }, + { + id: 'notes_last_updated', + label: 'Last Activity Date', + }, + { + id: 'notes_next_activity_date', + label: 'Next Activity Date', + }, + { + id: 'num_contacted_notes', + label: 'Number of times contacted', + }, + { + id: 'num_notes', + label: 'Number of Sales Activities', + }, + { + id: 'zip', + label: 'Postal Code', + }, + { + id: 'country', + label: 'Country/Region', + }, + { + id: 'hubspot_team_id', + label: 'HubSpot Team', + }, + { + id: 'hs_all_owner_ids', + label: 'All owner ids', + }, + { + id: 'website', + label: 'Website URL', + }, + { + id: 'domain', + label: 'Company Domain Name', + }, + { + id: 'hs_all_team_ids', + label: 'All team ids', + }, + { + id: 'hs_all_accessible_team_ids', + label: 'All accessible team ids', + }, + { + id: 'numberofemployees', + label: 'Number of Employees', + }, + { + id: 'industry', + label: 'Industry', + }, + { + id: 'annualrevenue', + label: 'Annual Revenue', + }, + { + id: 'lifecyclestage', + label: 'Lifecycle Stage', + }, + { + id: 'hs_lead_status', + label: 'Lead Status', + }, + { + id: 'hs_parent_company_id', + label: 'Parent Company', + }, + { + id: 'type', + label: 'Type', + }, + { + id: 'description', + label: 'Description', + }, + { + id: 'hs_num_child_companies', + label: 'Number of child companies', + }, + { + id: 'hubspotscore', + label: 'HubSpot Score', + }, + { + id: 'createdate', + label: 'Create Date', + }, + { + id: 'closedate', + label: 'Close Date', + }, + { + id: 'first_contact_createdate', + label: 'First Contact Create Date', + }, + { + id: 'days_to_close', + label: 'Days to Close', + }, + { + id: 'web_technologies', + label: 'Web Technologies', + }, +]; + +export const dealFields = [ + { + id: 'amount_in_home_currency', + label: 'Amount in company currency', + }, + { + id: 'days_to_close', + label: 'Days to close', + }, + { + id: 'deal_currency_code', + label: 'Currency', + }, + { + id: 'hs_acv', + label: 'Annual contract value', + }, + { + id: 'hs_analytics_source', + label: 'Original Source Type', + }, + { + id: 'hs_analytics_source_data_1', + label: 'Original Source Data 1', + }, + { + id: 'hs_analytics_source_data_2', + label: 'Original Source Data 2', + }, + { + id: 'hs_arr', + label: 'Annual recurring revenue', + }, + { + id: 'hs_campaign', + label: 'HubSpot Campaign', + }, + { + id: 'hs_closed_amount', + label: 'Closed Deal Amount', + }, + { + id: 'hs_closed_amount_in_home_currency', + label: 'Closed Deal Amount In Home Currency', + }, + { + id: 'hs_created_by_user_id', + label: 'Created by user ID', + }, + { + id: 'hs_date_entered_appointmentscheduled', + label: "Date entered 'Appointment Scheduled (Sales Pipeline)'", + }, + { + id: 'hs_date_entered_closedlost', + label: "Date entered 'Closed Lost (Sales Pipeline)'", + }, + { + id: 'hs_date_entered_closedwon', + label: "Date entered 'Closed Won (Sales Pipeline)'", + }, + { + id: 'hs_date_entered_contractsent', + label: "Date entered 'Contract Sent (Sales Pipeline)'", + }, + { + id: 'hs_date_entered_decisionmakerboughtin', + label: "Date entered 'Decision Maker Bought-In (Sales Pipeline)'", + }, + { + id: 'hs_date_entered_presentationscheduled', + label: "Date entered 'Presentation Scheduled (Sales Pipeline)'", + }, + { + id: 'hs_date_entered_qualifiedtobuy', + label: "Date entered 'Qualified To Buy (Sales Pipeline)'", + }, + { + id: 'hs_date_exited_appointmentscheduled', + label: "Date exited 'Appointment Scheduled (Sales Pipeline)'", + }, + { + id: 'hs_date_exited_closedlost', + label: "Date exited 'Closed Lost (Sales Pipeline)'", + }, + { + id: 'hs_date_exited_closedwon', + label: "Date exited 'Closed Won (Sales Pipeline)'", + }, + { + id: 'hs_date_exited_contractsent', + label: "Date exited 'Contract Sent (Sales Pipeline)'", + }, + { + id: 'hs_date_exited_decisionmakerboughtin', + label: "Date exited 'Decision Maker Bought-In (Sales Pipeline)'", + }, + { + id: 'hs_date_exited_presentationscheduled', + label: "Date exited 'Presentation Scheduled (Sales Pipeline)'", + }, + { + id: 'hs_date_exited_qualifiedtobuy', + label: "Date exited 'Qualified To Buy (Sales Pipeline)'", + }, + { + id: 'hs_deal_amount_calculation_preference', + label: 'Deal amount calculation preference', + }, + { + id: 'hs_deal_stage_probability', + label: 'Deal Stage Probability', + }, + { + id: 'hs_forecast_amount', + label: 'Forecast Amount', + }, + { + id: 'hs_forecast_probability', + label: 'Forecast Probability', + }, + { + id: 'hs_is_closed', + label: 'Is Deal Closed?', + }, + { + id: 'hs_lastmodifieddate', + label: 'Last Modified Date', + }, + { + id: 'hs_likelihood_to_close', + label: 'Likelihood to close by the close date', + }, + { + id: 'hs_line_item_global_term_hs_discount_percentage', + label: 'Global Term Line Item Discount Percentage', + }, + { + id: 'hs_line_item_global_term_hs_discount_percentage_enabled', + label: 'Global Term Line Item Discount Percentage Enabled', + }, + { + id: 'hs_line_item_global_term_hs_recurring_billing_period', + label: 'Global Term Line Item Recurring Billing Period', + }, + { + id: 'hs_line_item_global_term_hs_recurring_billing_period_enabled', + label: 'Global Term Line Item Recurring Billing Period Enabled', + }, + { + id: 'hs_line_item_global_term_hs_recurring_billing_start_date', + label: 'Global Term Line Item Recurring Billing Start Date', + }, + { + id: 'hs_line_item_global_term_hs_recurring_billing_start_date_enabled', + label: 'Global Term Line Item Recurring Billing Start Date Enabled', + }, + { + id: 'hs_line_item_global_term_recurringbillingfrequency', + label: 'Global Term Line Item Recurring Billing Frequency', + }, + { + id: 'hs_line_item_global_term_recurringbillingfrequency_enabled', + label: 'Global Term Line Item Recurring Billing Frequency Enabled', + }, + { + id: 'hs_manual_forecast_category', + label: 'Forecast category', + }, + { + id: 'hs_merged_object_ids', + label: 'Merged object IDs', + }, + { + id: 'hs_mrr', + label: 'Monthly recurring revenue', + }, + { + id: 'hs_next_step', + label: 'Next step', + }, + { + id: 'hs_object_id', + label: 'Deal ID', + }, + { + id: 'hs_predicted_amount', + label: 'The predicted deal amount', + }, + { + id: 'hs_predicted_amount_in_home_currency', + label: "The predicted deal amount in your company's currency", + }, + { + id: 'hs_projected_amount', + label: 'Projected Deal Amount', + }, + { + id: 'hs_projected_amount_in_home_currency', + label: 'Projected Deal Amount in Home Currency', + }, + { + id: 'hs_tcv', + label: 'Total contract value', + }, + { + id: 'hs_time_in_appointmentscheduled', + label: "Time in 'Appointment Scheduled (Sales Pipeline)'", + }, + { + id: 'hs_time_in_closedlost', + label: "Time in 'Closed Lost (Sales Pipeline)'", + }, + { + id: 'hs_time_in_closedwon', + label: "Time in 'Closed Won (Sales Pipeline)'", + }, + { + id: 'hs_time_in_contractsent', + label: "Time in 'Contract Sent (Sales Pipeline)'", + }, + { + id: 'hs_time_in_decisionmakerboughtin', + label: "Time in 'Decision Maker Bought-In (Sales Pipeline)'", + }, + { + id: 'hs_time_in_presentationscheduled', + label: "Time in 'Presentation Scheduled (Sales Pipeline)'", + }, + { + id: 'hs_time_in_qualifiedtobuy', + label: "Time in 'Qualified To Buy (Sales Pipeline)'", + }, + { + id: 'hs_updated_by_user_id', + label: 'Updated by user ID', + }, + { + id: 'hs_user_ids_of_all_owners', + label: 'User IDs of all owners', + }, + { + id: 'hubspot_owner_assigneddate', + label: 'Owner Assigned Date', + }, + { + id: 'testing', + label: 'testing', + }, + { + id: 'dealname', + label: 'Deal Name', + }, + { + id: 'amount', + label: 'Amount', + }, + { + id: 'dealstage', + label: 'Deal Stage', + }, + { + id: 'pipeline', + label: 'Pipeline', + }, + { + id: 'closedate', + label: 'Close Date', + }, + { + id: 'createdate', + label: 'Create Date', + }, + { + id: 'engagements_last_meeting_booked', + label: 'Date of last meeting booked in meetings tool', + }, + { + id: 'engagements_last_meeting_booked_campaign', + label: 'Campaign of last booking in meetings tool', + }, + { + id: 'engagements_last_meeting_booked_medium', + label: 'Medium of last booking in meetings tool', + }, + { + id: 'engagements_last_meeting_booked_source', + label: 'Source of last booking in meetings tool', + }, + { + id: 'hs_latest_meeting_activity', + label: 'Latest meeting activity', + }, + { + id: 'hs_sales_email_last_replied', + label: 'Recent Sales Email Replied Date', + }, + { + id: 'hubspot_owner_id', + label: 'Deal owner', + }, + { + id: 'notes_last_contacted', + label: 'Last Contacted', + }, + { + id: 'notes_last_updated', + label: 'Last Activity Date', + }, + { + id: 'notes_next_activity_date', + label: 'Next Activity Date', + }, + { + id: 'num_contacted_notes', + label: 'Number of times contacted', + }, + { + id: 'num_notes', + label: 'Number of Sales Activities', + }, + { + id: 'hs_createdate', + label: 'HubSpot Create Date', + }, + { + id: 'hubspot_team_id', + label: 'HubSpot Team', + }, + { + id: 'dealtype', + label: 'Deal Type', + }, + { + id: 'hs_all_owner_ids', + label: 'All owner ids', + }, + { + id: 'description', + label: 'Deal Description', + }, + { + id: 'hs_all_team_ids', + label: 'All team ids', + }, + { + id: 'hs_all_accessible_team_ids', + label: 'All accessible team ids', + }, + { + id: 'num_associated_contacts', + label: 'Number of Contacts', + }, + { + id: 'closed_lost_reason', + label: 'Closed Lost Reason', + }, + { + id: 'closed_won_reason', + label: 'Closed Won Reason', + }, +]; + +const reduceMetadatFields = (data: string[]) => { + return data + .reduce((a, v) => { + //@ts-ignore + a.push(...v.split(',')); + return a; + }, []) + .map((email) => ({ email })); +}; + +export const getEmailMetadata = (meta: IDataObject) => { + return { + from: { + ...(meta.fromEmail && { email: meta.fromEmail }), + ...(meta.firstName && { firstName: meta.firstName }), + ...(meta.lastName && { lastName: meta.lastName }), + }, + cc: reduceMetadatFields((meta.cc as string[]) || []), + bcc: reduceMetadatFields((meta.bcc as string[]) || []), + ...(meta.subject && { subject: meta.subject }), + ...(meta.html && { html: meta.html }), + ...(meta.text && { text: meta.text }), + }; +}; + +export const getTaskMetadata = (meta: IDataObject) => { + return { + ...(meta.body && { body: meta.body }), + ...(meta.subject && { subject: meta.subject }), + ...(meta.status && { status: meta.status }), + ...(meta.forObjectType && { forObjectType: meta.forObjectType }), + }; +}; + +export const getMeetingMetadata = (meta: IDataObject) => { + return { + ...(meta.body && { body: meta.body }), + ...(meta.startTime && { startTime: moment(meta.startTime as string).unix() }), + ...(meta.endTime && { endTime: moment(meta.endTime as string).unix() }), + ...(meta.title && { title: meta.title }), + ...(meta.internalMeetingNotes && { internalMeetingNotes: meta.internalMeetingNotes }), + }; +}; + +export const getCallMetadata = (meta: IDataObject) => { + return { + ...(meta.toNumber && { toNumber: meta.toNumber }), + ...(meta.fromNumber && { fromNumber: meta.fromNumber }), + ...(meta.status && { status: meta.status }), + ...(meta.durationMilliseconds && { durationMilliseconds: meta.durationMilliseconds }), + ...(meta.recordingUrl && { recordingUrl: meta.recordingUrl }), + ...(meta.body && { body: meta.body }), + }; +}; + +export const getAssociations = (associations: { + companyIds: string; + dealIds: string; + ownerIds: string; + contactIds: string; + ticketIds: string; +}) => { + return { + ...(associations.companyIds && { companyIds: associations.companyIds.toString().split(',') }), + ...(associations.contactIds && { contactIds: associations.contactIds.toString().split(',') }), + ...(associations.dealIds && { dealIds: associations.dealIds.toString().split(',') }), + ...(associations.ownerIds && { ownerIds: associations.ownerIds.toString().split(',') }), + ...(associations.ticketIds && { ticketIds: associations.ticketIds.toString().split(',') }), + }; +}; + +export async function validateCredentials( + this: ICredentialTestFunctions, + decryptedCredentials: ICredentialDataDecryptedObject, + // tslint:disable-next-line:no-any +): Promise { + const credentials = decryptedCredentials; + + const { apiKey, appToken } = credentials as { + appToken: string; + apiKey: string; + }; + + const options: OptionsWithUri = { + method: 'GET', + headers: {}, + uri: 'https://api.hubapi.com/deals/v1/deal/paged', + json: true, + }; + + if (apiKey) { + options.qs = { hapikey: apiKey }; + } else { + options.headers = { Authorization: `Bearer ${appToken}` }; + } + + return this.helpers.request(options); +} diff --git a/packages/nodes-base/nodes/Hubspot/V1/HubspotV1.node.ts b/packages/nodes-base/nodes/Hubspot/V1/HubspotV1.node.ts new file mode 100644 index 0000000000..946740b41c --- /dev/null +++ b/packages/nodes-base/nodes/Hubspot/V1/HubspotV1.node.ts @@ -0,0 +1,2739 @@ +import type { IExecuteFunctions } from 'n8n-core'; + +import type { + ICredentialDataDecryptedObject, + ICredentialsDecrypted, + ICredentialTestFunctions, + IDataObject, + ILoadOptionsFunctions, + INodeCredentialTestResult, + INodeExecutionData, + INodePropertyOptions, + INodeType, + INodeTypeBaseDescription, + INodeTypeDescription, + JsonObject, +} from 'n8n-workflow'; +import { NodeOperationError } from 'n8n-workflow'; + +import { + clean, + getAssociations, + getCallMetadata, + getEmailMetadata, + getMeetingMetadata, + getTaskMetadata, + hubspotApiRequest, + hubspotApiRequestAllItems, + validateCredentials, +} from './GenericFunctions'; + +import { contactFields, contactOperations } from './ContactDescription'; + +import { contactListFields, contactListOperations } from './ContactListDescription'; + +import { companyFields, companyOperations } from './CompanyDescription'; + +import { dealFields, dealOperations } from './DealDescription'; + +import { engagementFields, engagementOperations } from './EngagementDescription'; + +import { formFields, formOperations } from './FormDescription'; + +import { ticketFields, ticketOperations } from './TicketDescription'; + +import type { IForm } from './FormInterface'; + +import type { IAssociation, IDeal } from './DealInterface'; + +import { snakeCase } from 'change-case'; + +export class HubspotV1 implements INodeType { + description: INodeTypeDescription; + + constructor(baseDescription: INodeTypeBaseDescription) { + this.description = { + ...baseDescription, + group: ['output'], + version: 1, + subtitle: '={{$parameter["operation"] + ": " + $parameter["resource"]}}', + defaults: { + name: 'HubSpot', + }, + inputs: ['main'], + outputs: ['main'], + credentials: [ + { + name: 'hubspotApi', + required: true, + testedBy: 'hubspotApiTest', + displayOptions: { + show: { + authentication: ['apiKey'], + }, + }, + }, + { + name: 'hubspotAppToken', + required: true, + testedBy: 'hubspotApiTest', + displayOptions: { + show: { + authentication: ['appToken'], + }, + }, + }, + { + name: 'hubspotOAuth2Api', + required: true, + displayOptions: { + show: { + authentication: ['oAuth2'], + }, + }, + }, + ], + properties: [ + { + displayName: 'Authentication', + name: 'authentication', + type: 'options', + options: [ + { + name: 'API Key', + value: 'apiKey', + }, + { + name: 'APP Token', + value: 'appToken', + }, + { + name: 'OAuth2', + value: 'oAuth2', + }, + ], + default: 'apiKey', + }, + { + displayName: 'Resource', + name: 'resource', + type: 'options', + noDataExpression: true, + options: [ + { + name: 'Company', + value: 'company', + }, + { + name: 'Contact', + value: 'contact', + }, + { + name: 'Contact List', + value: 'contactList', + }, + { + name: 'Deal', + value: 'deal', + }, + { + name: 'Engagement', + value: 'engagement', + }, + { + name: 'Form', + value: 'form', + }, + { + name: 'Ticket', + value: 'ticket', + }, + ], + default: 'deal', + }, + // CONTACT + ...contactOperations, + ...contactFields, + // CONTACT LIST + ...contactListOperations, + ...contactListFields, + // COMPANY + ...companyOperations, + ...companyFields, + // DEAL + ...dealOperations, + ...dealFields, + // ENGAGEMENT + ...engagementOperations, + ...engagementFields, + // FORM + ...formOperations, + ...formFields, + // TICKET + ...ticketOperations, + ...ticketFields, + ], + }; + } + + methods = { + credentialTest: { + async hubspotApiTest( + this: ICredentialTestFunctions, + credential: ICredentialsDecrypted, + ): Promise { + try { + await validateCredentials.call(this, credential.data as ICredentialDataDecryptedObject); + } catch (error) { + const err = error as JsonObject; + if (err.statusCode === 401) { + return { + status: 'Error', + message: 'Invalid credentials', + }; + } + } + return { + status: 'OK', + message: 'Authentication successful', + }; + }, + }, + + loadOptions: { + /* -------------------------------------------------------------------------- */ + /* CONTACT */ + /* -------------------------------------------------------------------------- */ + + // Get all the contact lead statuses to display them to user so that they can + // select them easily + async getContactLeadStatuses(this: ILoadOptionsFunctions): Promise { + const returnData: INodePropertyOptions[] = []; + const endpoint = '/properties/v2/contacts/properties'; + const properties = await hubspotApiRequest.call(this, 'GET', endpoint, {}); + for (const property of properties) { + if (property.name === 'hs_lead_status') { + for (const option of property.options) { + const statusName = option.label; + const statusId = option.value; + returnData.push({ + name: statusName, + value: statusId, + }); + } + } + } + return returnData; + }, + + // Get all the contact legal basics to display them to user so that they can + // select them easily + async getContactLealBasics(this: ILoadOptionsFunctions): Promise { + const returnData: INodePropertyOptions[] = []; + const endpoint = '/properties/v2/contacts/properties'; + const properties = await hubspotApiRequest.call(this, 'GET', endpoint, {}); + for (const property of properties) { + if (property.name === 'hs_legal_basis') { + for (const option of property.options) { + const statusName = option.label; + const statusId = option.value; + returnData.push({ + name: statusName, + value: statusId, + }); + } + } + } + return returnData; + }, + + // Get all the contact lifecycle stages to display them to user so that they can + // select them easily + async getContactLifeCycleStages( + this: ILoadOptionsFunctions, + ): Promise { + const returnData: INodePropertyOptions[] = []; + const endpoint = '/properties/v2/contacts/properties'; + const properties = await hubspotApiRequest.call(this, 'GET', endpoint, {}); + for (const property of properties) { + if (property.name === 'lifecyclestage') { + for (const option of property.options) { + const stageName = option.label; + const stageId = option.value; + returnData.push({ + name: stageName, + value: stageId, + }); + } + } + } + return returnData; + }, + + // Get all the contact lifecycle stages to display them to user so that they can + // select them easily + async getContactOriginalSources( + this: ILoadOptionsFunctions, + ): Promise { + const returnData: INodePropertyOptions[] = []; + const endpoint = '/properties/v2/contacts/properties'; + const properties = await hubspotApiRequest.call(this, 'GET', endpoint, {}); + for (const property of properties) { + if (property.name === 'hs_analytics_source') { + for (const option of property.options) { + const sourceName = option.label; + const sourceId = option.value; + returnData.push({ + name: sourceName, + value: sourceId, + }); + } + } + } + return returnData; + }, + + // Get all the contact preffered languages to display them to user so that they can + // select them easily + async getContactPrefferedLanguages( + this: ILoadOptionsFunctions, + ): Promise { + const returnData: INodePropertyOptions[] = []; + const endpoint = '/properties/v2/contacts/properties'; + const properties = await hubspotApiRequest.call(this, 'GET', endpoint, {}); + for (const property of properties) { + if (property.name === 'hs_language') { + for (const option of property.options) { + const languageName = option.label; + const languageId = option.value; + returnData.push({ + name: languageName, + value: languageId, + }); + } + } + } + return returnData; + }, + + // Get all the contact preffered languages to display them to user so that they can + // select them easily + async getContactStatuses(this: ILoadOptionsFunctions): Promise { + const returnData: INodePropertyOptions[] = []; + const endpoint = '/properties/v2/contacts/properties'; + const properties = await hubspotApiRequest.call(this, 'GET', endpoint, {}); + for (const property of properties) { + if (property.name === 'hs_content_membership_status') { + for (const option of property.options) { + const languageName = option.label; + const languageId = option.value; + returnData.push({ + name: languageName, + value: languageId, + }); + } + } + } + return returnData; + }, + + // Get all the contact properties to display them to user so that they can + // select them easily + async getContactProperties(this: ILoadOptionsFunctions): Promise { + const returnData: INodePropertyOptions[] = []; + const endpoint = '/properties/v2/contacts/properties'; + const properties = await hubspotApiRequest.call(this, 'GET', endpoint, {}); + for (const property of properties) { + const propertyName = property.label; + const propertyId = property.name; + returnData.push({ + name: propertyName, + value: propertyId, + }); + } + return returnData; + }, + + // Get all the contact properties to display them to user so that they can + // select them easily + async getContactCustomProperties( + this: ILoadOptionsFunctions, + ): Promise { + const returnData: INodePropertyOptions[] = []; + const endpoint = '/properties/v2/contacts/properties'; + const properties = await hubspotApiRequest.call(this, 'GET', endpoint, {}); + for (const property of properties) { + if (property.hubspotDefined === null) { + const propertyName = property.label; + const propertyId = property.name; + returnData.push({ + name: propertyName, + value: propertyId, + }); + } + } + return returnData; + }, + + // Get all the contact number of employees options to display them to user so that they can + // select them easily + async getContactNumberOfEmployees( + this: ILoadOptionsFunctions, + ): Promise { + const returnData: INodePropertyOptions[] = []; + const endpoint = '/properties/v2/contacts/properties'; + const properties = await hubspotApiRequest.call(this, 'GET', endpoint, {}); + for (const property of properties) { + if (property.name === 'numemployees') { + for (const option of property.options) { + const optionName = option.label; + const optionId = option.value; + returnData.push({ + name: optionName, + value: optionId, + }); + } + } + } + return returnData; + }, + + /* -------------------------------------------------------------------------- */ + /* COMPANY */ + /* -------------------------------------------------------------------------- */ + + // Get all the company industries to display them to user so that they can + // select them easily + async getCompanyIndustries(this: ILoadOptionsFunctions): Promise { + const returnData: INodePropertyOptions[] = []; + const endpoint = '/properties/v2/companies/properties'; + const properties = await hubspotApiRequest.call(this, 'GET', endpoint, {}); + for (const property of properties) { + if (property.name === 'industry') { + for (const option of property.options) { + const industryName = option.label; + const industryId = option.value; + returnData.push({ + name: industryName, + value: industryId, + }); + } + } + } + return returnData; + }, + + // Get all the company lead statuses to display them to user so that they can + // select them easily + async getCompanyleadStatuses(this: ILoadOptionsFunctions): Promise { + const returnData: INodePropertyOptions[] = []; + const endpoint = '/properties/v2/companies/properties'; + const properties = await hubspotApiRequest.call(this, 'GET', endpoint, {}); + for (const property of properties) { + if (property.name === 'hs_lead_status') { + for (const option of property.options) { + const statusName = option.label; + const statusId = option.value; + returnData.push({ + name: statusName, + value: statusId, + }); + } + } + } + return returnData; + }, + + // Get all the company lifecycle stages to display them to user so that they can + // select them easily + async getCompanylifecycleStages( + this: ILoadOptionsFunctions, + ): Promise { + const returnData: INodePropertyOptions[] = []; + const endpoint = '/properties/v2/companies/properties'; + const properties = await hubspotApiRequest.call(this, 'GET', endpoint, {}); + for (const property of properties) { + if (property.name === 'lifecyclestage') { + for (const option of property.options) { + const stageName = option.label; + const stageId = option.value; + returnData.push({ + name: stageName, + value: stageId, + }); + } + } + } + return returnData; + }, + + // Get all the company types stages to display them to user so that they can + // select them easily + async getCompanyTypes(this: ILoadOptionsFunctions): Promise { + const returnData: INodePropertyOptions[] = []; + const endpoint = '/properties/v2/companies/properties'; + const properties = await hubspotApiRequest.call(this, 'GET', endpoint, {}); + for (const property of properties) { + if (property.name === 'type') { + for (const option of property.options) { + const typeName = option.label; + const typeId = option.value; + returnData.push({ + name: typeName, + value: typeId, + }); + } + } + } + return returnData; + }, + + // Get all the company types stages to display them to user so that they can + // select them easily + async getCompanyTargetAccounts(this: ILoadOptionsFunctions): Promise { + const returnData: INodePropertyOptions[] = []; + const endpoint = '/properties/v2/companies/properties'; + const properties = await hubspotApiRequest.call(this, 'GET', endpoint, {}); + for (const property of properties) { + if (property.name === 'hs_target_account') { + for (const option of property.options) { + const targetName = option.label; + const targetId = option.value; + returnData.push({ + name: targetName, + value: targetId, + }); + } + } + } + return returnData; + }, + + // Get all the company source types stages to display them to user so that they can + // select them easily + async getCompanySourceTypes(this: ILoadOptionsFunctions): Promise { + const returnData: INodePropertyOptions[] = []; + const endpoint = '/properties/v2/companies/properties'; + const properties = await hubspotApiRequest.call(this, 'GET', endpoint, {}); + for (const property of properties) { + if (property.name === 'hs_analytics_source') { + for (const option of property.options) { + const typeName = option.label; + const typeId = option.value; + returnData.push({ + name: typeName, + value: typeId, + }); + } + } + } + return returnData; + }, + + // Get all the company web technologies stages to display them to user so that they can + // select them easily + async getCompanyWebTechnologies( + this: ILoadOptionsFunctions, + ): Promise { + const returnData: INodePropertyOptions[] = []; + const endpoint = '/properties/v2/companies/properties'; + const properties = await hubspotApiRequest.call(this, 'GET', endpoint, {}); + for (const property of properties) { + if (property.name === 'web_technologies') { + for (const option of property.options) { + const technologyName = option.label; + const technologyId = option.value; + returnData.push({ + name: technologyName, + value: technologyId, + }); + } + } + } + return returnData; + }, + + // Get all the company properties to display them to user so that they can + // select them easily + async getCompanyProperties(this: ILoadOptionsFunctions): Promise { + const returnData: INodePropertyOptions[] = []; + const endpoint = '/properties/v2/companies/properties'; + const properties = await hubspotApiRequest.call(this, 'GET', endpoint, {}); + for (const property of properties) { + const propertyName = property.label; + const propertyId = property.name; + returnData.push({ + name: propertyName, + value: propertyId, + }); + } + return returnData; + }, + + // Get all the company custom properties to display them to user so that they can + // select them easily + async getCompanyCustomProperties( + this: ILoadOptionsFunctions, + ): Promise { + const returnData: INodePropertyOptions[] = []; + const endpoint = '/properties/v2/companies/properties'; + const properties = await hubspotApiRequest.call(this, 'GET', endpoint, {}); + for (const property of properties) { + if (property.hubspotDefined === null) { + const propertyName = property.label; + const propertyId = property.name; + returnData.push({ + name: propertyName, + value: propertyId, + }); + } + } + return returnData; + }, + + /* -------------------------------------------------------------------------- */ + /* DEAL */ + /* -------------------------------------------------------------------------- */ + + // Get all the groups to display them to user so that they can + // select them easily + async getDealStages(this: ILoadOptionsFunctions): Promise { + const returnData: INodePropertyOptions[] = []; + const endpoint = '/crm-pipelines/v1/pipelines/deals'; + let stages = await hubspotApiRequest.call(this, 'GET', endpoint, {}); + stages = stages.results[0].stages; + for (const stage of stages) { + const stageName = stage.label; + const stageId = stage.stageId; + returnData.push({ + name: stageName, + value: stageId, + }); + } + return returnData; + }, + + // Get all the deal types to display them to user so that they can + // select them easily + async getDealTypes(this: ILoadOptionsFunctions): Promise { + const returnData: INodePropertyOptions[] = []; + const endpoint = '/properties/v1/deals/properties/named/dealtype'; + const dealTypes = await hubspotApiRequest.call(this, 'GET', endpoint); + for (const dealType of dealTypes.options) { + const dealTypeName = dealType.label; + const dealTypeId = dealType.value; + returnData.push({ + name: dealTypeName, + value: dealTypeId, + }); + } + return returnData; + }, + + // Get all the deal properties to display them to user so that they can + // select them easily + async getDealCustomProperties(this: ILoadOptionsFunctions): Promise { + const returnData: INodePropertyOptions[] = []; + const endpoint = '/properties/v2/deals/properties'; + const properties = await hubspotApiRequest.call(this, 'GET', endpoint, {}); + for (const property of properties) { + if (property.hubspotDefined === null) { + const propertyName = property.label; + const propertyId = property.name; + returnData.push({ + name: propertyName, + value: propertyId, + }); + } + } + return returnData; + }, + // Get all the deal properties to display them to user so that they can + // select them easily + async getDealProperties(this: ILoadOptionsFunctions): Promise { + const returnData: INodePropertyOptions[] = []; + const endpoint = '/properties/v2/deals/properties'; + const properties = await hubspotApiRequest.call(this, 'GET', endpoint, {}); + for (const property of properties) { + const propertyName = property.label; + const propertyId = property.name; + returnData.push({ + name: propertyName, + value: propertyId, + }); + } + return returnData; + }, + + /* -------------------------------------------------------------------------- */ + /* FORM */ + /* -------------------------------------------------------------------------- */ + + // Get all the forms to display them to user so that they can + // select them easily + async getForms(this: ILoadOptionsFunctions): Promise { + const returnData: INodePropertyOptions[] = []; + const endpoint = '/forms/v2/forms'; + const forms = await hubspotApiRequest.call(this, 'GET', endpoint, {}, { formTypes: 'ALL' }); + for (const form of forms) { + const formName = form.name; + const formId = form.guid; + returnData.push({ + name: formName, + value: formId, + }); + } + return returnData; + }, + + // Get all the subscription types to display them to user so that they can + // select them easily + async getSubscriptionTypes(this: ILoadOptionsFunctions): Promise { + const returnData: INodePropertyOptions[] = []; + const endpoint = '/email/public/v1/subscriptions'; + const subscriptions = await hubspotApiRequestAllItems.call( + this, + 'subscriptionDefinitions', + 'GET', + endpoint, + {}, + ); + for (const subscription of subscriptions) { + const subscriptionName = subscription.name; + const subscriptionId = subscription.id; + returnData.push({ + name: subscriptionName, + value: subscriptionId, + }); + } + return returnData; + }, + + /* -------------------------------------------------------------------------- */ + /* TICKET */ + /* -------------------------------------------------------------------------- */ + + // Get all the ticket categories to display them to user so that they can + // select them easily + async getTicketCategories(this: ILoadOptionsFunctions): Promise { + const returnData: INodePropertyOptions[] = []; + const endpoint = '/properties/v2/tickets/properties'; + const properties = await hubspotApiRequest.call(this, 'GET', endpoint, {}); + for (const property of properties) { + if (property.name === 'hs_ticket_category') { + for (const option of property.options) { + const categoryName = option.label; + const categoryId = option.value; + returnData.push({ + name: categoryName, + value: categoryId, + }); + } + } + } + return returnData.sort((a, b) => (a.name < b.name ? 0 : 1)); + }, + + // Get all the ticket pipelines to display them to user so that they can + // select them easily + async getTicketPipelines(this: ILoadOptionsFunctions): Promise { + const returnData: INodePropertyOptions[] = []; + const endpoint = '/crm-pipelines/v1/pipelines/tickets'; + const { results } = await hubspotApiRequest.call(this, 'GET', endpoint, {}); + for (const pipeline of results) { + const pipelineName = pipeline.label; + const pipelineId = pipeline.pipelineId; + returnData.push({ + name: pipelineName, + value: pipelineId, + }); + } + return returnData; + }, + + // Get all the ticket resolutions to display them to user so that they can + // select them easily + async getTicketPriorities(this: ILoadOptionsFunctions): Promise { + const returnData: INodePropertyOptions[] = []; + const endpoint = '/properties/v2/tickets/properties'; + const properties = await hubspotApiRequest.call(this, 'GET', endpoint, {}); + for (const property of properties) { + if (property.name === 'hs_ticket_priority') { + for (const option of property.options) { + const priorityName = option.label; + const priorityId = option.value; + returnData.push({ + name: priorityName, + value: priorityId, + }); + } + } + } + return returnData; + }, + + // Get all the ticket properties to display them to user so that they can + // select them easily + async getTicketProperties(this: ILoadOptionsFunctions): Promise { + const returnData: INodePropertyOptions[] = []; + const endpoint = '/properties/v2/tickets/properties'; + const properties = await hubspotApiRequest.call(this, 'GET', endpoint, {}); + for (const property of properties) { + const propertyName = property.label; + const propertyId = property.name; + returnData.push({ + name: propertyName, + value: propertyId, + }); + } + return returnData; + }, + + // Get all the ticket resolutions to display them to user so that they can + // select them easily + async getTicketResolutions(this: ILoadOptionsFunctions): Promise { + const returnData: INodePropertyOptions[] = []; + const endpoint = '/properties/v2/tickets/properties'; + const properties = await hubspotApiRequest.call(this, 'GET', endpoint, {}); + for (const property of properties) { + if (property.name === 'hs_resolution') { + for (const option of property.options) { + const resolutionName = option.label; + const resolutionId = option.value; + returnData.push({ + name: resolutionName, + value: resolutionId, + }); + } + } + } + return returnData.sort((a, b) => (a.name < b.name ? 0 : 1)); + }, + + // Get all the ticket sources to display them to user so that they can + // select them easily + async getTicketSources(this: ILoadOptionsFunctions): Promise { + const returnData: INodePropertyOptions[] = []; + const endpoint = '/properties/v2/tickets/properties'; + const properties = await hubspotApiRequest.call(this, 'GET', endpoint, {}); + for (const property of properties) { + if (property.name === 'source_type') { + for (const option of property.options) { + const sourceName = option.label; + const sourceId = option.value; + returnData.push({ + name: sourceName, + value: sourceId, + }); + } + } + } + return returnData.sort((a, b) => (a.name < b.name ? 0 : 1)); + }, + + // Get all the ticket stages to display them to user so that they can + // select them easily + async getTicketStages(this: ILoadOptionsFunctions): Promise { + let currentPipelineId = this.getCurrentNodeParameter('pipelineId') as string; + if (currentPipelineId === undefined) { + currentPipelineId = this.getNodeParameter('updateFields.pipelineId', '') as string; + } + const returnData: INodePropertyOptions[] = []; + const endpoint = '/crm-pipelines/v1/pipelines/tickets'; + const { results } = await hubspotApiRequest.call(this, 'GET', endpoint, {}); + for (const pipeline of results) { + if (currentPipelineId === pipeline.pipelineId) { + for (const stage of pipeline.stages) { + const stageName = stage.label; + const stageId = stage.stageId; + returnData.push({ + name: stageName, + value: stageId, + }); + } + } + } + return returnData; + }, + + /* -------------------------------------------------------------------------- */ + /* COMMON */ + /* -------------------------------------------------------------------------- */ + + // Get all the owners to display them to user so that they can + // select them easily + async getOwners(this: ILoadOptionsFunctions): Promise { + const returnData: INodePropertyOptions[] = []; + const endpoint = '/owners/v2/owners'; + const owners = await hubspotApiRequest.call(this, 'GET', endpoint); + for (const owner of owners) { + const ownerName = owner.email; + const ownerId = owner.ownerId; + returnData.push({ + name: ownerName, + value: ownerId, + }); + } + return returnData; + }, + + // Get all the companies to display them to user so that they can + // select them easily + async getCompanies(this: ILoadOptionsFunctions): Promise { + const returnData: INodePropertyOptions[] = []; + const qs: IDataObject = { + properties: ['name'], + }; + const endpoint = '/companies/v2/companies/paged'; + const companies = await hubspotApiRequestAllItems.call( + this, + 'companies', + 'GET', + endpoint, + {}, + qs, + ); + for (const company of companies) { + const companyName = company.properties.name + ? company.properties.name.value + : company.companyId; + const companyId = company.companyId; + returnData.push({ + name: companyName, + value: companyId, + }); + } + return returnData.sort((a, b) => (a.name < b.name ? 0 : 1)); + }, + + // Get all the companies to display them to user so that they can + // select them easily + async getContacts(this: ILoadOptionsFunctions): Promise { + const returnData: INodePropertyOptions[] = []; + const endpoint = '/contacts/v1/lists/all/contacts/all'; + const contacts = await hubspotApiRequestAllItems.call(this, 'contacts', 'GET', endpoint); + + for (const contact of contacts) { + const firstName = contact.properties?.firstname?.value || ''; + const lastName = contact.properties?.lastname?.value || ''; + const contactName = `${firstName} ${lastName}`; + const contactId = contact.vid; + returnData.push({ + name: contactName, + value: contactId, + description: `Contact VID: ${contactId}`, + }); + } + return returnData.sort((a, b) => (a.name < b.name ? 0 : 1)); + }, + }, + }; + + async execute(this: IExecuteFunctions): Promise { + const items = this.getInputData(); + const returnData: INodeExecutionData[] = []; + const length = items.length; + let responseData; + const qs: IDataObject = {}; + const resource = this.getNodeParameter('resource', 0); + const operation = this.getNodeParameter('operation', 0); + + //https://legacydocs.hubspot.com/docs/methods/lists/contact-lists-overview + if (resource === 'contactList') { + try { + //https://legacydocs.hubspot.com/docs/methods/lists/add_contact_to_list + if (operation === 'add') { + const listId = this.getNodeParameter('listId', 0) as string; + const by = this.getNodeParameter('by', 0) as string; + const body: { [key: string]: [] } = { emails: [], vids: [] }; + for (let i = 0; i < length; i++) { + if (by === 'id') { + const id = this.getNodeParameter('id', i) as string; + body.vids.push(parseInt(id, 10) as never); + } else { + const email = this.getNodeParameter('email', i) as string; + body.emails.push(email as never); + } + } + responseData = await hubspotApiRequest.call( + this, + 'POST', + `/contacts/v1/lists/${listId}/add`, + body, + ); + } + //https://legacydocs.hubspot.com/docs/methods/lists/remove_contact_from_list + if (operation === 'remove') { + const listId = this.getNodeParameter('listId', 0) as string; + const body: { [key: string]: [] } = { vids: [] }; + for (let i = 0; i < length; i++) { + const id = this.getNodeParameter('id', i) as string; + body.vids.push(parseInt(id, 10) as never); + } + responseData = await hubspotApiRequest.call( + this, + 'POST', + `/contacts/v1/lists/${listId}/remove`, + body, + ); + returnData.push.apply(returnData, responseData as INodeExecutionData[]); + } + } catch (error) { + if (this.continueOnFail()) { + returnData.push({ json: { error: (error as JsonObject).message } }); + } else { + throw error; + } + } + } else { + for (let i = 0; i < length; i++) { + try { + //https://developers.hubspot.com/docs/methods/contacts/create_or_update + if (resource === 'contact') { + //https://developers.hubspot.com/docs/methods/companies/create_company + if (operation === 'upsert') { + const email = this.getNodeParameter('email', i) as string; + const resolveData = this.getNodeParameter('resolveData', i); + const additionalFields = this.getNodeParameter('additionalFields', i); + const body: IDataObject[] = []; + if (additionalFields.annualRevenue) { + body.push({ + property: 'annualrevenue', + value: (additionalFields.annualRevenue as number).toString(), + }); + } + if (additionalFields.city) { + body.push({ + property: 'city', + value: additionalFields.city, + }); + } + if (additionalFields.clickedFacebookAd) { + body.push({ + property: 'hs_facebook_ad_clicked', + value: additionalFields.clickedFacebookAd, + }); + } + if (additionalFields.closeDate) { + body.push({ + property: 'closedate', + value: new Date(additionalFields.closeDate as string).getTime(), + }); + } + if (additionalFields.companyName) { + body.push({ + property: 'company', + value: additionalFields.companyName, + }); + } + if (additionalFields.companySize) { + body.push({ + property: 'company_size', + value: additionalFields.companySize, + }); + } + if (additionalFields.description) { + body.push({ + property: 'description', + value: additionalFields.description, + }); + } + if (additionalFields.contactOwner) { + body.push({ + property: 'hubspot_owner_id', + value: additionalFields.contactOwner, + }); + } + if (additionalFields.country) { + body.push({ + property: 'country', + value: additionalFields.country, + }); + } + if (additionalFields.dateOfBirth) { + body.push({ + property: 'date_of_birth', + value: additionalFields.dateOfBirth, + }); + } + if (additionalFields.degree) { + body.push({ + property: 'degree', + value: additionalFields.degree, + }); + } + if (additionalFields.facebookClickId) { + body.push({ + property: 'hs_facebook_click_id', + value: additionalFields.facebookClickId, + }); + } + if (additionalFields.faxNumber) { + body.push({ + property: 'fax', + value: additionalFields.faxNumber, + }); + } + if (additionalFields.fieldOfStudy) { + body.push({ + property: 'field_of_study', + value: additionalFields.fieldOfStudy, + }); + } + if (additionalFields.firstName) { + body.push({ + property: 'firstname', + value: additionalFields.firstName, + }); + } + if (additionalFields.gender) { + body.push({ + property: 'gender', + value: additionalFields.gender, + }); + } + if (additionalFields.googleAdClickId) { + body.push({ + property: 'hs_google_click_id', + value: additionalFields.googleAdClickId, + }); + } + if (additionalFields.graduationDate) { + body.push({ + property: 'graduation_date', + value: additionalFields.graduationDate, + }); + } + if (additionalFields.industry) { + body.push({ + property: 'industry', + value: additionalFields.industry, + }); + } + if (additionalFields.jobFunction) { + body.push({ + property: 'job_function', + value: additionalFields.jobFunction, + }); + } + if (additionalFields.jobTitle) { + body.push({ + property: 'jobtitle', + value: additionalFields.jobTitle, + }); + } + if (additionalFields.lastName) { + body.push({ + property: 'lastname', + value: additionalFields.lastName, + }); + } + if (additionalFields.leadStatus) { + body.push({ + property: 'hs_lead_status', + value: additionalFields.leadStatus, + }); + } + if (additionalFields.processingContactData) { + body.push({ + property: 'hs_legal_basis', + value: additionalFields.processingContactData, + }); + } + if (additionalFields.lifeCycleStage) { + body.push({ + property: 'lifecyclestage', + value: additionalFields.lifeCycleStage, + }); + } + if (additionalFields.maritalStatus) { + body.push({ + property: 'marital_status', + value: additionalFields.maritalStatus, + }); + } + if (additionalFields.membershipNote) { + body.push({ + property: 'hs_content_membership_notes', + value: additionalFields.membershipNote, + }); + } + if (additionalFields.message) { + body.push({ + property: 'message', + value: additionalFields.message, + }); + } + if (additionalFields.mobilePhoneNumber) { + body.push({ + property: 'mobilephone', + value: additionalFields.mobilePhoneNumber, + }); + } + if (additionalFields.numberOfEmployees) { + body.push({ + property: 'numemployees', + value: additionalFields.numberOfEmployees, + }); + } + if (additionalFields.originalSource) { + body.push({ + property: 'hs_analytics_source', + value: additionalFields.originalSource, + }); + } + if (additionalFields.phoneNumber) { + body.push({ + property: 'phone', + value: additionalFields.phoneNumber, + }); + } + if (additionalFields.postalCode) { + body.push({ + property: 'zip', + value: additionalFields.postalCode, + }); + } + if (additionalFields.prefferedLanguage) { + body.push({ + property: 'hs_language', + value: additionalFields.prefferedLanguage, + }); + } + if (additionalFields.relationshipStatus) { + body.push({ + property: 'relationship_status', + value: additionalFields.relationshipStatus, + }); + } + if (additionalFields.salutation) { + body.push({ + property: 'salutation', + value: additionalFields.salutation, + }); + } + if (additionalFields.school) { + body.push({ + property: 'school', + value: additionalFields.school, + }); + } + if (additionalFields.seniority) { + body.push({ + property: 'seniority', + value: additionalFields.seniority, + }); + } + if (additionalFields.startDate) { + body.push({ + property: 'start_date', + value: additionalFields.startDate, + }); + } + if (additionalFields.stateRegion) { + body.push({ + property: 'state', + value: additionalFields.stateRegion, + }); + } + if (additionalFields.status) { + body.push({ + property: 'hs_content_membership_status', + value: additionalFields.status, + }); + } + if (additionalFields.streetAddress) { + body.push({ + property: 'address', + value: additionalFields.streetAddress, + }); + } + if (additionalFields.twitterUsername) { + body.push({ + property: 'twitterhandle', + value: additionalFields.twitterUsername, + }); + } + if (additionalFields.websiteUrl) { + body.push({ + property: 'website', + value: additionalFields.websiteUrl, + }); + } + if (additionalFields.workEmail) { + body.push({ + property: 'work_email', + value: additionalFields.workEmail, + }); + } + + if (additionalFields.customPropertiesUi) { + const customProperties = (additionalFields.customPropertiesUi as IDataObject) + .customPropertiesValues as IDataObject[]; + + if (customProperties) { + for (const customProperty of customProperties) { + body.push({ + property: customProperty.property, + value: customProperty.value, + }); + } + } + } + + const endpoint = `/contacts/v1/contact/createOrUpdate/email/${email}`; + responseData = await hubspotApiRequest.call(this, 'POST', endpoint, { + properties: body, + }); + + if (additionalFields.associatedCompanyId) { + const companyAssociations: IDataObject[] = []; + companyAssociations.push({ + fromObjectId: responseData.vid, + toObjectId: additionalFields.associatedCompanyId, + category: 'HUBSPOT_DEFINED', + definitionId: 1, + }); + await hubspotApiRequest.call( + this, + 'PUT', + '/crm-associations/v1/associations/create-batch', + companyAssociations, + ); + } + + if (resolveData) { + const isNew = responseData.isNew; + if (additionalFields.properties) { + qs.property = additionalFields.properties as string[]; + } + responseData = await hubspotApiRequest.call( + this, + 'GET', + `/contacts/v1/contact/vid/${responseData.vid}/profile`, + {}, + qs, + ); + responseData.isNew = isNew; + } + } + //https://developers.hubspot.com/docs/methods/contacts/get_contact + if (operation === 'get') { + const contactId = this.getNodeParameter('contactId', i) as string; + const additionalFields = this.getNodeParameter('additionalFields', i); + if (additionalFields.formSubmissionMode) { + qs.formSubmissionMode = additionalFields.formSubmissionMode as string; + } + if (additionalFields.listMemberships) { + qs.showListMemberships = additionalFields.listMemberships as boolean; + } + if (additionalFields.properties) { + qs.property = additionalFields.properties as string[]; + } + if (additionalFields.propertyMode) { + qs.propertyMode = snakeCase(additionalFields.propertyMode as string); + } + const endpoint = `/contacts/v1/contact/vid/${contactId}/profile`; + responseData = await hubspotApiRequest.call(this, 'GET', endpoint, {}, qs); + } + //https://developers.hubspot.com/docs/methods/contacts/get_contacts + if (operation === 'getAll') { + const additionalFields = this.getNodeParameter('additionalFields', i); + const returnAll = this.getNodeParameter('returnAll', 0); + if (additionalFields.formSubmissionMode) { + qs.formSubmissionMode = additionalFields.formSubmissionMode as string; + } + if (additionalFields.listMemberships) { + qs.showListMemberships = additionalFields.listMemberships as boolean; + } + if (additionalFields.properties) { + qs.property = additionalFields.properties as string[]; + } + if (additionalFields.propertyMode) { + qs.propertyMode = snakeCase(additionalFields.propertyMode as string); + } + const endpoint = '/contacts/v1/lists/all/contacts/all'; + if (returnAll) { + responseData = await hubspotApiRequestAllItems.call( + this, + 'contacts', + 'GET', + endpoint, + {}, + qs, + ); + } else { + qs.count = this.getNodeParameter('limit', 0); + responseData = await hubspotApiRequest.call(this, 'GET', endpoint, {}, qs); + responseData = responseData.contacts; + } + } + //https://developers.hubspot.com/docs/methods/contacts/get_recently_created_contacts + if (operation === 'getRecentlyCreatedUpdated') { + const returnAll = this.getNodeParameter('returnAll', 0); + const filters = this.getNodeParameter('filters', i); + if (filters.formSubmissionMode) { + qs.formSubmissionMode = filters.formSubmissionMode as string; + } + if (filters.listMemberships) { + qs.showListMemberships = filters.listMemberships as boolean; + } + if (filters.properties) { + qs.property = filters.properties as string[]; + } + if (filters.propertyMode) { + qs.propertyMode = snakeCase(filters.propertyMode as string); + } + + const endpoint = '/contacts/v1/lists/recently_updated/contacts/recent'; + + if (returnAll) { + responseData = await hubspotApiRequestAllItems.call( + this, + 'contacts', + 'GET', + endpoint, + {}, + qs, + ); + } else { + qs.count = this.getNodeParameter('limit', 0); + responseData = await hubspotApiRequest.call(this, 'GET', endpoint, {}, qs); + responseData = responseData.contacts; + } + } + //https://developers.hubspot.com/docs/methods/contacts/delete_contact + if (operation === 'delete') { + const contactId = this.getNodeParameter('contactId', i) as string; + const endpoint = `/contacts/v1/contact/vid/${contactId}`; + responseData = await hubspotApiRequest.call(this, 'DELETE', endpoint); + } + //https://developers.hubspot.com/docs/api/crm/search + if (operation === 'search') { + const additionalFields = this.getNodeParameter('additionalFields', i); + const returnAll = this.getNodeParameter('returnAll', 0); + const filtersGroupsUi = this.getNodeParameter('filterGroupsUi', i) as IDataObject; + const sortBy = additionalFields.sortBy || 'createdate'; + const direction = additionalFields.direction || 'DESCENDING'; + + const body: IDataObject = { + sorts: [ + { + propertyName: sortBy, + direction, + }, + ], + }; + + if (filtersGroupsUi) { + const filterGroupValues = filtersGroupsUi.filterGroupsValues as IDataObject[]; + if (filterGroupValues) { + body.filterGroups = []; + for (const filterGroupValue of filterGroupValues) { + if (filterGroupValue.filtersUi) { + const filterValues = (filterGroupValue.filtersUi as IDataObject) + .filterValues as IDataObject[]; + if (filterValues) { + //@ts-ignore + body.filterGroups.push({ filters: filterValues }); + } + } + } + } + } + + Object.assign(body, additionalFields); + + const endpoint = '/crm/v3/objects/contacts/search'; + + if (returnAll) { + responseData = await hubspotApiRequestAllItems.call( + this, + 'results', + 'POST', + endpoint, + body, + qs, + ); + } else { + body.limit = this.getNodeParameter('limit', 0); + responseData = await hubspotApiRequest.call(this, 'POST', endpoint, body, qs); + responseData = responseData.results; + } + } + } + //https://developers.hubspot.com/docs/methods/companies/companies-overview + if (resource === 'company') { + //https://developers.hubspot.com/docs/methods/companies/create_company + if (operation === 'create') { + const name = this.getNodeParameter('name', i) as string; + const additionalFields = this.getNodeParameter('additionalFields', i); + const body: IDataObject[] = []; + body.push({ + name: 'name', + value: name, + }); + if (additionalFields.aboutUs) { + body.push({ + name: 'about_us', + value: additionalFields.aboutUs, + }); + } + if (additionalFields.annualRevenue) { + body.push({ + name: 'annualrevenue', + value: (additionalFields.annualRevenue as number).toString(), + }); + } + if (additionalFields.city) { + body.push({ + name: 'city', + value: additionalFields.city, + }); + } + if (additionalFields.closeDate) { + body.push({ + name: 'closedate', + value: new Date(additionalFields.closeDate as string).getTime(), + }); + } + if (additionalFields.companyDomainName) { + body.push({ + name: 'domain', + value: additionalFields.companyDomainName, + }); + } + if (additionalFields.companyOwner) { + body.push({ + name: 'hubspot_owner_id', + value: additionalFields.companyOwner, + }); + } + if (additionalFields.countryRegion) { + body.push({ + name: 'country', + value: additionalFields.countryRegion, + }); + } + if (additionalFields.description) { + body.push({ + name: 'description', + value: additionalFields.description, + }); + } + if (additionalFields.facebookFans) { + body.push({ + name: 'facebookfans', + value: additionalFields.facebookFans, + }); + } + if (additionalFields.googlePlusPage) { + body.push({ + name: 'googleplus_page', + value: additionalFields.googlePlusPage, + }); + } + if (additionalFields.industry) { + body.push({ + name: 'industry', + value: additionalFields.industry, + }); + } + if (additionalFields.isPublic) { + body.push({ + name: 'is_public', + value: additionalFields.isPublic, + }); + } + if (additionalFields.leadStatus) { + body.push({ + name: 'hs_lead_status', + value: additionalFields.leadStatus, + }); + } + if (additionalFields.lifecycleStatus) { + body.push({ + name: 'lifecyclestage', + value: additionalFields.lifecycleStatus, + }); + } + if (additionalFields.linkedinBio) { + body.push({ + name: 'linkedinbio', + value: additionalFields.linkedinBio, + }); + } + if (additionalFields.linkedInCompanyPage) { + body.push({ + name: 'linkedin_company_page', + value: additionalFields.linkedInCompanyPage, + }); + } + if (additionalFields.numberOfEmployees) { + body.push({ + name: 'numberofemployees', + value: additionalFields.numberOfEmployees, + }); + } + if (additionalFields.originalSourceType) { + body.push({ + name: 'hs_analytics_source', + value: additionalFields.originalSourceType, + }); + } + if (additionalFields.phoneNumber) { + body.push({ + name: 'phone', + value: additionalFields.phoneNumber, + }); + } + if (additionalFields.postalCode) { + body.push({ + name: 'zip', + value: additionalFields.postalCode, + }); + } + if (additionalFields.stateRegion) { + body.push({ + name: 'state', + value: additionalFields.stateRegion, + }); + } + if (additionalFields.streetAddress) { + body.push({ + name: 'address', + value: additionalFields.streetAddress, + }); + } + if (additionalFields.streetAddress2) { + body.push({ + name: 'address2', + value: additionalFields.streetAddress2, + }); + } + if (additionalFields.targetAccount) { + body.push({ + name: 'hs_target_account', + value: additionalFields.targetAccount, + }); + } + if (additionalFields.timezone) { + body.push({ + name: 'timezone', + value: additionalFields.timezone, + }); + } + if (additionalFields.totalMoneyRaised) { + body.push({ + name: 'total_money_raised', + value: additionalFields.totalMoneyRaised, + }); + } + if (additionalFields.twitterBio) { + body.push({ + name: 'twitterbio', + value: additionalFields.twitterBio, + }); + } + if (additionalFields.twitterFollowers) { + body.push({ + name: 'twitterfollowers', + value: additionalFields.twitterFollowers, + }); + } + if (additionalFields.twitterHandle) { + body.push({ + name: 'twitterhandle', + value: additionalFields.twitterHandle, + }); + } + if (additionalFields.type) { + body.push({ + name: 'type', + value: additionalFields.type, + }); + } + if (additionalFields.websiteUrl) { + body.push({ + name: 'website', + value: additionalFields.websiteUrl, + }); + } + if (additionalFields.webTechnologies) { + body.push({ + name: 'web_technologies', + value: additionalFields.webTechnologies, + }); + } + if (additionalFields.yearFounded) { + body.push({ + name: 'founded_year', + value: additionalFields.yearFounded, + }); + } + if (additionalFields.customPropertiesUi) { + const customProperties = (additionalFields.customPropertiesUi as IDataObject) + .customPropertiesValues as IDataObject[]; + + if (customProperties) { + for (const customProperty of customProperties) { + body.push({ + name: customProperty.property, + value: customProperty.value, + }); + } + } + } + const endpoint = '/companies/v2/companies'; + responseData = await hubspotApiRequest.call(this, 'POST', endpoint, { + properties: body, + }); + } + //https://developers.hubspot.com/docs/methods/companies/update_company + if (operation === 'update') { + const companyId = this.getNodeParameter('companyId', i) as string; + const updateFields = this.getNodeParameter('updateFields', i); + const body: IDataObject[] = []; + if (updateFields.name) { + body.push({ + name: 'name', + value: updateFields.name, + }); + } + if (updateFields.aboutUs) { + body.push({ + name: 'about_us', + value: updateFields.aboutUs, + }); + } + if (updateFields.annualRevenue) { + body.push({ + name: 'annualrevenue', + value: (updateFields.annualRevenue as number).toString(), + }); + } + if (updateFields.city) { + body.push({ + name: 'city', + value: updateFields.city, + }); + } + if (updateFields.closeDate) { + body.push({ + name: 'closedate', + value: new Date(updateFields.closeDate as string).getTime(), + }); + } + if (updateFields.companyDomainName) { + body.push({ + name: 'domain', + value: updateFields.companyDomainName, + }); + } + if (updateFields.companyOwner) { + body.push({ + name: 'hubspot_owner_id', + value: updateFields.companyOwner, + }); + } + if (updateFields.countryRegion) { + body.push({ + name: 'country', + value: updateFields.countryRegion, + }); + } + if (updateFields.description) { + body.push({ + name: 'description', + value: updateFields.description, + }); + } + if (updateFields.facebookFans) { + body.push({ + name: 'facebookfans', + value: updateFields.facebookFans, + }); + } + if (updateFields.googlePlusPage) { + body.push({ + name: 'googleplus_page', + value: updateFields.googlePlusPage, + }); + } + if (updateFields.industry) { + body.push({ + name: 'industry', + value: updateFields.industry, + }); + } + if (updateFields.isPublic) { + body.push({ + name: 'is_public', + value: updateFields.isPublic, + }); + } + if (updateFields.leadStatus) { + body.push({ + name: 'hs_lead_status', + value: updateFields.leadStatus, + }); + } + if (updateFields.lifecycleStatus) { + body.push({ + name: 'lifecyclestage', + value: updateFields.lifecycleStatus, + }); + } + if (updateFields.linkedinBio) { + body.push({ + name: 'linkedinbio', + value: updateFields.linkedinBio, + }); + } + if (updateFields.linkedInCompanyPage) { + body.push({ + name: 'linkedin_company_page', + value: updateFields.linkedInCompanyPage, + }); + } + if (updateFields.numberOfEmployees) { + body.push({ + name: 'numberofemployees', + value: updateFields.numberOfEmployees, + }); + } + if (updateFields.originalSourceType) { + body.push({ + name: 'hs_analytics_source', + value: updateFields.originalSourceType, + }); + } + if (updateFields.phoneNumber) { + body.push({ + name: 'phone', + value: updateFields.phoneNumber, + }); + } + if (updateFields.postalCode) { + body.push({ + name: 'zip', + value: updateFields.postalCode, + }); + } + if (updateFields.stateRegion) { + body.push({ + name: 'state', + value: updateFields.stateRegion, + }); + } + if (updateFields.streetAddress) { + body.push({ + name: 'address', + value: updateFields.streetAddress, + }); + } + if (updateFields.streetAddress2) { + body.push({ + name: 'address2', + value: updateFields.streetAddress2, + }); + } + if (updateFields.targetAccount) { + body.push({ + name: 'hs_target_account', + value: updateFields.targetAccount, + }); + } + if (updateFields.timezone) { + body.push({ + name: 'timezone', + value: updateFields.timezone, + }); + } + if (updateFields.totalMoneyRaised) { + body.push({ + name: 'total_money_raised', + value: updateFields.totalMoneyRaised, + }); + } + if (updateFields.twitterBio) { + body.push({ + name: 'twitterbio', + value: updateFields.twitterBio, + }); + } + if (updateFields.twitterFollowers) { + body.push({ + name: 'twitterfollowers', + value: updateFields.twitterFollowers, + }); + } + if (updateFields.twitterHandle) { + body.push({ + name: 'twitterhandle', + value: updateFields.twitterHandle, + }); + } + if (updateFields.type) { + body.push({ + name: 'type', + value: updateFields.type, + }); + } + if (updateFields.websiteUrl) { + body.push({ + name: 'website', + value: updateFields.websiteUrl, + }); + } + if (updateFields.webTechnologies) { + body.push({ + name: 'web_technologies', + value: updateFields.webTechnologies, + }); + } + if (updateFields.yearFounded) { + body.push({ + name: 'founded_year', + value: updateFields.yearFounded, + }); + } + if (updateFields.customPropertiesUi) { + const customProperties = (updateFields.customPropertiesUi as IDataObject) + .customPropertiesValues as IDataObject[]; + + if (customProperties) { + for (const customProperty of customProperties) { + body.push({ + name: customProperty.property, + value: customProperty.value, + }); + } + } + } + const endpoint = `/companies/v2/companies/${companyId}`; + responseData = await hubspotApiRequest.call(this, 'PUT', endpoint, { + properties: body, + }); + } + //https://developers.hubspot.com/docs/methods/companies/get_company + if (operation === 'get') { + const companyId = this.getNodeParameter('companyId', i) as string; + const additionalFields = this.getNodeParameter('additionalFields', i); + if (additionalFields.includeMergeAudits) { + qs.includeMergeAudits = additionalFields.includeMergeAudits as boolean; + } + const endpoint = `/companies/v2/companies/${companyId}`; + responseData = await hubspotApiRequest.call(this, 'GET', endpoint, {}, qs); + } + //https://developers.hubspot.com/docs/methods/companies/get-all-companies + if (operation === 'getAll') { + const options = this.getNodeParameter('options', i); + const returnAll = this.getNodeParameter('returnAll', 0); + if (options.includeMergeAudits) { + qs.includeMergeAudits = options.includeMergeAudits as boolean; + } + if (options.properties) { + qs.properties = options.properties as string[]; + } + if (options.propertiesWithHistory) { + qs.propertiesWithHistory = (options.propertiesWithHistory as string).split(','); + } + const endpoint = '/companies/v2/companies/paged'; + if (returnAll) { + responseData = await hubspotApiRequestAllItems.call( + this, + 'companies', + 'GET', + endpoint, + {}, + qs, + ); + } else { + qs.limit = this.getNodeParameter('limit', 0); + responseData = await hubspotApiRequest.call(this, 'GET', endpoint, {}, qs); + responseData = responseData.companies; + } + } + //https://developers.hubspot.com/docs/methods/companies/get_companies_modified + if (operation === 'getRecentlyCreated' || operation === 'getRecentlyModified') { + let endpoint; + const returnAll = this.getNodeParameter('returnAll', 0); + if (operation === 'getRecentlyCreated') { + endpoint = '/companies/v2/companies/recent/created'; + } else { + const filters = this.getNodeParameter('filters', i); + if (filters.since) { + qs.since = new Date(filters.since as string).getTime(); + } + endpoint = '/companies/v2/companies/recent/modified'; + } + if (returnAll) { + responseData = await hubspotApiRequestAllItems.call( + this, + 'results', + 'GET', + endpoint, + {}, + qs, + ); + } else { + qs.count = this.getNodeParameter('limit', 0); + responseData = await hubspotApiRequest.call(this, 'GET', endpoint, {}, qs); + responseData = responseData.results; + } + } + //https://developers.hubspot.com/docs/methods/companies/search_companies_by_domain + if (operation === 'searchByDomain') { + const domain = this.getNodeParameter('domain', i) as string; + const options = this.getNodeParameter('options', i); + const returnAll = this.getNodeParameter('returnAll', 0); + const body: IDataObject = { + requestOptions: {}, + }; + if (options.properties) { + body.requestOptions = { properties: options.properties as string[] }; + } + const endpoint = `/companies/v2/domains/${domain}/companies`; + if (returnAll) { + responseData = await hubspotApiRequestAllItems.call( + this, + 'results', + 'POST', + endpoint, + body, + ); + } else { + body.limit = this.getNodeParameter('limit', 0); + responseData = await hubspotApiRequest.call(this, 'POST', endpoint, body); + responseData = responseData.results; + } + } + //https://developers.hubspot.com/docs/methods/companies/delete_company + if (operation === 'delete') { + const companyId = this.getNodeParameter('companyId', i) as string; + const endpoint = `/companies/v2/companies/${companyId}`; + responseData = await hubspotApiRequest.call(this, 'DELETE', endpoint); + } + } + //https://developers.hubspot.com/docs/methods/deals/deals_overview + if (resource === 'deal') { + if (operation === 'create') { + const body: IDeal = {}; + body.properties = []; + const association: IAssociation = {}; + const additionalFields = this.getNodeParameter('additionalFields', i); + const stage = this.getNodeParameter('stage', i) as string; + if (stage) { + body.properties.push({ + name: 'dealstage', + value: stage, + }); + } + if (additionalFields.associatedCompany) { + association.associatedCompanyIds = additionalFields.associatedCompany as number[]; + } + if (additionalFields.associatedVids) { + association.associatedVids = additionalFields.associatedVids as number[]; + } + if (additionalFields.dealName) { + body.properties.push({ + name: 'dealname', + value: additionalFields.dealName as string, + }); + } + if (additionalFields.closeDate) { + body.properties.push({ + name: 'closedate', + value: new Date(additionalFields.closeDate as string).getTime(), + }); + } + if (additionalFields.amount) { + body.properties.push({ + name: 'amount', + value: additionalFields.amount as string, + }); + } + if (additionalFields.dealType) { + body.properties.push({ + name: 'dealtype', + value: additionalFields.dealType as string, + }); + } + if (additionalFields.pipeline) { + body.properties.push({ + name: 'pipeline', + value: additionalFields.pipeline as string, + }); + } + if (additionalFields.description) { + body.properties.push({ + name: 'description', + value: additionalFields.description as string, + }); + } + if (additionalFields.customPropertiesUi) { + const customProperties = (additionalFields.customPropertiesUi as IDataObject) + .customPropertiesValues as IDataObject[]; + if (customProperties) { + for (const customProperty of customProperties) { + body.properties.push({ + name: customProperty.property, + value: customProperty.value, + }); + } + } + } + body.associations = association; + const endpoint = '/deals/v1/deal'; + responseData = await hubspotApiRequest.call(this, 'POST', endpoint, body); + } + if (operation === 'update') { + const body: IDeal = {}; + body.properties = []; + const updateFields = this.getNodeParameter('updateFields', i); + const dealId = this.getNodeParameter('dealId', i) as string; + if (updateFields.stage) { + body.properties.push({ + name: 'dealstage', + value: updateFields.stage as string, + }); + } + if (updateFields.dealName) { + body.properties.push({ + name: 'dealname', + value: updateFields.dealName as string, + }); + } + if (updateFields.closeDate) { + body.properties.push({ + name: 'closedate', + value: new Date(updateFields.closeDate as string).getTime(), + }); + } + if (updateFields.amount) { + body.properties.push({ + name: 'amount', + value: updateFields.amount as string, + }); + } + if (updateFields.dealType) { + body.properties.push({ + name: 'dealtype', + value: updateFields.dealType as string, + }); + } + if (updateFields.pipeline) { + body.properties.push({ + name: 'pipeline', + value: updateFields.pipeline as string, + }); + } + if (updateFields.description) { + body.properties.push({ + name: 'description', + value: updateFields.description as string, + }); + } + if (updateFields.customPropertiesUi) { + const customProperties = (updateFields.customPropertiesUi as IDataObject) + .customPropertiesValues as IDataObject[]; + if (customProperties) { + for (const customProperty of customProperties) { + body.properties.push({ + name: customProperty.property, + value: customProperty.value, + }); + } + } + } + const endpoint = `/deals/v1/deal/${dealId}`; + responseData = await hubspotApiRequest.call(this, 'PUT', endpoint, body); + } + if (operation === 'get') { + const dealId = this.getNodeParameter('dealId', i) as string; + const additionalFields = this.getNodeParameter('additionalFields', i); + if (additionalFields.includePropertyVersions) { + qs.includePropertyVersions = additionalFields.includePropertyVersions as boolean; + } + const endpoint = `/deals/v1/deal/${dealId}`; + responseData = await hubspotApiRequest.call(this, 'GET', endpoint); + } + if (operation === 'getAll') { + const filters = this.getNodeParameter('filters', i); + const returnAll = this.getNodeParameter('returnAll', 0); + if (filters.includeAssociations) { + qs.includeAssociations = filters.includeAssociations as boolean; + } + if (filters.properties) { + const properties = filters.properties as string | string[]; + qs.properties = !Array.isArray(filters.properties) + ? (properties as string).split(',') + : properties; + } + if (filters.propertiesWithHistory) { + const propertiesWithHistory = filters.propertiesWithHistory as string | string[]; + qs.propertiesWithHistory = !Array.isArray(filters.propertiesWithHistory) + ? (propertiesWithHistory as string).split(',') + : propertiesWithHistory; + } + const endpoint = '/deals/v1/deal/paged'; + if (returnAll) { + responseData = await hubspotApiRequestAllItems.call( + this, + 'deals', + 'GET', + endpoint, + {}, + qs, + ); + } else { + qs.limit = this.getNodeParameter('limit', 0); + responseData = await hubspotApiRequest.call(this, 'GET', endpoint, {}, qs); + responseData = responseData.deals; + } + } + if (operation === 'getRecentlyCreated' || operation === 'getRecentlyModified') { + let endpoint; + const filters = this.getNodeParameter('filters', i); + const returnAll = this.getNodeParameter('returnAll', 0); + if (filters.since) { + qs.since = new Date(filters.since as string).getTime(); + } + if (filters.includePropertyVersions) { + qs.includePropertyVersions = filters.includePropertyVersions as boolean; + } + if (operation === 'getRecentlyCreated') { + endpoint = '/deals/v1/deal/recent/created'; + } else { + endpoint = '/deals/v1/deal/recent/modified'; + } + if (returnAll) { + responseData = await hubspotApiRequestAllItems.call( + this, + 'results', + 'GET', + endpoint, + {}, + qs, + ); + } else { + qs.count = this.getNodeParameter('limit', 0); + responseData = await hubspotApiRequest.call(this, 'GET', endpoint, {}, qs); + responseData = responseData.results; + } + } + if (operation === 'delete') { + const dealId = this.getNodeParameter('dealId', i) as string; + const endpoint = `/deals/v1/deal/${dealId}`; + responseData = await hubspotApiRequest.call(this, 'DELETE', endpoint); + } + //https://developers.hubspot.com/docs/api/crm/search + if (operation === 'search') { + const additionalFields = this.getNodeParameter('additionalFields', i); + const returnAll = this.getNodeParameter('returnAll', 0); + const filtersGroupsUi = this.getNodeParameter('filterGroupsUi', i) as IDataObject; + const sortBy = additionalFields.sortBy || 'createdate'; + const direction = additionalFields.direction || 'DESCENDING'; + + const body: IDataObject = { + sorts: [ + { + propertyName: sortBy, + direction, + }, + ], + }; + + if (filtersGroupsUi) { + const filterGroupValues = filtersGroupsUi.filterGroupsValues as IDataObject[]; + if (filterGroupValues) { + body.filterGroups = []; + for (const filterGroupValue of filterGroupValues) { + if (filterGroupValue.filtersUi) { + const filterValues = (filterGroupValue.filtersUi as IDataObject) + .filterValues as IDataObject[]; + if (filterValues) { + //@ts-ignore + body.filterGroups.push({ filters: filterValues }); + } + } + } + } + } + + Object.assign(body, additionalFields); + + const endpoint = '/crm/v3/objects/deals/search'; + + if (returnAll) { + responseData = await hubspotApiRequestAllItems.call( + this, + 'results', + 'POST', + endpoint, + body, + qs, + ); + } else { + body.limit = this.getNodeParameter('limit', 0); + responseData = await hubspotApiRequest.call(this, 'POST', endpoint, body, qs); + responseData = responseData.results; + } + } + } + if (resource === 'engagement') { + //https://legacydocs.hubspot.com/docs/methods/engagements/create_engagement + if (operation === 'create') { + const type = this.getNodeParameter('type', i) as string; + const metadata = this.getNodeParameter('metadata', i) as IDataObject; + const associations = this.getNodeParameter( + 'additionalFields.associations', + i, + {}, + ) as IDataObject; + + if (!Object.keys(metadata).length) { + throw new NodeOperationError( + this.getNode(), + 'At least one metadata field needs to set', + { itemIndex: i }, + ); + } + + const body: { + engagement: { type: string }; + metadata: IDataObject; + associations: IDataObject; + } = { + engagement: { + type: type.toUpperCase(), + }, + metadata: {}, + associations: {}, + }; + + if (type === 'email') { + body.metadata = getEmailMetadata(metadata); + } + + if (type === 'task') { + body.metadata = getTaskMetadata(metadata); + } + + if (type === 'meeting') { + body.metadata = getMeetingMetadata(metadata); + } + + if (type === 'call') { + body.metadata = getCallMetadata(metadata); + } + + //@ts-ignore + body.associations = getAssociations(associations); + + const endpoint = '/engagements/v1/engagements'; + responseData = await hubspotApiRequest.call(this, 'POST', endpoint, body); + } + //https://legacydocs.hubspot.com/docs/methods/engagements/get_engagement + if (operation === 'delete') { + const engagementId = this.getNodeParameter('engagementId', i) as string; + const endpoint = `/engagements/v1/engagements/${engagementId}`; + responseData = await hubspotApiRequest.call(this, 'DELETE', endpoint, {}, qs); + responseData = { success: true }; + } + //https://legacydocs.hubspot.com/docs/methods/engagements/get_engagement + if (operation === 'get') { + const engagementId = this.getNodeParameter('engagementId', i) as string; + const endpoint = `/engagements/v1/engagements/${engagementId}`; + responseData = await hubspotApiRequest.call(this, 'GET', endpoint, {}, qs); + } + //https://legacydocs.hubspot.com/docs/methods/engagements/get-all-engagements + if (operation === 'getAll') { + const returnAll = this.getNodeParameter('returnAll', 0); + const endpoint = '/engagements/v1/engagements/paged'; + if (returnAll) { + responseData = await hubspotApiRequestAllItems.call( + this, + 'results', + 'GET', + endpoint, + {}, + qs, + ); + } else { + qs.limit = this.getNodeParameter('limit', 0); + responseData = await hubspotApiRequest.call(this, 'GET', endpoint, {}, qs); + responseData = responseData.results; + } + } + } + //https://developers.hubspot.com/docs/methods/forms/forms_overview + if (resource === 'form') { + //https://developers.hubspot.com/docs/methods/forms/v2/get_fields + if (operation === 'getFields') { + const formId = this.getNodeParameter('formId', i) as string; + responseData = await hubspotApiRequest.call( + this, + 'GET', + `/forms/v2/fields/${formId}`, + ); + } + //https://developers.hubspot.com/docs/methods/forms/submit_form_v3 + if (operation === 'submit') { + const formId = this.getNodeParameter('formId', i) as string; + const additionalFields = this.getNodeParameter('additionalFields', i); + const context = (this.getNodeParameter('contextUi', i) as IDataObject) + .contextValue as IDataObject; + const legalConsent = (this.getNodeParameter('lengalConsentUi', i) as IDataObject) + .lengalConsentValues as IDataObject; + const legitimateInteres = (this.getNodeParameter('lengalConsentUi', i) as IDataObject) + .legitimateInterestValues as IDataObject; + const { portalId } = await hubspotApiRequest.call( + this, + 'GET', + `/forms/v2/forms/${formId}`, + ); + const body: IForm = { + formId, + portalId, + legalConsentOptions: {}, + fields: [], + }; + if (additionalFields.submittedAt) { + body.submittedAt = new Date(additionalFields.submittedAt as string).getTime(); + } + if (additionalFields.skipValidation) { + body.skipValidation = additionalFields.skipValidation as boolean; + } + const consent: IDataObject = {}; + if (legalConsent) { + if (legalConsent.consentToProcess) { + consent.consentToProcess = legalConsent.consentToProcess as boolean; + } + if (legalConsent.text) { + consent.text = legalConsent.text as string; + } + if (legalConsent.communicationsUi) { + consent.communications = (legalConsent.communicationsUi as IDataObject) + .communicationValues as IDataObject; + } + } + body.legalConsentOptions!.consent = consent; + const fields: IDataObject = items[i].json; + for (const key of Object.keys(fields)) { + body.fields?.push({ name: key, value: fields[key] }); + } + if (body.legalConsentOptions!.legitimateInterest) { + Object.assign(body, { + legalConsentOptions: { legitimateInterest: legitimateInteres }, + }); + } + if (context) { + clean(context); + Object.assign(body, { context }); + } + const uri = `https://api.hsforms.com/submissions/v3/integration/submit/${portalId}/${formId}`; + responseData = await hubspotApiRequest.call(this, 'POST', '', body, {}, uri); + } + } + //https://developers.hubspot.com/docs/methods/tickets/tickets-overview + if (resource === 'ticket') { + //https://developers.hubspot.com/docs/methods/tickets/create-ticket + if (operation === 'create') { + const additionalFields = this.getNodeParameter('additionalFields', i); + const pipelineId = this.getNodeParameter('pipelineId', i) as string; + const stageId = this.getNodeParameter('stageId', i) as string; + const ticketName = this.getNodeParameter('ticketName', i) as string; + const body: IDataObject[] = [ + { + // eslint-disable-next-line n8n-nodes-base/node-param-display-name-miscased + name: 'hs_pipeline', + value: pipelineId, + }, + { + // eslint-disable-next-line n8n-nodes-base/node-param-display-name-miscased + name: 'hs_pipeline_stage', + value: stageId, + }, + { + // eslint-disable-next-line n8n-nodes-base/node-param-display-name-miscased + name: 'subject', + value: ticketName, + }, + ]; + if (additionalFields.category) { + body.push({ + name: 'hs_ticket_category', + value: additionalFields.category as string, + }); + } + if (additionalFields.closeDate) { + body.push({ + name: 'closed_date', + value: new Date(additionalFields.closeDate as string).getTime(), + }); + } + if (additionalFields.createDate) { + body.push({ + name: 'createdate', + value: new Date(additionalFields.createDate as string).getTime(), + }); + } + if (additionalFields.description) { + body.push({ + name: 'content', + value: additionalFields.description as string, + }); + } + if (additionalFields.priority) { + body.push({ + name: 'hs_ticket_priority', + value: additionalFields.priority as string, + }); + } + if (additionalFields.resolution) { + body.push({ + name: 'hs_resolution', + value: additionalFields.resolution as string, + }); + } + if (additionalFields.source) { + body.push({ + name: 'source_type', + value: additionalFields.source as string, + }); + } + if (additionalFields.ticketOwnerId) { + body.push({ + name: 'hubspot_owner_id', + value: additionalFields.ticketOwnerId as string, + }); + } + const endpoint = '/crm-objects/v1/objects/tickets'; + responseData = await hubspotApiRequest.call(this, 'POST', endpoint, body); + + if (additionalFields.associatedCompanyIds) { + const companyAssociations: IDataObject[] = []; + for (const companyId of additionalFields.associatedCompanyIds as IDataObject[]) { + companyAssociations.push({ + fromObjectId: responseData.objectId, + toObjectId: companyId, + category: 'HUBSPOT_DEFINED', + definitionId: 26, + }); + } + await hubspotApiRequest.call( + this, + 'PUT', + '/crm-associations/v1/associations/create-batch', + companyAssociations, + ); + } + + if (additionalFields.associatedContactIds) { + const contactAssociations: IDataObject[] = []; + for (const contactId of additionalFields.associatedContactIds as IDataObject[]) { + contactAssociations.push({ + fromObjectId: responseData.objectId, + toObjectId: contactId, + category: 'HUBSPOT_DEFINED', + definitionId: 16, + }); + } + await hubspotApiRequest.call( + this, + 'PUT', + '/crm-associations/v1/associations/create-batch', + contactAssociations, + ); + } + } + //https://developers.hubspot.com/docs/methods/tickets/get_ticket_by_id + if (operation === 'get') { + const ticketId = this.getNodeParameter('ticketId', i) as string; + const additionalFields = this.getNodeParameter('additionalFields', i); + if (additionalFields.properties) { + qs.properties = additionalFields.properties as string[]; + } + if (additionalFields.propertiesWithHistory) { + qs.propertiesWithHistory = (additionalFields.propertiesWithHistory as string).split( + ',', + ); + } + if (additionalFields.includeDeleted) { + qs.includeDeleted = additionalFields.includeDeleted as boolean; + } + const endpoint = `/crm-objects/v1/objects/tickets/${ticketId}`; + responseData = await hubspotApiRequest.call(this, 'GET', endpoint, {}, qs); + } + //https://developers.hubspot.com/docs/methods/tickets/get-all-tickets + if (operation === 'getAll') { + const additionalFields = this.getNodeParameter('additionalFields', i); + const returnAll = this.getNodeParameter('returnAll', 0); + if (additionalFields.properties) { + qs.properties = additionalFields.properties as string[]; + } + if (additionalFields.propertiesWithHistory) { + qs.propertiesWithHistory = (additionalFields.propertiesWithHistory as string).split( + ',', + ); + } + const endpoint = '/crm-objects/v1/objects/tickets/paged'; + if (returnAll) { + responseData = await hubspotApiRequestAllItems.call( + this, + 'objects', + 'GET', + endpoint, + {}, + qs, + ); + } else { + qs.limit = this.getNodeParameter('limit', 0); + responseData = await hubspotApiRequestAllItems.call( + this, + 'objects', + 'GET', + endpoint, + {}, + qs, + ); + responseData = responseData.splice(0, qs.limit); + } + } + //https://developers.hubspot.com/docs/methods/tickets/delete-ticket + if (operation === 'delete') { + const ticketId = this.getNodeParameter('ticketId', i) as string; + const endpoint = `/crm-objects/v1/objects/tickets/${ticketId}`; + await hubspotApiRequest.call(this, 'DELETE', endpoint); + responseData = { success: true }; + } + //https://developers.hubspot.com/docs/methods/tickets/update-ticket + if (operation === 'update') { + const updateFields = this.getNodeParameter('updateFields', i); + const ticketId = this.getNodeParameter('ticketId', i) as string; + const body: IDataObject[] = []; + if (updateFields.pipelineId) { + body.push({ + name: 'hs_pipeline', + value: updateFields.pipelineId as string, + }); + } + if (updateFields.stageId) { + body.push({ + name: 'hs_pipeline_stage', + value: updateFields.stageId as string, + }); + } + if (updateFields.ticketName) { + body.push({ + name: 'subject', + value: updateFields.ticketName as string, + }); + } + if (updateFields.category) { + body.push({ + name: 'hs_ticket_category', + value: updateFields.category as string, + }); + } + if (updateFields.closeDate) { + body.push({ + name: 'closed_date', + value: new Date(updateFields.createDate as string).getTime(), + }); + } + if (updateFields.createDate) { + body.push({ + name: 'createdate', + value: new Date(updateFields.createDate as string).getTime(), + }); + } + if (updateFields.description) { + body.push({ + name: 'content', + value: updateFields.description as string, + }); + } + if (updateFields.priority) { + body.push({ + name: 'hs_ticket_priority', + value: updateFields.priority as string, + }); + } + if (updateFields.resolution) { + body.push({ + name: 'hs_resolution', + value: updateFields.resolution as string, + }); + } + if (updateFields.source) { + body.push({ + name: 'source_type', + value: updateFields.source as string, + }); + } + if (updateFields.ticketOwnerId) { + body.push({ + name: 'hubspot_owner_id', + value: updateFields.ticketOwnerId as string, + }); + } + const endpoint = `/crm-objects/v1/objects/tickets/${ticketId}`; + responseData = await hubspotApiRequest.call(this, 'PUT', endpoint, body); + + if (updateFields.associatedCompanyIds) { + const companyAssociations: IDataObject[] = []; + for (const companyId of updateFields.associatedCompanyIds as IDataObject[]) { + companyAssociations.push({ + fromObjectId: responseData.objectId, + toObjectId: companyId, + category: 'HUBSPOT_DEFINED', + definitionId: 26, + }); + } + await hubspotApiRequest.call( + this, + 'PUT', + '/crm-associations/v1/associations/create-batch', + companyAssociations, + ); + } + + if (updateFields.associatedContactIds) { + const contactAssociations: IDataObject[] = []; + for (const contactId of updateFields.associatedContactIds as IDataObject[]) { + contactAssociations.push({ + fromObjectId: responseData.objectId, + toObjectId: contactId, + category: 'HUBSPOT_DEFINED', + definitionId: 16, + }); + } + await hubspotApiRequest.call( + this, + 'PUT', + '/crm-associations/v1/associations/create-batch', + contactAssociations, + ); + } + } + } + const executionData = this.helpers.constructExecutionMetaData( + this.helpers.returnJsonArray(responseData as IDataObject[]), + { itemData: { item: i } }, + ); + returnData.push(...executionData); + } catch (error) { + if (this.continueOnFail()) { + returnData.push({ json: { error: (error as JsonObject).message } }); + continue; + } + throw error; + } + } + } + return this.prepareOutputData(returnData); + } +} diff --git a/packages/nodes-base/nodes/Hubspot/TicketDescription.ts b/packages/nodes-base/nodes/Hubspot/V1/TicketDescription.ts similarity index 100% rename from packages/nodes-base/nodes/Hubspot/TicketDescription.ts rename to packages/nodes-base/nodes/Hubspot/V1/TicketDescription.ts diff --git a/packages/nodes-base/nodes/Hubspot/V2/CompanyDescription.ts b/packages/nodes-base/nodes/Hubspot/V2/CompanyDescription.ts new file mode 100644 index 0000000000..93361b27dd --- /dev/null +++ b/packages/nodes-base/nodes/Hubspot/V2/CompanyDescription.ts @@ -0,0 +1,1206 @@ +import type { INodeProperties } from 'n8n-workflow'; + +export const companyOperations: INodeProperties[] = [ + { + displayName: 'Operation', + name: 'operation', + type: 'options', + noDataExpression: true, + displayOptions: { + show: { + resource: ['company'], + }, + }, + options: [ + { + name: 'Create', + value: 'create', + description: 'Create a company', + action: 'Create a company', + }, + { + name: 'Delete', + value: 'delete', + description: 'Delete a company', + action: 'Delete a company', + }, + { + name: 'Get', + value: 'get', + description: 'Get a company', + action: 'Get a company', + }, + { + name: 'Get Many', + value: 'getAll', + description: 'Get many companies', + action: 'Get many companies', + }, + { + name: 'Get Recently Created/Updated', + value: 'getRecentlyCreatedUpdated', + description: 'Get recently created/updated companies', + action: 'Get recently created/updated companies', + }, + { + name: 'Search', + value: 'searchByDomain', + description: 'Search companies by their website domain', + action: 'Search for a company by Domain', + }, + { + name: 'Update', + value: 'update', + description: 'Update a company', + action: 'Update a company', + }, + ], + default: 'create', + }, +]; + +export const companyFields: INodeProperties[] = [ + /* -------------------------------------------------------------------------- */ + /* company:create */ + /* -------------------------------------------------------------------------- */ + { + displayName: 'Name', + name: 'name', + type: 'string', + required: true, + displayOptions: { + show: { + resource: ['company'], + operation: ['create'], + }, + }, + default: '', + }, + { + displayName: 'Company Properties', + name: 'additionalFields', + type: 'collection', + placeholder: 'Add Property', + default: {}, + displayOptions: { + show: { + resource: ['company'], + operation: ['create'], + }, + }, + options: [ + { + displayName: 'About Us', + name: 'aboutUs', + type: 'string', + typeOptions: { + alwaysOpenEditWindow: true, + }, + default: '', + }, + { + displayName: 'Annual Revenue', + name: 'annualRevenue', + type: 'number', + typeOptions: { + minValue: 0, + }, + default: 0, + description: 'The actual or estimated annual revenue of the company', + }, + { + displayName: 'City', + name: 'city', + type: 'string', + default: '', + description: 'The city where the company is located', + }, + { + displayName: 'Close Date', + name: 'closeDate', + type: 'dateTime', + default: '', + description: + 'The date the company or organization was closed as a customer. When using expressions, the time should be specified in YYYY-MM-DD hh-mm-ss format.', + }, + { + displayName: 'Company Domain Name', + name: 'companyDomainName', + type: 'string', + default: '', + description: 'The domain name of the company or organization', + }, + { + displayName: 'Company Owner Name or ID', + name: 'companyOwner', + type: 'options', + typeOptions: { + loadOptionsMethod: 'getOwners', + }, + default: '', + description: + 'The owner of the company. Choose from the list, or specify an ID using an expression.', + }, + { + displayName: 'Country/Region', + name: 'countryRegion', + type: 'string', + default: '', + description: 'The country/region in which the company or organization is located', + }, + { + displayName: 'Custom Properties', + name: 'customPropertiesUi', + placeholder: 'Add Custom Property', + type: 'fixedCollection', + typeOptions: { + multipleValues: true, + }, + default: {}, + options: [ + { + name: 'customPropertiesValues', + displayName: 'Custom Property', + values: [ + { + displayName: 'Property Name or ID', + name: 'property', + type: 'options', + typeOptions: { + loadOptionsMethod: 'getCompanyCustomProperties', + }, + default: {}, + description: + 'Name of the property. Choose from the list, or specify an ID using an expression.', + }, + { + displayName: 'Value', + name: 'value', + type: 'string', + default: '', + required: true, + description: 'Value of the property', + }, + ], + }, + ], + }, + { + displayName: 'Description', + name: 'description', + type: 'string', + default: '', + description: "A short statement about the company's mission and goals", + }, + { + displayName: 'Facebook Fans', + name: 'facebookFans', + type: 'number', + typeOptions: { + minValue: 0, + }, + default: 0, + description: 'Number of facebook fans', + }, + { + displayName: 'Google Plus Page', + name: 'googlePlusPage', + type: 'string', + default: '', + description: 'The URL of the Google Plus page for the company or organization', + }, + { + displayName: 'Industry Name or ID', + name: 'industry', + type: 'options', + typeOptions: { + loadOptionsMethod: 'getCompanyIndustries', + }, + default: '', + description: + 'The type of business the company performs. Choose from the list, or specify an ID using an expression.', + }, + { + displayName: 'Is Public', + name: 'isPublic', + type: 'boolean', + default: false, + description: 'Whether that the company is publicly traded', + }, + { + displayName: 'Lead Status Name or ID', + name: 'leadStatus', + type: 'options', + typeOptions: { + loadOptionsMethod: 'getCompanyleadStatuses', + }, + default: '', + description: + 'The company\'s sales, prospecting or outreach status. Choose from the list, or specify an ID using an expression.', + }, + { + displayName: 'Lifecycle Stage Name or ID', + name: 'lifecycleStatus', + type: 'options', + typeOptions: { + loadOptionsMethod: 'getCompanylifecycleStages', + }, + default: '', + description: + 'The most advanced lifecycle stage across all contacts associated with this company or organization. Choose from the list, or specify an ID using an expression.', + }, + { + displayName: 'LinkedIn Bio', + name: 'linkedinBio', + type: 'string', + default: '', + description: 'The LinkedIn bio for the company or organization', + }, + { + displayName: 'LinkedIn Company Page', + name: 'linkedInCompanyPage', + type: 'string', + default: '', + description: 'The URL of the LinkedIn company page for the company or organization', + }, + { + displayName: 'Number Of Employees', + name: 'numberOfEmployees', + type: 'number', + typeOptions: { + minValue: 0, + }, + default: 0, + description: 'The total number of employees who work for the company or organization', + }, + { + displayName: 'Original Source Type Name or ID', + name: 'originalSourceType', + type: 'options', + typeOptions: { + loadOptionsMethod: 'getCompanySourceTypes', + }, + default: '', + description: + 'Original source for the contact with the earliest activity for this company or organization. Choose from the list, or specify an ID using an expression.', + }, + { + displayName: 'Phone Number', + name: 'phoneNumber', + type: 'string', + default: '', + description: "A company's primary phone number. Powered by HubSpot Insights.", + }, + { + displayName: 'Postal Code', + name: 'postalCode', + type: 'string', + default: '', + description: + 'The postal or zip code of the company or organization. Powered by HubSpot Insights.', + }, + { + displayName: 'State/Region', + name: 'stateRegion', + type: 'string', + default: '', + description: + 'The state or region in which the company or organization is located. Powered by HubSpot Insights.', + }, + { + displayName: 'Street Address', + name: 'streetAddress', + type: 'string', + default: '', + description: + 'The street address of the company or organization, including unit number. Powered by HubSpot Insights.', + }, + { + displayName: 'Street Address 2', + name: 'streetAddress2', + type: 'string', + default: '', + description: + 'The additional address of the company or organization. Powered by HubSpot Insights.', + }, + + { + displayName: 'Target Account Name or ID', + name: 'targetAccount', + type: 'options', + typeOptions: { + loadOptionsMethod: 'getCompanyTargetAccounts', + }, + default: '', + description: + 'The Target Account property is a means to flag high priority companies if you are following an account based strategy. Choose from the list, or specify an ID using an expression.', + }, + { + displayName: 'Timezone', + name: 'timezone', + type: 'string', + default: '', + description: + 'The time zone where the company or organization is located. Powered by HubSpot Insights.', + }, + { + displayName: 'Total Money Raised', + name: 'totalMoneyRaised', + type: 'number', + typeOptions: { + minValue: 0, + }, + default: 0, + description: + 'The total amount of money raised by the company. Powered by HubSpot Insights.', + }, + { + displayName: 'Twitter Bio', + name: 'twitterBio', + type: 'string', + typeOptions: { + alwaysOpenEditWindow: true, + }, + default: '', + description: 'The Twitter bio of the company or organization', + }, + { + displayName: 'Twitter Followers', + name: 'twitterFollowers', + type: 'number', + typeOptions: { + minValue: 0, + }, + default: 0, + description: 'The number of Twitter followers of the company or organization', + }, + { + displayName: 'Twitter Handle', + name: 'twitterHandle', + type: 'string', + default: '', + description: 'The main twitter account of the company or organization', + }, + { + displayName: 'Type Name or ID', + name: 'type', + type: 'options', + typeOptions: { + loadOptionsMethod: 'getCompanyTypes', + }, + default: '', + description: + 'The optional classification of this company record - prospect, partner, etc. Choose from the list, or specify an ID using an expression.', + }, + { + displayName: 'Web Technologies Name or ID', + name: 'webTechnologies', + type: 'options', + typeOptions: { + loadOptionsMethod: 'getCompanyWebTechnologies', + }, + default: '', + description: + 'The web technologies used by the company or organization. Powered by HubSpot Insights. Choose from the list, or specify an ID using an expression.', + }, + { + displayName: 'Website URL', + name: 'websiteUrl', + type: 'string', + default: '', + description: + 'The main website of the company or organization. This property is used to identify unique companies. Powered by HubSpot Insights.', + }, + { + displayName: 'Year Founded', + name: 'yearFounded', + type: 'string', + default: '', + description: 'The year the company was created. Powered by HubSpot Insights.', + }, + ], + }, + + /* -------------------------------------------------------------------------- */ + /* company:update */ + /* -------------------------------------------------------------------------- */ + { + displayName: 'Company to Update', + name: 'companyId', + type: 'resourceLocator', + default: { mode: 'list', value: '' }, + required: true, + displayOptions: { + show: { + resource: ['company'], + operation: ['update'], + }, + }, + modes: [ + { + displayName: 'From List', + name: 'list', + type: 'list', + placeholder: 'Select from the list', + typeOptions: { + searchListMethod: 'searchCompanies', + }, + }, + { + displayName: 'By Id', + name: 'id', + type: 'string', + placeholder: '58539222', + validation: [ + { + type: 'regex', + properties: { + regex: '[0-9]+', + errorMessage: 'Not a valid HubSpot Company ID', + }, + }, + ], + }, + ], + }, + { + displayName: 'Update Fields', + name: 'updateFields', + type: 'collection', + placeholder: 'Add Field', + default: {}, + displayOptions: { + show: { + resource: ['company'], + operation: ['update'], + }, + }, + options: [ + { + displayName: 'About Us', + name: 'aboutUs', + type: 'string', + typeOptions: { + alwaysOpenEditWindow: true, + }, + default: '', + }, + { + displayName: 'Annual Revenue', + name: 'annualRevenue', + type: 'number', + typeOptions: { + minValue: 0, + }, + default: 0, + description: 'The actual or estimated annual revenue of the company', + }, + { + displayName: 'City', + name: 'city', + type: 'string', + default: '', + description: 'The city where the company is located', + }, + { + displayName: 'Close Date', + name: 'closeDate', + type: 'dateTime', + default: '', + description: + 'The date the company or organization was closed as a customer. When using expressions, the time should be specified in YYYY-MM-DD hh-mm-ss format.', + }, + { + displayName: 'Company Domain Name', + name: 'companyDomainName', + type: 'string', + default: '', + description: 'The domain name of the company or organization', + }, + { + displayName: 'Company Owmer Name or ID', + name: 'companyOwner', + type: 'options', + typeOptions: { + loadOptionsMethod: 'getOwners', + }, + default: '', + description: + 'The owner of the company. Choose from the list, or specify an ID using an expression.', + }, + { + displayName: 'Country/Region', + name: 'countryRegion', + type: 'string', + default: '', + description: 'The country/region in which the company or organization is located', + }, + { + displayName: 'Custom Properties', + name: 'customPropertiesUi', + placeholder: 'Add Custom Property', + type: 'fixedCollection', + typeOptions: { + multipleValues: true, + }, + default: {}, + options: [ + { + name: 'customPropertiesValues', + displayName: 'Custom Property', + values: [ + { + displayName: 'Property Name or ID', + name: 'property', + type: 'options', + typeOptions: { + loadOptionsMethod: 'getCompanyCustomProperties', + }, + default: '', + description: + 'Name of the property. Choose from the list, or specify an ID using an expression.', + }, + { + displayName: 'Value', + name: 'value', + type: 'string', + default: '', + required: true, + description: 'Value of the property', + }, + ], + }, + ], + }, + { + displayName: 'Description', + name: 'description', + type: 'string', + default: '', + description: "A short statement about the company's mission and goals", + }, + { + displayName: 'Facebook Fans', + name: 'facebookFans', + type: 'number', + typeOptions: { + minValue: 0, + }, + default: 0, + description: 'Number of facebook fans', + }, + { + displayName: 'Google Plus Page', + name: 'googlePlusPage', + type: 'string', + default: '', + description: 'The URL of the Google Plus page for the company or organization', + }, + { + displayName: 'Industry Name or ID', + name: 'industry', + type: 'options', + typeOptions: { + loadOptionsMethod: 'getCompanyIndustries', + }, + default: '', + description: + 'The type of business the company performs. Choose from the list, or specify an ID using an expression.', + }, + { + displayName: 'Is Public', + name: 'isPublic', + type: 'boolean', + default: false, + description: 'Whether that the company is publicly traded', + }, + { + displayName: 'Lead Status Name or ID', + name: 'leadStatus', + type: 'options', + typeOptions: { + loadOptionsMethod: 'getCompanyleadStatuses', + }, + default: '', + description: + 'The company\'s sales, prospecting or outreach status. Choose from the list, or specify an ID using an expression.', + }, + { + displayName: 'Lifecycle Stage Name or ID', + name: 'lifecycleStatus', + type: 'options', + typeOptions: { + loadOptionsMethod: 'getCompanylifecycleStages', + }, + default: '', + description: + 'The most advanced lifecycle stage across all contacts associated with this company or organization. Choose from the list, or specify an ID using an expression.', + }, + { + displayName: 'Linkedin Bio', + name: 'linkedinBio', + type: 'string', + default: '', + description: 'The LinkedIn bio for the company or organization', + }, + { + displayName: 'LinkedIn Company Page', + name: 'linkedInCompanyPage', + type: 'string', + default: '', + description: 'The URL of the LinkedIn company page for the company or organization', + }, + { + displayName: 'Name', + name: 'name', + type: 'string', + default: '', + }, + { + displayName: 'Number Of Employees', + name: 'numberOfEmployees', + type: 'number', + typeOptions: { + minValue: 0, + }, + default: 0, + description: 'The total number of employees who work for the company or organization', + }, + { + displayName: 'Original Source Type Name or ID', + name: 'originalSourceType', + type: 'options', + typeOptions: { + loadOptionsMethod: 'getCompanySourceTypes', + }, + default: '', + description: + 'Original source for the contact with the earliest activity for this company or organization. Choose from the list, or specify an ID using an expression.', + }, + { + displayName: 'Phone Number', + name: 'phoneNumber', + type: 'string', + default: '', + description: "A company's primary phone number. Powered by HubSpot Insights.", + }, + { + displayName: 'Postal Code', + name: 'postalCode', + type: 'string', + default: '', + description: + 'The postal or zip code of the company or organization. Powered by HubSpot Insights.', + }, + { + displayName: 'State/Region', + name: 'stateRegion', + type: 'string', + default: '', + description: + 'The state or region in which the company or organization is located. Powered by HubSpot Insights.', + }, + { + displayName: 'Street Address', + name: 'streetAddress', + type: 'string', + default: '', + description: + 'The street address of the company or organization, including unit number. Powered by HubSpot Insights.', + }, + { + displayName: 'Street Address 2', + name: 'streetAddress2', + type: 'string', + default: '', + description: + 'The additional address of the company or organization. Powered by HubSpot Insights.', + }, + + { + displayName: 'Target Account Name or ID', + name: 'targetAccount', + type: 'options', + typeOptions: { + loadOptionsMethod: 'getCompanyTargetAccounts', + }, + default: '', + description: + 'The Target Account property is a means to flag high priority companies if you are following an account based strategy. Choose from the list, or specify an ID using an expression.', + }, + { + displayName: 'Timezone', + name: 'timezone', + type: 'string', + default: '', + description: + 'The time zone where the company or organization is located. Powered by HubSpot Insights.', + }, + { + displayName: 'Total Money Raised', + name: 'totalMoneyRaised', + type: 'number', + typeOptions: { + minValue: 0, + }, + default: 0, + description: + 'The total amount of money raised by the company. Powered by HubSpot Insights.', + }, + { + displayName: 'Twitter Bio', + name: 'twitterBio', + type: 'string', + typeOptions: { + alwaysOpenEditWindow: true, + }, + default: '', + description: 'The Twitter bio of the company or organization', + }, + { + displayName: 'Twitter Followers', + name: 'twitterFollowers', + type: 'number', + typeOptions: { + minValue: 0, + }, + default: 0, + description: 'The number of Twitter followers of the company or organization', + }, + { + displayName: 'Twitter Handle', + name: 'twitterHandle', + type: 'string', + default: '', + description: 'The main twitter account of the company or organization', + }, + { + displayName: 'Type Name or ID', + name: 'type', + type: 'options', + typeOptions: { + loadOptionsMethod: 'getCompanyTypes', + }, + default: '', + description: + 'The optional classification of this company record - prospect, partner, etc. Choose from the list, or specify an ID using an expression.', + }, + { + displayName: 'Web Technologies Name or ID', + name: 'webTechnologies', + type: 'options', + typeOptions: { + loadOptionsMethod: 'getCompanyWebTechnologies', + }, + default: '', + description: + 'The web technologies used by the company or organization. Powered by HubSpot Insights. Choose from the list, or specify an ID using an expression.', + }, + { + displayName: 'Website URL', + name: 'websiteUrl', + type: 'string', + default: '', + description: + 'The main website of the company or organization. This property is used to identify unique companies. Powered by HubSpot Insights.', + }, + { + displayName: 'Year Founded', + name: 'yearFounded', + type: 'string', + default: '', + description: 'The year the company was created. Powered by HubSpot Insights.', + }, + ], + }, + + /* -------------------------------------------------------------------------- */ + /* company:get */ + /* -------------------------------------------------------------------------- */ + { + displayName: 'Company to Get', + name: 'companyId', + type: 'resourceLocator', + default: { mode: 'list', value: '' }, + required: true, + displayOptions: { + show: { + resource: ['company'], + operation: ['get'], + }, + }, + modes: [ + { + displayName: 'From List', + name: 'list', + type: 'list', + placeholder: 'Select from the list', + typeOptions: { + searchListMethod: 'searchCompanies', + }, + }, + { + displayName: 'By Id', + name: 'id', + type: 'string', + placeholder: '58539222', + validation: [ + { + type: 'regex', + properties: { + regex: '[0-9]+', + errorMessage: 'Not a valid HubSpot Company ID', + }, + }, + ], + }, + ], + }, + { + displayName: 'Options', + name: 'additionalFields', + type: 'collection', + placeholder: 'Add Option', + default: {}, + displayOptions: { + show: { + resource: ['company'], + operation: ['get'], + }, + }, + options: [ + { + displayName: 'Include Merge Audits', + name: 'includeMergeAudits', + type: 'boolean', + default: false, + description: + 'Whether to return any merge history if the company has been previously merged with another company record. Defaults to false.', + }, + ], + }, + + /* -------------------------------------------------------------------------- */ + /* company:getAll */ + /* -------------------------------------------------------------------------- */ + { + displayName: 'Return All', + name: 'returnAll', + type: 'boolean', + displayOptions: { + show: { + resource: ['company'], + operation: ['getAll'], + }, + }, + default: false, + description: 'Whether to return all results or only up to a given limit', + }, + { + displayName: 'Limit', + name: 'limit', + type: 'number', + displayOptions: { + show: { + resource: ['company'], + operation: ['getAll'], + returnAll: [false], + }, + }, + typeOptions: { + minValue: 1, + maxValue: 250, + }, + default: 100, + description: 'Max number of results to return', + }, + { + displayName: 'Options', + name: 'options', + type: 'collection', + placeholder: 'Add Field', + default: {}, + displayOptions: { + show: { + resource: ['company'], + operation: ['getAll'], + }, + }, + options: [ + { + displayName: 'Include Merge Audits', + name: 'includeMergeAudits', + type: 'boolean', + default: false, + description: + 'Whether to return any merge history if a company has been previously merged with another company record. Defaults to false.', + }, + { + displayName: 'Company Properties to Include', + name: 'propertiesCollection', + type: 'fixedCollection', + default: {}, + options: [ + { + name: 'propertiesValues', + displayName: 'Companies Properties to Include', + values: [ + { + displayName: 'Companies Properties to Include', + name: 'properties', + type: 'multiOptions', + typeOptions: { + loadOptionsMethod: 'getCompanyProperties', + }, + default: [], + description: + 'Whether to include specific Company properties in the returned results. Choose from a list, or specify IDs using an expression. Choose from the list, or specify IDs using an expression.', + }, + { + displayName: 'Include', + name: 'propertyMode', + type: 'options', + options: [ + { + name: 'Value And History', + value: 'valueAndHistory', + }, + { + name: 'Value Only', + value: 'valueOnly', + }, + ], + default: 'valueAndHistory', + description: + 'Specify if the current value for a property should be fetched, or the value and all the historical values for that property', + }, + ], + }, + ], + description: + 'Whether to include specific Company properties in the returned results. Choose from a list, or specify IDs using an expression.', + }, + ], + }, + + /* -------------------------------------------------------------------------- */ + /* company:delete */ + /* -------------------------------------------------------------------------- */ + { + displayName: 'Company to Delete', + name: 'companyId', + type: 'resourceLocator', + default: { mode: 'list', value: '' }, + required: true, + displayOptions: { + show: { + resource: ['company'], + operation: ['delete'], + }, + }, + modes: [ + { + displayName: 'From List', + name: 'list', + type: 'list', + placeholder: 'Select from the list', + typeOptions: { + searchListMethod: 'searchCompanies', + }, + }, + { + displayName: 'By Id', + name: 'id', + type: 'string', + placeholder: '58539222', + validation: [ + { + type: 'regex', + properties: { + regex: '[0-9]+', + errorMessage: 'Not a valid HubSpot Company ID', + }, + }, + ], + }, + ], + }, + + /* -------------------------------------------------------------------------- */ + /* getRecentlyCreatedUpdate */ + /* -------------------------------------------------------------------------- */ + { + displayName: 'Return All', + name: 'returnAll', + type: 'boolean', + displayOptions: { + show: { + resource: ['company'], + operation: ['getRecentlyCreatedUpdated'], + }, + }, + default: false, + description: 'Whether to return all results or only up to a given limit', + }, + { + displayName: 'Limit', + name: 'limit', + type: 'number', + displayOptions: { + show: { + resource: ['company'], + operation: ['getRecentlyCreatedUpdated'], + returnAll: [false], + }, + }, + typeOptions: { + minValue: 1, + maxValue: 250, + }, + default: 100, + description: 'Max number of results to return', + }, + { + displayName: 'Options', + name: 'additionalFields', + type: 'collection', + placeholder: 'Add Option', + default: {}, + displayOptions: { + show: { + resource: ['company'], + operation: ['getRecentlyCreatedUpdated'], + }, + }, + options: [ + { + displayName: 'Since', + name: 'since', + type: 'dateTime', + default: '', + description: + 'Only return companys created after timestamp x. When using expressions, the time should be specified in YYYY-MM-DD hh-mm-ss format.', + }, + { + displayName: 'Company Properties to Include', + name: 'propertiesCollection', + type: 'fixedCollection', + default: {}, + options: [ + { + name: 'propertiesValues', + displayName: 'Companies Properties to Include', + values: [ + { + displayName: 'Companies Properties to Include', + name: 'properties', + type: 'multiOptions', + typeOptions: { + loadOptionsMethod: 'getCompanyProperties', + }, + default: [], + description: + 'Whether to include specific Company properties in the returned results. Choose from a list, or specify IDs using an expression. Choose from the list, or specify IDs using an expression.', + }, + { + displayName: 'Include', + name: 'propertyMode', + type: 'options', + options: [ + { + name: 'Value And History', + value: 'valueAndHistory', + }, + { + name: 'Value Only', + value: 'valueOnly', + }, + ], + default: 'valueAndHistory', + description: + 'Specify if the current value for a property should be fetched, or the value and all the historical values for that property', + }, + ], + }, + ], + description: + 'Whether to include specific Company properties in the returned results. Choose from a list, or specify IDs using an expression.', + }, + ], + }, + + /* -------------------------------------------------------------------------- */ + /* company:searchByDomain */ + /* -------------------------------------------------------------------------- */ + { + displayName: 'Domain', + name: 'domain', + type: 'string', + displayOptions: { + show: { + resource: ['company'], + operation: ['searchByDomain'], + }, + }, + required: true, + default: '', + description: "The company's website domain to search for, like n8n.io", + }, + { + displayName: 'Return All', + name: 'returnAll', + type: 'boolean', + displayOptions: { + show: { + resource: ['company'], + operation: ['searchByDomain'], + }, + }, + default: false, + description: 'Whether to return all results or only up to a given limit', + }, + { + displayName: 'Limit', + name: 'limit', + type: 'number', + displayOptions: { + show: { + resource: ['company'], + operation: ['searchByDomain'], + returnAll: [false], + }, + }, + typeOptions: { + minValue: 1, + maxValue: 250, + }, + default: 100, + description: 'Max number of results to return', + }, + { + displayName: 'Options', + name: 'options', + type: 'collection', + placeholder: 'Add Option', + default: {}, + displayOptions: { + show: { + resource: ['company'], + operation: ['searchByDomain'], + }, + }, + options: [ + { + displayName: 'Company Properties to Include', + name: 'properties', + type: 'multiOptions', + typeOptions: { + loadOptionsMethod: 'getCompanyProperties', + }, + default: [], + description: + 'Whether to include specific Company properties in the returned results. Choose from a list, or specify IDs using an expression. Choose from the list, or specify IDs using an expression.', + }, + ], + }, +]; diff --git a/packages/nodes-base/nodes/Hubspot/V2/ContactDescription.ts b/packages/nodes-base/nodes/Hubspot/V2/ContactDescription.ts new file mode 100644 index 0000000000..c8a9417ce6 --- /dev/null +++ b/packages/nodes-base/nodes/Hubspot/V2/ContactDescription.ts @@ -0,0 +1,1225 @@ +import type { INodeProperties } from 'n8n-workflow'; + +export const contactOperations: INodeProperties[] = [ + { + displayName: 'Operation', + name: 'operation', + type: 'options', + noDataExpression: true, + displayOptions: { + show: { + resource: ['contact'], + }, + }, + options: [ + { + name: 'Create or Update', + value: 'upsert', + description: + 'Create a new contact, or update the current one if it already exists (upsert)', + action: 'Create or update a contact', + }, + { + name: 'Delete', + value: 'delete', + description: 'Delete a contact', + action: 'Delete a contact', + }, + { + name: 'Get', + value: 'get', + description: 'Get a contact', + action: 'Get a contact', + }, + { + name: 'Get Many', + value: 'getAll', + description: 'Get many contacts', + action: 'Get many contacts', + }, + { + name: 'Get Recently Created/Updated', + value: 'getRecentlyCreatedUpdated', + description: 'Get recently created/updated contacts', + action: 'Get recently created/updated contacts', + }, + { + name: 'Search', + value: 'search', + description: 'Search contacts', + action: 'Search contacts', + }, + ], + default: 'upsert', + }, +]; + +export const contactFields: INodeProperties[] = [ + /* -------------------------------------------------------------------------- */ + /* contact:upsert */ + /* -------------------------------------------------------------------------- */ + { + displayName: 'Email', + name: 'email', + type: 'string', + placeholder: 'name@email.com', + required: true, + displayOptions: { + show: { + resource: ['contact'], + operation: ['upsert'], + }, + }, + default: '', + }, + { + displayName: 'Contact Properties', + name: 'additionalFields', + type: 'collection', + placeholder: 'Add Property', + default: {}, + displayOptions: { + show: { + resource: ['contact'], + operation: ['upsert'], + }, + }, + options: [ + { + displayName: 'Annual Revenue', + name: 'annualRevenue', + type: 'number', + typeOptions: { + minValue: 0, + }, + default: 0, + }, + { + displayName: 'Associated Company Name or ID', + name: 'associatedCompanyId', + type: 'options', + typeOptions: { + loadOptionsMethod: 'getCompanies', + }, + default: '', + description: + 'Companies associated with the ticket. Choose from the list, or specify an ID using an expression.', + }, + { + displayName: 'City', + name: 'city', + type: 'string', + default: '', + }, + { + displayName: 'Clicked Facebook Ad', + name: 'clickedFacebookAd', + type: 'string', + default: '', + }, + { + displayName: 'Close Date', + name: 'closeDate', + type: 'dateTime', + default: '', + description: + 'When using expressions, the time should be specified in YYYY-MM-DD hh-mm-ss format', + }, + { + displayName: 'Company Name', + name: 'companyName', + type: 'string', + default: '', + }, + { + displayName: 'Company Size', + name: 'companySize', + type: 'string', + default: '', + }, + { + displayName: 'Contact Owner Name or ID', + name: 'contactOwner', + type: 'options', + description: + 'Choose from the list, or specify an ID using an expression', + typeOptions: { + loadOptionsMethod: 'getOwners', + }, + default: '', + }, + { + displayName: 'Country/Region', + name: 'country', + type: 'string', + default: '', + }, + { + displayName: 'Custom Properties', + name: 'customPropertiesUi', + placeholder: 'Add Custom Property', + type: 'fixedCollection', + typeOptions: { + multipleValues: true, + }, + default: {}, + options: [ + { + name: 'customPropertiesValues', + displayName: 'Custom Property', + values: [ + { + displayName: 'Property Name or ID', + name: 'property', + type: 'options', + typeOptions: { + loadOptionsMethod: 'getContactCustomProperties', + }, + default: '', + description: + 'Name of the property. Choose from the list, or specify an ID using an expression.', + }, + { + displayName: 'Value', + name: 'value', + type: 'string', + default: '', + required: true, + description: 'Value of the property', + }, + ], + }, + ], + }, + { + displayName: 'Date of Birth', + name: 'dateOfBirth', + type: 'dateTime', + default: '', + description: + 'When using expressions, the time should be specified in YYYY-MM-DD hh-mm-ss format', + }, + { + displayName: 'Degree', + name: 'degree', + type: 'string', + default: '', + }, + { + displayName: 'Facebook Click ID', + name: 'facebookClickId', + type: 'number', + default: '', + }, + { + displayName: 'Fax Number', + name: 'faxNumber', + type: 'string', + default: '', + }, + { + displayName: 'Field Of Study', + name: 'fieldOfStudy', + type: 'string', + default: '', + description: + "A contact's field of study. This property is required for the Facebook Ads Integration. This property will be automatically synced via the Lead Ads tool", + }, + { + displayName: 'First Name', + name: 'firstName', + type: 'string', + default: '', + description: "A contact's first name", + }, + { + displayName: 'Gender', + name: 'gender', + type: 'string', + default: '', + }, + { + displayName: 'Google Ad Click ID', + name: 'googleAdClickId', + type: 'number', + default: '', + }, + { + displayName: 'Graduation Date', + name: 'graduationDate', + type: 'dateTime', + default: '', + description: + "A contact's graduation date. This property is required for the Facebook Ads Integration. This property will be automatically synced via the Lead Ads tool. When using expressions, the time should be specified in YYYY-MM-DD hh-mm-ss format", + }, + { + displayName: 'Industry', + name: 'industry', + type: 'string', + default: '', + description: 'The industry a contact is in', + }, + { + displayName: 'Job Function', + name: 'jobFunction', + type: 'string', + default: '', + description: + "A contact's job function. This property is required for the Facebook Ads Integration. This property will be automatically synced via the Lead Ads tool", + }, + { + displayName: 'Job Title', + name: 'jobTitle', + type: 'string', + default: '', + description: "A contact's job title", + }, + { + displayName: 'Last Name', + name: 'lastName', + type: 'string', + default: '', + description: "A contact's last name", + }, + { + displayName: 'Lead Status Name or ID', + name: 'leadStatus', + type: 'options', + typeOptions: { + loadOptionsMethod: 'getContactLeadStatuses', + }, + default: '', + description: + 'The contact\'s sales, prospecting or outreach status. Choose from the list, or specify an ID using an expression.', + }, + { + displayName: 'Legal Basic For Processing Contact Data Name or ID', + name: 'processingContactData', + type: 'options', + typeOptions: { + loadOptionsMethod: 'getContactLealBasics', + }, + default: '', + description: + "Legal basis for processing contact's data; 'Not applicable' will exempt the contact from GDPR protections. Choose from the list, or specify an ID using an expression.", + }, + { + displayName: 'Lifecycle Stage Name or ID', + name: 'lifeCycleStage', + type: 'options', + typeOptions: { + loadOptionsMethod: 'getContactLifeCycleStages', + }, + default: '', + description: + 'The qualification of contacts to sales readiness. It can be set through imports, forms, workflows, and manually on a per contact basis. Choose from the list, or specify an ID using an expression.', + }, + { + displayName: 'Marital Status', + name: 'maritalStatus', + type: 'string', + default: '', + description: + "A contact's marital status. This property is required for the Facebook Ads Integration. This property will be automatically synced via the Lead Ads tool", + }, + { + displayName: 'Membership Note', + name: 'membershipNote', + type: 'string', + typeOptions: { + alwaysOpenEditWindow: true, + }, + default: '', + description: "The notes relating to the contact's content membership", + }, + { + displayName: 'Message', + name: 'message', + type: 'string', + typeOptions: { + alwaysOpenEditWindow: true, + }, + default: '', + description: + 'A default property to be used for any message or comments a contact may want to leave on a form', + }, + { + displayName: 'Mobile Phone Number', + name: 'mobilePhoneNumber', + type: 'string', + default: '', + description: "A contact's mobile phone number", + }, + { + // eslint-disable-next-line n8n-nodes-base/node-param-display-name-wrong-for-dynamic-options + displayName: 'Number Of Employees', + name: 'numberOfEmployees', + type: 'options', + typeOptions: { + loadOptionsMethod: 'getContactNumberOfEmployees', + }, + default: '', + description: + 'The number of company employees. Choose from the list, or specify an ID using an expression.', + }, + { + displayName: 'Original Source Name or ID', + name: 'originalSource', + type: 'options', + typeOptions: { + loadOptionsMethod: 'getContactOriginalSources', + }, + default: '', + description: + 'The first known source through which a contact found your website. Source is automatically set by HubSpot, but may be updated manually. Choose from the list, or specify an ID using an expression.', + }, + { + displayName: 'Phone Number', + name: 'phoneNumber', + type: 'string', + default: '', + description: "A contact's primary phone number", + }, + { + displayName: 'Contact Properties to Include', + name: 'properties', + type: 'multiOptions', + typeOptions: { + loadOptionsMethod: 'getContactProperties', + }, + displayOptions: { + show: { + '/resolveData': [false], + }, + }, + default: [], + description: + 'Whether to include specific Contact properties in the returned results. Choose from a list, or specify IDs using an expression. Choose from the list, or specify IDs using an expression.', + }, + { + displayName: 'Postal Code', + name: 'postalCode', + type: 'string', + default: '', + description: "The contact's zip code. This might be set via import, form, or integration.", + }, + { + displayName: 'Preffered Language Name or ID', + name: 'prefferedLanguage', + type: 'options', + typeOptions: { + loadOptionsMethod: 'getContactPrefferedLanguages', + }, + default: '', + description: + 'Set your contact\'s preferred language for communications. This property can be changed from an import, form, or integration. Choose from the list, or specify an ID using an expression.', + }, + { + displayName: 'Relationship Status', + name: 'relationshipStatus', + type: 'string', + default: '', + description: + "A contact's relationship status. This property is required for the Facebook Ads Integration. This property will be automatically synced via the Lead Ads tool", + }, + { + displayName: 'Salutation', + name: 'salutation', + type: 'string', + default: '', + description: 'The title used to address a contact', + }, + { + displayName: 'School', + name: 'school', + type: 'string', + default: '', + description: + "A contact's school. This property is required for the Facebook Ads Integration. This property will be automatically synced via the Lead Ads tool", + }, + { + displayName: 'Seniority', + name: 'seniority', + type: 'string', + default: '', + description: + "A contact's seniority. This property is required for the Facebook Ads Integration. This property will be automatically synced via the Lead Ads tool", + }, + { + displayName: 'Start Date', + name: 'startDate', + type: 'dateTime', + default: '', + description: + "A contact's start date. This property is required for the Facebook Ads Integration. This property will be automatically synced via the Lead Ads tool. When using expressions, the time should be specified in YYYY-MM-DD hh-mm-ss format", + }, + { + displayName: 'State/Region', + name: 'stateRegion', + type: 'string', + default: '', + description: + "The contact's state of residence. This might be set via import, form, or integration.", + }, + { + displayName: 'Status Name or ID', + name: 'status', + type: 'options', + typeOptions: { + loadOptionsMethod: 'getContactStatuses', + }, + default: '', + description: + 'The status of the contact\'s content membership. Choose from the list, or specify an ID using an expression.', + }, + { + displayName: 'Street Address', + name: 'streetAddress', + type: 'string', + default: '', + description: "A contact's street address, including apartment or unit #", + }, + { + displayName: 'Twitter Username', + name: 'twitterUsername', + type: 'string', + default: '', + description: + "The contact's Twitter handle. This is set by HubSpot using the contact's email address.", + }, + { + displayName: 'Website URL', + name: 'websiteUrl', + type: 'string', + default: '', + description: "The contact's company website", + }, + { + displayName: 'Work Email', + name: 'workEmail', + type: 'string', + default: '', + description: + "A contact's work email. This property is required for the Facebook Ads Integration. This property will be automatically synced via the Lead Ads tool", + }, + ], + }, + { + displayName: 'Options', + name: 'options', + type: 'collection', + placeholder: 'Add Option', + default: {}, + displayOptions: { + show: { + resource: ['contact'], + operation: ['upsert'], + }, + }, + options: [ + { + displayName: 'Simplify Output', + name: 'resolveData', + type: 'boolean', + default: false, + // eslint-disable-next-line n8n-nodes-base/node-param-description-boolean-without-whether + description: + 'By default the response only includes the ID. If this option gets activated, it will resolve the data automatically.', + }, + ], + }, + + /* -------------------------------------------------------------------------- */ + /* contact:get */ + /* -------------------------------------------------------------------------- */ + { + displayName: 'Contact to Get', + name: 'contactId', + type: 'resourceLocator', + default: { mode: 'list', value: '' }, + required: true, + hint: 'To lookup a user by their email, use the Search operation', + displayOptions: { + show: { + resource: ['contact'], + operation: ['get'], + }, + }, + modes: [ + { + displayName: 'From List', + name: 'list', + type: 'list', + placeholder: 'Select from the list', + typeOptions: { + searchListMethod: 'searchContacts', + searchable: true, + }, + }, + { + displayName: 'By Id', + name: 'id', + type: 'string', + placeholder: '58539222', + validation: [ + { + type: 'regex', + properties: { + regex: '[0-9]+', + errorMessage: 'Not a valid HubSpot Contact ID', + }, + }, + ], + }, + ], + description: "This is not a contact's email but a number like 1485", + }, + { + displayName: 'Options', + name: 'additionalFields', + type: 'collection', + placeholder: 'Add Option', + default: {}, + displayOptions: { + show: { + resource: ['contact'], + operation: ['get'], + }, + }, + options: [ + { + displayName: 'Form Submission Mode', + name: 'formSubmissionMode', + type: 'options', + options: [ + { + name: 'All', + value: 'all', + }, + { + name: 'None', + value: 'none', + }, + { + name: 'Newest', + value: 'newest', + }, + { + name: 'Oldest', + value: 'oldest', + }, + ], + default: 'all', + description: 'Specify which form submissions should be fetched', + }, + { + displayName: 'Include List Memberships', + name: 'listMemberships', + type: 'boolean', + default: true, + description: 'Whether current list memberships should be fetched for the contact', + }, + { + displayName: 'Contact Properties to Include', + name: 'propertiesCollection', + type: 'fixedCollection', + default: {}, + options: [ + { + name: 'propertiesValues', + displayName: 'Contact Properties to Include', + values: [ + { + displayName: 'Contact Properties to Include', + name: 'properties', + type: 'multiOptions', + typeOptions: { + multipleValueButtonText: 'test', + loadOptionsMethod: 'getContactProperties', + }, + default: [], + description: + 'Whether to include specific Contact properties in the returned results. Choose from a list, or specify IDs using an expression. Choose from the list, or specify IDs using an expression.', + }, + { + displayName: 'Include', + name: 'propertyMode', + type: 'options', + options: [ + { + name: 'Value And History', + value: 'valueAndHistory', + }, + { + name: 'Value Only', + value: 'valueOnly', + }, + ], + default: 'valueAndHistory', + description: + 'Specify if the current value for a property should be fetched, or the value and all the historical values for that property', + }, + ], + }, + ], + description: + '

Used to include specific contact properties in the results. By default, the results will only include Contact ID and will not include the values for any properties for your Contact.

Including this parameter will include the data for the specified property in the results. You can include this parameter multiple times to request multiple properties separated by a comma: ,.

. Choose from the list, or specify IDs using an expression.', + }, + ], + }, + + /* -------------------------------------------------------------------------- */ + /* contact:getAll */ + /* -------------------------------------------------------------------------- */ + { + displayName: 'Return All', + name: 'returnAll', + type: 'boolean', + displayOptions: { + show: { + resource: ['contact'], + operation: ['getAll'], + }, + }, + default: false, + description: 'Whether to return all results or only up to a given limit', + }, + { + displayName: 'Limit', + name: 'limit', + type: 'number', + displayOptions: { + show: { + resource: ['contact'], + operation: ['getAll'], + returnAll: [false], + }, + }, + typeOptions: { + minValue: 1, + maxValue: 250, + }, + default: 100, + description: 'Max number of results to return', + }, + { + displayName: 'Options', + name: 'additionalFields', + type: 'collection', + placeholder: 'Add Option', + default: {}, + displayOptions: { + show: { + resource: ['contact'], + operation: ['getAll'], + }, + }, + options: [ + { + displayName: 'Form Submission Mode', + name: 'formSubmissionMode', + type: 'options', + options: [ + { + name: 'All', + value: 'all', + }, + { + name: 'None', + value: 'none', + }, + { + name: 'Newest', + value: 'newest', + }, + { + name: 'Oldest', + value: 'oldest', + }, + ], + default: 'all', + description: 'Specify which form submissions should be fetched', + }, + { + displayName: 'Include List Memberships', + name: 'listMemberships', + type: 'boolean', + default: true, + description: 'Whether current list memberships should be fetched for the contact', + }, + { + displayName: 'Contact Properties to Include', + name: 'propertiesCollection', + type: 'fixedCollection', + default: {}, + options: [ + { + name: 'propertiesValues', + displayName: 'Contact Properties to Include', + values: [ + { + displayName: 'Contact Properties to Include', + name: 'properties', + type: 'multiOptions', + typeOptions: { + loadOptionsMethod: 'getContactProperties', + }, + default: [], + description: + 'Whether to include specific Contact properties in the returned results. Choose from a list, or specify IDs using an expression. Choose from the list, or specify IDs using an expression.', + }, + { + displayName: 'Include', + name: 'propertyMode', + type: 'options', + options: [ + { + name: 'Value And History', + value: 'valueAndHistory', + }, + { + name: 'Value Only', + value: 'valueOnly', + }, + ], + default: 'valueAndHistory', + description: + 'Specify if the current value for a property should be fetched, or the value and all the historical values for that property', + }, + ], + }, + ], + description: + '

Used to include specific contact properties in the results. By default, the results will only include Contact ID and will not include the values for any properties for your Contact.

Including this parameter will include the data for the specified property in the results. You can include this parameter multiple times to request multiple properties separated by a comma: ,.

. Choose from the list, or specify IDs using an expression.', + }, + ], + }, + + /* -------------------------------------------------------------------------- */ + /* contact:delete */ + /* -------------------------------------------------------------------------- */ + { + displayName: 'Contact to Delete', + name: 'contactId', + type: 'resourceLocator', + default: { mode: 'list', value: '' }, + required: true, + hint: 'To lookup a user by their email, use the Search operation', + displayOptions: { + show: { + resource: ['contact'], + operation: ['delete'], + }, + }, + modes: [ + { + displayName: 'From List', + name: 'list', + type: 'list', + placeholder: 'Select from the list', + typeOptions: { + searchListMethod: 'searchContacts', + searchable: true, + }, + }, + { + displayName: 'By Id', + name: 'id', + type: 'string', + placeholder: '58539222', + validation: [ + { + type: 'regex', + properties: { + regex: '[0-9]+', + errorMessage: 'Not a valid HubSpot Contact ID', + }, + }, + ], + }, + ], + description: "This is not a contact's email but a number like 1485", + }, + + /* -------------------------------------------------------------------------- */ + /* contact:getRecentlyCreatedUpdated */ + /* -------------------------------------------------------------------------- */ + { + displayName: 'Return All', + name: 'returnAll', + type: 'boolean', + displayOptions: { + show: { + resource: ['contact'], + operation: ['getRecentlyCreatedUpdated'], + }, + }, + default: false, + description: 'Whether to return all results or only up to a given limit', + }, + { + displayName: 'Limit', + name: 'limit', + type: 'number', + displayOptions: { + show: { + resource: ['contact'], + operation: ['getRecentlyCreatedUpdated'], + returnAll: [false], + }, + }, + typeOptions: { + minValue: 1, + maxValue: 250, + }, + default: 100, + description: 'Max number of results to return', + }, + { + displayName: 'Options', + name: 'additionalFields', + type: 'collection', + placeholder: 'Add Option', + default: {}, + displayOptions: { + show: { + resource: ['contact'], + operation: ['getRecentlyCreatedUpdated'], + }, + }, + options: [ + { + displayName: 'Form Submission Mode', + name: 'formSubmissionMode', + type: 'options', + options: [ + { + name: 'All', + value: 'all', + }, + { + name: 'None', + value: 'none', + }, + { + name: 'Newest', + value: 'newest', + }, + { + name: 'Oldest', + value: 'oldest', + }, + ], + default: 'all', + description: 'Specify which form submissions should be fetched', + }, + { + displayName: 'Include List Memberships', + name: 'listMemberships', + type: 'boolean', + default: true, + description: 'Whether current list memberships should be fetched for the contact', + }, + { + displayName: 'Contact Properties to Include', + name: 'propertiesCollection', + type: 'fixedCollection', + default: {}, + options: [ + { + name: 'propertiesValues', + displayName: 'Contact Properties to Include', + values: [ + { + displayName: 'Contact Properties to Include', + name: 'properties', + type: 'multiOptions', + typeOptions: { + loadOptionsMethod: 'getContactProperties', + }, + default: [], + description: + 'Whether to include specific Contact properties in the returned results. Choose from a list, or specify IDs using an expression. Choose from the list, or specify IDs using an expression.', + }, + { + displayName: 'Include', + name: 'propertyMode', + type: 'options', + options: [ + { + name: 'Value And History', + value: 'valueAndHistory', + }, + { + name: 'Value Only', + value: 'valueOnly', + }, + ], + default: 'valueAndHistory', + description: + 'Specify if the current value for a property should be fetched, or the value and all the historical values for that property', + }, + ], + }, + ], + description: + '

Used to include specific contact properties in the results. By default, the results will only include Contact ID and will not include the values for any properties for your Contact.

Including this parameter will include the data for the specified property in the results. You can include this parameter multiple times to request multiple properties separated by a comma: ,.

. Choose from the list, or specify IDs using an expression.', + }, + ], + }, + + //*-------------------------------------------------------------------------- */ + /* contact:search */ + /* -------------------------------------------------------------------------- */ + { + displayName: 'Return All', + name: 'returnAll', + type: 'boolean', + displayOptions: { + show: { + resource: ['contact'], + operation: ['search'], + }, + }, + default: false, + description: 'Whether to return all results or only up to a given limit', + }, + { + displayName: 'Limit', + name: 'limit', + type: 'number', + displayOptions: { + show: { + resource: ['contact'], + operation: ['search'], + returnAll: [false], + }, + }, + typeOptions: { + minValue: 1, + maxValue: 250, + }, + default: 100, + description: 'Max number of results to return', + }, + { + displayName: 'Filter Groups', + name: 'filterGroupsUi', + type: 'fixedCollection', + default: {}, + placeholder: 'Add Filter Group', + typeOptions: { + multipleValues: true, + }, + displayOptions: { + show: { + resource: ['contact'], + operation: ['search'], + }, + }, + options: [ + { + name: 'filterGroupsValues', + displayName: 'Filter Group', + values: [ + { + displayName: 'Filters', + name: 'filtersUi', + type: 'fixedCollection', + default: {}, + placeholder: 'Add Filter', + typeOptions: { + multipleValues: true, + }, + options: [ + { + name: 'filterValues', + displayName: 'Filter', + values: [ + { + displayName: 'Property Name or ID', + name: 'propertyName', + type: 'options', + description: + 'Choose from the list, or specify an ID using an expression', + typeOptions: { + loadOptionsMethod: 'getContactPropertiesWithType', + }, + default: '', + }, + { + displayName: 'Type', + name: 'type', + type: 'hidden', + default: '={{$parameter["&propertyName"].split("|")[1]}}', + }, + { + displayName: 'Operator', + name: 'operator', + type: 'options', + displayOptions: { + hide: { + type: ['number'], + }, + }, + options: [ + { + name: 'Contains Exactly', + value: 'CONTAINS_TOKEN', + }, + { + name: 'Equal', + value: 'EQ', + }, + { + name: 'Is Known', + value: 'HAS_PROPERTY', + }, + { + name: 'Is Unknown', + value: 'NOT_HAS_PROPERTY', + }, + { + name: 'Not Equal', + value: 'NEQ', + }, + ], + default: 'EQ', + }, + { + displayName: 'Operator', + name: 'operator', + type: 'options', + displayOptions: { + show: { + type: ['number'], + }, + }, + options: [ + { + name: 'Contains Exactly', + value: 'CONTAINS_TOKEN', + }, + { + name: 'Equal', + value: 'EQ', + }, + { + name: 'Greater Than', + value: 'GT', + }, + { + name: 'Greater Than Or Equal', + value: 'GTE', + }, + { + name: 'Is Known', + value: 'HAS_PROPERTY', + }, + { + name: 'Is Unknown', + value: 'NOT_HAS_PROPERTY', + }, + { + name: 'Less Than', + value: 'LT', + }, + { + name: 'Less Than Or Equal', + value: 'LTE', + }, + { + name: 'Not Equal', + value: 'NEQ', + }, + ], + default: 'EQ', + }, + { + displayName: 'Value', + name: 'value', + displayOptions: { + hide: { + operator: ['HAS_PROPERTY', 'NOT_HAS_PROPERTY'], + }, + }, + required: true, + type: 'string', + default: '', + }, + ], + }, + ], + description: + 'Use filters to limit the results to only CRM objects with matching property values. More info here.', + }, + ], + }, + ], + description: + 'When multiple filters are provided within a Filter Group, they will be combined using a logical AND operator. When multiple Filter Groups are provided, they will be combined using a logical OR operator. The system supports a maximum of three Filter Groups with up to three filters each. More info here', + }, + { + displayName: 'Options', + name: 'additionalFields', + type: 'collection', + placeholder: 'Add Option', + default: {}, + displayOptions: { + show: { + resource: ['contact'], + operation: ['search'], + }, + }, + options: [ + { + displayName: 'Sort Order', + name: 'direction', + type: 'options', + options: [ + { + name: 'Ascending', + value: 'ASCENDING', + }, + { + name: 'Descending', + value: 'DESCENDING', + }, + ], + default: 'DESCENDING', + description: + 'Defines the direction in which search results are ordered. Default value is Descending.', + }, + { + displayName: 'Field Names or IDs', + name: 'properties', + type: 'multiOptions', + typeOptions: { + loadOptionsMethod: 'getContactProperties', + }, + default: ['firstname', 'lastname', 'email'], + description: + 'Whether to include specific Contact properties in the returned results. Choose from a list, or specify IDs using an expression. Choose from the list, or specify IDs using an expression.', + }, + { + displayName: 'Query', + name: 'query', + type: 'string', + default: '', + description: 'Perform a text search against all property values for an object type', + }, + { + // eslint-disable-next-line n8n-nodes-base/node-param-display-name-wrong-for-dynamic-options + displayName: 'Sort By', + name: 'sortBy', + type: 'options', + description: + 'Choose from the list, or specify an ID using an expression', + typeOptions: { + loadOptionsMethod: 'getContactProperties', + }, + default: 'createdate', + }, + ], + }, +]; diff --git a/packages/nodes-base/nodes/Hubspot/V2/ContactListDescription.ts b/packages/nodes-base/nodes/Hubspot/V2/ContactListDescription.ts new file mode 100644 index 0000000000..b7c4f3dfaf --- /dev/null +++ b/packages/nodes-base/nodes/Hubspot/V2/ContactListDescription.ts @@ -0,0 +1,131 @@ +import type { INodeProperties } from 'n8n-workflow'; + +export const contactListOperations: INodeProperties[] = [ + { + displayName: 'Operation', + name: 'operation', + type: 'options', + noDataExpression: true, + displayOptions: { + show: { + resource: ['contactList'], + }, + }, + options: [ + { + name: 'Add', + value: 'add', + description: 'Add contact to a list', + action: 'Add a contact to a list', + }, + { + name: 'Remove', + value: 'remove', + description: 'Remove a contact from a list', + action: 'Remove a contact from a list', + }, + ], + default: 'add', + }, +]; + +export const contactListFields: INodeProperties[] = [ + /* -------------------------------------------------------------------------- */ + /* contactList:add */ + /* -------------------------------------------------------------------------- */ + { + displayName: 'By', + name: 'by', + type: 'options', + options: [ + { + name: 'Contact ID', + value: 'id', + }, + { + name: 'Email', + value: 'email', + }, + ], + required: true, + displayOptions: { + show: { + resource: ['contactList'], + operation: ['add'], + }, + }, + default: 'email', + }, + { + displayName: 'Email', + name: 'email', + type: 'string', + placeholder: 'name@email.com', + required: true, + displayOptions: { + show: { + resource: ['contactList'], + operation: ['add'], + by: ['email'], + }, + }, + default: '', + }, + { + displayName: 'Contact to Add', + name: 'id', + type: 'number', + required: true, + displayOptions: { + show: { + resource: ['contactList'], + operation: ['add'], + by: ['id'], + }, + }, + default: '', + }, + { + displayName: 'List to Add From', + name: 'listId', + type: 'number', + required: true, + displayOptions: { + show: { + resource: ['contactList'], + operation: ['add'], + }, + }, + default: '', + }, + + /* -------------------------------------------------------------------------- */ + /* contactList:remove */ + /* -------------------------------------------------------------------------- */ + { + displayName: 'Contact to Remove', + name: 'id', + type: 'number', + required: true, + displayOptions: { + show: { + resource: ['contactList'], + operation: ['remove'], + }, + }, + default: '', + }, + { + displayName: 'List to Remove From', + name: 'listId', + type: 'number', + required: true, + displayOptions: { + show: { + resource: ['contactList'], + operation: ['remove'], + }, + }, + default: '', + }, +]; diff --git a/packages/nodes-base/nodes/Hubspot/V2/DealDescription.ts b/packages/nodes-base/nodes/Hubspot/V2/DealDescription.ts new file mode 100644 index 0000000000..638090418a --- /dev/null +++ b/packages/nodes-base/nodes/Hubspot/V2/DealDescription.ts @@ -0,0 +1,1004 @@ +import type { INodeProperties } from 'n8n-workflow'; + +export const dealOperations: INodeProperties[] = [ + { + displayName: 'Operation', + name: 'operation', + type: 'options', + noDataExpression: true, + displayOptions: { + show: { + resource: ['deal'], + }, + }, + options: [ + { + name: 'Create', + value: 'create', + description: 'Create a deal', + action: 'Create a deal', + }, + { + name: 'Delete', + value: 'delete', + description: 'Delete a deal', + action: 'Delete a deal', + }, + { + name: 'Get', + value: 'get', + description: 'Get a deal', + action: 'Get a deal', + }, + { + name: 'Get Many', + value: 'getAll', + description: 'Get many deals', + action: 'Get many deals', + }, + { + name: 'Get Recently Created/Updated', + value: 'getRecentlyCreatedUpdated', + description: 'Get recently created/updated deals', + action: 'Get recently created/updated deals', + }, + { + name: 'Search', + value: 'search', + description: 'Search deals', + action: 'Search for deals', + }, + { + name: 'Update', + value: 'update', + description: 'Update a deal', + action: 'Update a deal', + }, + ], + default: 'create', + }, +]; + +export const dealFields: INodeProperties[] = [ + /* -------------------------------------------------------------------------- */ + /* deal:create */ + /* -------------------------------------------------------------------------- */ + { + displayName: 'Deal Stage Name or ID', + name: 'stage', + type: 'options', + required: true, + typeOptions: { + loadOptionsMethod: 'getDealStages', + }, + displayOptions: { + show: { + resource: ['deal'], + operation: ['create'], + }, + }, + default: '', + options: [], + description: + 'The deal stage is required when creating a deal. See the CRM Pipelines API for details on managing pipelines and stages. Choose from the list, or specify an ID using an expression.', + }, + { + displayName: 'Deal Properties', + name: 'additionalFields', + type: 'collection', + placeholder: 'Add Field', + default: {}, + displayOptions: { + show: { + resource: ['deal'], + operation: ['create'], + }, + }, + options: [ + { + displayName: 'Amount', + name: 'amount', + type: 'string', + default: '', + }, + { + displayName: 'Associated Company Names or IDs', + name: 'associatedCompany', + type: 'multiOptions', + description: + 'Whether to include specific Associated Company properties in the returned results. Choose from a list, or specify IDs using an expression. Choose from the list, or specify IDs using an expression.', + typeOptions: { + loadOptionsMethod: 'getCompanies', + }, + default: [], + }, + { + displayName: 'Associated Vid Names or IDs', + name: 'associatedVids', + type: 'multiOptions', + description: + 'Whether to include specific Associated Vid in the returned results. Choose from a list, or specify IDs using an expression. Choose from the list, or specify IDs using an expression.', + typeOptions: { + loadOptionsMethod: 'getContacts', + }, + default: [], + }, + { + displayName: 'Close Date', + name: 'closeDate', + type: 'dateTime', + default: '', + description: + 'When using expressions, the time should be specified in YYYY-MM-DD hh-mm-ss format', + }, + { + displayName: 'Custom Properties', + name: 'customPropertiesUi', + placeholder: 'Add Custom Property', + type: 'fixedCollection', + typeOptions: { + multipleValues: true, + }, + default: {}, + options: [ + { + name: 'customPropertiesValues', + displayName: 'Custom Property', + values: [ + { + displayName: 'Property Name or ID', + name: 'property', + type: 'options', + typeOptions: { + loadOptionsMethod: 'getDealCustomProperties', + }, + default: '', + description: + 'Name of the property. Choose from the list, or specify an ID using an expression.', + }, + { + displayName: 'Value', + name: 'value', + type: 'string', + default: '', + required: true, + description: 'Value of the property', + }, + ], + }, + ], + }, + { + displayName: 'Deal Description', + name: 'description', + type: 'string', + default: '', + }, + { + displayName: 'Deal Name', + name: 'dealName', + type: 'string', + default: '', + }, + { + displayName: 'Deal Owner', + name: 'dealOwner', + type: 'resourceLocator', + default: { mode: 'list', value: '' }, + modes: [ + { + displayName: 'From List', + name: 'list', + type: 'list', + placeholder: 'Select from the list', + typeOptions: { + searchListMethod: 'searchOwners', + }, + }, + { + displayName: 'By Id', + name: 'id', + type: 'string', + placeholder: '58539222', + validation: [ + { + type: 'regex', + properties: { + regex: '[0-9]+', + errorMessage: 'Not a valid HubSpot Owner ID', + }, + }, + ], + }, + ], + description: 'The HubSpot user to be assigned to the deal', + }, + { + displayName: 'Deal Type Name or ID', + name: 'dealType', + type: 'options', + description: + 'Choose from the list, or specify an ID using an expression', + typeOptions: { + loadOptionsMethod: 'getDealTypes', + }, + default: '', + }, + { + displayName: 'Pipeline Name or ID', + name: 'pipeline', + type: 'options', + description: + 'Choose from the list, or specify an ID using an expression', + typeOptions: { + loadOptionsMethod: 'getDealPipelines', + }, + default: '', + }, + ], + }, + + /* -------------------------------------------------------------------------- */ + /* deal:update */ + /* -------------------------------------------------------------------------- */ + { + displayName: 'Deal to Update', + name: 'dealId', + type: 'resourceLocator', + default: { mode: 'list', value: '' }, + required: true, + displayOptions: { + show: { + resource: ['deal'], + operation: ['update'], + }, + }, + modes: [ + { + displayName: 'From List', + name: 'list', + type: 'list', + placeholder: 'Select from the list', + typeOptions: { + searchListMethod: 'searchDeals', + searchable: true, + }, + }, + { + displayName: 'By Id', + name: 'id', + type: 'string', + placeholder: '58539222', + validation: [ + { + type: 'regex', + properties: { + regex: '[0-9]+', + errorMessage: 'Not a valid HubSpot Deal ID', + }, + }, + ], + }, + ], + }, + { + displayName: 'Update Fields', + name: 'updateFields', + type: 'collection', + placeholder: 'Add Update Field', + default: {}, + displayOptions: { + show: { + resource: ['deal'], + operation: ['update'], + }, + }, + options: [ + { + displayName: 'Amount', + name: 'amount', + type: 'string', + default: '', + }, + { + displayName: 'Close Date', + name: 'closeDate', + type: 'dateTime', + default: '', + description: + 'When using expressions, the time should be specified in YYYY-MM-DD hh-mm-ss format', + }, + { + displayName: 'Custom Properties', + name: 'customPropertiesUi', + placeholder: 'Add Custom Property', + type: 'fixedCollection', + typeOptions: { + multipleValues: true, + }, + default: {}, + options: [ + { + name: 'customPropertiesValues', + displayName: 'Custom Property', + values: [ + { + displayName: 'Property Name or ID', + name: 'property', + type: 'options', + typeOptions: { + loadOptionsMethod: 'getDealCustomProperties', + }, + default: '', + description: + 'Name of the property. Choose from the list, or specify an ID using an expression.', + }, + { + displayName: 'Value', + name: 'value', + type: 'string', + default: '', + required: true, + description: 'Value of the property', + }, + ], + }, + ], + }, + { + displayName: 'Deal Description', + name: 'description', + type: 'string', + default: '', + }, + { + displayName: 'Deal Name', + name: 'dealName', + type: 'string', + default: '', + }, + { + displayName: 'Deal Owner', + name: 'dealOwner', + type: 'resourceLocator', + default: { mode: 'list', value: '' }, + modes: [ + { + displayName: 'From List', + name: 'list', + type: 'list', + placeholder: 'Select from the list', + typeOptions: { + searchListMethod: 'searchOwners', + }, + }, + { + displayName: 'By Id', + name: 'id', + type: 'string', + placeholder: '58539222', + validation: [ + { + type: 'regex', + properties: { + regex: '[0-9]+', + errorMessage: 'Not a valid HubSpot Owner ID', + }, + }, + ], + }, + ], + description: 'The HubSpot user to be assigned to the deal', + }, + { + displayName: 'Deal Stage Name or ID', + name: 'stage', + type: 'options', + typeOptions: { + loadOptionsMethod: 'getDealStages', + }, + default: '', + description: + 'The deal stage is required when creating a deal. See the CRM Pipelines API for details on managing pipelines and stages. Choose from the list, or specify an ID using an expression.', + }, + { + displayName: 'Deal Type Name or ID', + name: 'dealType', + type: 'options', + description: + 'Choose from the list, or specify an ID using an expression', + typeOptions: { + loadOptionsMethod: 'getDealTypes', + }, + default: '', + }, + { + displayName: 'Pipeline', + name: 'pipeline', + type: 'string', + default: '', + }, + ], + }, + + /* -------------------------------------------------------------------------- */ + /* deal:get */ + /* -------------------------------------------------------------------------- */ + { + displayName: 'Deal to Get', + name: 'dealId', + type: 'resourceLocator', + default: { mode: 'list', value: '' }, + required: true, + displayOptions: { + show: { + resource: ['deal'], + operation: ['get'], + }, + }, + modes: [ + { + displayName: 'From List', + name: 'list', + type: 'list', + placeholder: 'Select from the list', + typeOptions: { + searchListMethod: 'searchDeals', + searchable: true, + }, + }, + { + displayName: 'By Id', + name: 'id', + type: 'string', + placeholder: '58539222', + validation: [ + { + type: 'regex', + properties: { + regex: '[0-9]+', + errorMessage: 'Not a valid HubSpot Deal ID', + }, + }, + ], + }, + ], + }, + { + displayName: 'Filters', + name: 'filters', + type: 'collection', + placeholder: 'Add Filter', + default: {}, + displayOptions: { + show: { + resource: ['deal'], + operation: ['get'], + }, + }, + options: [ + { + displayName: 'Include Property Versions', + name: 'includePropertyVersions', + type: 'boolean', + default: false, + // eslint-disable-next-line n8n-nodes-base/node-param-description-boolean-without-whether + description: + 'By default, you will only get data for the most recent version of a property in the "versions" data. If you include this parameter, you will get data for all previous versions.', + }, + { + displayName: 'Deal Properties to Include', + name: 'propertiesCollection', + type: 'fixedCollection', + default: {}, + options: [ + { + name: 'propertiesValues', + displayName: 'Deal Properties to Include', + values: [ + { + displayName: 'Deal Properties to Include', + name: 'properties', + type: 'multiOptions', + typeOptions: { + loadOptionsMethod: 'getDealProperties', + }, + default: [], + description: + 'Whether to include specific Deal properties in the returned results. Choose from a list, or specify IDs using an expression. Choose from the list, or specify IDs using an expression.', + }, + { + displayName: 'Include', + name: 'propertyMode', + type: 'options', + options: [ + { + name: 'Value And History', + value: 'valueAndHistory', + }, + { + name: 'Value Only', + value: 'valueOnly', + }, + ], + default: 'valueAndHistory', + description: + 'Specify if the current value for a property should be fetched, or the value and all the historical values for that property', + }, + ], + }, + ], + description: + '

Used to include specific deal properties in the results. By default, the results will only include Deal ID and will not include the values for any properties for your Deals.

Including this parameter will include the data for the specified property in the results. You can include this parameter multiple times to request multiple properties separated by a comma: ,.

. Choose from the list, or specify IDs using an expression.', + }, + ], + }, + /* -------------------------------------------------------------------------- */ + /* deal:getAll */ + /* -------------------------------------------------------------------------- */ + { + displayName: 'Return All', + name: 'returnAll', + type: 'boolean', + displayOptions: { + show: { + resource: ['deal'], + operation: ['getAll'], + }, + }, + default: false, + description: 'Whether to return all results or only up to a given limit', + }, + { + displayName: 'Limit', + name: 'limit', + type: 'number', + displayOptions: { + show: { + resource: ['deal'], + operation: ['getAll'], + returnAll: [false], + }, + }, + typeOptions: { + minValue: 1, + maxValue: 250, + }, + default: 100, + description: 'Max number of results to return', + }, + { + displayName: 'Options', + name: 'filters', + type: 'collection', + placeholder: 'Add Option', + default: {}, + displayOptions: { + show: { + resource: ['deal'], + operation: ['getAll'], + }, + }, + options: [ + { + displayName: 'Include Associations', + name: 'includeAssociations', + type: 'boolean', + default: false, + description: + 'Whether to include the IDs of the associated contacts and companies in the results. This will also automatically include the num_associated_contacts property.', + }, + { + displayName: 'Deal Properties to Include', + name: 'propertiesCollection', + type: 'fixedCollection', + default: {}, + options: [ + { + name: 'propertiesValues', + displayName: 'Deal Properties to Include', + values: [ + { + displayName: 'Deal Properties to Include', + name: 'properties', + type: 'multiOptions', + typeOptions: { + loadOptionsMethod: 'getDealPropertiesWithType', + }, + default: [], + description: + 'Whether to include specific Deal properties in the returned results. Choose from a list, or specify IDs using an expression. Choose from the list, or specify IDs using an expression.', + }, + { + displayName: 'Include', + name: 'propertyMode', + type: 'options', + options: [ + { + name: 'Value And History', + value: 'valueAndHistory', + }, + { + name: 'Value Only', + value: 'valueOnly', + }, + ], + default: 'valueAndHistory', + description: + 'Specify if the current value for a property should be fetched, or the value and all the historical values for that property', + }, + ], + }, + ], + description: + '

Used to include specific deal properties in the results. By default, the results will only include Deal ID and will not include the values for any properties for your Deals.

Including this parameter will include the data for the specified property in the results. You can include this parameter multiple times to request multiple properties separated by a comma: ,.

. Choose from the list, or specify IDs using an expression.', + }, + ], + }, + + /* -------------------------------------------------------------------------- */ + /* deal:delete */ + /* -------------------------------------------------------------------------- */ + { + displayName: 'Deal to Delete', + name: 'dealId', + type: 'resourceLocator', + default: { mode: 'list', value: '' }, + required: true, + displayOptions: { + show: { + resource: ['deal'], + operation: ['delete'], + }, + }, + modes: [ + { + displayName: 'From List', + name: 'list', + type: 'list', + placeholder: 'Select from the list', + typeOptions: { + searchListMethod: 'searchDeals', + searchable: true, + }, + }, + { + displayName: 'By Id', + name: 'id', + type: 'string', + placeholder: '58539222', + validation: [ + { + type: 'regex', + properties: { + regex: '[0-9]+', + errorMessage: 'Not a valid HubSpot Deal ID', + }, + }, + ], + }, + ], + }, + + /* -------------------------------------------------------------------------- */ + /* deal:getRecentDeals */ + /* -------------------------------------------------------------------------- */ + { + displayName: 'Return All', + name: 'returnAll', + type: 'boolean', + displayOptions: { + show: { + resource: ['deal'], + operation: ['getRecentlyCreatedUpdated'], + }, + }, + default: false, + description: 'Whether to return all results or only up to a given limit', + }, + { + displayName: 'Limit', + name: 'limit', + type: 'number', + displayOptions: { + show: { + resource: ['deal'], + operation: ['getRecentlyCreatedUpdated'], + returnAll: [false], + }, + }, + typeOptions: { + minValue: 1, + maxValue: 250, + }, + default: 100, + description: 'Max number of results to return', + }, + { + displayName: 'Options', + name: 'filters', + type: 'collection', + placeholder: 'Add Option', + default: {}, + displayOptions: { + show: { + resource: ['deal'], + operation: ['getRecentlyCreatedUpdated'], + }, + }, + options: [ + { + displayName: 'Since', + name: 'since', + type: 'dateTime', + default: '', + description: + 'Only return deals created after timestamp x. When using expressions, the time should be specified in YYYY-MM-DD hh-mm-ss format.', + }, + { + displayName: 'Include Property Versions', + name: 'includePropertyVersions', + type: 'boolean', + default: false, + // eslint-disable-next-line n8n-nodes-base/node-param-description-boolean-without-whether + description: + 'By default, you will only get data for the most recent version of a property in the "versions" data. If you include this parameter, you will get data for all previous versions.', + }, + ], + }, + + /*--------------------------------------------------------------------------- */ + /* deal:search */ + /* -------------------------------------------------------------------------- */ + { + displayName: 'Return All', + name: 'returnAll', + type: 'boolean', + displayOptions: { + show: { + resource: ['deal'], + operation: ['search'], + }, + }, + default: false, + description: 'Whether to return all results or only up to a given limit', + }, + { + displayName: 'Limit', + name: 'limit', + type: 'number', + displayOptions: { + show: { + resource: ['deal'], + operation: ['search'], + returnAll: [false], + }, + }, + typeOptions: { + minValue: 1, + maxValue: 250, + }, + default: 100, + description: 'Max number of results to return', + }, + { + displayName: 'Filter Groups', + name: 'filterGroupsUi', + type: 'fixedCollection', + default: {}, + placeholder: 'Add Filter Group', + typeOptions: { + multipleValues: true, + }, + displayOptions: { + show: { + resource: ['deal'], + operation: ['search'], + }, + }, + options: [ + { + name: 'filterGroupsValues', + displayName: 'Filter Group', + values: [ + { + displayName: 'Filters', + name: 'filtersUi', + type: 'fixedCollection', + default: {}, + placeholder: 'Add Filter', + typeOptions: { + multipleValues: true, + }, + options: [ + { + name: 'filterValues', + displayName: 'Filter', + values: [ + { + displayName: 'Property Name or ID', + name: 'propertyName', + type: 'options', + description: + 'Choose from the list, or specify an ID using an expression', + typeOptions: { + loadOptionsMethod: 'getDealProperties', + }, + default: '', + }, + { + displayName: 'Type', + name: 'type', + type: 'hidden', + default: '={{$parameter["&propertyName"].split("|")[1]}}', + }, + { + displayName: 'Operator', + name: 'operator', + type: 'options', + displayOptions: { + hide: { + type: ['number'], + }, + }, + options: [ + { + name: 'Contains Exactly', + value: 'CONTAINS_TOKEN', + }, + { + name: 'Equal', + value: 'EQ', + }, + { + name: 'Is Known', + value: 'HAS_PROPERTY', + }, + { + name: 'Is Unknown', + value: 'NOT_HAS_PROPERTY', + }, + { + name: 'Not Equal', + value: 'NEQ', + }, + ], + default: 'EQ', + }, + { + displayName: 'Operator', + name: 'operator', + type: 'options', + displayOptions: { + show: { + type: ['number'], + }, + }, + options: [ + { + name: 'Contains Exactly', + value: 'CONTAINS_TOKEN', + }, + { + name: 'Equal', + value: 'EQ', + }, + { + name: 'Greater Than', + value: 'GT', + }, + { + name: 'Greater Than Or Equal', + value: 'GTE', + }, + { + name: 'Is Known', + value: 'HAS_PROPERTY', + }, + { + name: 'Is Unknown', + value: 'NOT_HAS_PROPERTY', + }, + { + name: 'Less Than', + value: 'LT', + }, + { + name: 'Less Than Or Equal', + value: 'LTE', + }, + { + name: 'Not Equal', + value: 'NEQ', + }, + ], + default: 'EQ', + }, + { + displayName: 'Value', + name: 'value', + displayOptions: { + hide: { + operator: ['HAS_PROPERTY', 'NOT_HAS_PROPERTY'], + }, + }, + required: true, + type: 'string', + default: '', + }, + ], + }, + ], + description: + 'Use filters to limit the results to only CRM objects with matching property values. More info here.', + }, + ], + }, + ], + description: + 'When multiple filters are provided within a Filter Group, they will be combined using a logical AND operator. When multiple Filter Groups are provided, they will be combined using a logical OR operator. The system supports a maximum of three Filter Groups with up to three filters each. More info here', + }, + { + displayName: 'Options', + name: 'additionalFields', + type: 'collection', + placeholder: 'Add Option', + default: {}, + displayOptions: { + show: { + resource: ['deal'], + operation: ['search'], + }, + }, + options: [ + { + displayName: 'Direction', + name: 'direction', + type: 'options', + options: [ + { + name: 'Ascending', + value: 'ASCENDING', + }, + { + name: 'Descending', + value: 'DESCENDING', + }, + ], + default: 'DESCENDING', + description: + 'Defines the direction in which search results are ordered. Default value is Descending.', + }, + { + displayName: 'Field Names or IDs', + name: 'properties', + type: 'multiOptions', + typeOptions: { + loadOptionsMethod: 'getDealProperties', + }, + default: [], + description: + 'Whether to include specific Deal properties in the returned results. Choose from a list, or specify IDs using an expression. Choose from the list, or specify IDs using an expression.', + }, + { + displayName: 'Query', + name: 'query', + type: 'string', + default: '', + description: 'Perform a text search against all property values for an object type', + }, + { + // eslint-disable-next-line n8n-nodes-base/node-param-display-name-wrong-for-dynamic-options + displayName: 'Sort By', + name: 'sortBy', + type: 'options', + description: + 'Choose from the list, or specify an ID using an expression', + typeOptions: { + loadOptionsMethod: 'getDealProperties', + }, + default: 'createdate', + }, + ], + }, +]; diff --git a/packages/nodes-base/nodes/Hubspot/V2/DealInterface.ts b/packages/nodes-base/nodes/Hubspot/V2/DealInterface.ts new file mode 100644 index 0000000000..b3d5d1d0c0 --- /dev/null +++ b/packages/nodes-base/nodes/Hubspot/V2/DealInterface.ts @@ -0,0 +1,11 @@ +import type { IDataObject } from 'n8n-workflow'; + +export interface IAssociation { + associatedCompanyIds?: number[]; + associatedVids?: number[]; +} + +export interface IDeal { + associations?: IAssociation; + properties?: IDataObject[]; +} diff --git a/packages/nodes-base/nodes/Hubspot/V2/EngagementDescription.ts b/packages/nodes-base/nodes/Hubspot/V2/EngagementDescription.ts new file mode 100644 index 0000000000..7ae6f4b449 --- /dev/null +++ b/packages/nodes-base/nodes/Hubspot/V2/EngagementDescription.ts @@ -0,0 +1,543 @@ +import type { INodeProperties } from 'n8n-workflow'; + +export const engagementOperations: INodeProperties[] = [ + { + displayName: 'Operation', + name: 'operation', + type: 'options', + noDataExpression: true, + displayOptions: { + show: { + resource: ['engagement'], + }, + }, + options: [ + { + name: 'Create', + value: 'create', + description: 'Create an engagement', + action: 'Create an engagement', + }, + { + name: 'Delete', + value: 'delete', + description: 'Delete an engagement', + action: 'Delete an engagement', + }, + { + name: 'Get', + value: 'get', + description: 'Get an engagement', + action: 'Get an engagement', + }, + { + name: 'Get Many', + value: 'getAll', + description: 'Get many engagements', + action: 'Get many engagements', + }, + ], + default: 'create', + }, +]; + +export const engagementFields: INodeProperties[] = [ + /* -------------------------------------------------------------------------- */ + /* engagement:create */ + /* -------------------------------------------------------------------------- */ + { + displayName: 'Type', + name: 'type', + type: 'options', + required: true, + options: [ + { + name: 'Call', + value: 'call', + }, + { + name: 'Email', + value: 'email', + }, + { + name: 'Meeting', + value: 'meeting', + }, + { + name: 'Task', + value: 'task', + }, + ], + displayOptions: { + show: { + resource: ['engagement'], + operation: ['create'], + }, + }, + default: '', + }, + { + displayName: 'Metadata', + name: 'metadata', + type: 'collection', + placeholder: 'Add Field', + default: {}, + displayOptions: { + show: { + resource: ['engagement'], + operation: ['create'], + type: ['task'], + }, + }, + options: [ + { + displayName: 'Body', + name: 'body', + type: 'string', + default: '', + }, + { + displayName: 'For Object Type', + name: 'forObjectType', + type: 'options', + options: [ + { + name: 'Company', + value: 'COMPANY', + }, + { + name: 'Contact', + value: 'CONTACT', + }, + ], + default: '', + }, + { + displayName: 'Status', + name: 'status', + type: 'options', + options: [ + { + name: 'Completed', + value: 'COMPLETED', + }, + { + name: 'Deferred', + value: 'DEFERRED', + }, + { + name: 'In Progress', + value: 'IN_PROGRESS', + }, + { + name: 'Not Started', + value: 'NOT_STARTED', + }, + { + name: 'Waiting', + value: 'WAITING', + }, + ], + default: '', + }, + { + displayName: 'Subject', + name: 'subject', + type: 'string', + default: '', + }, + ], + }, + { + displayName: 'Metadata', + name: 'metadata', + type: 'collection', + placeholder: 'Add Field', + default: {}, + displayOptions: { + show: { + resource: ['engagement'], + operation: ['create'], + type: ['email'], + }, + }, + options: [ + { + displayName: 'BCC', + name: 'bcc', + type: 'string', + typeOptions: { + multipleValues: true, + multipleValueButtonText: 'Add BCC', + }, + default: '', + }, + { + displayName: 'CC', + name: 'cc', + type: 'string', + typeOptions: { + multipleValues: true, + multipleValueButtonText: 'Add CC', + }, + default: '', + }, + { + displayName: 'From Email', + name: 'fromEmail', + type: 'string', + default: '', + }, + { + displayName: 'From First Name', + name: 'firstName', + type: 'string', + default: '', + }, + { + displayName: 'From Last Name', + name: 'lastName', + type: 'string', + default: '', + }, + { + displayName: 'HTML', + name: 'html', + type: 'string', + default: '', + }, + { + displayName: 'Subject', + name: 'subject', + type: 'string', + default: '', + }, + { + displayName: 'To Emails', + name: 'toEmail', + type: 'string', + typeOptions: { + multipleValues: true, + multipleValueButtonText: 'Add Email', + }, + default: '', + }, + ], + }, + { + displayName: 'Metadata', + name: 'metadata', + type: 'collection', + placeholder: 'Add Field', + default: {}, + displayOptions: { + show: { + resource: ['engagement'], + operation: ['create'], + type: ['meeting'], + }, + }, + options: [ + { + displayName: 'Body', + name: 'body', + type: 'string', + default: '', + }, + { + displayName: 'End Time', + name: 'endTime', + type: 'dateTime', + default: '', + description: + 'When using expressions, the time should be specified in YYYY-MM-DD hh-mm-ss format', + }, + { + displayName: 'Internal Meeting Notes', + name: 'internalMeetingNotes', + type: 'string', + default: '', + }, + { + displayName: 'Start Time', + name: 'startTime', + type: 'dateTime', + default: '', + description: + 'When using expressions, the time should be specified in YYYY-MM-DD hh-mm-ss format', + }, + { + displayName: 'Title', + name: 'title', + type: 'string', + default: '', + }, + ], + }, + { + displayName: 'Metadata', + name: 'metadata', + type: 'collection', + placeholder: 'Add Field', + default: {}, + displayOptions: { + show: { + resource: ['engagement'], + operation: ['create'], + type: ['call'], + }, + }, + options: [ + { + displayName: 'Body', + name: 'body', + type: 'string', + default: '', + }, + { + displayName: 'Duration Milliseconds', + name: 'durationMilliseconds', + type: 'number', + default: 0, + }, + { + displayName: 'From Number', + name: 'fromNumber', + type: 'string', + default: '', + }, + { + displayName: 'Recording URL', + name: 'recordingUrl', + type: 'string', + default: '', + }, + { + displayName: 'Status', + name: 'status', + type: 'options', + options: [ + { + name: 'Busy', + value: 'BUSY', + }, + { + name: 'Calling CRM User', + value: 'CALLING_CRM_USER', + }, + { + name: 'Canceled', + value: 'CANCELED', + }, + { + name: 'Completed', + value: 'COMPLETED', + }, + { + name: 'Connecting', + value: 'CONNECTING', + }, + { + name: 'Failed', + value: 'FAILED', + }, + { + name: 'In Progress', + value: 'IN_PROGRESS', + }, + { + name: 'No Answer', + value: 'NO_ANSWER', + }, + { + name: 'Queued', + value: 'QUEUED', + }, + { + name: 'Ringing', + value: 'RINGING', + }, + ], + default: 'QUEUED', + }, + { + displayName: 'To Number', + name: 'toNumber', + type: 'string', + default: '', + }, + ], + }, + { + displayName: 'Engagement Properties', + name: 'additionalFields', + type: 'collection', + placeholder: 'Add Property', + default: {}, + displayOptions: { + show: { + resource: ['engagement'], + operation: ['create'], + }, + }, + options: [ + { + displayName: 'Associations', + name: 'associations', + type: 'collection', + placeholder: 'Add Field', + default: {}, + options: [ + { + displayName: 'Company IDs', + name: 'companyIds', + type: 'string', + default: '', + }, + { + displayName: 'Contact IDs', + name: 'contactIds', + type: 'string', + default: '', + }, + { + displayName: 'Deals IDs', + name: 'dealIds', + type: 'string', + default: '', + }, + { + displayName: 'Owner IDs', + name: 'ownerIds', + type: 'string', + default: '', + }, + { + displayName: 'Ticket IDs', + name: 'ticketIds', + type: 'string', + default: '', + }, + ], + }, + ], + }, + + /* -------------------------------------------------------------------------- */ + /* engagement:get/delete */ + /* -------------------------------------------------------------------------- */ + { + displayName: 'Engagement to Get', + name: 'engagementId', + type: 'resourceLocator', + default: { mode: 'list', value: '' }, + required: true, + displayOptions: { + show: { + resource: ['engagement'], + operation: ['get'], + }, + }, + modes: [ + { + displayName: 'From List', + name: 'list', + type: 'list', + placeholder: 'Select from the list', + typeOptions: { + searchListMethod: 'searchEngagements', + }, + }, + { + displayName: 'By Id', + name: 'id', + type: 'string', + placeholder: '58539222', + validation: [ + { + type: 'regex', + properties: { + regex: '[0-9]+', + errorMessage: 'Not a valid HubSpot Engagement ID', + }, + }, + ], + }, + ], + }, + + { + displayName: 'Engagement to Delete', + name: 'engagementId', + type: 'resourceLocator', + default: { mode: 'list', value: '' }, + required: true, + displayOptions: { + show: { + resource: ['engagement'], + operation: ['delete'], + }, + }, + modes: [ + { + displayName: 'From List', + name: 'list', + type: 'list', + placeholder: 'Select from the list', + typeOptions: { + searchListMethod: 'searchEngagements', + }, + }, + { + displayName: 'By Id', + name: 'id', + type: 'string', + placeholder: '58539222', + validation: [ + { + type: 'regex', + properties: { + regex: '[0-9]+', + errorMessage: 'Not a valid HubSpot Engagement ID', + }, + }, + ], + }, + ], + }, + + /* -------------------------------------------------------------------------- */ + /* engagement:getAll */ + /* -------------------------------------------------------------------------- */ + { + displayName: 'Return All', + name: 'returnAll', + type: 'boolean', + displayOptions: { + show: { + resource: ['engagement'], + operation: ['getAll'], + }, + }, + default: false, + description: 'Whether to return all results or only up to a given limit', + }, + { + displayName: 'Limit', + name: 'limit', + type: 'number', + displayOptions: { + show: { + resource: ['engagement'], + operation: ['getAll'], + returnAll: [false], + }, + }, + typeOptions: { + minValue: 1, + maxValue: 250, + }, + default: 100, + description: 'Max number of results to return', + }, +]; diff --git a/packages/nodes-base/nodes/Hubspot/V2/FormDescription.ts b/packages/nodes-base/nodes/Hubspot/V2/FormDescription.ts new file mode 100644 index 0000000000..09c12497f5 --- /dev/null +++ b/packages/nodes-base/nodes/Hubspot/V2/FormDescription.ts @@ -0,0 +1,315 @@ +import type { INodeProperties } from 'n8n-workflow'; + +export const formOperations: INodeProperties[] = [ + { + displayName: 'Operation', + name: 'operation', + type: 'options', + noDataExpression: true, + displayOptions: { + show: { + resource: ['form'], + }, + }, + options: [ + { + name: 'Get Fields', + value: 'getFields', + description: 'Get all fields from a form', + action: 'Get all fields from a form', + }, + { + name: 'Submit', + value: 'submit', + description: 'Submit data to a form', + action: 'Submit a form', + }, + ], + default: 'getFields', + }, +]; + +export const formFields: INodeProperties[] = [ + /* -------------------------------------------------------------------------- */ + /* form:submit */ + /* -------------------------------------------------------------------------- */ + { + displayName: 'Form Name or ID', + name: 'formId', + type: 'options', + typeOptions: { + loadOptionsMethod: 'getForms', + }, + required: true, + displayOptions: { + show: { + resource: ['form'], + operation: ['submit'], + }, + }, + default: '', + description: + 'The ID of the form you\'re sending data to. Choose from the list, or specify an ID using an expression.', + }, + { + displayName: 'Options', + name: 'additionalFields', + type: 'collection', + placeholder: 'Add Option', + default: {}, + displayOptions: { + show: { + resource: ['form'], + operation: ['submit'], + }, + }, + options: [ + { + displayName: 'Skip Validation', + name: 'skipValidation', + type: 'boolean', + default: false, + description: 'Whether or not to skip validation based on the form settings', + }, + { + displayName: 'Submitted At', + name: 'submittedAt', + type: 'dateTime', + default: '', + description: + 'Time of the form submission. When using expressions, the time should be specified in YYYY-MM-DD hh-mm-ss format.', + }, + ], + }, + { + displayName: 'Context', + name: 'contextUi', + placeholder: 'Add Context', + type: 'fixedCollection', + typeOptions: { + multipleValues: false, + }, + displayOptions: { + show: { + resource: ['form'], + operation: ['submit'], + }, + }, + default: {}, + options: [ + { + displayName: 'Context', + name: 'contextValue', + values: [ + { + displayName: 'HubSpot Usertoken', + name: 'hutk', + type: 'string', + default: '', + description: + 'Include this parameter and set it to the hubspotutk cookie value to enable cookie tracking on your submission', + }, + { + displayName: 'IP Address', + name: 'ipAddress', + type: 'string', + default: '', + description: 'The IP address of the visitor filling out the form', + }, + { + displayName: 'Page URI', + name: 'pageUri', + type: 'string', + default: '', + description: 'The URI of the page the submission happened on', + }, + { + displayName: 'Page Name', + name: 'pageName', + type: 'string', + default: '', + description: 'The name or title of the page the submission happened on', + }, + { + displayName: 'Page ID', + name: 'pageId', + type: 'number', + default: '', + description: 'The ID of a page created on the HubSpot CMS', + }, + { + displayName: 'SFDC Campaign ID', + name: 'sfdcCampaignId', + type: 'number', + default: '', + description: + 'If the form is for an account using the HubSpot Salesforce Integration, you can include the ID of a Salesforce campaign to add the contact to the specified campaign', + }, + { + displayName: 'Go to Webinar Webinar ID', + name: 'goToWebinarWebinarKey', + type: 'number', + default: '', + description: + 'If the form is for an account using the HubSpot GoToWebinar Integration, you can include the ID of a webinar to enroll the contact in that webinar when they submit the form', + }, + ], + }, + ], + }, + { + displayName: 'Legal Consent', + name: 'lengalConsentUi', + placeholder: 'Add Legal Consent', + type: 'fixedCollection', + typeOptions: { + multipleValues: false, + }, + displayOptions: { + show: { + resource: ['form'], + operation: ['submit'], + }, + }, + default: {}, + options: [ + { + displayName: 'Consent', + name: 'lengalConsentValues', + values: [ + { + displayName: 'Consent To Process', + name: 'consentToProcess', + type: 'boolean', + default: false, + description: 'Whether or not the visitor checked the Consent to process checkbox', + }, + { + displayName: 'Text', + name: 'text', + type: 'string', + default: '', + description: 'The text displayed to the visitor for the Consent to process checkbox', + }, + { + displayName: 'Communications', + name: 'communicationsUi', + placeholder: 'Add Communication', + type: 'fixedCollection', + typeOptions: { + multipleValues: true, + }, + default: {}, + options: [ + { + displayName: 'Communication', + name: 'communicationValues', + values: [ + { + displayName: 'Subcription Type Name or ID', + name: 'subscriptionTypeId', + type: 'options', + typeOptions: { + loadOptionsMethod: 'getSubscriptionTypes', + }, + default: '', + description: + 'The ID of the specific subscription type. Choose from the list, or specify an ID using an expression.', + }, + { + displayName: 'Value', + name: 'value', + type: 'boolean', + default: false, + description: + 'Whether or not the visitor checked the checkbox for this subscription type', + }, + { + displayName: 'Text', + name: 'text', + type: 'string', + default: '', + description: + 'The text displayed to the visitor for this specific subscription checkbox', + }, + ], + }, + ], + }, + ], + }, + { + displayName: 'Legitimate Interest', + name: 'legitimateInterestValues', + values: [ + { + displayName: 'Subcription Type Name or ID', + name: 'subscriptionTypeId', + type: 'options', + typeOptions: { + loadOptionsMethod: 'getSubscriptionTypes', + }, + default: '', + description: + 'The ID of the specific subscription type that this forms indicates interest to. Choose from the list, or specify an ID using an expression.', + }, + { + displayName: 'Value', + name: 'value', + type: 'boolean', + default: false, + // eslint-disable-next-line n8n-nodes-base/node-param-description-boolean-without-whether + description: + "This must be true when using the 'legitimateInterest' option, as it reflects the consent indicated by the visitor when submitting the form", + }, + { + displayName: 'Legal Basis', + name: 'legalBasis', + type: 'options', + options: [ + { + name: 'Customer', + value: 'CUSTOMER', + }, + { + name: 'Lead', + value: 'LEAD', + }, + ], + default: '', + description: 'The privacy text displayed to the visitor', + }, + { + displayName: 'Text', + name: 'text', + type: 'string', + default: '', + description: 'The privacy text displayed to the visitor', + }, + ], + }, + ], + }, + + /* -------------------------------------------------------------------------- */ + /* form:getFields */ + /* -------------------------------------------------------------------------- */ + { + displayName: 'Form Name or ID', + name: 'formId', + type: 'options', + typeOptions: { + loadOptionsMethod: 'getForms', + }, + required: true, + displayOptions: { + show: { + resource: ['form'], + operation: ['getFields'], + }, + }, + default: '', + description: + 'The ID of the form. Choose from the list, or specify an ID using an expression.', + }, +]; diff --git a/packages/nodes-base/nodes/Hubspot/V2/FormInterface.ts b/packages/nodes-base/nodes/Hubspot/V2/FormInterface.ts new file mode 100644 index 0000000000..83f1153cb3 --- /dev/null +++ b/packages/nodes-base/nodes/Hubspot/V2/FormInterface.ts @@ -0,0 +1,21 @@ +import type { IDataObject } from 'n8n-workflow'; + +export interface IContext { + goToWebinarWebinarKey?: string; + hutk?: string; + ipAddress?: string; + pageId?: string; + pageName?: string; + pageUri?: string; + sfdcCampaignId?: string; +} + +export interface IForm { + portalId?: number; + formId?: string; + fields?: IDataObject[]; + legalConsentOptions?: IDataObject; + context?: IContext[]; + submittedAt?: number; + skipValidation?: boolean; +} diff --git a/packages/nodes-base/nodes/Hubspot/GenericFunctions.ts b/packages/nodes-base/nodes/Hubspot/V2/GenericFunctions.ts similarity index 100% rename from packages/nodes-base/nodes/Hubspot/GenericFunctions.ts rename to packages/nodes-base/nodes/Hubspot/V2/GenericFunctions.ts diff --git a/packages/nodes-base/nodes/Hubspot/V2/HubspotV2.node.ts b/packages/nodes-base/nodes/Hubspot/V2/HubspotV2.node.ts new file mode 100644 index 0000000000..5710bd3f99 --- /dev/null +++ b/packages/nodes-base/nodes/Hubspot/V2/HubspotV2.node.ts @@ -0,0 +1,3040 @@ +import type { IExecuteFunctions } from 'n8n-core'; + +import type { + ICredentialDataDecryptedObject, + ICredentialsDecrypted, + ICredentialTestFunctions, + IDataObject, + ILoadOptionsFunctions, + INodeCredentialTestResult, + INodeExecutionData, + INodeListSearchItems, + INodeListSearchResult, + INodePropertyOptions, + INodeType, + INodeTypeBaseDescription, + INodeTypeDescription, + JsonObject, +} from 'n8n-workflow'; +import { NodeOperationError } from 'n8n-workflow'; + +import { + clean, + getAssociations, + getCallMetadata, + getEmailMetadata, + getMeetingMetadata, + getTaskMetadata, + hubspotApiRequest, + hubspotApiRequestAllItems, + validateCredentials, +} from './GenericFunctions'; + +import { contactFields, contactOperations } from './ContactDescription'; + +import { contactListFields, contactListOperations } from './ContactListDescription'; + +import { companyFields, companyOperations } from './CompanyDescription'; + +import { dealFields, dealOperations } from './DealDescription'; + +import { engagementFields, engagementOperations } from './EngagementDescription'; + +import { ticketFields, ticketOperations } from './TicketDescription'; + +import type { IForm } from './FormInterface'; + +import type { IAssociation, IDeal } from './DealInterface'; + +import { snakeCase } from 'change-case'; + +export class HubspotV2 implements INodeType { + description: INodeTypeDescription; + + constructor(baseDescription: INodeTypeBaseDescription) { + this.description = { + ...baseDescription, + group: ['output'], + version: 2, + subtitle: '={{$parameter["operation"] + ": " + $parameter["resource"]}}', + defaults: { + name: 'HubSpot', + }, + inputs: ['main'], + outputs: ['main'], + credentials: [ + { + name: 'hubspotApi', + required: true, + testedBy: 'hubspotApiTest', + displayOptions: { + show: { + authentication: ['apiKey'], + }, + }, + }, + { + name: 'hubspotAppToken', + required: true, + testedBy: 'hubspotApiTest', + displayOptions: { + show: { + authentication: ['appToken'], + }, + }, + }, + { + name: 'hubspotOAuth2Api', + required: true, + displayOptions: { + show: { + authentication: ['oAuth2'], + }, + }, + }, + ], + properties: [ + { + displayName: 'Authentication', + name: 'authentication', + type: 'options', + options: [ + { + name: 'API Key', + value: 'apiKey', + }, + { + name: 'APP Token', + value: 'appToken', + }, + { + name: 'OAuth2', + value: 'oAuth2', + }, + ], + default: 'apiKey', + }, + { + displayName: 'Resource', + name: 'resource', + type: 'options', + noDataExpression: true, + options: [ + { + name: 'Company', + value: 'company', + }, + { + name: 'Contact', + value: 'contact', + }, + { + name: 'Contact List', + value: 'contactList', + }, + { + name: 'Deal', + value: 'deal', + }, + { + name: 'Engagement', + value: 'engagement', + }, + // { + // name: 'Form', + // value: 'form', + // }, + { + name: 'Ticket', + value: 'ticket', + }, + ], + default: 'contact', + }, + // CONTACT + ...contactOperations, + ...contactFields, + // CONTACT LIST + ...contactListOperations, + ...contactListFields, + // COMPANY + ...companyOperations, + ...companyFields, + // DEAL + ...dealOperations, + ...dealFields, + // ENGAGEMENT + ...engagementOperations, + ...engagementFields, + //! FORM Deprecated + //...formOperations, + //...formFields, + // TICKET + ...ticketOperations, + ...ticketFields, + ], + }; + } + + methods = { + credentialTest: { + async hubspotApiTest( + this: ICredentialTestFunctions, + credential: ICredentialsDecrypted, + ): Promise { + try { + await validateCredentials.call(this, credential.data as ICredentialDataDecryptedObject); + } catch (error) { + const err = error as JsonObject; + if (err.statusCode === 401) { + return { + status: 'Error', + message: 'Invalid credentials', + }; + } + } + return { + status: 'OK', + message: 'Authentication successful', + }; + }, + }, + loadOptions: { + /* -------------------------------------------------------------------------- */ + /* CONTACT */ + /* -------------------------------------------------------------------------- */ + + // Get all the contact lead statuses to display them to user so that they can + // select them easily + async getContactLeadStatuses(this: ILoadOptionsFunctions): Promise { + const returnData: INodePropertyOptions[] = []; + const endpoint = '/properties/v2/contacts/properties'; + const properties = await hubspotApiRequest.call(this, 'GET', endpoint, {}); + for (const property of properties) { + if (property.name === 'hs_lead_status') { + for (const option of property.options) { + const statusName = option.label; + const statusId = option.value; + returnData.push({ + name: statusName, + value: statusId, + }); + } + } + } + return returnData; + }, + + // Get all the contact legal basics to display them to user so that they can + // select them easily + async getContactLealBasics(this: ILoadOptionsFunctions): Promise { + const returnData: INodePropertyOptions[] = []; + const endpoint = '/properties/v2/contacts/properties'; + const properties = await hubspotApiRequest.call(this, 'GET', endpoint, {}); + for (const property of properties) { + if (property.name === 'hs_legal_basis') { + for (const option of property.options) { + const statusName = option.label; + const statusId = option.value; + returnData.push({ + name: statusName, + value: statusId, + }); + } + } + } + return returnData; + }, + + // Get all the contact lifecycle stages to display them to user so that they can + // select them easily + async getContactLifeCycleStages( + this: ILoadOptionsFunctions, + ): Promise { + const returnData: INodePropertyOptions[] = []; + const endpoint = '/properties/v2/contacts/properties'; + const properties = await hubspotApiRequest.call(this, 'GET', endpoint, {}); + for (const property of properties) { + if (property.name === 'lifecyclestage') { + for (const option of property.options) { + const stageName = option.label; + const stageId = option.value; + returnData.push({ + name: stageName, + value: stageId, + }); + } + } + } + return returnData; + }, + + // Get all the contact lifecycle stages to display them to user so that they can + // select them easily + async getContactOriginalSources( + this: ILoadOptionsFunctions, + ): Promise { + const returnData: INodePropertyOptions[] = []; + const endpoint = '/properties/v2/contacts/properties'; + const properties = await hubspotApiRequest.call(this, 'GET', endpoint, {}); + for (const property of properties) { + if (property.name === 'hs_analytics_source') { + for (const option of property.options) { + const sourceName = option.label; + const sourceId = option.value; + returnData.push({ + name: sourceName, + value: sourceId, + }); + } + } + } + return returnData; + }, + + // Get all the contact preffered languages to display them to user so that they can + // select them easily + async getContactPrefferedLanguages( + this: ILoadOptionsFunctions, + ): Promise { + const returnData: INodePropertyOptions[] = []; + const endpoint = '/properties/v2/contacts/properties'; + const properties = await hubspotApiRequest.call(this, 'GET', endpoint, {}); + for (const property of properties) { + if (property.name === 'hs_language') { + for (const option of property.options) { + const languageName = option.label; + const languageId = option.value; + returnData.push({ + name: languageName, + value: languageId, + }); + } + } + } + return returnData; + }, + + // Get all the contact preffered languages to display them to user so that they can + // select them easily + async getContactStatuses(this: ILoadOptionsFunctions): Promise { + const returnData: INodePropertyOptions[] = []; + const endpoint = '/properties/v2/contacts/properties'; + const properties = await hubspotApiRequest.call(this, 'GET', endpoint, {}); + for (const property of properties) { + if (property.name === 'hs_content_membership_status') { + for (const option of property.options) { + const languageName = option.label; + const languageId = option.value; + returnData.push({ + name: languageName, + value: languageId, + }); + } + } + } + return returnData; + }, + // Get all the contact properties to display them to user so that they can + // select them easily + async getContactProperties(this: ILoadOptionsFunctions): Promise { + const returnData: INodePropertyOptions[] = []; + const endpoint = '/properties/v2/contacts/properties'; + const properties = await hubspotApiRequest.call(this, 'GET', endpoint, {}); + for (const property of properties) { + const propertyName = property.label; + const propertyId = property.name; + returnData.push({ + name: propertyName, + value: propertyId, + }); + } + return returnData; + }, + // Get all the contact properties to display them to user so that they can + // select them easily + async getContactPropertiesWithType( + this: ILoadOptionsFunctions, + ): Promise { + const returnData: INodePropertyOptions[] = []; + const endpoint = '/properties/v2/contacts/properties'; + const properties = await hubspotApiRequest.call(this, 'GET', endpoint, {}); + for (const property of properties) { + const propertyName = property.label; + const propertyId = property.name; + const propertyType = property.type; + returnData.push({ + name: propertyName, + // Hacky way to get the property type need to be parsed to be use in the api + value: `${propertyId}|${propertyType}`, + }); + } + return returnData; + }, + + // Get all the contact properties to display them to user so that they can + // select them easily + async getContactCustomProperties( + this: ILoadOptionsFunctions, + ): Promise { + const returnData: INodePropertyOptions[] = []; + const endpoint = '/properties/v2/contacts/properties'; + const properties = await hubspotApiRequest.call(this, 'GET', endpoint, {}); + for (const property of properties) { + if (property.hubspotDefined === null) { + const propertyName = property.label; + const propertyId = property.name; + returnData.push({ + name: propertyName, + value: propertyId, + }); + } + } + return returnData; + }, + + // Get all the contact number of employees options to display them to user so that they can + // select them easily + async getContactNumberOfEmployees( + this: ILoadOptionsFunctions, + ): Promise { + const returnData: INodePropertyOptions[] = []; + const endpoint = '/properties/v2/contacts/properties'; + const properties = await hubspotApiRequest.call(this, 'GET', endpoint, {}); + for (const property of properties) { + if (property.name === 'numemployees') { + for (const option of property.options) { + const optionName = option.label; + const optionId = option.value; + returnData.push({ + name: optionName, + value: optionId, + }); + } + } + } + return returnData; + }, + + /* -------------------------------------------------------------------------- */ + /* COMPANY */ + /* -------------------------------------------------------------------------- */ + + // Get all the company industries to display them to user so that they can + // select them easily + async getCompanyIndustries(this: ILoadOptionsFunctions): Promise { + const returnData: INodePropertyOptions[] = []; + const endpoint = '/properties/v2/companies/properties'; + const properties = await hubspotApiRequest.call(this, 'GET', endpoint, {}); + for (const property of properties) { + if (property.name === 'industry') { + for (const option of property.options) { + const industryName = option.label; + const industryId = option.value; + returnData.push({ + name: industryName, + value: industryId, + }); + } + } + } + return returnData; + }, + + // Get all the company lead statuses to display them to user so that they can + // select them easily + async getCompanyleadStatuses(this: ILoadOptionsFunctions): Promise { + const returnData: INodePropertyOptions[] = []; + const endpoint = '/properties/v2/companies/properties'; + const properties = await hubspotApiRequest.call(this, 'GET', endpoint, {}); + for (const property of properties) { + if (property.name === 'hs_lead_status') { + for (const option of property.options) { + const statusName = option.label; + const statusId = option.value; + returnData.push({ + name: statusName, + value: statusId, + }); + } + } + } + return returnData; + }, + + // Get all the company lifecycle stages to display them to user so that they can + // select them easily + async getCompanylifecycleStages( + this: ILoadOptionsFunctions, + ): Promise { + const returnData: INodePropertyOptions[] = []; + const endpoint = '/properties/v2/companies/properties'; + const properties = await hubspotApiRequest.call(this, 'GET', endpoint, {}); + for (const property of properties) { + if (property.name === 'lifecyclestage') { + for (const option of property.options) { + const stageName = option.label; + const stageId = option.value; + returnData.push({ + name: stageName, + value: stageId, + }); + } + } + } + return returnData; + }, + + // Get all the company types stages to display them to user so that they can + // select them easily + async getCompanyTypes(this: ILoadOptionsFunctions): Promise { + const returnData: INodePropertyOptions[] = []; + const endpoint = '/properties/v2/companies/properties'; + const properties = await hubspotApiRequest.call(this, 'GET', endpoint, {}); + for (const property of properties) { + if (property.name === 'type') { + for (const option of property.options) { + const typeName = option.label; + const typeId = option.value; + returnData.push({ + name: typeName, + value: typeId, + }); + } + } + } + return returnData; + }, + + // Get all the company types stages to display them to user so that they can + // select them easily + async getCompanyTargetAccounts(this: ILoadOptionsFunctions): Promise { + const returnData: INodePropertyOptions[] = []; + const endpoint = '/properties/v2/companies/properties'; + const properties = await hubspotApiRequest.call(this, 'GET', endpoint, {}); + for (const property of properties) { + if (property.name === 'hs_target_account') { + for (const option of property.options) { + const targetName = option.label; + const targetId = option.value; + returnData.push({ + name: targetName, + value: targetId, + }); + } + } + } + return returnData; + }, + + // Get all the company source types stages to display them to user so that they can + // select them easily + async getCompanySourceTypes(this: ILoadOptionsFunctions): Promise { + const returnData: INodePropertyOptions[] = []; + const endpoint = '/properties/v2/companies/properties'; + const properties = await hubspotApiRequest.call(this, 'GET', endpoint, {}); + for (const property of properties) { + if (property.name === 'hs_analytics_source') { + for (const option of property.options) { + const typeName = option.label; + const typeId = option.value; + returnData.push({ + name: typeName, + value: typeId, + }); + } + } + } + return returnData; + }, + + // Get all the company web technologies stages to display them to user so that they can + // select them easily + async getCompanyWebTechnologies( + this: ILoadOptionsFunctions, + ): Promise { + const returnData: INodePropertyOptions[] = []; + const endpoint = '/properties/v2/companies/properties'; + const properties = await hubspotApiRequest.call(this, 'GET', endpoint, {}); + for (const property of properties) { + if (property.name === 'web_technologies') { + for (const option of property.options) { + const technologyName = option.label; + const technologyId = option.value; + returnData.push({ + name: technologyName, + value: technologyId, + }); + } + } + } + return returnData; + }, + + // Get all the company properties to display them to user so that they can + // select them easily + async getCompanyProperties(this: ILoadOptionsFunctions): Promise { + const returnData: INodePropertyOptions[] = []; + const endpoint = '/properties/v2/companies/properties'; + const properties = await hubspotApiRequest.call(this, 'GET', endpoint, {}); + for (const property of properties) { + const propertyName = property.label; + const propertyId = property.name; + returnData.push({ + name: propertyName, + value: propertyId, + }); + } + return returnData; + }, + + // Get all the company custom properties to display them to user so that they can + // select them easily + async getCompanyCustomProperties( + this: ILoadOptionsFunctions, + ): Promise { + const returnData: INodePropertyOptions[] = []; + const endpoint = '/properties/v2/companies/properties'; + const properties = await hubspotApiRequest.call(this, 'GET', endpoint, {}); + for (const property of properties) { + if (property.hubspotDefined === null) { + const propertyName = property.label; + const propertyId = property.name; + returnData.push({ + name: propertyName, + value: propertyId, + }); + } + } + return returnData; + }, + + /* -------------------------------------------------------------------------- */ + /* DEAL */ + /* -------------------------------------------------------------------------- */ + + // Get all the groups to display them to user so that they can + // select them easily + async getDealStages(this: ILoadOptionsFunctions): Promise { + const returnData: INodePropertyOptions[] = []; + const endpoint = '/crm-pipelines/v1/pipelines/deals'; + let stages = await hubspotApiRequest.call(this, 'GET', endpoint, {}); + stages = stages.results[0].stages; + for (const stage of stages) { + const stageName = stage.label; + const stageId = stage.stageId; + returnData.push({ + name: stageName, + value: stageId, + }); + } + return returnData; + }, + + // Get all the deal types to display them to user so that they can + // select them easily + async getDealTypes(this: ILoadOptionsFunctions): Promise { + const returnData: INodePropertyOptions[] = []; + const endpoint = '/properties/v1/deals/properties/named/dealtype'; + const dealTypes = await hubspotApiRequest.call(this, 'GET', endpoint); + for (const dealType of dealTypes.options) { + const dealTypeName = dealType.label; + const dealTypeId = dealType.value; + returnData.push({ + name: dealTypeName, + value: dealTypeId, + }); + } + return returnData; + }, + + // Get all the deal properties to display them to user so that they can + // select them easily + async getDealCustomProperties(this: ILoadOptionsFunctions): Promise { + const returnData: INodePropertyOptions[] = []; + const endpoint = '/properties/v2/deals/properties'; + const properties = await hubspotApiRequest.call(this, 'GET', endpoint, {}); + for (const property of properties) { + if (property.hubspotDefined === null) { + const propertyName = property.label; + const propertyId = property.name; + returnData.push({ + name: propertyName, + value: propertyId, + }); + } + } + return returnData; + }, + // Get all the deal properties to display them to user so that they can + // select them easily + async getDealProperties(this: ILoadOptionsFunctions): Promise { + const returnData: INodePropertyOptions[] = []; + const endpoint = '/properties/v2/deals/properties'; + const properties = await hubspotApiRequest.call(this, 'GET', endpoint, {}); + for (const property of properties) { + const propertyName = property.label; + const propertyId = property.name; + returnData.push({ + name: propertyName, + value: propertyId, + }); + } + return returnData; + }, + // Get all the deal properties to display them to user so that they can + // select them easily + async getDealPropertiesWithType( + this: ILoadOptionsFunctions, + ): Promise { + const returnData: INodePropertyOptions[] = []; + const endpoint = '/properties/v2/deals/properties'; + const properties = await hubspotApiRequest.call(this, 'GET', endpoint, {}); + for (const property of properties) { + const propertyName = property.label; + const propertyId = property.name; + const propertyType = property.type; + returnData.push({ + name: propertyName, + // Hacky way to get the property type need to be parsed to be use in the api + value: `${propertyId}|${propertyType}`, + }); + } + return returnData; + }, + + // Get all the deal pipelines to display them to user so that they can + // select them easily + async getDealPipelines(this: ILoadOptionsFunctions): Promise { + const returnData: INodePropertyOptions[] = []; + const endpoint = '/crm/v3/pipelines/deals'; + const data = await hubspotApiRequest.call(this, 'GET', endpoint, {}); + for (const pipeline of data.results) { + returnData.push({ + name: pipeline.label, + value: pipeline.id, + }); + } + return returnData; + }, + + /* -------------------------------------------------------------------------- */ + /* FORM */ + /* -------------------------------------------------------------------------- */ + + // Get all the forms to display them to user so that they can + // select them easily + async getForms(this: ILoadOptionsFunctions): Promise { + const returnData: INodePropertyOptions[] = []; + const endpoint = '/forms/v2/forms'; + const forms = await hubspotApiRequest.call(this, 'GET', endpoint, {}, { formTypes: 'ALL' }); + for (const form of forms) { + const formName = form.name; + const formId = form.guid; + returnData.push({ + name: formName, + value: formId, + }); + } + return returnData; + }, + + // Get all the subscription types to display them to user so that they can + // select them easily + async getSubscriptionTypes(this: ILoadOptionsFunctions): Promise { + const returnData: INodePropertyOptions[] = []; + const endpoint = '/email/public/v1/subscriptions'; + const subscriptions = await hubspotApiRequestAllItems.call( + this, + 'subscriptionDefinitions', + 'GET', + endpoint, + {}, + ); + for (const subscription of subscriptions) { + const subscriptionName = subscription.name; + const subscriptionId = subscription.id; + returnData.push({ + name: subscriptionName, + value: subscriptionId, + }); + } + return returnData; + }, + + /* -------------------------------------------------------------------------- */ + /* TICKET */ + /* -------------------------------------------------------------------------- */ + + // Get all the ticket categories to display them to user so that they can + // select them easily + async getTicketCategories(this: ILoadOptionsFunctions): Promise { + const returnData: INodePropertyOptions[] = []; + const endpoint = '/properties/v2/tickets/properties'; + const properties = await hubspotApiRequest.call(this, 'GET', endpoint, {}); + for (const property of properties) { + if (property.name === 'hs_ticket_category') { + for (const option of property.options) { + const categoryName = option.label; + const categoryId = option.value; + returnData.push({ + name: categoryName, + value: categoryId, + }); + } + } + } + return returnData.sort((a, b) => (a.name < b.name ? 0 : 1)); + }, + + // Get all the ticket pipelines to display them to user so that they can + // select them easily + async getTicketPipelines(this: ILoadOptionsFunctions): Promise { + const returnData: INodePropertyOptions[] = []; + const endpoint = '/crm-pipelines/v1/pipelines/tickets'; + const { results } = await hubspotApiRequest.call(this, 'GET', endpoint, {}); + for (const pipeline of results) { + const pipelineName = pipeline.label; + const pipelineId = pipeline.pipelineId; + returnData.push({ + name: pipelineName, + value: pipelineId, + }); + } + return returnData; + }, + + // Get all the ticket resolutions to display them to user so that they can + // select them easily + async getTicketPriorities(this: ILoadOptionsFunctions): Promise { + const returnData: INodePropertyOptions[] = []; + const endpoint = '/properties/v2/tickets/properties'; + const properties = await hubspotApiRequest.call(this, 'GET', endpoint, {}); + for (const property of properties) { + if (property.name === 'hs_ticket_priority') { + for (const option of property.options) { + const priorityName = option.label; + const priorityId = option.value; + returnData.push({ + name: priorityName, + value: priorityId, + }); + } + } + } + return returnData; + }, + + // Get all the ticket properties to display them to user so that they can + // select them easily + async getTicketProperties(this: ILoadOptionsFunctions): Promise { + const returnData: INodePropertyOptions[] = []; + const endpoint = '/properties/v2/tickets/properties'; + const properties = await hubspotApiRequest.call(this, 'GET', endpoint, {}); + for (const property of properties) { + const propertyName = property.label; + const propertyId = property.name; + returnData.push({ + name: propertyName, + value: propertyId, + }); + } + return returnData; + }, + + // Get all the ticket resolutions to display them to user so that they can + // select them easily + async getTicketResolutions(this: ILoadOptionsFunctions): Promise { + const returnData: INodePropertyOptions[] = []; + const endpoint = '/properties/v2/tickets/properties'; + const properties = await hubspotApiRequest.call(this, 'GET', endpoint, {}); + for (const property of properties) { + if (property.name === 'hs_resolution') { + for (const option of property.options) { + const resolutionName = option.label; + const resolutionId = option.value; + returnData.push({ + name: resolutionName, + value: resolutionId, + }); + } + } + } + return returnData.sort((a, b) => (a.name < b.name ? 0 : 1)); + }, + + // Get all the ticket sources to display them to user so that they can + // select them easily + async getTicketSources(this: ILoadOptionsFunctions): Promise { + const returnData: INodePropertyOptions[] = []; + const endpoint = '/properties/v2/tickets/properties'; + const properties = await hubspotApiRequest.call(this, 'GET', endpoint, {}); + for (const property of properties) { + if (property.name === 'source_type') { + for (const option of property.options) { + const sourceName = option.label; + const sourceId = option.value; + returnData.push({ + name: sourceName, + value: sourceId, + }); + } + } + } + return returnData.sort((a, b) => (a.name < b.name ? 0 : 1)); + }, + + // Get all the ticket stages to display them to user so that they can + // select them easily + async getTicketStages(this: ILoadOptionsFunctions): Promise { + let currentPipelineId = this.getCurrentNodeParameter('pipelineId') as string; + if (currentPipelineId === undefined) { + currentPipelineId = this.getNodeParameter('updateFields.pipelineId', '') as string; + } + const returnData: INodePropertyOptions[] = []; + const endpoint = '/crm-pipelines/v1/pipelines/tickets'; + const { results } = await hubspotApiRequest.call(this, 'GET', endpoint, {}); + for (const pipeline of results) { + if (currentPipelineId === pipeline.pipelineId) { + for (const stage of pipeline.stages) { + const stageName = stage.label; + const stageId = stage.stageId; + returnData.push({ + name: stageName, + value: stageId, + }); + } + } + } + return returnData; + }, + + /* -------------------------------------------------------------------------- */ + /* COMMON */ + /* -------------------------------------------------------------------------- */ + + // Get all the owners to display them to user so that they can + // select them easily + async getOwners(this: ILoadOptionsFunctions): Promise { + const returnData: INodePropertyOptions[] = []; + const endpoint = '/owners/v2/owners'; + const owners = await hubspotApiRequest.call(this, 'GET', endpoint); + for (const owner of owners) { + const ownerName = owner.email; + const ownerId = owner.ownerId; + returnData.push({ + name: ownerName, + value: ownerId, + }); + } + return returnData; + }, + + // Get all the companies to display them to user so that they can + // select them easily + async getCompanies(this: ILoadOptionsFunctions): Promise { + const returnData: INodePropertyOptions[] = []; + const qs: IDataObject = { + properties: ['name'], + }; + const endpoint = '/companies/v2/companies/paged'; + const companies = await hubspotApiRequestAllItems.call( + this, + 'companies', + 'GET', + endpoint, + {}, + qs, + ); + for (const company of companies) { + const companyName = company.properties.name + ? company.properties.name.value + : company.companyId; + const companyId = company.companyId; + returnData.push({ + name: companyName, + value: companyId, + }); + } + return returnData.sort((a, b) => (a.name < b.name ? 0 : 1)); + }, + + // Get all the companies to display them to user so that they can + // select them easily + async getContacts(this: ILoadOptionsFunctions): Promise { + const returnData: INodePropertyOptions[] = []; + const endpoint = '/contacts/v1/lists/all/contacts/all'; + const contacts = await hubspotApiRequestAllItems.call(this, 'contacts', 'GET', endpoint); + + for (const contact of contacts) { + const firstName = contact.properties?.firstname?.value || ''; + const lastName = contact.properties?.lastname?.value || ''; + const contactName = `${firstName} ${lastName}`; + const contactId = contact.vid; + returnData.push({ + name: contactName, + value: contactId, + description: `Contact VID: ${contactId}`, + }); + } + return returnData.sort((a, b) => (a.name < b.name ? 0 : 1)); + }, + }, + listSearch: { + async searchCompanies(this: ILoadOptionsFunctions): Promise { + const qs: IDataObject = { + properties: ['name'], + }; + const endpoint = '/companies/v2/companies/paged'; + const searchResults = await hubspotApiRequestAllItems.call( + this, + 'companies', + 'GET', + endpoint, + {}, + qs, + ); + return { + // tslint:disable-next-line: no-any + results: searchResults.map((b: any) => ({ + name: b.properties?.name?.value || b.companyId, + value: b.companyId, + })), + }; + }, + async searchContacts( + this: ILoadOptionsFunctions, + filter?: string, + ): Promise { + const endpoint = '/contacts/v1/lists/all/contacts/all'; + const qs: IDataObject = { + property: ['email'], + }; + const contacts = (await hubspotApiRequestAllItems.call( + this, + 'contacts', + 'GET', + endpoint, + {}, + qs, + )) as Array<{ vid: string; properties: { email: { value: string } } }>; + const results: INodeListSearchItems[] = contacts + .map((c) => ({ + name: c.properties.email.value || c.vid, + value: c.vid, + })) + .filter( + (c) => + !filter || + c.name.toLowerCase().includes(filter.toLowerCase()) || + c.value?.toString() === filter, + ) + .sort((a, b) => { + return a.name.toLowerCase() < b.name.toLowerCase() ? -1 : 1; + }); + + return { results }; + }, + async searchDeals( + this: ILoadOptionsFunctions, + filter?: string, + ): Promise { + const endpoint = '/deals/v1/deal/paged'; + const qs: IDataObject = { + properties: ['dealname'], + }; + const deals = (await hubspotApiRequestAllItems.call( + this, + 'deals', + 'GET', + endpoint, + {}, + qs, + )) as Array<{ dealId: string; properties: { dealname: { value: string } } }>; + const results: INodeListSearchItems[] = deals + .map((c) => ({ + name: c.properties?.dealname?.value || c.dealId, + value: c.dealId, + })) + .filter( + (c) => + !filter || + c.name.toString().toLowerCase().includes(filter.toString().toLowerCase()) || + c.value?.toString() === filter, + ) + .sort((a, b) => { + return a.name.toString().toLowerCase() < b.name.toString().toLowerCase() ? -1 : 1; + }); + return { + results, + }; + }, + async searchEngagements(this: ILoadOptionsFunctions): Promise { + const endpoint = '/engagements/v1/engagements/paged'; + const qs: IDataObject = { + properties: ['name'], + }; + const engagements = await hubspotApiRequest.call(this, 'GET', endpoint, {}, qs); + return { + // tslint:disable-next-line: no-any + results: engagements.results.map((b: any) => ({ + name: + b.properties?.name?.value || b.engagement?.type + ? `${b.engagement?.type}: ${b.engagement.id}` + : b.engagement.id, + value: b.engagement.id, + })), + }; + }, + async searchTickets(this: ILoadOptionsFunctions): Promise { + const endpoint = '/crm-objects/v1/objects/tickets/paged'; + const qs: IDataObject = { + properties: ['ticket_name'], + }; + const tickets = await hubspotApiRequestAllItems.call( + this, + 'objects', + 'GET', + endpoint, + {}, + qs, + ); + return { + // tslint:disable-next-line: no-any + results: tickets.map((b: any) => ({ + name: b.objectId, + value: b.objectId, + })), + }; + }, + async searchOwners(this: ILoadOptionsFunctions): Promise { + const endpoint = '/owners/v2/owners'; + const owners = await hubspotApiRequest.call(this, 'GET', endpoint, {}); + return { + // tslint:disable-next-line: no-any + results: owners.map((b: any) => ({ + name: b.email, + value: b.ownerId, + })), + }; + }, + }, + }; + + async execute(this: IExecuteFunctions): Promise { + const items = this.getInputData(); + const returnData: INodeExecutionData[] = []; + const length = items.length; + let responseData; + const qs: IDataObject = {}; + const resource = this.getNodeParameter('resource', 0); + const operation = this.getNodeParameter('operation', 0); + + //https://legacydocs.hubspot.com/docs/methods/lists/contact-lists-overview + if (resource === 'contactList') { + try { + //https://legacydocs.hubspot.com/docs/methods/lists/add_contact_to_list + if (operation === 'add') { + const listId = this.getNodeParameter('listId', 0) as string; + const by = this.getNodeParameter('by', 0) as string; + const body: { [key: string]: [] } = { emails: [], vids: [] }; + for (let i = 0; i < length; i++) { + if (by === 'id') { + const id = this.getNodeParameter('id', i) as string; + body.vids.push(parseInt(id, 10) as never); + } else { + const email = this.getNodeParameter('email', i) as string; + body.emails.push(email as never); + } + } + responseData = await hubspotApiRequest.call( + this, + 'POST', + `/contacts/v1/lists/${listId}/add`, + body, + ); + returnData.push.apply(returnData, responseData as INodeExecutionData[]); + } + //https://legacydocs.hubspot.com/docs/methods/lists/remove_contact_from_list + if (operation === 'remove') { + const listId = this.getNodeParameter('listId', 0) as string; + const body: { [key: string]: [] } = { vids: [] }; + for (let i = 0; i < length; i++) { + const id = this.getNodeParameter('id', i) as string; + body.vids.push(parseInt(id, 10) as never); + } + responseData = await hubspotApiRequest.call( + this, + 'POST', + `/contacts/v1/lists/${listId}/remove`, + body, + ); + returnData.push.apply(returnData, responseData as INodeExecutionData[]); + } + } catch (error) { + if (this.continueOnFail()) { + returnData.push({ json: { error: (error as JsonObject).message } }); + } else { + throw error; + } + } + } else { + for (let i = 0; i < length; i++) { + try { + //https://developers.hubspot.com/docs/methods/contacts/create_or_update + if (resource === 'contact') { + //https://developers.hubspot.com/docs/methods/companies/create_company + if (operation === 'upsert') { + const email = this.getNodeParameter('email', i) as string; + const additionalFields = this.getNodeParameter('additionalFields', i); + const options = this.getNodeParameter('options', i); + const body: IDataObject[] = []; + if (additionalFields.annualRevenue) { + body.push({ + property: 'annualrevenue', + value: (additionalFields.annualRevenue as number).toString(), + }); + } + if (additionalFields.city) { + body.push({ + property: 'city', + value: additionalFields.city, + }); + } + if (additionalFields.clickedFacebookAd) { + body.push({ + property: 'hs_facebook_ad_clicked', + value: additionalFields.clickedFacebookAd, + }); + } + if (additionalFields.closeDate) { + body.push({ + property: 'closedate', + value: new Date(additionalFields.closeDate as string).getTime(), + }); + } + if (additionalFields.companyName) { + body.push({ + property: 'company', + value: additionalFields.companyName, + }); + } + if (additionalFields.companySize) { + body.push({ + property: 'company_size', + value: additionalFields.companySize, + }); + } + if (additionalFields.description) { + body.push({ + property: 'description', + value: additionalFields.description, + }); + } + if (additionalFields.contactOwner) { + body.push({ + property: 'hubspot_owner_id', + value: additionalFields.contactOwner, + }); + } + if (additionalFields.country) { + body.push({ + property: 'country', + value: additionalFields.country, + }); + } + if (additionalFields.dateOfBirth) { + body.push({ + property: 'date_of_birth', + value: additionalFields.dateOfBirth, + }); + } + if (additionalFields.degree) { + body.push({ + property: 'degree', + value: additionalFields.degree, + }); + } + if (additionalFields.facebookClickId) { + body.push({ + property: 'hs_facebook_click_id', + value: additionalFields.facebookClickId, + }); + } + if (additionalFields.faxNumber) { + body.push({ + property: 'fax', + value: additionalFields.faxNumber, + }); + } + if (additionalFields.fieldOfStudy) { + body.push({ + property: 'field_of_study', + value: additionalFields.fieldOfStudy, + }); + } + if (additionalFields.firstName) { + body.push({ + property: 'firstname', + value: additionalFields.firstName, + }); + } + if (additionalFields.gender) { + body.push({ + property: 'gender', + value: additionalFields.gender, + }); + } + if (additionalFields.googleAdClickId) { + body.push({ + property: 'hs_google_click_id', + value: additionalFields.googleAdClickId, + }); + } + if (additionalFields.graduationDate) { + body.push({ + property: 'graduation_date', + value: additionalFields.graduationDate, + }); + } + if (additionalFields.industry) { + body.push({ + property: 'industry', + value: additionalFields.industry, + }); + } + if (additionalFields.jobFunction) { + body.push({ + property: 'job_function', + value: additionalFields.jobFunction, + }); + } + if (additionalFields.jobTitle) { + body.push({ + property: 'jobtitle', + value: additionalFields.jobTitle, + }); + } + if (additionalFields.lastName) { + body.push({ + property: 'lastname', + value: additionalFields.lastName, + }); + } + if (additionalFields.leadStatus) { + body.push({ + property: 'hs_lead_status', + value: additionalFields.leadStatus, + }); + } + if (additionalFields.processingContactData) { + body.push({ + property: 'hs_legal_basis', + value: additionalFields.processingContactData, + }); + } + if (additionalFields.lifeCycleStage) { + body.push({ + property: 'lifecyclestage', + value: additionalFields.lifeCycleStage, + }); + } + if (additionalFields.maritalStatus) { + body.push({ + property: 'marital_status', + value: additionalFields.maritalStatus, + }); + } + if (additionalFields.membershipNote) { + body.push({ + property: 'hs_content_membership_notes', + value: additionalFields.membershipNote, + }); + } + if (additionalFields.message) { + body.push({ + property: 'message', + value: additionalFields.message, + }); + } + if (additionalFields.mobilePhoneNumber) { + body.push({ + property: 'mobilephone', + value: additionalFields.mobilePhoneNumber, + }); + } + if (additionalFields.numberOfEmployees) { + body.push({ + property: 'numemployees', + value: additionalFields.numberOfEmployees, + }); + } + if (additionalFields.originalSource) { + body.push({ + property: 'hs_analytics_source', + value: additionalFields.originalSource, + }); + } + if (additionalFields.phoneNumber) { + body.push({ + property: 'phone', + value: additionalFields.phoneNumber, + }); + } + if (additionalFields.postalCode) { + body.push({ + property: 'zip', + value: additionalFields.postalCode, + }); + } + if (additionalFields.prefferedLanguage) { + body.push({ + property: 'hs_language', + value: additionalFields.prefferedLanguage, + }); + } + if (additionalFields.relationshipStatus) { + body.push({ + property: 'relationship_status', + value: additionalFields.relationshipStatus, + }); + } + if (additionalFields.salutation) { + body.push({ + property: 'salutation', + value: additionalFields.salutation, + }); + } + if (additionalFields.school) { + body.push({ + property: 'school', + value: additionalFields.school, + }); + } + if (additionalFields.seniority) { + body.push({ + property: 'seniority', + value: additionalFields.seniority, + }); + } + if (additionalFields.startDate) { + body.push({ + property: 'start_date', + value: additionalFields.startDate, + }); + } + if (additionalFields.stateRegion) { + body.push({ + property: 'state', + value: additionalFields.stateRegion, + }); + } + if (additionalFields.status) { + body.push({ + property: 'hs_content_membership_status', + value: additionalFields.status, + }); + } + if (additionalFields.streetAddress) { + body.push({ + property: 'address', + value: additionalFields.streetAddress, + }); + } + if (additionalFields.twitterUsername) { + body.push({ + property: 'twitterhandle', + value: additionalFields.twitterUsername, + }); + } + if (additionalFields.websiteUrl) { + body.push({ + property: 'website', + value: additionalFields.websiteUrl, + }); + } + if (additionalFields.workEmail) { + body.push({ + property: 'work_email', + value: additionalFields.workEmail, + }); + } + + if (additionalFields.customPropertiesUi) { + const customProperties = (additionalFields.customPropertiesUi as IDataObject) + .customPropertiesValues as IDataObject[]; + + if (customProperties) { + for (const customProperty of customProperties) { + body.push({ + property: customProperty.property, + value: customProperty.value, + }); + } + } + } + + const endpoint = `/contacts/v1/contact/createOrUpdate/email/${email}`; + responseData = await hubspotApiRequest.call(this, 'POST', endpoint, { + properties: body, + }); + + if (additionalFields.associatedCompanyId) { + const companyAssociations: IDataObject[] = []; + companyAssociations.push({ + fromObjectId: responseData.vid, + toObjectId: additionalFields.associatedCompanyId, + category: 'HUBSPOT_DEFINED', + definitionId: 1, + }); + await hubspotApiRequest.call( + this, + 'PUT', + '/crm-associations/v1/associations/create-batch', + companyAssociations, + ); + } + + if (!options.resolveData) { + const isNew = responseData.isNew; + if (additionalFields.properties) { + qs.property = additionalFields.properties as string[]; + } + responseData = await hubspotApiRequest.call( + this, + 'GET', + `/contacts/v1/contact/vid/${responseData.vid}/profile`, + {}, + qs, + ); + responseData.isNew = isNew; + } + } + //https://developers.hubspot.com/docs/methods/contacts/get_contact + if (operation === 'get') { + const contactId = this.getNodeParameter('contactId', i, undefined, { + extractValue: true, + }) as string; + const additionalFields = this.getNodeParameter('additionalFields', i); + if (additionalFields.formSubmissionMode) { + qs.formSubmissionMode = additionalFields.formSubmissionMode as string; + } + if (additionalFields.listMemberships) { + qs.showListMemberships = additionalFields.listMemberships as boolean; + } + if (additionalFields.propertiesCollection) { + const propertiesValues = additionalFields.propertiesCollection // @ts-ignore + .propertiesValues as IDataObject; + const properties = propertiesValues.properties as string | string[]; + qs.properties = !Array.isArray(propertiesValues.properties) + ? (properties as string).split(',') + : properties; + qs.propertyMode = snakeCase(propertiesValues.propertyMode as string); + } + const endpoint = `/contacts/v1/contact/vid/${contactId}/profile`; + responseData = await hubspotApiRequest.call(this, 'GET', endpoint, {}, qs); + } + //https://developers.hubspot.com/docs/methods/contacts/get_contacts + if (operation === 'getAll') { + const additionalFields = this.getNodeParameter('additionalFields', i); + const returnAll = this.getNodeParameter('returnAll', 0); + if (additionalFields.formSubmissionMode) { + qs.formSubmissionMode = additionalFields.formSubmissionMode as string; + } + if (additionalFields.listMemberships) { + qs.showListMemberships = additionalFields.listMemberships as boolean; + } + if (additionalFields.propertiesCollection) { + const propertiesValues = additionalFields.propertiesCollection // @ts-ignore + .propertiesValues as IDataObject; + const properties = propertiesValues.properties as string | string[]; + qs.properties = !Array.isArray(propertiesValues.properties) + ? (properties as string).split(',') + : properties; + qs.propertyMode = snakeCase(propertiesValues.propertyMode as string); + } + const endpoint = '/contacts/v1/lists/all/contacts/all'; + if (returnAll) { + responseData = await hubspotApiRequestAllItems.call( + this, + 'contacts', + 'GET', + endpoint, + {}, + qs, + ); + } else { + qs.count = this.getNodeParameter('limit', 0); + responseData = await hubspotApiRequest.call(this, 'GET', endpoint, {}, qs); + responseData = responseData.contacts; + } + } + //https://developers.hubspot.com/docs/methods/contacts/get_recently_created_contacts + if (operation === 'getRecentlyCreatedUpdated') { + const returnAll = this.getNodeParameter('returnAll', 0); + const additionalFields = this.getNodeParameter('additionalFields', i); + if (additionalFields.formSubmissionMode) { + qs.formSubmissionMode = additionalFields.formSubmissionMode as string; + } + if (additionalFields.listMemberships) { + qs.showListMemberships = additionalFields.listMemberships as boolean; + } + if (additionalFields.propertiesCollection) { + const propertiesValues = additionalFields.propertiesCollection // @ts-ignore + .propertiesValues as IDataObject; + const properties = propertiesValues.properties as string | string[]; + qs.properties = !Array.isArray(propertiesValues.properties) + ? (properties as string).split(',') + : properties; + qs.propertyMode = snakeCase(propertiesValues.propertyMode as string); + } + + const endpoint = '/contacts/v1/lists/recently_updated/contacts/recent'; + + if (returnAll) { + responseData = await hubspotApiRequestAllItems.call( + this, + 'contacts', + 'GET', + endpoint, + {}, + qs, + ); + } else { + qs.count = this.getNodeParameter('limit', 0); + responseData = await hubspotApiRequest.call(this, 'GET', endpoint, {}, qs); + responseData = responseData.contacts; + } + } + //https://developers.hubspot.com/docs/methods/contacts/delete_contact + if (operation === 'delete') { + const contactId = this.getNodeParameter('contactId', i, undefined, { + extractValue: true, + }) as string; + const endpoint = `/contacts/v1/contact/vid/${contactId}`; + responseData = await hubspotApiRequest.call(this, 'DELETE', endpoint); + responseData = + responseData === undefined ? { vid: contactId, deleted: true } : responseData; + } + //https://developers.hubspot.com/docs/api/crm/search + if (operation === 'search') { + const additionalFields = this.getNodeParameter('additionalFields', i); + const returnAll = this.getNodeParameter('returnAll', 0); + const filtersGroupsUi = this.getNodeParameter('filterGroupsUi', i) as IDataObject; + const sortBy = additionalFields.sortBy || 'createdate'; + const direction = additionalFields.direction || 'DESCENDING'; + + const body: IDataObject = { + sorts: [ + { + propertyName: sortBy, + direction, + }, + ], + }; + + if (filtersGroupsUi?.filterGroupsValues) { + const filterGroupValues = filtersGroupsUi.filterGroupsValues as IDataObject[]; + body.filterGroups = []; + for (const filterGroupValue of filterGroupValues) { + if (filterGroupValue.filtersUi) { + const filterValues = (filterGroupValue.filtersUi as IDataObject) + .filterValues as IDataObject[]; + for (const filter of filterValues) { + delete filter.type; + // Hacky way to get the filter value as we concat the values with a | and the type + filter.propertyName = filter.propertyName?.toString().split('|')[0]; + //@ts-ignore + } + (body.filterGroups as IDataObject[]).push({ filters: filterValues }); + } + } + //@ts-ignore + if (body.filterGroups.length > 3) { + throw new NodeOperationError(this.getNode(), 'You can only have 3 filter groups'); + } + } + + Object.assign(body, additionalFields); + + const endpoint = '/crm/v3/objects/contacts/search'; + + if (returnAll) { + responseData = await hubspotApiRequestAllItems.call( + this, + 'results', + 'POST', + endpoint, + body, + qs, + ); + } else { + body.limit = this.getNodeParameter('limit', 0); + responseData = await hubspotApiRequest.call(this, 'POST', endpoint, body, qs); + responseData = responseData.results; + } + } + } + //https://developers.hubspot.com/docs/methods/companies/companies-overview + if (resource === 'company') { + //https://developers.hubspot.com/docs/methods/companies/create_company + if (operation === 'create') { + const name = this.getNodeParameter('name', i) as string; + const additionalFields = this.getNodeParameter('additionalFields', i); + const body: IDataObject[] = []; + body.push({ + name: 'name', + value: name, + }); + if (additionalFields.aboutUs) { + body.push({ + name: 'about_us', + value: additionalFields.aboutUs, + }); + } + if (additionalFields.annualRevenue) { + body.push({ + name: 'annualrevenue', + value: (additionalFields.annualRevenue as number).toString(), + }); + } + if (additionalFields.city) { + body.push({ + name: 'city', + value: additionalFields.city, + }); + } + if (additionalFields.closeDate) { + body.push({ + name: 'closedate', + value: new Date(additionalFields.closeDate as string).getTime(), + }); + } + if (additionalFields.companyDomainName) { + body.push({ + name: 'domain', + value: additionalFields.companyDomainName, + }); + } + if (additionalFields.companyOwner) { + body.push({ + name: 'hubspot_owner_id', + value: additionalFields.companyOwner, + }); + } + if (additionalFields.countryRegion) { + body.push({ + name: 'country', + value: additionalFields.countryRegion, + }); + } + if (additionalFields.description) { + body.push({ + name: 'description', + value: additionalFields.description, + }); + } + if (additionalFields.facebookFans) { + body.push({ + name: 'facebookfans', + value: additionalFields.facebookFans, + }); + } + if (additionalFields.googlePlusPage) { + body.push({ + name: 'googleplus_page', + value: additionalFields.googlePlusPage, + }); + } + if (additionalFields.industry) { + body.push({ + name: 'industry', + value: additionalFields.industry, + }); + } + if (additionalFields.isPublic) { + body.push({ + name: 'is_public', + value: additionalFields.isPublic, + }); + } + if (additionalFields.leadStatus) { + body.push({ + name: 'hs_lead_status', + value: additionalFields.leadStatus, + }); + } + if (additionalFields.lifecycleStatus) { + body.push({ + name: 'lifecyclestage', + value: additionalFields.lifecycleStatus, + }); + } + if (additionalFields.linkedinBio) { + body.push({ + name: 'linkedinbio', + value: additionalFields.linkedinBio, + }); + } + if (additionalFields.linkedInCompanyPage) { + body.push({ + name: 'linkedin_company_page', + value: additionalFields.linkedInCompanyPage, + }); + } + if (additionalFields.numberOfEmployees) { + body.push({ + name: 'numberofemployees', + value: additionalFields.numberOfEmployees, + }); + } + if (additionalFields.originalSourceType) { + body.push({ + name: 'hs_analytics_source', + value: additionalFields.originalSourceType, + }); + } + if (additionalFields.phoneNumber) { + body.push({ + name: 'phone', + value: additionalFields.phoneNumber, + }); + } + if (additionalFields.postalCode) { + body.push({ + name: 'zip', + value: additionalFields.postalCode, + }); + } + if (additionalFields.stateRegion) { + body.push({ + name: 'state', + value: additionalFields.stateRegion, + }); + } + if (additionalFields.streetAddress) { + body.push({ + name: 'address', + value: additionalFields.streetAddress, + }); + } + if (additionalFields.streetAddress2) { + body.push({ + name: 'address2', + value: additionalFields.streetAddress2, + }); + } + if (additionalFields.targetAccount) { + body.push({ + name: 'hs_target_account', + value: additionalFields.targetAccount, + }); + } + if (additionalFields.timezone) { + body.push({ + name: 'timezone', + value: additionalFields.timezone, + }); + } + if (additionalFields.totalMoneyRaised) { + body.push({ + name: 'total_money_raised', + value: additionalFields.totalMoneyRaised, + }); + } + if (additionalFields.twitterBio) { + body.push({ + name: 'twitterbio', + value: additionalFields.twitterBio, + }); + } + if (additionalFields.twitterFollowers) { + body.push({ + name: 'twitterfollowers', + value: additionalFields.twitterFollowers, + }); + } + if (additionalFields.twitterHandle) { + body.push({ + name: 'twitterhandle', + value: additionalFields.twitterHandle, + }); + } + if (additionalFields.type) { + body.push({ + name: 'type', + value: additionalFields.type, + }); + } + if (additionalFields.websiteUrl) { + body.push({ + name: 'website', + value: additionalFields.websiteUrl, + }); + } + if (additionalFields.webTechnologies) { + body.push({ + name: 'web_technologies', + value: additionalFields.webTechnologies, + }); + } + if (additionalFields.yearFounded) { + body.push({ + name: 'founded_year', + value: additionalFields.yearFounded, + }); + } + if (additionalFields.customPropertiesUi) { + const customProperties = (additionalFields.customPropertiesUi as IDataObject) + .customPropertiesValues as IDataObject[]; + + if (customProperties) { + for (const customProperty of customProperties) { + body.push({ + name: customProperty.property, + value: customProperty.value, + }); + } + } + } + const endpoint = '/companies/v2/companies'; + responseData = await hubspotApiRequest.call(this, 'POST', endpoint, { + properties: body, + }); + } + //https://developers.hubspot.com/docs/methods/companies/update_company + if (operation === 'update') { + const companyId = this.getNodeParameter('companyId', i, undefined, { + extractValue: true, + }) as string; + const updateFields = this.getNodeParameter('updateFields', i); + const body: IDataObject[] = []; + if (updateFields.name) { + body.push({ + name: 'name', + value: updateFields.name, + }); + } + if (updateFields.aboutUs) { + body.push({ + name: 'about_us', + value: updateFields.aboutUs, + }); + } + if (updateFields.annualRevenue) { + body.push({ + name: 'annualrevenue', + value: (updateFields.annualRevenue as number).toString(), + }); + } + if (updateFields.city) { + body.push({ + name: 'city', + value: updateFields.city, + }); + } + if (updateFields.closeDate) { + body.push({ + name: 'closedate', + value: new Date(updateFields.closeDate as string).getTime(), + }); + } + if (updateFields.companyDomainName) { + body.push({ + name: 'domain', + value: updateFields.companyDomainName, + }); + } + if (updateFields.companyOwner) { + body.push({ + name: 'hubspot_owner_id', + value: updateFields.companyOwner, + }); + } + if (updateFields.countryRegion) { + body.push({ + name: 'country', + value: updateFields.countryRegion, + }); + } + if (updateFields.description) { + body.push({ + name: 'description', + value: updateFields.description, + }); + } + if (updateFields.facebookFans) { + body.push({ + name: 'facebookfans', + value: updateFields.facebookFans, + }); + } + if (updateFields.googlePlusPage) { + body.push({ + name: 'googleplus_page', + value: updateFields.googlePlusPage, + }); + } + if (updateFields.industry) { + body.push({ + name: 'industry', + value: updateFields.industry, + }); + } + if (updateFields.isPublic) { + body.push({ + name: 'is_public', + value: updateFields.isPublic, + }); + } + if (updateFields.leadStatus) { + body.push({ + name: 'hs_lead_status', + value: updateFields.leadStatus, + }); + } + if (updateFields.lifecycleStatus) { + body.push({ + name: 'lifecyclestage', + value: updateFields.lifecycleStatus, + }); + } + if (updateFields.linkedinBio) { + body.push({ + name: 'linkedinbio', + value: updateFields.linkedinBio, + }); + } + if (updateFields.linkedInCompanyPage) { + body.push({ + name: 'linkedin_company_page', + value: updateFields.linkedInCompanyPage, + }); + } + if (updateFields.numberOfEmployees) { + body.push({ + name: 'numberofemployees', + value: updateFields.numberOfEmployees, + }); + } + if (updateFields.originalSourceType) { + body.push({ + name: 'hs_analytics_source', + value: updateFields.originalSourceType, + }); + } + if (updateFields.phoneNumber) { + body.push({ + name: 'phone', + value: updateFields.phoneNumber, + }); + } + if (updateFields.postalCode) { + body.push({ + name: 'zip', + value: updateFields.postalCode, + }); + } + if (updateFields.stateRegion) { + body.push({ + name: 'state', + value: updateFields.stateRegion, + }); + } + if (updateFields.streetAddress) { + body.push({ + name: 'address', + value: updateFields.streetAddress, + }); + } + if (updateFields.streetAddress2) { + body.push({ + name: 'address2', + value: updateFields.streetAddress2, + }); + } + if (updateFields.targetAccount) { + body.push({ + name: 'hs_target_account', + value: updateFields.targetAccount, + }); + } + if (updateFields.timezone) { + body.push({ + name: 'timezone', + value: updateFields.timezone, + }); + } + if (updateFields.totalMoneyRaised) { + body.push({ + name: 'total_money_raised', + value: updateFields.totalMoneyRaised, + }); + } + if (updateFields.twitterBio) { + body.push({ + name: 'twitterbio', + value: updateFields.twitterBio, + }); + } + if (updateFields.twitterFollowers) { + body.push({ + name: 'twitterfollowers', + value: updateFields.twitterFollowers, + }); + } + if (updateFields.twitterHandle) { + body.push({ + name: 'twitterhandle', + value: updateFields.twitterHandle, + }); + } + if (updateFields.type) { + body.push({ + name: 'type', + value: updateFields.type, + }); + } + if (updateFields.websiteUrl) { + body.push({ + name: 'website', + value: updateFields.websiteUrl, + }); + } + if (updateFields.webTechnologies) { + body.push({ + name: 'web_technologies', + value: updateFields.webTechnologies, + }); + } + if (updateFields.yearFounded) { + body.push({ + name: 'founded_year', + value: updateFields.yearFounded, + }); + } + if (updateFields.customPropertiesUi) { + const customProperties = (updateFields.customPropertiesUi as IDataObject) + .customPropertiesValues as IDataObject[]; + + if (customProperties) { + for (const customProperty of customProperties) { + body.push({ + name: customProperty.property, + value: customProperty.value, + }); + } + } + } + const endpoint = `/companies/v2/companies/${companyId}`; + responseData = await hubspotApiRequest.call(this, 'PUT', endpoint, { + properties: body, + }); + } + //https://developers.hubspot.com/docs/methods/companies/get_company + if (operation === 'get') { + const companyId = this.getNodeParameter('companyId', i, undefined, { + extractValue: true, + }) as string; + const additionalFields = this.getNodeParameter('additionalFields', i); + if (additionalFields.includeMergeAudits) { + qs.includeMergeAudits = additionalFields.includeMergeAudits as boolean; + } + const endpoint = `/companies/v2/companies/${companyId}`; + responseData = await hubspotApiRequest.call(this, 'GET', endpoint, {}, qs); + } + //https://developers.hubspot.com/docs/methods/companies/get-all-companies + if (operation === 'getAll') { + const additionalFields = this.getNodeParameter('options', i); + const returnAll = this.getNodeParameter('returnAll', 0); + if (additionalFields.formSubmissionMode) { + qs.formSubmissionMode = additionalFields.formSubmissionMode as string; + } + if (additionalFields.listMerberships) { + qs.showListMemberships = additionalFields.listMerberships as boolean; + } + if (additionalFields.propertiesCollection) { + const propertiesValues = additionalFields.propertiesCollection // @ts-ignore + .propertiesValues as IDataObject; + const properties = propertiesValues.properties as string | string[]; + qs.properties = !Array.isArray(propertiesValues.properties) + ? (properties as string).split(',') + : properties; + qs.propertyMode = snakeCase(propertiesValues.propertyMode as string); + } + const endpoint = '/companies/v2/companies/paged'; + if (returnAll) { + responseData = await hubspotApiRequestAllItems.call( + this, + 'companies', + 'GET', + endpoint, + {}, + qs, + ); + } else { + qs.limit = this.getNodeParameter('limit', 0); + responseData = await hubspotApiRequest.call(this, 'GET', endpoint, {}, qs); + responseData = responseData.companies; + } + } + //https://developers.hubspot.com/docs/methods/companies/get_companies_modified + if (operation === 'getRecentlyCreatedUpdated') { + const returnAll = this.getNodeParameter('returnAll', 0); + const additionalFields = this.getNodeParameter('additionalFields', i); + if (additionalFields.since) { + qs.since = new Date(additionalFields.since as string).getTime(); + } + if (additionalFields.propertiesCollection) { + const propertiesValues = additionalFields.propertiesCollection // @ts-ignore + .propertiesValues as IDataObject; + const properties = propertiesValues.properties as string | string[]; + qs.properties = !Array.isArray(propertiesValues.properties) + ? (properties as string).split(',') + : properties; + qs.propertyMode = snakeCase(propertiesValues.propertyMode as string); + } + const endpoint = '/companies/v2/companies/recent/modified'; + if (returnAll) { + responseData = await hubspotApiRequestAllItems.call( + this, + 'results', + 'GET', + endpoint, + {}, + qs, + ); + } else { + qs.count = this.getNodeParameter('limit', 0); + responseData = await hubspotApiRequest.call(this, 'GET', endpoint, {}, qs); + responseData = responseData.results; + } + } + //https://developers.hubspot.com/docs/methods/companies/search_companies_by_domain + if (operation === 'searchByDomain') { + let domain = this.getNodeParameter('domain', i) as string; + const options = this.getNodeParameter('options', i); + const returnAll = this.getNodeParameter('returnAll', 0); + if (domain.includes('https://')) { + domain = domain.replace('https://', ''); + } else if (domain.includes('http://')) { + domain = domain.replace('http://', ''); + } + const body: IDataObject = { + requestOptions: {}, + }; + if (options.properties) { + body.requestOptions = { properties: options.properties as string[] }; + } + const endpoint = `/companies/v2/domains/${domain}/companies`; + if (returnAll) { + responseData = await hubspotApiRequestAllItems.call( + this, + 'results', + 'POST', + endpoint, + body, + ); + } else { + body.limit = this.getNodeParameter('limit', 0); + responseData = await hubspotApiRequest.call(this, 'POST', endpoint, body); + responseData = responseData.results; + } + } + //https://developers.hubspot.com/docs/methods/companies/delete_company + if (operation === 'delete') { + const companyId = this.getNodeParameter('companyId', i, undefined, { + extractValue: true, + }) as string; + const endpoint = `/crm/v3/objects/companies/${companyId}`; + responseData = await hubspotApiRequest.call(this, 'DELETE', endpoint); + responseData = + responseData === undefined ? { vid: companyId, deleted: true } : responseData; + } + } + //https://developers.hubspot.com/docs/methods/deals/deals_overview + if (resource === 'deal') { + if (operation === 'create') { + const body: IDeal = {}; + body.properties = []; + const association: IAssociation = {}; + const additionalFields = this.getNodeParameter('additionalFields', i); + const stage = this.getNodeParameter('stage', i) as string; + if (stage) { + body.properties.push({ + name: 'dealstage', + value: stage, + }); + } + if (additionalFields.associatedCompany) { + association.associatedCompanyIds = additionalFields.associatedCompany as number[]; + } + if (additionalFields.associatedVids) { + association.associatedVids = additionalFields.associatedVids as number[]; + } + if (additionalFields.dealName) { + body.properties.push({ + name: 'dealname', + value: additionalFields.dealName as string, + }); + } + if (additionalFields.dealOwner) { + const dealOwner = additionalFields.dealOwner as IDataObject; + body.properties.push({ + name: 'hubspot_owner_id', + value: dealOwner.value, + }); + } + if (additionalFields.closeDate) { + body.properties.push({ + name: 'closedate', + value: new Date(additionalFields.closeDate as string).getTime(), + }); + } + if (additionalFields.amount) { + body.properties.push({ + name: 'amount', + value: additionalFields.amount as string, + }); + } + if (additionalFields.dealType) { + body.properties.push({ + name: 'dealtype', + value: additionalFields.dealType as string, + }); + } + if (additionalFields.pipeline) { + body.properties.push({ + name: 'pipeline', + value: additionalFields.pipeline as string, + }); + } + if (additionalFields.description) { + body.properties.push({ + name: 'description', + value: additionalFields.description as string, + }); + } + if (additionalFields.customPropertiesUi) { + const customProperties = (additionalFields.customPropertiesUi as IDataObject) + .customPropertiesValues as IDataObject[]; + if (customProperties) { + for (const customProperty of customProperties) { + body.properties.push({ + name: customProperty.property, + value: customProperty.value, + }); + } + } + } + body.associations = association; + const endpoint = '/deals/v1/deal'; + responseData = await hubspotApiRequest.call(this, 'POST', endpoint, body); + } + if (operation === 'update') { + const body: IDeal = {}; + body.properties = []; + const updateFields = this.getNodeParameter('updateFields', i); + const dealId = this.getNodeParameter( + 'dealId', + i, + {}, + { extractValue: true }, + ) as string; + if (updateFields.stage) { + body.properties.push({ + name: 'dealstage', + value: updateFields.stage as string, + }); + } + if (updateFields.dealName) { + body.properties.push({ + name: 'dealname', + value: updateFields.dealName as string, + }); + } + if (updateFields.closeDate) { + body.properties.push({ + name: 'closedate', + value: new Date(updateFields.closeDate as string).getTime(), + }); + } + if (updateFields.amount) { + body.properties.push({ + name: 'amount', + value: updateFields.amount as string, + }); + } + if (updateFields.dealType) { + body.properties.push({ + name: 'dealtype', + value: updateFields.dealType as string, + }); + } + if (updateFields.pipeline) { + body.properties.push({ + name: 'pipeline', + value: updateFields.pipeline as string, + }); + } + if (updateFields.description) { + body.properties.push({ + name: 'description', + value: updateFields.description as string, + }); + } + if (updateFields.customPropertiesUi) { + const customProperties = (updateFields.customPropertiesUi as IDataObject) + .customPropertiesValues as IDataObject[]; + if (customProperties) { + for (const customProperty of customProperties) { + body.properties.push({ + name: customProperty.property, + value: customProperty.value, + }); + } + } + } + const endpoint = `/deals/v1/deal/${dealId}`; + responseData = await hubspotApiRequest.call(this, 'PUT', endpoint, body); + } + if (operation === 'get') { + const dealId = this.getNodeParameter( + 'dealId', + i, + {}, + { extractValue: true }, + ) as string; + const filters = this.getNodeParameter('filters', i); + if (filters.includePropertyVersions) { + qs.includePropertyVersions = filters.includePropertyVersions as boolean; + } + + if (filters.propertiesCollection) { + const propertiesValues = filters.propertiesCollection // @ts-ignore + .propertiesValues as IDataObject; + const properties = propertiesValues.properties as string | string[]; + qs.properties = !Array.isArray(propertiesValues.properties) + ? (properties as string).split(',') + : properties; + qs.propertyMode = snakeCase(propertiesValues.propertyMode as string); + } + const endpoint = `/deals/v1/deal/${dealId}`; + responseData = await hubspotApiRequest.call(this, 'GET', endpoint); + } + if (operation === 'getAll') { + const filters = this.getNodeParameter('filters', i); + const returnAll = this.getNodeParameter('returnAll', 0); + if (filters.includeAssociations) { + qs.includeAssociations = filters.includeAssociations as boolean; + } + + if (filters.propertiesCollection) { + const propertiesValues = filters.propertiesCollection // @ts-ignore + .propertiesValues as IDataObject; + const properties = propertiesValues.properties as string | string[]; + qs.properties = !Array.isArray(propertiesValues.properties) + ? (properties as string).split(',') + : properties; + qs.propertyMode = snakeCase(propertiesValues.propertyMode as string); + } + + const endpoint = '/deals/v1/deal/paged'; + if (returnAll) { + responseData = await hubspotApiRequestAllItems.call( + this, + 'deals', + 'GET', + endpoint, + {}, + qs, + ); + } else { + qs.limit = this.getNodeParameter('limit', 0); + responseData = await hubspotApiRequest.call(this, 'GET', endpoint, {}, qs); + responseData = responseData.deals; + } + } + if (operation === 'getRecentlyCreatedUpdated') { + const endpoint = '/deals/v1/deal/recent/created'; + const filters = this.getNodeParameter('filters', i); + const returnAll = this.getNodeParameter('returnAll', 0); + if (filters.since) { + qs.since = new Date(filters.since as string).getTime(); + } + if (filters.includePropertyVersions) { + qs.includePropertyVersions = filters.includePropertyVersions as boolean; + } + if (returnAll) { + responseData = await hubspotApiRequestAllItems.call( + this, + 'results', + 'GET', + endpoint, + {}, + qs, + ); + } else { + qs.count = this.getNodeParameter('limit', 0); + responseData = await hubspotApiRequest.call(this, 'GET', endpoint, {}, qs); + responseData = responseData.results; + } + } + if (operation === 'delete') { + const dealId = this.getNodeParameter( + 'dealId', + i, + {}, + { extractValue: true }, + ) as string; + const endpoint = `/deals/v1/deal/${dealId}`; + responseData = await hubspotApiRequest.call(this, 'DELETE', endpoint); + responseData = + responseData === undefined ? { vid: dealId, deleted: true } : responseData; + } + //https://developers.hubspot.com/docs/api/crm/search + if (operation === 'search') { + const additionalFields = this.getNodeParameter('additionalFields', i); + const returnAll = this.getNodeParameter('returnAll', 0); + const filtersGroupsUi = this.getNodeParameter('filterGroupsUi', i) as IDataObject; + const sortBy = additionalFields.sortBy || 'createdate'; + const direction = additionalFields.direction || 'DESCENDING'; + + const body: IDataObject = { + sorts: [ + { + propertyName: sortBy, + direction, + }, + ], + }; + + if (filtersGroupsUi?.filterGroupsValues) { + const filterGroupValues = filtersGroupsUi.filterGroupsValues as IDataObject[]; + body.filterGroups = []; + for (const filterGroupValue of filterGroupValues) { + if (filterGroupValue.filtersUi) { + const filterValues = (filterGroupValue.filtersUi as IDataObject) + .filterValues as IDataObject[]; + for (const filter of filterValues) { + delete filter.type; + // Hacky way to get the filter value as we concat the values with a | and the type + filter.propertyName = filter.propertyName?.toString().split('|')[0]; + } + (body.filterGroups as IDataObject[]).push({ filters: filterValues }); + } + } + //@ts-ignore + if (body.filterGroups.length > 3) { + throw new NodeOperationError(this.getNode(), 'You can only have 3 filter groups'); + } + } + + Object.assign(body, additionalFields); + + const endpoint = '/crm/v3/objects/deals/search'; + + if (returnAll) { + responseData = await hubspotApiRequestAllItems.call( + this, + 'results', + 'POST', + endpoint, + body, + qs, + ); + } else { + body.limit = this.getNodeParameter('limit', 0); + responseData = await hubspotApiRequest.call(this, 'POST', endpoint, body, qs); + responseData = responseData.results; + } + } + } + if (resource === 'engagement') { + //https://legacydocs.hubspot.com/docs/methods/engagements/create_engagement + if (operation === 'create') { + const type = this.getNodeParameter('type', i) as string; + const metadata = this.getNodeParameter('metadata', i) as IDataObject; + const associations = this.getNodeParameter( + 'additionalFields.associations', + i, + {}, + ) as IDataObject; + + if (!Object.keys(metadata).length) { + throw new NodeOperationError( + this.getNode(), + 'At least one metadata field needs to set', + { itemIndex: i }, + ); + } + + const body: { + engagement: { type: string }; + metadata: IDataObject; + associations: IDataObject; + } = { + engagement: { + type: type.toUpperCase(), + }, + metadata: {}, + associations: {}, + }; + + if (type === 'email') { + body.metadata = getEmailMetadata(metadata); + } + + if (type === 'task') { + body.metadata = getTaskMetadata(metadata); + } + + if (type === 'meeting') { + body.metadata = getMeetingMetadata(metadata); + } + + if (type === 'call') { + body.metadata = getCallMetadata(metadata); + } + + //@ts-ignore + body.associations = getAssociations(associations); + + const endpoint = '/engagements/v1/engagements'; + responseData = await hubspotApiRequest.call(this, 'POST', endpoint, body); + } + //https://legacydocs.hubspot.com/docs/methods/engagements/get_engagement + if (operation === 'delete') { + const engagementId = this.getNodeParameter('engagementId', i, undefined, { + extractValue: true, + }) as string; + const endpoint = `/engagements/v1/engagements/${engagementId}`; + responseData = await hubspotApiRequest.call(this, 'DELETE', endpoint, {}, qs); + responseData = + responseData === undefined ? { vid: engagementId, deleted: true } : responseData; + } + //https://legacydocs.hubspot.com/docs/methods/engagements/get_engagement + if (operation === 'get') { + const engagementId = this.getNodeParameter('engagementId', i, undefined, { + extractValue: true, + }) as string; + const endpoint = `/engagements/v1/engagements/${engagementId}`; + responseData = await hubspotApiRequest.call(this, 'GET', endpoint, {}, qs); + } + //https://legacydocs.hubspot.com/docs/methods/engagements/get-all-engagements + if (operation === 'getAll') { + const returnAll = this.getNodeParameter('returnAll', 0); + const endpoint = '/engagements/v1/engagements/paged'; + if (returnAll) { + responseData = await hubspotApiRequestAllItems.call( + this, + 'results', + 'GET', + endpoint, + {}, + qs, + ); + } else { + qs.limit = this.getNodeParameter('limit', 0); + responseData = await hubspotApiRequest.call(this, 'GET', endpoint, {}, qs); + responseData = responseData.results; + } + } + } + //https://developers.hubspot.com/docs/methods/forms/forms_overview + if (resource === 'form') { + //https://developers.hubspot.com/docs/methods/forms/v2/get_fields + if (operation === 'getFields') { + const formId = this.getNodeParameter('formId', i) as string; + responseData = await hubspotApiRequest.call( + this, + 'GET', + `/forms/v2/fields/${formId}`, + ); + } + //https://developers.hubspot.com/docs/methods/forms/submit_form_v3 + if (operation === 'submit') { + const formId = this.getNodeParameter('formId', i) as string; + const additionalFields = this.getNodeParameter('additionalFields', i); + const context = (this.getNodeParameter('contextUi', i) as IDataObject) + .contextValue as IDataObject; + const legalConsent = (this.getNodeParameter('lengalConsentUi', i) as IDataObject) + .lengalConsentValues as IDataObject; + const legitimateInteres = (this.getNodeParameter('lengalConsentUi', i) as IDataObject) + .legitimateInterestValues as IDataObject; + const { portalId } = await hubspotApiRequest.call( + this, + 'GET', + `/forms/v2/forms/${formId}`, + ); + const body: IForm = { + formId, + portalId, + legalConsentOptions: {}, + fields: [], + }; + if (additionalFields.submittedAt) { + body.submittedAt = new Date(additionalFields.submittedAt as string).getTime(); + } + if (additionalFields.skipValidation) { + body.skipValidation = additionalFields.skipValidation as boolean; + } + const consent: IDataObject = {}; + if (legalConsent) { + if (legalConsent.consentToProcess) { + consent.consentToProcess = legalConsent.consentToProcess as boolean; + } + if (legalConsent.text) { + consent.text = legalConsent.text as string; + } + if (legalConsent.communicationsUi) { + consent.communications = (legalConsent.communicationsUi as IDataObject) + .communicationValues as IDataObject; + } + } + body.legalConsentOptions!.consent = consent; + const fields: IDataObject = items[i].json; + for (const key of Object.keys(fields)) { + body.fields?.push({ name: key, value: fields[key] }); + } + if (body.legalConsentOptions!.legitimateInterest) { + Object.assign(body, { + legalConsentOptions: { legitimateInterest: legitimateInteres }, + }); + } + if (context) { + clean(context); + Object.assign(body, { context }); + } + const uri = `https://api.hsforms.com/submissions/v3/integration/submit/${portalId}/${formId}`; + responseData = await hubspotApiRequest.call(this, 'POST', '', body, {}, uri); + } + } + //https://developers.hubspot.com/docs/methods/tickets/tickets-overview + if (resource === 'ticket') { + //https://developers.hubspot.com/docs/methods/tickets/create-ticket + if (operation === 'create') { + const additionalFields = this.getNodeParameter('additionalFields', i); + const pipelineId = this.getNodeParameter('pipelineId', i) as string; + const stageId = this.getNodeParameter('stageId', i) as string; + const ticketName = this.getNodeParameter('ticketName', i) as string; + const body: IDataObject[] = [ + { + // eslint-disable-next-line n8n-nodes-base/node-param-display-name-miscased + name: 'hs_pipeline', + value: pipelineId, + }, + { + // eslint-disable-next-line n8n-nodes-base/node-param-display-name-miscased + name: 'hs_pipeline_stage', + value: stageId, + }, + { + // eslint-disable-next-line n8n-nodes-base/node-param-display-name-miscased + name: 'subject', + value: ticketName, + }, + ]; + if (additionalFields.category) { + body.push({ + name: 'hs_ticket_category', + value: additionalFields.category as string, + }); + } + if (additionalFields.closeDate) { + body.push({ + name: 'closed_date', + value: new Date(additionalFields.closeDate as string).getTime(), + }); + } + if (additionalFields.createDate) { + body.push({ + name: 'createdate', + value: new Date(additionalFields.createDate as string).getTime(), + }); + } + if (additionalFields.description) { + body.push({ + name: 'content', + value: additionalFields.description as string, + }); + } + if (additionalFields.priority) { + body.push({ + name: 'hs_ticket_priority', + value: additionalFields.priority as string, + }); + } + if (additionalFields.resolution) { + body.push({ + name: 'hs_resolution', + value: additionalFields.resolution as string, + }); + } + if (additionalFields.source) { + body.push({ + name: 'source_type', + value: additionalFields.source as string, + }); + } + if (additionalFields.ticketOwnerId) { + body.push({ + name: 'hubspot_owner_id', + value: additionalFields.ticketOwnerId as string, + }); + } + const endpoint = '/crm-objects/v1/objects/tickets'; + responseData = await hubspotApiRequest.call(this, 'POST', endpoint, body); + + if (additionalFields.associatedCompanyIds) { + const companyAssociations: IDataObject[] = []; + for (const companyId of additionalFields.associatedCompanyIds as IDataObject[]) { + companyAssociations.push({ + fromObjectId: responseData.objectId, + toObjectId: companyId, + category: 'HUBSPOT_DEFINED', + definitionId: 26, + }); + } + await hubspotApiRequest.call( + this, + 'PUT', + '/crm-associations/v1/associations/create-batch', + companyAssociations, + ); + } + + if (additionalFields.associatedContactIds) { + const contactAssociations: IDataObject[] = []; + for (const contactId of additionalFields.associatedContactIds as IDataObject[]) { + contactAssociations.push({ + fromObjectId: responseData.objectId, + toObjectId: contactId, + category: 'HUBSPOT_DEFINED', + definitionId: 16, + }); + } + await hubspotApiRequest.call( + this, + 'PUT', + '/crm-associations/v1/associations/create-batch', + contactAssociations, + ); + } + } + //https://developers.hubspot.com/docs/methods/tickets/get_ticket_by_id + if (operation === 'get') { + const ticketId = this.getNodeParameter('ticketId', i, undefined, { + extractValue: true, + }) as string; + const additionalFields = this.getNodeParameter('additionalFields', i); + if (additionalFields.properties) { + qs.properties = additionalFields.properties as string[]; + } + if (additionalFields.propertiesWithHistory) { + qs.propertiesWithHistory = (additionalFields.propertiesWithHistory as string).split( + ',', + ); + } + if (additionalFields.includeDeleted) { + qs.includeDeleted = additionalFields.includeDeleted as boolean; + } + const endpoint = `/crm-objects/v1/objects/tickets/${ticketId}`; + responseData = await hubspotApiRequest.call(this, 'GET', endpoint, {}, qs); + } + //https://developers.hubspot.com/docs/methods/tickets/get-all-tickets + if (operation === 'getAll') { + const additionalFields = this.getNodeParameter('additionalFields', i); + const returnAll = this.getNodeParameter('returnAll', 0); + if (additionalFields.properties) { + qs.properties = additionalFields.properties as string[]; + } + if (additionalFields.propertiesWithHistory) { + qs.propertiesWithHistory = (additionalFields.propertiesWithHistory as string).split( + ',', + ); + } + const endpoint = '/crm-objects/v1/objects/tickets/paged'; + if (returnAll) { + responseData = await hubspotApiRequestAllItems.call( + this, + 'objects', + 'GET', + endpoint, + {}, + qs, + ); + } else { + qs.limit = this.getNodeParameter('limit', 0); + responseData = await hubspotApiRequestAllItems.call( + this, + 'objects', + 'GET', + endpoint, + {}, + qs, + ); + responseData = responseData.splice(0, qs.limit); + } + } + //https://developers.hubspot.com/docs/methods/tickets/delete-ticket + if (operation === 'delete') { + const ticketId = this.getNodeParameter('ticketId', i, undefined, { + extractValue: true, + }) as string; + const endpoint = `/crm-objects/v1/objects/tickets/${ticketId}`; + await hubspotApiRequest.call(this, 'DELETE', endpoint); + responseData = + responseData === undefined ? { vid: ticketId, deleted: true } : responseData; + } + //https://developers.hubspot.com/docs/methods/tickets/update-ticket + if (operation === 'update') { + const updateFields = this.getNodeParameter('updateFields', i); + const ticketId = this.getNodeParameter('ticketId', i, undefined, { + extractValue: true, + }) as string; + const body: IDataObject[] = []; + if (updateFields.pipelineId) { + body.push({ + name: 'hs_pipeline', + value: updateFields.pipelineId as string, + }); + } + if (updateFields.stageId) { + body.push({ + name: 'hs_pipeline_stage', + value: updateFields.stageId as string, + }); + } + if (updateFields.ticketName) { + body.push({ + name: 'subject', + value: updateFields.ticketName as string, + }); + } + if (updateFields.category) { + body.push({ + name: 'hs_ticket_category', + value: updateFields.category as string, + }); + } + if (updateFields.closeDate) { + body.push({ + name: 'closed_date', + value: new Date(updateFields.createDate as string).getTime(), + }); + } + if (updateFields.createDate) { + body.push({ + name: 'createdate', + value: new Date(updateFields.createDate as string).getTime(), + }); + } + if (updateFields.description) { + body.push({ + name: 'content', + value: updateFields.description as string, + }); + } + if (updateFields.priority) { + body.push({ + name: 'hs_ticket_priority', + value: updateFields.priority as string, + }); + } + if (updateFields.resolution) { + body.push({ + name: 'hs_resolution', + value: updateFields.resolution as string, + }); + } + if (updateFields.source) { + body.push({ + name: 'source_type', + value: updateFields.source as string, + }); + } + if (updateFields.ticketOwnerId) { + body.push({ + name: 'hubspot_owner_id', + value: updateFields.ticketOwnerId as string, + }); + } + const endpoint = `/crm-objects/v1/objects/tickets/${ticketId}`; + responseData = await hubspotApiRequest.call(this, 'PUT', endpoint, body); + + if (updateFields.associatedCompanyIds) { + const companyAssociations: IDataObject[] = []; + for (const companyId of updateFields.associatedCompanyIds as IDataObject[]) { + companyAssociations.push({ + fromObjectId: responseData.objectId, + toObjectId: companyId, + category: 'HUBSPOT_DEFINED', + definitionId: 26, + }); + } + await hubspotApiRequest.call( + this, + 'PUT', + '/crm-associations/v1/associations/create-batch', + companyAssociations, + ); + } + + if (updateFields.associatedContactIds) { + const contactAssociations: IDataObject[] = []; + for (const contactId of updateFields.associatedContactIds as IDataObject[]) { + contactAssociations.push({ + fromObjectId: responseData.objectId, + toObjectId: contactId, + category: 'HUBSPOT_DEFINED', + definitionId: 16, + }); + } + await hubspotApiRequest.call( + this, + 'PUT', + '/crm-associations/v1/associations/create-batch', + contactAssociations, + ); + } + } + } + const executionData = this.helpers.constructExecutionMetaData( + this.helpers.returnJsonArray(responseData as IDataObject[]), + { itemData: { item: i } }, + ); + returnData.push(...executionData); + } catch (errorObject) { + const error = errorObject.cause.cause ? errorObject.cause : errorObject; + if ( + error.cause.error?.validationResults && + error.cause.error.validationResults[0].error === 'INVALID_EMAIL' + ) { + throw new NodeOperationError( + this.getNode(), + error.cause.error.validationResults[0].message as string, + ); + } + if (error.cause.error?.message !== 'The resource you are requesting could not be found') { + if (error.httpCode === '404' && error.description === 'resource not found') { + throw new NodeOperationError( + this.getNode(), + `${error.node.parameters.resource} #${ + error.node.parameters[`${error.node.parameters.resource}Id`].value + } could not be found. Check your ${error.node.parameters.resource} ID is correct`, + ); + } + throw new NodeOperationError(this.getNode(), error as string); + } + if (this.continueOnFail()) { + returnData.push({ json: { error: (error as JsonObject).message } }); + continue; + } + throw error; + } + } + } + return this.prepareOutputData(returnData); + } +} diff --git a/packages/nodes-base/nodes/Hubspot/V2/TicketDescription.ts b/packages/nodes-base/nodes/Hubspot/V2/TicketDescription.ts new file mode 100644 index 0000000000..50a4082f41 --- /dev/null +++ b/packages/nodes-base/nodes/Hubspot/V2/TicketDescription.ts @@ -0,0 +1,606 @@ +import type { INodeProperties } from 'n8n-workflow'; + +export const ticketOperations: INodeProperties[] = [ + { + displayName: 'Operation', + name: 'operation', + type: 'options', + noDataExpression: true, + displayOptions: { + show: { + resource: ['ticket'], + }, + }, + options: [ + { + name: 'Create', + value: 'create', + description: 'Create a ticket', + action: 'Create a ticket', + }, + { + name: 'Delete', + value: 'delete', + description: 'Delete a ticket', + action: 'Delete a ticket', + }, + { + name: 'Get', + value: 'get', + description: 'Get a ticket', + action: 'Get a ticket', + }, + { + name: 'Get Many', + value: 'getAll', + description: 'Get many tickets', + action: 'Get many tickets', + }, + { + name: 'Update', + value: 'update', + description: 'Update a ticket', + action: 'Update a ticket', + }, + ], + default: 'create', + }, +]; + +export const ticketFields: INodeProperties[] = [ + /* -------------------------------------------------------------------------- */ + /* ticket:create */ + /* -------------------------------------------------------------------------- */ + { + displayName: 'Pipeline Name or ID', + name: 'pipelineId', + type: 'options', + required: true, + typeOptions: { + loadOptionsMethod: 'getTicketPipelines', + }, + displayOptions: { + show: { + resource: ['ticket'], + operation: ['create'], + }, + }, + default: '', + description: + 'The ID of the pipeline the ticket is in. Choose from the list, or specify an ID using an expression.', + }, + { + displayName: 'Stage Name or ID', + name: 'stageId', + type: 'options', + required: true, + typeOptions: { + loadOptionsMethod: 'getTicketStages', + loadOptionsDependsOn: ['pipelineId'], + }, + displayOptions: { + show: { + resource: ['ticket'], + operation: ['create'], + }, + }, + default: '', + description: + 'The stage ID of the pipeline the ticket is in; depends on Pipeline ID. Choose from the list, or specify an ID using an expression.', + }, + { + displayName: 'Ticket Name', + name: 'ticketName', + type: 'string', + required: true, + displayOptions: { + show: { + resource: ['ticket'], + operation: ['create'], + }, + }, + default: '', + }, + { + displayName: 'Ticket Properties', + name: 'additionalFields', + type: 'collection', + placeholder: 'Add Property', + default: {}, + displayOptions: { + show: { + resource: ['ticket'], + operation: ['create'], + }, + }, + options: [ + { + displayName: 'Company Names or IDs', + name: 'associatedCompanyIds', + type: 'multiOptions', + typeOptions: { + loadOptionsMethod: 'getCompanies', + }, + default: [], + description: + 'Whether to include specific Company properties in the returned results. Choose from a list, or specify IDs using an expression. Choose from the list, or specify IDs using an expression.', + }, + { + displayName: 'Contact Names or IDs', + name: 'associatedContactIds', + type: 'multiOptions', + typeOptions: { + loadOptionsMethod: 'getContacts', + }, + default: [], + description: + 'Whether to include specific Contact properties in the returned results. Choose from a list, or specify IDs using an expression. Choose from the list, or specify IDs using an expression.', + }, + { + displayName: 'Category Name or ID', + name: 'category', + type: 'options', + typeOptions: { + loadOptionsMethod: 'getTicketCategories', + }, + default: '', + description: + 'Main reason customer reached out for help. Choose from the list, or specify an ID using an expression.', + }, + { + displayName: 'Close Date', + name: 'closeDate', + type: 'dateTime', + default: '', + description: + 'The date the ticket was closed. When using expressions, the time should be specified in YYYY-MM-DD hh-mm-ss format.', + }, + { + displayName: 'Create Date', + name: 'createDate', + type: 'dateTime', + default: '', + description: + 'The date the ticket was created. When using expressions, the time should be specified in YYYY-MM-DD hh-mm-ss format.', + }, + { + displayName: 'Description', + name: 'description', + type: 'string', + typeOptions: { + alwaysOpenEditWindow: true, + }, + default: '', + description: 'Description of the ticket', + }, + { + displayName: 'Priority Name or ID', + name: 'priority', + type: 'options', + typeOptions: { + loadOptionsMethod: 'getTicketPriorities', + }, + default: '', + description: + 'The level of attention needed on the ticket. Choose from the list, or specify an ID using an expression.', + }, + { + displayName: 'Resolution Name or ID', + name: 'resolution', + type: 'options', + typeOptions: { + loadOptionsMethod: 'getTicketResolutions', + }, + default: '', + description: + 'The action taken to resolve the ticket. Choose from the list, or specify an ID using an expression.', + }, + { + displayName: 'Source Name or ID', + name: 'source', + type: 'options', + typeOptions: { + loadOptionsMethod: 'getTicketSources', + }, + default: '', + description: + 'Channel where ticket was originally submitted. Choose from the list, or specify an ID using an expression.', + }, + { + displayName: 'Ticket Owner Name or ID', + name: 'ticketOwnerId', + type: 'options', + typeOptions: { + loadOptionsMethod: 'getOwners', + }, + default: '', + description: + 'The user from your team that the ticket is assigned to. You can assign additional users to a ticket record by creating a custom HubSpot user property. Choose from the list, or specify an ID using an expression.', + }, + ], + }, + /* -------------------------------------------------------------------------- */ + /* ticket:update */ + /* -------------------------------------------------------------------------- */ + { + displayName: 'Ticket to Update', + name: 'ticketId', + type: 'resourceLocator', + default: { mode: 'list', value: '' }, + required: true, + displayOptions: { + show: { + resource: ['ticket'], + operation: ['update'], + }, + }, + modes: [ + { + displayName: 'From List', + name: 'list', + type: 'list', + placeholder: 'Select from the list', + typeOptions: { + searchListMethod: 'searchTickets', + }, + }, + { + displayName: 'By Id', + name: 'id', + type: 'string', + placeholder: '58539222', + validation: [ + { + type: 'regex', + properties: { + regex: '[0-9]+', + errorMessage: 'Not a valid HubSpot Ticket ID', + }, + }, + ], + }, + ], + }, + { + displayName: 'Update Fields', + name: 'updateFields', + type: 'collection', + placeholder: 'Add Field', + default: {}, + displayOptions: { + show: { + resource: ['ticket'], + operation: ['update'], + }, + }, + options: [ + { + displayName: 'Company Names or IDs', + name: 'associatedCompanyIds', + type: 'multiOptions', + typeOptions: { + loadOptionsMethod: 'getCompanies', + }, + default: [], + description: + 'Whether to include specific Company properties in the returned results. Choose from a list, or specify IDs using an expression. Choose from the list, or specify IDs using an expression.', + }, + { + displayName: 'Contact Names or IDs', + name: 'associatedContactIds', + type: 'multiOptions', + typeOptions: { + loadOptionsMethod: 'getContacts', + }, + default: [], + description: + 'Whether to include specific Contact properties in the returned results. Choose from a list, or specify IDs using an expression. Choose from the list, or specify IDs using an expression.', + }, + { + displayName: 'Category Name or ID', + name: 'category', + type: 'options', + typeOptions: { + loadOptionsMethod: 'getTicketCategories', + }, + default: '', + description: + 'Main reason customer reached out for help. Choose from the list, or specify an ID using an expression.', + }, + { + displayName: 'Close Date', + name: 'closeDate', + type: 'dateTime', + default: '', + description: + 'The date the ticket was closed. When using expressions, the time should be specified in YYYY-MM-DD hh-mm-ss format.', + }, + { + displayName: 'Create Date', + name: 'createDate', + type: 'dateTime', + default: '', + description: + 'The date the ticket was created. When using expressions, the time should be specified in YYYY-MM-DD hh-mm-ss format.', + }, + { + displayName: 'Description', + name: 'description', + type: 'string', + typeOptions: { + alwaysOpenEditWindow: true, + }, + default: '', + description: 'Description of the ticket', + }, + { + displayName: 'Pipeline Name or ID', + name: 'pipelineId', + type: 'options', + typeOptions: { + loadOptionsMethod: 'getTicketPipelines', + }, + default: '', + description: + 'The ID of the pipeline the ticket is in. Choose from the list, or specify an ID using an expression.', + }, + { + displayName: 'Priority Name or ID', + name: 'priority', + type: 'options', + typeOptions: { + loadOptionsMethod: 'getTicketPriorities', + }, + default: '', + description: + 'The level of attention needed on the ticket. Choose from the list, or specify an ID using an expression.', + }, + { + displayName: 'Resolution Name or ID', + name: 'resolution', + type: 'options', + typeOptions: { + loadOptionsMethod: 'getTicketResolutions', + }, + default: '', + description: + 'The action taken to resolve the ticket. Choose from the list, or specify an ID using an expression.', + }, + { + displayName: 'Source Name or ID', + name: 'source', + type: 'options', + typeOptions: { + loadOptionsMethod: 'getTicketSources', + }, + default: '', + description: + 'Channel where ticket was originally submitted. Choose from the list, or specify an ID using an expression.', + }, + { + displayName: 'Stage Name or ID', + name: 'stageId', + type: 'options', + typeOptions: { + loadOptionsMethod: 'getTicketStages', + loadOptionsDependsOn: ['updateFields.pipelineId'], + }, + default: '', + description: + 'The stage ID of the pipeline the ticket is in; depends on Pipeline ID. Choose from the list, or specify an ID using an expression.', + }, + { + displayName: 'Ticket Name', + name: 'ticketName', + type: 'string', + default: '', + }, + { + displayName: 'Ticket Owner Name or ID', + name: 'ticketOwnerId', + type: 'options', + typeOptions: { + loadOptionsMethod: 'getOwners', + }, + default: '', + description: + 'The user from your team that the ticket is assigned to. You can assign additional users to a ticket record by creating a custom HubSpot user property. Choose from the list, or specify an ID using an expression.', + }, + ], + }, + + /* -------------------------------------------------------------------------- */ + /* ticket:get */ + /* -------------------------------------------------------------------------- */ + { + displayName: 'Ticket to Get', + name: 'ticketId', + type: 'resourceLocator', + default: { mode: 'list', value: '' }, + required: true, + displayOptions: { + show: { + resource: ['ticket'], + operation: ['get'], + }, + }, + modes: [ + { + displayName: 'From List', + name: 'list', + type: 'list', + placeholder: 'Select from the list', + typeOptions: { + searchListMethod: 'searchTickets', + }, + }, + { + displayName: 'By Id', + name: 'id', + type: 'string', + placeholder: '58539222', + validation: [ + { + type: 'regex', + properties: { + regex: '[0-9]+', + errorMessage: 'Not a valid HubSpot Ticket ID', + }, + }, + ], + }, + ], + }, + { + displayName: 'Options', + name: 'additionalFields', + type: 'collection', + placeholder: 'Add Option', + default: {}, + displayOptions: { + show: { + resource: ['ticket'], + operation: ['get'], + }, + }, + options: [ + { + displayName: 'Include Deleted', + name: 'includeDeleted', + type: 'boolean', + default: false, + }, + { + displayName: 'Ticket Properties to Include', + name: 'properties', + type: 'multiOptions', + typeOptions: { + loadOptionsMethod: 'getTicketProperties', + }, + default: [], + description: + 'Whether to include specific Ticket properties in the returned results. Choose from a list, or specify IDs using an expression. Choose from the list, or specify IDs using an expression.', + }, + { + 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, 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: 'Whether to return all results 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: 'Max number of results to return', + }, + { + displayName: 'Options', + name: 'additionalFields', + type: 'collection', + placeholder: 'Add Option', + default: {}, + displayOptions: { + show: { + resource: ['ticket'], + operation: ['getAll'], + }, + }, + options: [ + { + displayName: 'Ticket Properties to Include', + name: 'properties', + type: 'multiOptions', + typeOptions: { + loadOptionsMethod: 'getTicketProperties', + }, + default: [], + description: + 'Whether to include specific Ticket properties in the returned results. Choose from a list, or specify IDs using an expression. Choose from the list, or specify IDs using an expression.', + }, + { + 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, 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 */ + /* -------------------------------------------------------------------------- */ + { + displayName: 'Ticket to Delete', + name: 'ticketId', + type: 'resourceLocator', + default: { mode: 'list', value: '' }, + required: true, + displayOptions: { + show: { + resource: ['ticket'], + operation: ['delete'], + }, + }, + modes: [ + { + displayName: 'From List', + name: 'list', + type: 'list', + placeholder: 'Select from the list', + typeOptions: { + searchListMethod: 'searchTickets', + }, + }, + { + displayName: 'By Id', + name: 'id', + type: 'string', + placeholder: '58539222', + validation: [ + { + type: 'regex', + properties: { + regex: '[0-9]+', + errorMessage: 'Not a valid HubSpot Ticket ID', + }, + }, + ], + }, + ], + }, +];