From 4fc66c9e1c40975c4d5834f4c628f5d1f9b4b7a2 Mon Sep 17 00:00:00 2001 From: ricardo Date: Wed, 1 Apr 2020 18:10:41 -0400 Subject: [PATCH 1/3] :sparkles: Infusionsoft node --- .../InfusionsoftOAuth2Api.credentials.ts | 48 ++ .../nodes/Infusionsoft/CompanyDescription.ts | 374 ++++++++ .../nodes/Infusionsoft/CompanyInterface.ts | 12 + .../nodes/Infusionsoft/ConctactInterface.ts | 72 ++ .../nodes/Infusionsoft/ContactDescription.ts | 760 ++++++++++++++++ .../Infusionsoft/ContactNoteDescription.ts | 382 +++++++++ .../Infusionsoft/ContactNoteInterface.ts | 8 + .../Infusionsoft/ContactTagDescription.ts | 179 ++++ .../Infusionsoft/EcommerceOrderDescripion.ts | 486 +++++++++++ .../Infusionsoft/EcommerceOrderInterface.ts | 35 + .../EcommerceProductDescription.ts | 236 +++++ .../Infusionsoft/EcommerceProductInterface.ts | 10 + .../nodes/Infusionsoft/EmaiIInterface.ts | 15 + .../nodes/Infusionsoft/EmailDescription.ts | 464 ++++++++++ .../nodes/Infusionsoft/FileDescription.ts | 404 +++++++++ .../nodes/Infusionsoft/FileInterface.ts | 8 + .../nodes/Infusionsoft/GenericFunctions.ts | 82 ++ .../nodes/Infusionsoft/Infusionsoft.node.ts | 811 ++++++++++++++++++ .../Infusionsoft/InfusionsoftTrigger.node.ts | 196 +++++ .../nodes/Infusionsoft/infusionsoft.png | Bin 0 -> 4307 bytes packages/nodes-base/package.json | 7 +- 21 files changed, 4587 insertions(+), 2 deletions(-) create mode 100644 packages/nodes-base/credentials/InfusionsoftOAuth2Api.credentials.ts create mode 100644 packages/nodes-base/nodes/Infusionsoft/CompanyDescription.ts create mode 100644 packages/nodes-base/nodes/Infusionsoft/CompanyInterface.ts create mode 100644 packages/nodes-base/nodes/Infusionsoft/ConctactInterface.ts create mode 100644 packages/nodes-base/nodes/Infusionsoft/ContactDescription.ts create mode 100644 packages/nodes-base/nodes/Infusionsoft/ContactNoteDescription.ts create mode 100644 packages/nodes-base/nodes/Infusionsoft/ContactNoteInterface.ts create mode 100644 packages/nodes-base/nodes/Infusionsoft/ContactTagDescription.ts create mode 100644 packages/nodes-base/nodes/Infusionsoft/EcommerceOrderDescripion.ts create mode 100644 packages/nodes-base/nodes/Infusionsoft/EcommerceOrderInterface.ts create mode 100644 packages/nodes-base/nodes/Infusionsoft/EcommerceProductDescription.ts create mode 100644 packages/nodes-base/nodes/Infusionsoft/EcommerceProductInterface.ts create mode 100644 packages/nodes-base/nodes/Infusionsoft/EmaiIInterface.ts create mode 100644 packages/nodes-base/nodes/Infusionsoft/EmailDescription.ts create mode 100644 packages/nodes-base/nodes/Infusionsoft/FileDescription.ts create mode 100644 packages/nodes-base/nodes/Infusionsoft/FileInterface.ts create mode 100644 packages/nodes-base/nodes/Infusionsoft/GenericFunctions.ts create mode 100644 packages/nodes-base/nodes/Infusionsoft/Infusionsoft.node.ts create mode 100644 packages/nodes-base/nodes/Infusionsoft/InfusionsoftTrigger.node.ts create mode 100644 packages/nodes-base/nodes/Infusionsoft/infusionsoft.png diff --git a/packages/nodes-base/credentials/InfusionsoftOAuth2Api.credentials.ts b/packages/nodes-base/credentials/InfusionsoftOAuth2Api.credentials.ts new file mode 100644 index 000000000..63bd89038 --- /dev/null +++ b/packages/nodes-base/credentials/InfusionsoftOAuth2Api.credentials.ts @@ -0,0 +1,48 @@ +import { + ICredentialType, + NodePropertyTypes, +} from 'n8n-workflow'; + +const scopes = [ + 'full', +]; + +export class InfusionsoftOAuth2Api implements ICredentialType { + name = 'infusionsoftOAuth2Api'; + extends = [ + 'oAuth2Api', + ]; + displayName = 'Infusionsoft OAuth2 API'; + properties = [ + { + displayName: 'Authorization URL', + name: 'authUrl', + type: 'hidden' as NodePropertyTypes, + default: 'https://signin.infusionsoft.com/app/oauth/authorize', + }, + { + displayName: 'Access Token URL', + name: 'accessTokenUrl', + type: 'hidden' as NodePropertyTypes, + default: 'https://api.infusionsoft.com/token', + }, + { + displayName: 'Scope', + name: 'scope', + type: 'hidden' as NodePropertyTypes, + default: scopes.join(' '), + }, + { + displayName: 'Auth URI Query Parameters', + name: 'authQueryParameters', + type: 'hidden' as NodePropertyTypes, + default: '', + }, + { + displayName: 'Authentication', + name: 'authentication', + type: 'hidden' as NodePropertyTypes, + default: 'body', + }, + ]; +} diff --git a/packages/nodes-base/nodes/Infusionsoft/CompanyDescription.ts b/packages/nodes-base/nodes/Infusionsoft/CompanyDescription.ts new file mode 100644 index 000000000..b0278d856 --- /dev/null +++ b/packages/nodes-base/nodes/Infusionsoft/CompanyDescription.ts @@ -0,0 +1,374 @@ +import { + INodeProperties, + } from "n8n-workflow"; + +export const companyOperations = [ + { + displayName: 'Operation', + name: 'operation', + type: 'options', + displayOptions: { + show: { + resource: [ + 'company', + ], + }, + }, + options: [ + { + name: 'Create', + value: 'create', + description: 'Create a company', + }, + { + name: 'Get All', + value: 'getAll', + description: 'Retrieve all companies', + }, + ], + default: 'create', + description: 'The operation to perform.', + }, +] as INodeProperties[]; + +export const companyFields = [ + +/* -------------------------------------------------------------------------- */ +/* company:create */ +/* -------------------------------------------------------------------------- */ + { + displayName: 'Company Name', + name: 'companyName', + required: true, + type: 'string', + displayOptions: { + show: { + operation: [ + 'create', + ], + resource: [ + 'company', + ], + }, + }, + default: '', + }, + { + displayName: 'Additional Fields', + name: 'additionalFields', + type: 'collection', + placeholder: 'Add Field', + default: {}, + displayOptions: { + show: { + operation: [ + 'create', + ], + resource: [ + 'company', + ], + }, + }, + options: [ + { + displayName: 'Email', + name: 'emailAddress', + type: 'string', + default: '', + }, + { + displayName: 'Notes', + name: 'notes', + type: 'string', + typeOptions: { + alwaysOpenEditWindow: true, + }, + default: '', + }, + { + displayName: 'Opt In Reason', + name: 'optInReason', + type: 'string', + default: '', + }, + { + displayName: 'Website', + name: 'website', + type: 'string', + default: '', + }, + ], + }, + { + displayName: 'Addresses', + name: 'addressesUi', + type: 'fixedCollection', + typeOptions: { + multipleValues: false, + }, + default: '', + placeholder: 'Add Address', + displayOptions: { + show: { + resource: [ + 'company', + ], + operation: [ + 'create', + ], + }, + }, + options: [ + { + name: 'addressesValues', + displayName: 'Address', + values: [ + { + displayName: 'Country Code', + name: 'countryCode', + type: 'string', + default: '', + description: 'ISO Alpha-3 Code' + }, + { + displayName: 'Line 1', + name: 'line1', + type: 'string', + default: '', + }, + { + displayName: 'Line 2', + name: 'line2', + type: 'string', + default: '', + }, + { + displayName: 'Locality', + name: 'locality', + type: 'string', + default: '', + }, + { + displayName: 'Postal Code', + name: 'postalCode', + type: 'string', + default: '', + }, + { + displayName: 'Region', + name: 'region', + type: 'string', + default: '', + }, + { + displayName: 'Zip Code', + name: 'zipCode', + type: 'string', + default: '', + }, + { + displayName: 'Zip Four', + name: 'zipFour', + type: 'string', + default: '', + }, + ], + }, + ], + }, + { + displayName: 'Faxes', + name: 'faxesUi', + type: 'fixedCollection', + default: {}, + typeOptions: { + multipleValues: false, + }, + placeholder: 'Add Fax', + displayOptions: { + show: { + resource: [ + 'company', + ], + operation: [ + 'create', + ], + }, + }, + options: [ + { + name: 'faxesValues', + displayName: 'Fax', + values: [ + { + displayName: 'Type', + name: 'type', + type: 'string', + default: '', + }, + { + displayName: 'Number', + name: 'number', + type: 'string', + default: '', + }, + ], + } + ], + }, + { + displayName: 'Phones', + name: 'phonesUi', + type: 'fixedCollection', + default: {}, + typeOptions: { + multipleValues: true, + }, + placeholder: 'Add Phone', + displayOptions: { + show: { + resource: [ + 'company', + ], + operation: [ + 'create', + ], + }, + }, + options: [ + { + name: 'phonesValues', + displayName: 'Phones', + values: [ + { + displayName: 'Type', + name: 'type', + type: 'string', + default: '', + }, + { + displayName: 'Number', + name: 'number', + type: 'string', + default: '', + }, + ], + }, + ], + }, +/* -------------------------------------------------------------------------- */ +/* company:getAll */ +/* -------------------------------------------------------------------------- */ + { + displayName: 'Return All', + name: 'returnAll', + type: 'boolean', + displayOptions: { + show: { + operation: [ + 'getAll', + ], + resource: [ + 'company', + ], + }, + }, + default: false, + description: 'If all results should be returned or only up to a given limit.', + }, + { + displayName: 'Limit', + name: 'limit', + type: 'number', + displayOptions: { + show: { + operation: [ + 'getAll', + ], + resource: [ + 'company', + ], + returnAll: [ + false, + ], + }, + }, + typeOptions: { + minValue: 1, + maxValue: 200, + }, + default: 100, + description: 'How many results to return.', + }, + { + displayName: 'Options', + name: 'options', + type: 'collection', + placeholder: 'Add Option', + default: {}, + displayOptions: { + show: { + operation: [ + 'getAll', + ], + resource: [ + 'company', + ], + }, + }, + options: [ + { + displayName: 'Company Name', + name: 'companyName', + type: 'string', + default: '', + description: 'Company name to query on', + }, + { + displayName: 'Order', + name: 'order', + type: 'options', + options: [ + { + name: 'Date Created', + value: 'datecreated', + }, + { + name: 'ID', + value: 'id', + }, + { + name: 'Name', + value: 'name', + }, + ], + default: '', + description: 'Attribute to order items by', + }, + { + displayName: 'Order Direction', + name: 'orderDirection', + type: 'options', + options: [ + { + name: 'ASC', + value: 'ascending', + }, + { + name: 'DES', + value: 'descending', + }, + ], + default: '', + }, + { + displayName: 'Fields', + name: 'fields', + type: 'string', + default: '', + description: `Comma-delimited list of Company properties to include in the response.
+ (Fields such as notes, fax_number and custom_fields aren't included, by default.)`, + }, + ], + }, +] as INodeProperties[]; diff --git a/packages/nodes-base/nodes/Infusionsoft/CompanyInterface.ts b/packages/nodes-base/nodes/Infusionsoft/CompanyInterface.ts new file mode 100644 index 000000000..c8006e9f4 --- /dev/null +++ b/packages/nodes-base/nodes/Infusionsoft/CompanyInterface.ts @@ -0,0 +1,12 @@ +import { IDataObject } from "n8n-workflow"; + +export interface ICompany { + address?: IDataObject; + company_name?: string; + email_address?: string; + fax_number?: IDataObject; + notes?: string; + opt_in_reason?: string; + phone_number?: IDataObject; + website?: string; +} diff --git a/packages/nodes-base/nodes/Infusionsoft/ConctactInterface.ts b/packages/nodes-base/nodes/Infusionsoft/ConctactInterface.ts new file mode 100644 index 000000000..370888a17 --- /dev/null +++ b/packages/nodes-base/nodes/Infusionsoft/ConctactInterface.ts @@ -0,0 +1,72 @@ +import { + IDataObject, + } from "n8n-workflow"; + +export interface IAddress { + country_code?: string; + field?: string; + line1?: string; + line2?: string; + locality?: string; + postal_code?: string; + region?: string; + zip_code?: string; + zip_four?: string; +} + +export interface ICustomField { + content: IDataObject; + id: number; +} + +export interface IEmailContact { + email?: string; + field?: string; +} + +export interface IFax { + field?: string; + number?: string; + type?: string; +} + +export interface IPhone { + extension?: string; + field?: string; + number?: string; + type?: string; +} + +export interface ISocialAccount { + name?: string; + type?: string; +} + +export interface IContact { + addresses?: IAddress[]; + anniversary?: string; + company?: IDataObject; + contact_type?: string; + custom_fields?: ICustomField[]; + duplicate_option?: string; + email_addresses?: IEmailContact[]; + family_name?: string; + fax_numbers?: IFax[]; + given_name?: string; + job_title?: string; + lead_source_id?: number; + middle_name?: string; + opt_in_reason?: string; + origin?: IDataObject; + owner_id?: number; + phone_numbers?: IPhone[]; + preferred_locale?: string; + preferred_name?: string; + prefix?: string; + social_accounts?: ISocialAccount[]; + source_type?: string; + spouse_name?: string; + suffix?: string; + time_zone?: string; + website?: string; +} diff --git a/packages/nodes-base/nodes/Infusionsoft/ContactDescription.ts b/packages/nodes-base/nodes/Infusionsoft/ContactDescription.ts new file mode 100644 index 000000000..198224fff --- /dev/null +++ b/packages/nodes-base/nodes/Infusionsoft/ContactDescription.ts @@ -0,0 +1,760 @@ +import { + INodeProperties, + } from "n8n-workflow"; + +export const contactOperations = [ + { + displayName: 'Operation', + name: 'operation', + type: 'options', + displayOptions: { + show: { + resource: [ + 'contact', + ], + }, + }, + options: [ + { + name: 'Create/Update', + value: 'create/update', + description: 'Create/update a contact', + }, + { + name: 'Delete', + value: 'delete', + description: 'Delete an contact', + }, + { + name: 'Get', + value: 'get', + description: 'Retrieve an contact', + }, + { + name: 'Get All', + value: 'getAll', + description: 'Retrieve all contacts', + }, + ], + default: 'create/update', + description: 'The operation to perform.', + }, +] as INodeProperties[]; + +export const contactFields = [ + +/* -------------------------------------------------------------------------- */ +/* contact:create/update */ +/* -------------------------------------------------------------------------- */ + { + displayName: 'Duplicate Option', + name: 'duplicateOption', + required: true, + type: 'options', + options: [ + { + name: 'Email', + value: 'email', + }, + { + name: 'Email And Name', + value: 'emailAndName', + }, + ], + displayOptions: { + show: { + operation: [ + 'create/update', + ], + resource: [ + 'contact', + ], + }, + }, + default: 'email', + description: `Performs duplicate checking by one of the following options: Email, EmailAndName,
+ if a match is found using the option provided, the existing contact will be updated` + }, + { + displayName: 'Additional Fields', + name: 'additionalFields', + type: 'collection', + placeholder: 'Add Field', + default: {}, + displayOptions: { + show: { + operation: [ + 'create/update', + ], + resource: [ + 'contact', + ], + }, + }, + options: [ + { + displayName: 'Anniversary', + name: 'anniversary', + type: 'dateTime', + default: '', + }, + { + displayName: 'Company ID', + name: 'companyId', + type: 'number', + typeOptions: { + minValue: 0, + }, + default: 0, + }, + { + displayName: 'Contact Type', + name: 'contactType', + type: 'options', + typeOptions: { + loadOptionsMethod: 'getContactTypes', + }, + default: '', + }, + { + displayName: 'Family Name', + name: 'familyName', + type: 'string', + default: '', + }, + { + displayName: 'Given Name', + name: 'givenName', + type: 'string', + default: '', + }, + { + displayName: 'IP Address', + name: 'ipAddress', + type: 'string', + default: '', + }, + { + displayName: 'Job Title', + name: 'jobTitle', + type: 'string', + default: '', + }, + { + displayName: 'Lead Source ID', + name: 'leadSourceId', + type: 'number', + default: 0, + }, + { + displayName: 'Middle Name', + name: 'middleName', + type: 'string', + default: '', + }, + { + displayName: 'Opt In Reason', + name: 'optInReason', + type: 'string', + default: '', + }, + { + displayName: 'Owner ID', + name: 'ownerId', + type: 'options', + typeOptions: { + loadOptionsMethod: 'getUsers', + }, + default: '', + }, + { + displayName: 'Preferred Locale', + name: 'preferredLocale', + type: 'string', + placeholder: 'en', + default: '', + }, + { + displayName: 'Preferred Name', + name: 'preferredName', + type: 'string', + default: '', + }, + { + displayName: 'Source Type', + name: 'sourceType', + type: 'options', + options: [ + { + name: 'API', + value: 'API', + }, + { + name: 'Import', + value: 'IMPORT', + }, + { + name: 'Landing Page', + value: 'LANDINGPAGE', + }, + { + name: 'Manual', + value: 'MANUAL', + }, + { + name: 'Other', + value: 'OTHER', + }, + { + name: 'Unknown', + value: 'UNKNOWN', + }, + ], + default: '', + }, + { + displayName: 'Spouse Name', + name: 'spouseName', + type: 'string', + default: '', + }, + { + displayName: 'Timezone', + name: 'timezone', + type: 'options', + typeOptions: { + loadOptionsMethod: 'getTimezones', + }, + default: '', + }, + { + displayName: 'Website', + name: 'website', + type: 'string', + default: '', + }, + ], + }, + { + displayName: 'Addresses', + name: 'addressesUi', + type: 'fixedCollection', + typeOptions: { + multipleValues: true, + }, + default: '', + placeholder: 'Add Address', + displayOptions: { + show: { + resource: [ + 'contact', + ], + operation: [ + 'create/update', + ], + }, + }, + options: [ + { + name: 'addressesValues', + displayName: 'Address', + values: [ + { + displayName: 'Field', + name: 'field', + type: 'options', + options: [ + { + name: 'Billing', + value: 'BILLING', + }, + { + name: 'Shipping', + value: 'SHIPPING', + }, + { + name: 'Other', + value: 'OTHER', + }, + ], + default: '', + }, + { + displayName: 'Country Code', + name: 'countryCode', + type: 'options', + typeOptions: { + loadOptionsMethod: 'getCountries', + }, + default: '', + }, + { + displayName: 'Line 1', + name: 'line1', + type: 'string', + default: '', + }, + { + displayName: 'Line 2', + name: 'line2', + type: 'string', + default: '', + }, + { + displayName: 'Locality', + name: 'locality', + type: 'string', + default: '', + }, + { + displayName: 'Postal Code', + name: 'postalCode', + type: 'string', + default: '', + }, + { + displayName: 'Region', + name: 'region', + type: 'string', + default: '', + }, + { + displayName: 'Zip Code', + name: 'zipCode', + type: 'string', + default: '', + }, + { + displayName: 'Zip Four', + name: 'zipFour', + type: 'string', + default: '', + }, + ], + }, + ], + }, + { + displayName: 'Emails', + name: 'emailsUi', + type: 'fixedCollection', + default: {}, + typeOptions: { + multipleValues: true, + }, + placeholder: 'Add Email', + displayOptions: { + show: { + resource: [ + 'contact', + ], + operation: [ + 'create/update', + ], + }, + }, + options: [ + { + name: 'emailsValues', + displayName: 'Email', + values: [ + { + displayName: 'Field', + name: 'field', + type: 'options', + options: [ + { + name: 'Email 1', + value: 'EMAIL1', + }, + { + name: 'Email 2', + value: 'EMAIL2', + }, + { + name: 'Email 3', + value: 'EMAIL3', + }, + ], + default: '', + }, + { + displayName: 'Email', + name: 'email', + type: 'string', + default: '', + }, + ], + } + ], + }, + { + displayName: 'Faxes', + name: 'faxesUi', + type: 'fixedCollection', + default: {}, + typeOptions: { + multipleValues: true, + }, + placeholder: 'Add Fax', + displayOptions: { + show: { + resource: [ + 'contact', + ], + operation: [ + 'create/update', + ], + }, + }, + options: [ + { + name: 'faxesValues', + displayName: 'Fax', + values: [ + { + displayName: 'Field', + name: 'field', + type: 'options', + options: [ + { + name: 'Fax 1', + value: 'FAX1', + }, + { + name: 'Fax 2', + value: 'FAX2', + }, + ], + default: '', + }, + { + displayName: 'Number', + name: 'number', + type: 'string', + default: '', + }, + ], + } + ], + }, + { + displayName: 'Phones', + name: 'phonesUi', + type: 'fixedCollection', + default: {}, + typeOptions: { + multipleValues: true, + }, + placeholder: 'Add Phone', + displayOptions: { + show: { + resource: [ + 'contact', + ], + operation: [ + 'create/update', + ], + }, + }, + options: [ + { + name: 'phonesValues', + displayName: 'Phones', + values: [ + { + displayName: 'Field', + name: 'field', + type: 'options', + options: [ + { + name: 'Phone 1', + value: 'PHONE1', + }, + { + name: 'Phone 2', + value: 'PHONE2', + }, + { + name: 'Phone 3', + value: 'PHONE3', + }, + { + name: 'Phone 4', + value: 'PHONE4', + }, + { + name: 'Phone 5', + value: 'PHONE5', + }, + ], + default: '', + }, + { + displayName: 'Number', + name: 'number', + type: 'string', + default: '', + }, + ], + }, + ], + }, + { + displayName: 'Social Accounts', + name: 'socialAccountsUi', + type: 'fixedCollection', + typeOptions: { + multipleValues: true, + }, + default: '', + placeholder: 'Add Social Account', + displayOptions: { + show: { + resource: [ + 'contact', + ], + operation: [ + 'create/update', + ], + }, + }, + options: [ + { + name: 'socialAccountsValues', + displayName: 'Social Account', + values: [ + { + displayName: 'Type', + name: 'type', + type: 'options', + options: [ + { + name: 'Facebook', + value: 'Facebook', + }, + { + name: 'Twitter', + value: 'Twitter', + }, + { + name: 'LinkedIn', + value: 'LinkedIn', + }, + ], + default: '', + }, + { + displayName: 'Name', + name: 'name', + type: 'string', + default: '', + }, + ], + }, + ], + }, +/* -------------------------------------------------------------------------- */ +/* contact:delete */ +/* -------------------------------------------------------------------------- */ + { + displayName: 'Contact ID', + name: 'contactId', + type: 'string', + required: true, + displayOptions: { + show: { + operation: [ + 'delete', + ], + resource: [ + 'contact', + ], + }, + }, + default: '', + }, +/* -------------------------------------------------------------------------- */ +/* contact:get */ +/* -------------------------------------------------------------------------- */ + { + displayName: 'Contact ID', + name: 'contactId', + type: 'string', + required: true, + displayOptions: { + show: { + operation: [ + 'get', + ], + resource: [ + 'contact', + ], + }, + }, + default: '', + }, + { + displayName: 'Options', + name: 'options', + type: 'collection', + placeholder: 'Add Options', + default: {}, + displayOptions: { + show: { + operation: [ + 'get', + ], + resource: [ + 'contact', + ], + }, + }, + options: [ + { + displayName: 'Fields', + name: 'fields', + type: 'string', + default: '', + description: `Comma-delimited list of Contact properties to include in the response.
+ (Some fields such as lead_source_id, custom_fields, and job_title aren't included, by default.)`, + }, + ], + }, +/* -------------------------------------------------------------------------- */ +/* contact:getAll */ +/* -------------------------------------------------------------------------- */ + { + displayName: 'Return All', + name: 'returnAll', + type: 'boolean', + displayOptions: { + show: { + operation: [ + 'getAll', + ], + resource: [ + 'contact', + ], + }, + }, + default: false, + description: 'If all results should be returned or only up to a given limit.', + }, + { + displayName: 'Limit', + name: 'limit', + type: 'number', + displayOptions: { + show: { + operation: [ + 'getAll', + ], + resource: [ + 'contact', + ], + returnAll: [ + false, + ], + }, + }, + typeOptions: { + minValue: 1, + maxValue: 200, + }, + default: 100, + description: 'How many results to return.', + }, + { + displayName: 'Options', + name: 'options', + type: 'collection', + placeholder: 'Add Option', + default: {}, + displayOptions: { + show: { + operation: [ + 'getAll', + ], + resource: [ + 'contact', + ], + }, + }, + options: [ + { + displayName: 'Email', + name: 'email', + type: 'string', + default: '', + }, + { + displayName: 'Given Name', + name: 'givenName', + type: 'string', + default: '', + }, + { + displayName: 'Family Name', + name: 'familyName', + type: 'string', + default: '', + }, + { + displayName: 'Order', + name: 'order', + type: 'options', + options: [ + { + name: 'Date', + value: 'date', + }, + { + name: 'Email', + value: 'email', + }, + { + name: 'ID', + value: 'id', + }, + { + name: 'Name', + value: 'name', + }, + ], + default: '', + description: 'Attribute to order items by', + }, + { + displayName: 'Order Direction', + name: 'orderDirection', + type: 'options', + options: [ + { + name: 'ASC', + value: 'ascending', + }, + { + name: 'DES', + value: 'descending', + }, + ], + default: '', + }, + { + displayName: 'Since', + name: 'since', + type: 'dateTime', + default: '', + description: 'Date to start searching from on LastUpdated', + }, + { + displayName: 'Until', + name: 'until', + type: 'dateTime', + default: '', + description: 'Date to search to on LastUpdated', + }, + ], + }, +] as INodeProperties[]; diff --git a/packages/nodes-base/nodes/Infusionsoft/ContactNoteDescription.ts b/packages/nodes-base/nodes/Infusionsoft/ContactNoteDescription.ts new file mode 100644 index 000000000..d916702b2 --- /dev/null +++ b/packages/nodes-base/nodes/Infusionsoft/ContactNoteDescription.ts @@ -0,0 +1,382 @@ +import { + INodeProperties, + } from "n8n-workflow"; + +export const contactNoteOperations = [ + { + displayName: 'Operation', + name: 'operation', + type: 'options', + displayOptions: { + show: { + resource: [ + 'contactNote', + ], + }, + }, + options: [ + { + name: 'Create', + value: 'create', + description: 'Create a note', + }, + { + name: 'Delete', + value: 'delete', + description: 'Delete a note', + }, + { + name: 'Get', + value: 'get', + description: 'Get a notes', + }, + { + name: 'Get All', + value: 'getAll', + description: 'Retrieve all notes', + }, + { + name: 'Update', + value: 'update', + description: 'Update a note', + }, + ], + default: 'create', + description: 'The operation to perform.', + }, +] as INodeProperties[]; + +export const contactNoteFields = [ + +/* -------------------------------------------------------------------------- */ +/* contactNote:create */ +/* -------------------------------------------------------------------------- */ + { + displayName: 'User ID', + name: 'userId', + type: 'options', + typeOptions: { + loadOptionsMethod: 'getUsers', + }, + displayOptions: { + show: { + operation: [ + 'create', + ], + resource: [ + 'contactNote', + ], + }, + }, + default: '', + description: 'The infusionsoft user to create the note on behalf of', + }, + { + displayName: 'Contact ID', + name: 'contactId', + type: 'string', + displayOptions: { + show: { + operation: [ + 'create', + ], + resource: [ + 'contactNote', + ], + }, + }, + default: '', + }, + { + displayName: 'Additional Fields', + name: 'additionalFields', + type: 'collection', + placeholder: 'Add Field', + default: {}, + displayOptions: { + show: { + operation: [ + 'create', + ], + resource: [ + 'contactNote', + ], + }, + }, + options: [ + { + displayName: 'Body', + name: 'body', + type: 'string', + typeOptions: { + alwaysOpenEditWindow: true, + }, + default: '', + }, + { + displayName: 'Title', + name: 'title', + type: 'string', + default: '', + }, + { + displayName: 'Type', + name: 'type', + type: 'options', + options: [ + { + name: 'Appointment', + value: 'appointment', + }, + { + name: 'Call', + value: 'call', + }, + { + name: 'Email', + value: 'email', + }, + { + name: 'Fax', + value: 'fax', + }, + { + name: 'Letter', + value: 'letter', + }, + { + name: 'Other', + value: 'other', + }, + ], + default: '', + }, + ], + }, +/* -------------------------------------------------------------------------- */ +/* contactNote:delete */ +/* -------------------------------------------------------------------------- */ + { + displayName: 'Note ID', + name: 'noteId', + type: 'string', + required: true, + displayOptions: { + show: { + operation: [ + 'delete', + ], + resource: [ + 'contactNote', + ], + }, + }, + default: '', + }, +/* -------------------------------------------------------------------------- */ +/* contactNote:get */ +/* -------------------------------------------------------------------------- */ + { + displayName: 'Note ID', + name: 'noteId', + type: 'string', + required: true, + displayOptions: { + show: { + operation: [ + 'get', + ], + resource: [ + 'contactNote', + ], + }, + }, + default: '', + }, +/* -------------------------------------------------------------------------- */ +/* contactNote:getAll */ +/* -------------------------------------------------------------------------- */ + { + displayName: 'Return All', + name: 'returnAll', + type: 'boolean', + displayOptions: { + show: { + operation: [ + 'getAll', + ], + resource: [ + 'contactNote', + ], + }, + }, + default: false, + description: 'If all results should be returned or only up to a given limit.', + }, + { + displayName: 'Limit', + name: 'limit', + type: 'number', + displayOptions: { + show: { + operation: [ + 'getAll', + ], + resource: [ + 'contactNote', + ], + returnAll: [ + false, + ], + }, + }, + typeOptions: { + minValue: 1, + maxValue: 200, + }, + default: 100, + description: 'How many results to return.', + }, + { + displayName: 'Filters', + name: 'filters', + type: 'collection', + placeholder: 'Add Filter', + default: {}, + displayOptions: { + show: { + operation: [ + 'getAll', + ], + resource: [ + 'contactNote', + ], + }, + }, + options: [ + { + displayName: 'Contact ID', + name: 'contactId', + type: 'number', + typeOptions: { + minValue: 0, + }, + default: 0, + }, + { + displayName: 'User ID', + name: 'userId', + type: 'options', + typeOptions: { + loadOptionsMethod: 'getUsers', + }, + default: '', + }, + ], + }, +/* -------------------------------------------------------------------------- */ +/* contactNote:update */ +/* -------------------------------------------------------------------------- */ + { + displayName: 'Note ID', + name: 'noteId', + type: 'string', + required: true, + displayOptions: { + show: { + operation: [ + 'update', + ], + resource: [ + 'contactNote', + ], + }, + }, + default: '', + }, + { + displayName: 'Additional Fields', + name: 'additionalFields', + type: 'collection', + placeholder: 'Add Field', + default: {}, + displayOptions: { + show: { + operation: [ + 'update', + ], + resource: [ + 'contactNote', + ], + }, + }, + options: [ + { + displayName: 'Body', + name: 'body', + type: 'string', + typeOptions: { + alwaysOpenEditWindow: true, + }, + default: '', + }, + { + displayName: 'Contact ID', + name: 'contactId', + type: 'number', + typeOptions: { + minValue: 0 + }, + default: 0, + }, + { + displayName: 'Title', + name: 'title', + type: 'string', + default: '', + }, + { + displayName: 'Type', + name: 'type', + type: 'options', + options: [ + { + name: 'Appointment', + value: 'appointment', + }, + { + name: 'Call', + value: 'call', + }, + { + name: 'Email', + value: 'email', + }, + { + name: 'Fax', + value: 'fax', + }, + { + name: 'Letter', + value: 'letter', + }, + { + name: 'Other', + value: 'other', + }, + ], + default: '', + }, + { + displayName: 'User ID', + name: 'userId', + type: 'options', + typeOptions: { + loadOptionsMethod: 'getUsers', + }, + default: '', + description: 'The infusionsoft user to create the note on behalf of', + }, + ], + }, +] as INodeProperties[]; diff --git a/packages/nodes-base/nodes/Infusionsoft/ContactNoteInterface.ts b/packages/nodes-base/nodes/Infusionsoft/ContactNoteInterface.ts new file mode 100644 index 000000000..221bd73e3 --- /dev/null +++ b/packages/nodes-base/nodes/Infusionsoft/ContactNoteInterface.ts @@ -0,0 +1,8 @@ + +export interface INote { + body?: string; + contact_id?: number; + title?: string; + type?: string; + user_id?: number; +} diff --git a/packages/nodes-base/nodes/Infusionsoft/ContactTagDescription.ts b/packages/nodes-base/nodes/Infusionsoft/ContactTagDescription.ts new file mode 100644 index 000000000..9643530ad --- /dev/null +++ b/packages/nodes-base/nodes/Infusionsoft/ContactTagDescription.ts @@ -0,0 +1,179 @@ +import { + INodeProperties, + } from "n8n-workflow"; + +export const contactTagOperations = [ + { + displayName: 'Operation', + name: 'operation', + type: 'options', + displayOptions: { + show: { + resource: [ + 'contactTag', + ], + }, + }, + options: [ + { + name: 'Create', + value: 'create', + description: 'Add a list of tags to a contact', + }, + { + name: 'Delete', + value: 'delete', + description: `Delete a contact's tag`, + }, + { + name: 'Get All', + value: 'getAll', + description: `Retrieve all contact's tags`, + }, + ], + default: 'create', + description: 'The operation to perform.', + }, +] as INodeProperties[]; + +export const contactTagFields = [ + +/* -------------------------------------------------------------------------- */ +/* contactTag:create */ +/* -------------------------------------------------------------------------- */ + { + displayName: 'Contact ID', + name: 'contactId', + type: 'string', + required: true, + displayOptions: { + show: { + operation: [ + 'create', + ], + resource: [ + 'contactTag', + ], + }, + }, + default: '', + }, + { + displayName: 'Tag IDs', + name: 'tagIds', + type: 'multiOptions', + typeOptions: { + loadOptionsMethod: 'getTags', + }, + required: true, + displayOptions: { + show: { + operation: [ + 'create', + ], + resource: [ + 'contactTag', + ], + }, + }, + default: [], + }, +/* -------------------------------------------------------------------------- */ +/* contactTag:delete */ +/* -------------------------------------------------------------------------- */ + { + displayName: 'Contact ID', + name: 'contactId', + type: 'string', + required: true, + displayOptions: { + show: { + operation: [ + 'delete', + ], + resource: [ + 'contactTag', + ], + }, + }, + default: '', + }, + { + displayName: 'Tag IDs', + name: 'tagIds', + type: 'string', + required: true, + displayOptions: { + show: { + operation: [ + 'delete', + ], + resource: [ + 'contactTag', + ], + }, + }, + default: 'Tag IDs, multiple ids can be set separated by comma.', + }, +/* -------------------------------------------------------------------------- */ +/* contactTag:getAll */ +/* -------------------------------------------------------------------------- */ + { + displayName: 'Contact ID', + name: 'contactId', + type: 'string', + required: true, + displayOptions: { + show: { + operation: [ + 'getAll', + ], + resource: [ + 'contactTag', + ], + }, + }, + default: '', + }, + { + displayName: 'Return All', + name: 'returnAll', + type: 'boolean', + displayOptions: { + show: { + operation: [ + 'getAll', + ], + resource: [ + 'contactTag', + ], + }, + }, + default: false, + description: 'If all results should be returned or only up to a given limit.', + }, + { + displayName: 'Limit', + name: 'limit', + type: 'number', + displayOptions: { + show: { + operation: [ + 'getAll', + ], + resource: [ + 'contactTag', + ], + returnAll: [ + false, + ], + }, + }, + typeOptions: { + minValue: 1, + maxValue: 200, + }, + default: 100, + description: 'How many results to return.', + }, +] as INodeProperties[]; diff --git a/packages/nodes-base/nodes/Infusionsoft/EcommerceOrderDescripion.ts b/packages/nodes-base/nodes/Infusionsoft/EcommerceOrderDescripion.ts new file mode 100644 index 000000000..df601111f --- /dev/null +++ b/packages/nodes-base/nodes/Infusionsoft/EcommerceOrderDescripion.ts @@ -0,0 +1,486 @@ +import { + INodeProperties, + } from "n8n-workflow"; + +export const ecommerceOrderOperations = [ + { + displayName: 'Operation', + name: 'operation', + type: 'options', + displayOptions: { + show: { + resource: [ + 'ecommerceOrder', + ], + }, + }, + options: [ + { + name: 'Create', + value: 'create', + description: 'Create an ecommerce order', + }, + { + name: 'Get', + value: 'get', + description: 'Get an ecommerce order', + }, + { + name: 'Delete', + value: 'delete', + description: 'Delete an ecommerce order', + }, + { + name: 'Get All', + value: 'getAll', + description: 'Retrieve all ecommerce orders', + }, + ], + default: 'create', + description: 'The operation to perform.', + }, +] as INodeProperties[]; + +export const ecommerceOrderFields = [ + +/* -------------------------------------------------------------------------- */ +/* ecommerceOrder:create */ +/* -------------------------------------------------------------------------- */ + { + displayName: 'Contact ID', + name: 'contactId', + type: 'string', + required: true, + displayOptions: { + show: { + operation: [ + 'create', + ], + resource: [ + 'ecommerceOrder', + ], + }, + }, + default: '', + }, + { + displayName: 'Order Date', + name: 'orderDate', + type: 'dateTime', + required: true, + displayOptions: { + show: { + operation: [ + 'create', + ], + resource: [ + 'ecommerceOrder', + ], + }, + }, + default: '', + }, + { + displayName: 'Order Title', + name: 'orderTitle', + type: 'dateTime', + required: true, + displayOptions: { + show: { + operation: [ + 'create', + ], + resource: [ + 'ecommerceOrder', + ], + }, + }, + default: '', + }, + { + displayName: 'Order Type', + name: 'orderType', + type: 'options', + options: [ + { + name: 'Offline', + value: 'offline', + }, + { + name: 'Online', + value: 'online', + }, + ], + required: true, + displayOptions: { + show: { + operation: [ + 'create', + ], + resource: [ + 'ecommerceOrder', + ], + }, + }, + default: '', + }, + { + displayName: 'Additional Fields', + name: 'additionalFields', + type: 'collection', + placeholder: 'Add Field', + default: {}, + displayOptions: { + show: { + operation: [ + 'create', + ], + resource: [ + 'ecommerceOrder', + ], + }, + }, + options: [ + { + displayName: 'Lead Affiliate ID', + name: 'leadAffiliateId', + type: 'number', + typeOptions: { + minValue: 0, + }, + default: 0, + }, + { + displayName: 'Promo Codes', + name: 'promoCodes', + type: 'string', + default: '', + description: `Uses multiple strings separated by comma as promo codes.
+ The corresponding discount will be applied to the order.` + }, + { + displayName: 'Sales Affiliate ID', + name: 'salesAffiliateId', + type: 'number', + typeOptions: { + minValue: 0, + }, + default: 0, + }, + ], + }, + { + displayName: 'Shipping Address', + name: 'addressUi', + type: 'fixedCollection', + typeOptions: { + multipleValues: false, + }, + default: '', + placeholder: 'Add Address', + displayOptions: { + show: { + resource: [ + 'ecommerceOrder', + ], + operation: [ + 'create', + ], + }, + }, + options: [ + { + name: 'addressValues', + displayName: 'Address', + values: [ + { + displayName: 'Company', + name: 'company', + type: 'string', + default: '', + }, + { + displayName: 'Country Code', + name: 'countryCode', + type: 'options', + typeOptions: { + loadOptionsMethod: 'getCountries', + }, + default: '', + }, + { + displayName: 'First Name', + name: 'firstName', + type: 'string', + default: '', + }, + { + displayName: 'Middle Name', + name: 'middleName', + type: 'string', + default: '', + }, + { + displayName: 'Last Name', + name: 'lastName', + type: 'string', + default: '', + }, + { + displayName: 'Line 1', + name: 'line1', + type: 'string', + default: '', + }, + { + displayName: 'Line 2', + name: 'line2', + type: 'string', + default: '', + }, + { + displayName: 'Locality', + name: 'locality', + type: 'string', + default: '', + }, + { + displayName: 'Region', + name: 'region', + type: 'string', + default: '', + }, + { + displayName: 'Zip Code', + name: 'zipCode', + type: 'string', + default: '', + }, + { + displayName: 'Zip Four', + name: 'zipFour', + type: 'string', + default: '', + }, + { + displayName: 'Phone', + name: 'phone', + type: 'string', + default: '', + }, + ], + }, + ], + }, + { + displayName: 'Order Items', + name: 'orderItemsUi', + type: 'fixedCollection', + placeholder: 'Add Order Item', + typeOptions: { + multipleValues: true, + }, + default: {}, + displayOptions: { + show: { + resource: [ + 'ecommerceOrder', + ], + operation: [ + 'create', + ], + }, + }, + options: [ + { + name: 'orderItemsValues', + displayName: 'Order Item', + values: [ + { + displayName: 'Description', + name: 'description', + type: 'string', + default: '', + }, + { + displayName: 'Price', + name: 'price', + type: 'number', + typeOptions: { + minValue: 0, + }, + default: 0, + description: `Overridable price of the product, if not specified,
+ the default will be used.`, + }, + { + displayName: 'Product ID', + name: 'product ID', + type: 'number', + typeOptions: { + minValue: 0, + }, + default: 0, + }, + { + displayName: 'Quantity', + name: 'quantity', + type: 'number', + typeOptions: { + minValue: 1, + }, + default: 1, + }, + ], + }, + ], + }, +/* -------------------------------------------------------------------------- */ +/* ecommerceOrder:delete */ +/* -------------------------------------------------------------------------- */ + { + displayName: 'Order ID', + name: 'orderId', + type: 'string', + required: true, + displayOptions: { + show: { + operation: [ + 'delete', + ], + resource: [ + 'ecommerceOrder', + ], + }, + }, + default: '', + }, +/* -------------------------------------------------------------------------- */ +/* ecommerceOrder:get */ +/* -------------------------------------------------------------------------- */ + { + displayName: 'Order ID', + name: 'orderId', + type: 'string', + required: true, + displayOptions: { + show: { + operation: [ + 'get', + ], + resource: [ + 'ecommerceOrder', + ], + }, + }, + default: '', + }, +/* -------------------------------------------------------------------------- */ +/* ecommerceOrder:getAll */ +/* -------------------------------------------------------------------------- */ + { + displayName: 'Return All', + name: 'returnAll', + type: 'boolean', + displayOptions: { + show: { + operation: [ + 'getAll', + ], + resource: [ + 'ecommerceOrder', + ], + }, + }, + default: false, + description: 'If all results should be returned or only up to a given limit.', + }, + { + displayName: 'Limit', + name: 'limit', + type: 'number', + displayOptions: { + show: { + operation: [ + 'getAll', + ], + resource: [ + 'ecommerceOrder', + ], + returnAll: [ + false, + ], + }, + }, + typeOptions: { + minValue: 1, + maxValue: 200, + }, + default: 100, + description: 'How many results to return.', + }, + { + displayName: 'Options', + name: 'options', + type: 'collection', + placeholder: 'Add Option', + default: {}, + displayOptions: { + show: { + operation: [ + 'getAll', + ], + resource: [ + 'ecommerceOrder', + ], + }, + }, + options: [ + { + displayName: 'Since', + name: 'since', + type: 'dateTime', + default: '', + description: 'Date to start searching from', + }, + { + displayName: 'Until', + name: 'until', + type: 'dateTime', + default: '', + description: 'Date to search to', + }, + { + displayName: 'Paid', + name: 'paid', + type: 'boolean', + default: false, + }, + { + displayName: 'Order', + name: 'order', + type: 'string', + default: '', + description: 'Attribute to order items by', + }, + { + displayName: 'Contact ID', + name: 'contactId', + type: 'number', + typeOptions: { + minValue: 0, + }, + default: 0, + }, + { + displayName: 'Product ID', + name: 'productId', + type: 'number', + typeOptions: { + minValue: 0, + }, + default: 0, + }, + ], + }, +] as INodeProperties[]; diff --git a/packages/nodes-base/nodes/Infusionsoft/EcommerceOrderInterface.ts b/packages/nodes-base/nodes/Infusionsoft/EcommerceOrderInterface.ts new file mode 100644 index 000000000..ba7eb8264 --- /dev/null +++ b/packages/nodes-base/nodes/Infusionsoft/EcommerceOrderInterface.ts @@ -0,0 +1,35 @@ + + +export interface IItem { + description?: string; + price?: number; + product_id?: number; + quantity?: number; +} + +export interface IShippingAddress { + company?: string; + country_code?: string; + first_name?: string; + last_name?: string; + line1?: string; + line2?: string; + locality?: string; + middle_name?: string; + postal_code?: string; + region?: string; + zip_code?: string; + zip_four?: string; +} + +export interface IEcommerceOrder { + contact_id: number; + lead_affiliate_id?: string; + order_date: string; + order_items?: IItem[]; + order_title: string; + order_type?: string; + promo_codes?: string[]; + sales_affiliate_id?: number; + shipping_address?: IShippingAddress; +} diff --git a/packages/nodes-base/nodes/Infusionsoft/EcommerceProductDescription.ts b/packages/nodes-base/nodes/Infusionsoft/EcommerceProductDescription.ts new file mode 100644 index 000000000..f0b3ef364 --- /dev/null +++ b/packages/nodes-base/nodes/Infusionsoft/EcommerceProductDescription.ts @@ -0,0 +1,236 @@ +import { + INodeProperties, + } from "n8n-workflow"; + +export const ecommerceProductOperations = [ + { + displayName: 'Operation', + name: 'operation', + type: 'options', + displayOptions: { + show: { + resource: [ + 'ecommerceProduct', + ], + }, + }, + options: [ + { + name: 'Create', + value: 'create', + description: 'Create an ecommerce product', + }, + { + name: 'Delete', + value: 'delete', + description: 'Delete an ecommerce product', + }, + { + name: 'Get', + value: 'get', + description: 'Get an ecommerce product', + }, + { + name: 'Get All', + value: 'getAll', + description: 'Retrieve all ecommerce product', + }, + ], + default: 'create', + description: 'The operation to perform.', + }, +] as INodeProperties[]; + +export const ecommerceProductFields = [ + +/* -------------------------------------------------------------------------- */ +/* ecommerceProduct:create */ +/* -------------------------------------------------------------------------- */ + { + displayName: 'Product Name', + name: 'productName', + type: 'string', + required: true, + displayOptions: { + show: { + operation: [ + 'create', + ], + resource: [ + 'ecommerceProduct', + ], + }, + }, + default: '', + }, + { + displayName: 'Additional Fields', + name: 'additionalFields', + type: 'collection', + placeholder: 'Add Field', + default: {}, + displayOptions: { + show: { + operation: [ + 'create', + ], + resource: [ + 'ecommerceProduct', + ], + }, + }, + options: [ + { + displayName: 'Active', + name: 'active', + type: 'boolean', + default: false, + }, + { + displayName: 'Product Description', + name: 'productDesc', + typeOptions: { + alwaysOpenEditWindow: true, + }, + type: 'string', + default: '', + }, + { + displayName: 'Product Price', + name: 'productPrice', + type: 'number', + typeOptions: { + minValue: 0, + }, + default: 0, + }, + { + displayName: 'Product Short Desc', + name: 'productShortDesc', + type: 'string', + default: '', + }, + { + displayName: 'SKU', + name: 'sku', + type: 'string', + default: '', + }, + { + displayName: 'Subscription Only', + name: 'subscriptionOnly', + type: 'boolean', + default: false, + }, + ], + }, +/* -------------------------------------------------------------------------- */ +/* ecommerceProduct:delete */ +/* -------------------------------------------------------------------------- */ + { + displayName: 'Product ID', + name: 'productId', + type: 'string', + required: true, + displayOptions: { + show: { + operation: [ + 'delete', + ], + resource: [ + 'ecommerceProduct', + ], + }, + }, + default: '', + }, +/* -------------------------------------------------------------------------- */ +/* ecommerceProduct:get */ +/* -------------------------------------------------------------------------- */ + { + displayName: 'Product ID', + name: 'productId', + type: 'string', + required: true, + displayOptions: { + show: { + operation: [ + 'get', + ], + resource: [ + 'ecommerceProduct', + ], + }, + }, + default: '', + }, +/* -------------------------------------------------------------------------- */ +/* ecommerceProduct:getAll */ +/* -------------------------------------------------------------------------- */ + { + displayName: 'Return All', + name: 'returnAll', + type: 'boolean', + displayOptions: { + show: { + operation: [ + 'getAll', + ], + resource: [ + 'ecommerceProduct', + ], + }, + }, + default: false, + description: 'If all results should be returned or only up to a given limit.', + }, + { + displayName: 'Limit', + name: 'limit', + type: 'number', + displayOptions: { + show: { + operation: [ + 'getAll', + ], + resource: [ + 'ecommerceProduct', + ], + returnAll: [ + false, + ], + }, + }, + typeOptions: { + minValue: 1, + maxValue: 200, + }, + default: 100, + description: 'How many results to return.', + }, + { + displayName: 'Filters', + name: 'filters', + type: 'collection', + placeholder: 'Add Field', + default: {}, + displayOptions: { + show: { + operation: [ + 'getAll', + ], + resource: [ + 'ecommerceProduct', + ], + }, + }, + options: [ + { + displayName: 'Active', + name: 'active', + type: 'boolean', + default: false, + }, + ], + }, +] as INodeProperties[]; diff --git a/packages/nodes-base/nodes/Infusionsoft/EcommerceProductInterface.ts b/packages/nodes-base/nodes/Infusionsoft/EcommerceProductInterface.ts new file mode 100644 index 000000000..23c2fbb49 --- /dev/null +++ b/packages/nodes-base/nodes/Infusionsoft/EcommerceProductInterface.ts @@ -0,0 +1,10 @@ + +export interface IEcommerceProduct { + active?: string; + product_name?: string; + product_desc?: string; + product_price?: number; + product_short_desc?: string; + sku?: string; + subscription_only?: boolean; +} diff --git a/packages/nodes-base/nodes/Infusionsoft/EmaiIInterface.ts b/packages/nodes-base/nodes/Infusionsoft/EmaiIInterface.ts new file mode 100644 index 000000000..5a8fdf512 --- /dev/null +++ b/packages/nodes-base/nodes/Infusionsoft/EmaiIInterface.ts @@ -0,0 +1,15 @@ +export interface IAttachment { + file_data?: string; + file_name?: string; +} + + +export interface IEmail { + address_field?: string; + attachments?: IAttachment[]; + contacts: number[]; + html_content?: string; + plain_content?: string; + subject?: string; + user_id: number; +} diff --git a/packages/nodes-base/nodes/Infusionsoft/EmailDescription.ts b/packages/nodes-base/nodes/Infusionsoft/EmailDescription.ts new file mode 100644 index 000000000..f7875c13f --- /dev/null +++ b/packages/nodes-base/nodes/Infusionsoft/EmailDescription.ts @@ -0,0 +1,464 @@ +import { + INodeProperties, + } from "n8n-workflow"; + +export const emailOperations = [ + { + displayName: 'Operation', + name: 'operation', + type: 'options', + displayOptions: { + show: { + resource: [ + 'email', + ], + }, + }, + options: [ + { + name: 'Create Record', + value: 'createRecord', + description: 'Create a record of an email sent to a contact', + }, + { + name: 'Get All', + value: 'getAll', + description: 'Retrieve all sent emails', + }, + { + name: 'Send', + value: 'send', + description: 'Send Email', + }, + ], + default: 'createRecord', + description: 'The operation to perform.', + }, +] as INodeProperties[]; + +export const emailFields = [ + +/* -------------------------------------------------------------------------- */ +/* email:createRecord */ +/* -------------------------------------------------------------------------- */ + { + displayName: 'Sent To Address', + name: 'sentToAddress', + type: 'string', + required: true, + displayOptions: { + show: { + operation: [ + 'createRecord', + ], + resource: [ + 'email', + ], + }, + }, + default: '', + }, + { + displayName: 'Sent From Address', + name: 'sentFromAddress', + type: 'string', + default: '', + required: true, + displayOptions: { + show: { + operation: [ + 'createRecord', + ], + resource: [ + 'email', + ], + }, + }, + }, + { + displayName: 'Additional Fields', + name: 'additionalFields', + type: 'collection', + placeholder: 'Add Field', + default: {}, + displayOptions: { + show: { + operation: [ + 'createRecord', + ], + resource: [ + 'email', + ], + }, + }, + options: [ + { + displayName: 'Clicked Date', + name: 'clickedDate', + type: 'dateTime', + default: '', + }, + { + displayName: 'Contact ID', + name: 'contactId', + type: 'number', + typeOptions: { + minValue: 0, + }, + default: 0, + }, + { + displayName: 'Headers', + name: 'headers', + type: 'string', + default: '', + }, + { + displayName: 'HTML content', + name: 'htmlContent', + type: 'string', + default: '', + description: 'Base64 encoded HTML', + }, + { + displayName: 'Opened Date', + name: 'openedDate', + type: 'dateTime', + default: '', + }, + { + displayName: 'Original Provider', + name: 'originalProvider', + type: 'options', + options: [ + { + name: 'Unknown', + value: 'UNKNOWN', + }, + { + name: 'Infusionsoft', + value: 'INFUSIONSOFT', + }, + { + name: 'Microsoft', + value: 'MICROSOFT', + }, + { + name: 'Google', + value: 'GOOGLE', + }, + ], + default: 'UNKNOWN', + description: 'Provider that sent the email case insensitive, must be in list', + }, + { + displayName: 'Original Provider ID', + name: 'originalProviderId', + type: 'string', + default: '', + description: `Provider id that sent the email, must be unique when combined with provider.
+ If omitted a UUID without dashes is autogenerated for the record.` + }, + { + displayName: 'Plain Content', + name: 'plainContent', + type: 'string', + default: '', + description: 'Base64 encoded text', + }, + { + displayName: 'Provider Source ID', + name: 'providerSourceId', + type: 'string', + default: 'The email address of the synced email account that generated this message.', + }, + { + displayName: 'Received Date', + name: 'receivedDate', + type: 'dateTime', + default: '', + }, + { + displayName: 'Sent Date', + name: 'sentDate', + type: 'dateTime', + default: '', + }, + { + displayName: 'Sent From Reply Address', + name: 'sentFromReplyAddress', + type: 'string', + default: '', + }, + { + displayName: 'Sent To Bcc Addresses', + name: 'sentToBccAddresses', + type: 'string', + default: '', + }, + { + displayName: 'Sent To CC Addresses', + name: 'sentToCCAddresses', + type: 'string', + default: '', + }, + { + displayName: 'Subject', + name: 'subject', + type: 'string', + default: '', + }, + ], + }, +/* -------------------------------------------------------------------------- */ +/* email:getAll */ +/* -------------------------------------------------------------------------- */ + { + displayName: 'Return All', + name: 'returnAll', + type: 'boolean', + displayOptions: { + show: { + operation: [ + 'getAll', + ], + resource: [ + 'email', + ], + }, + }, + default: false, + description: 'If all results should be returned or only up to a given limit.', + }, + { + displayName: 'Limit', + name: 'limit', + type: 'number', + displayOptions: { + show: { + operation: [ + 'getAll', + ], + resource: [ + 'email', + ], + returnAll: [ + false, + ], + }, + }, + typeOptions: { + minValue: 1, + maxValue: 200, + }, + default: 100, + description: 'How many results to return.', + }, + { + displayName: 'Filters', + name: 'filters', + type: 'collection', + placeholder: 'Add Filter', + default: {}, + displayOptions: { + show: { + operation: [ + 'getAll', + ], + resource: [ + 'email', + ], + }, + }, + options: [ + { + displayName: 'Contact ID', + name: 'contactId', + type: 'number', + typeOptions: { + minValue: 0, + }, + default: 0, + }, + { + displayName: 'Email', + name: 'email', + type: 'string', + default: '', + }, + { + displayName: 'Since Sent Date', + name: 'sinceSentDate', + type: 'dateTime', + default: '', + description: 'Emails sent since the provided date, must be present if untilDate is provided', + + }, + { + displayName: 'Until Sent Date', + name: 'untilSentDate', + type: 'dateTime', + default: '', + description: 'Email sent until the provided date', + }, + ], + }, +/* -------------------------------------------------------------------------- */ +/* email:send */ +/* -------------------------------------------------------------------------- */ + { + displayName: 'User ID', + name: 'userId', + type: 'options', + required: true, + typeOptions: { + loadOptionsMethod: 'getUsers', + }, + displayOptions: { + show: { + operation: [ + 'send', + ], + resource: [ + 'email', + ], + }, + }, + default: '', + description: 'The infusionsoft user to send the email on behalf of', + }, + { + displayName: 'Contact IDs', + name: 'contactIds', + type: 'string', + displayOptions: { + show: { + operation: [ + 'send', + ], + resource: [ + 'email', + ], + }, + }, + default: '', + description: 'Contact Ids to receive the email. Multiple can be added seperated by comma', + }, + { + displayName: 'Subject', + name: 'subject', + type: 'string', + displayOptions: { + show: { + operation: [ + 'send', + ], + resource: [ + 'email', + ], + }, + }, + default: '', + description: 'The subject line of the email', + }, + { + displayName: 'Additional Fields', + name: 'additionalFields', + type: 'collection', + placeholder: 'Add Field', + default: {}, + displayOptions: { + show: { + operation: [ + 'send', + ], + resource: [ + 'email', + ], + }, + }, + options: [ + { + displayName: 'Address field', + name: 'addressField', + type: 'string', + default: '', + description: `Email field of each Contact record to address the email to, such as
+ 'EmailAddress1', 'EmailAddress2', 'EmailAddress3', defaulting to the contact's primary email`, + }, + { + displayName: 'HTML Content', + name: 'htmlContent', + type: 'string', + default: '', + description: 'The HTML-formatted content of the email, encoded in Base64', + }, + { + displayName: 'Plain Content', + name: 'plainContent', + type: 'string', + default: '', + description: 'The plain-text content of the email, encoded in Base64', + }, + ], + }, + { + displayName: 'Attachments', + name: 'attachmentsUi', + placeholder: 'Add Attachments', + type: 'fixedCollection', + typeOptions: { + multipleValues: true, + }, + displayOptions: { + show: { + operation: [ + 'send', + ], + resource: [ + 'email', + ], + }, + }, + options: [ + { + name: 'attachmentsValues', + displayName: 'Attachments Values', + values: [ + { + displayName: 'File Data', + name: 'fileData', + type: 'string', + typeOptions: { + alwaysOpenEditWindow: true, + }, + default: '', + description: 'The content of the attachment, encoded in Base64', + }, + { + displayName: 'File Name', + name: 'fileName', + type: 'string', + default: '', + description: 'The filename of the attached file, including extension', + }, + ], + }, + { + name: 'attachmentsBinary', + displayName: 'Attachments Binary', + values: [ + { + displayName: 'Property', + name: 'property', + type: 'string', + default: '', + description: 'Name of the binary properties which contain data which should be added to email as attachment', + }, + ], + }, + ], + default: '', + description: 'Attachments to be sent with each copy of the email, maximum of 10 with size of 1MB each', + }, +] as INodeProperties[]; diff --git a/packages/nodes-base/nodes/Infusionsoft/FileDescription.ts b/packages/nodes-base/nodes/Infusionsoft/FileDescription.ts new file mode 100644 index 000000000..9d90f4411 --- /dev/null +++ b/packages/nodes-base/nodes/Infusionsoft/FileDescription.ts @@ -0,0 +1,404 @@ +import { + INodeProperties, + } from "n8n-workflow"; + +export const fileOperations = [ + { + displayName: 'Operation', + name: 'operation', + type: 'options', + displayOptions: { + show: { + resource: [ + 'file', + ], + }, + }, + options: [ + { + name: 'Delete', + value: 'delete', + description: 'Delete a file', + }, + { + name: 'Get All', + value: 'getAll', + description: 'Retrieve all files', + }, + { + name: 'Upload', + value: 'upload', + description: 'Upload a file', + }, + ], + default: 'delete', + description: 'The operation to perform.', + }, +] as INodeProperties[]; + +export const fileFields = [ +/* -------------------------------------------------------------------------- */ +/* file:upload */ +/* -------------------------------------------------------------------------- */ + { + displayName: 'Binary Data', + name: 'binaryData', + type: 'boolean', + default: false, + displayOptions: { + show: { + operation: [ + 'upload' + ], + resource: [ + 'file', + ], + }, + }, + description: 'If the data to upload should be taken from binary field.', + }, + { + displayName: 'Binary Property', + name: 'binaryPropertyName', + type: 'string', + default: 'data', + required: true, + displayOptions: { + show: { + operation: [ + 'upload' + ], + resource: [ + 'file', + ], + binaryData: [ + true, + ], + }, + }, + description: 'Name of the binary property which contains
the data for the file to be uploaded.', + }, + { + displayName: 'File Association', + name: 'fileAssociation', + type: 'options', + options: [ + { + name: 'Company', + value: 'company', + }, + { + name: 'Contact', + value: 'contact', + }, + { + name: 'User', + value: 'user', + }, + ], + required: true, + displayOptions: { + show: { + operation: [ + 'upload', + ], + resource: [ + 'file', + ], + }, + }, + default: '', + }, + { + displayName: 'Contact ID', + name: 'contactId', + type: 'string', + required: true, + displayOptions: { + show: { + operation: [ + 'upload', + ], + resource: [ + 'file', + ], + fileAssociation: [ + 'contact', + ], + }, + }, + default: '', + }, + { + displayName: 'File Name', + name: 'fileName', + type: 'string', + required: true, + displayOptions: { + show: { + operation: [ + 'upload', + ], + resource: [ + 'file', + ], + binaryData: [ + false, + ], + }, + }, + default: '', + description: 'The filename of the attached file, including extension', + }, + { + displayName: 'File Data', + name: 'fileData', + type: 'string', + typeOptions: { + alwaysOpenEditWindow: true, + }, + required: true, + displayOptions: { + show: { + operation: [ + 'upload', + ], + resource: [ + 'file', + ], + binaryData: [ + false, + ], + }, + }, + default: '', + description: 'The content of the attachment, encoded in Base64', + }, + { + displayName: 'Is Public', + name: 'isPublic', + type: 'boolean', + default: false, + displayOptions: { + show: { + operation: [ + 'upload' + ], + resource: [ + 'file', + ], + }, + }, + }, +/* -------------------------------------------------------------------------- */ +/* file:delete */ +/* -------------------------------------------------------------------------- */ + { + displayName: 'File ID', + name: 'fileId', + type: 'string', + required: true, + displayOptions: { + show: { + operation: [ + 'delete', + ], + resource: [ + 'file', + ], + }, + }, + default: '', + }, +/* -------------------------------------------------------------------------- */ +/* file:getAll */ +/* -------------------------------------------------------------------------- */ + { + displayName: 'Return All', + name: 'returnAll', + type: 'boolean', + displayOptions: { + show: { + operation: [ + 'getAll', + ], + resource: [ + 'file', + ], + }, + }, + default: false, + description: 'If all results should be returned or only up to a given limit.', + }, + { + displayName: 'Limit', + name: 'limit', + type: 'number', + displayOptions: { + show: { + operation: [ + 'getAll', + ], + resource: [ + 'file', + ], + returnAll: [ + false, + ], + }, + }, + typeOptions: { + minValue: 1, + maxValue: 200, + }, + default: 100, + description: 'How many results to return.', + }, + { + displayName: 'Filters', + name: 'filters', + type: 'collection', + placeholder: 'Add Field', + default: {}, + displayOptions: { + show: { + operation: [ + 'getAll', + ], + resource: [ + 'file', + ], + }, + }, + options: [ + { + displayName: 'Contact ID', + name: 'contactId', + type: 'number', + typeOptions: { + minValue: 0 + }, + default: 0, + description: 'Filter based on Contact Id, if user has permission to see Contact files.', + }, + { + displayName: 'Name', + name: 'name', + type: 'string', + default: '', + description: `Filter files based on name, with '*' preceding or following to indicate LIKE queries.`, + }, + { + displayName: 'Permission', + name: 'permission', + type: 'options', + options: [ + { + name: 'User', + value: 'user', + }, + { + name: 'Company', + value: 'company', + }, + { + name: 'Both', + value: 'both', + }, + ], + default: 'both', + description: 'Filter based on the permission of files', + }, + { + displayName: 'Type', + name: 'type', + type: 'options', + options: [ + { + name: 'Application', + value: 'application', + }, + { + name: 'Image', + value: 'image', + }, + { + name: 'Fax', + value: 'fax', + }, + { + name: 'Attachment', + value: 'attachment', + }, + { + name: 'Ticket', + value: 'ticket', + }, + { + name: 'Contact', + value: 'contact', + }, + { + name: 'Digital Product', + value: 'digitalProduct', + }, + { + name: 'Import', + value: 'import', + }, + { + name: 'Hidden', + value: 'hidden', + }, + { + name: 'Webform', + value: 'webform', + }, + { + name: 'Style Cart', + value: 'styleCart', + }, + { + name: 'Re Sampled Image', + value: 'reSampledImage', + }, + { + name: 'Template Thumnail', + value: 'templateThumnail', + }, + { + name: 'Funnel', + value: 'funnel', + }, + { + name: 'Logo Thumnail', + value: 'logoThumnail', + }, + ], + default: '', + description: 'Filter based on the type of file.', + }, + { + displayName: 'Viewable', + name: 'viewable', + type: 'options', + options: [ + { + name: 'Public', + value: 'public', + }, + { + name: 'Private', + value: 'private', + }, + { + name: 'Both', + value: 'both', + }, + ], + default: 'both', + description: 'Include public or private files in response', + }, + ], + }, +] as INodeProperties[]; diff --git a/packages/nodes-base/nodes/Infusionsoft/FileInterface.ts b/packages/nodes-base/nodes/Infusionsoft/FileInterface.ts new file mode 100644 index 000000000..e924826d2 --- /dev/null +++ b/packages/nodes-base/nodes/Infusionsoft/FileInterface.ts @@ -0,0 +1,8 @@ + +export interface IFile { + file_name?: string; + file_data?: string; + contact_id?: number; + is_public?: boolean; + file_association?: string; +} diff --git a/packages/nodes-base/nodes/Infusionsoft/GenericFunctions.ts b/packages/nodes-base/nodes/Infusionsoft/GenericFunctions.ts new file mode 100644 index 000000000..4d942ccd6 --- /dev/null +++ b/packages/nodes-base/nodes/Infusionsoft/GenericFunctions.ts @@ -0,0 +1,82 @@ +import { + OptionsWithUri, + } from 'request'; + +import { + IExecuteFunctions, + IHookFunctions, + ILoadOptionsFunctions, + IWebhookFunctions, +} from 'n8n-core'; + +import { + IDataObject +} from 'n8n-workflow'; + +import { + snakeCase, + } from 'change-case'; + +export async function infusionsoftApiRequest(this: IWebhookFunctions | IHookFunctions | IExecuteFunctions | ILoadOptionsFunctions, method: string, resource: string, body: any = {}, qs: IDataObject = {}, uri?: string, headers: IDataObject = {}, option: IDataObject = {}): Promise { // tslint:disable-line:no-any + let options: OptionsWithUri = { + headers: { + 'Content-Type': 'application/json', + }, + method, + body, + qs, + uri: uri || `https://api.infusionsoft.com/crm/rest/v1${resource}`, + json: true + }; + try { + options = Object.assign({}, options, option); + if (Object.keys(headers).length !== 0) { + options.headers = Object.assign({}, options.headers, headers); + } + if (Object.keys(body).length === 0) { + delete options.body; + } + //@ts-ignore + return await this.helpers.requestOAuth.call(this, 'infusionsoftOAuth2Api', options); + } catch (error) { + if (error.response && error.response.body && error.response.body.message) { + // Try to return the error prettier + throw new Error(`Infusionsoft error response [${error.statusCode}]: ${error.response.body.message}`); + } + throw error; + } +} + +export async function infusionsoftApiRequestAllItems(this: IHookFunctions| IExecuteFunctions | ILoadOptionsFunctions, propertyName: string, method: string, endpoint: string, body: any = {}, query: IDataObject = {}): Promise { // tslint:disable-line:no-any + + const returnData: IDataObject[] = []; + + let responseData; + let uri: string | undefined; + query.limit = 50; + + do { + responseData = await infusionsoftApiRequest.call(this, method, endpoint, body, query, uri); + uri = responseData.next; + returnData.push.apply(returnData, responseData[propertyName]); + } while ( + returnData.length < responseData.count + ); + + return returnData; +} + +export function keysToSnakeCase(elements: IDataObject[] | IDataObject) : IDataObject[] { + if (!Array.isArray(elements)) { + elements = [elements]; + } + for (const element of elements) { + for (const key of Object.keys(element)) { + if (key !== snakeCase(key)) { + element[snakeCase(key)] = element[key]; + delete element[key]; + } + } + } + return elements; +} diff --git a/packages/nodes-base/nodes/Infusionsoft/Infusionsoft.node.ts b/packages/nodes-base/nodes/Infusionsoft/Infusionsoft.node.ts new file mode 100644 index 000000000..a2740c0f4 --- /dev/null +++ b/packages/nodes-base/nodes/Infusionsoft/Infusionsoft.node.ts @@ -0,0 +1,811 @@ +import { + IExecuteFunctions, +} from 'n8n-core'; + +import { + IDataObject, + INodeExecutionData, + INodeTypeDescription, + INodeType, + ILoadOptionsFunctions, + INodePropertyOptions, + IBinaryKeyData, +} from 'n8n-workflow'; + +import { + infusionsoftApiRequest, + infusionsoftApiRequestAllItems, + keysToSnakeCase, +} from './GenericFunctions'; + +import { + contactOperations, + contactFields, +} from './ContactDescription'; + +import { + contactNoteOperations, + contactNoteFields, +} from './ContactNoteDescription'; + +import { + contactTagOperations, + contactTagFields, +} from './ContactTagDescription'; + +import { + ecommerceOrderOperations, + ecommerceOrderFields, +} from './EcommerceOrderDescripion'; + +import { + ecommerceProductOperations, + ecommerceProductFields, +} from './EcommerceProductDescription'; + +import { + emailOperations, + emailFields, +} from './EmailDescription'; + +import { + fileOperations, + fileFields, +} from './FileDescription'; + +import { + companyOperations, + companyFields, + } from './CompanyDescription'; + +import { + IContact, + IAddress, + IFax, + IEmailContact, + ISocialAccount, + IPhone, +} from './ConctactInterface'; + +import { + IEmail, + IAttachment, +} from './EmaiIInterface'; + +import { + INote, +} from './ContactNoteInterface'; + +import { + IEcommerceOrder, + IItem, + IShippingAddress, +} from './EcommerceOrderInterface'; + +import { + IEcommerceProduct, +} from './EcommerceProductInterface'; + +import { + IFile, +} from './FileInterface'; + +import { + ICompany, +} from './CompanyInterface'; + +import { + pascalCase, + titleCase, +} from 'change-case'; + +import * as moment from 'moment-timezone'; + +export class Infusionsoft implements INodeType { + description: INodeTypeDescription = { + displayName: 'Infusionsoft', + name: ' infusionsoft', + icon: 'file:infusionsoft.png', + group: ['input'], + version: 1, + subtitle: '={{$parameter["operation"] + ": " + $parameter["resource"]}}', + description: 'Consume Infusionsoft API.', + defaults: { + name: 'Infusionsoft', + color: '#79af53', + }, + inputs: ['main'], + outputs: ['main'], + credentials: [ + { + name: 'infusionsoftOAuth2Api', + required: true, + }, + ], + properties: [ + { + displayName: 'Resource', + name: 'resource', + type: 'options', + options: [ + { + name: 'Company', + value: 'company', + }, + { + name: 'Contact', + value: 'contact', + }, + { + name: 'Contact Note', + value: 'contactNote', + }, + { + name: 'Contact Tag', + value: 'contactTag', + }, + { + name: 'Ecommerce Order', + value: 'ecommerceOrder', + }, + { + name: 'Ecommerce Product', + value: 'ecommerceProduct', + }, + { + name: 'Email', + value: 'email', + }, + { + name: 'File', + value: 'file', + }, + ], + default: 'company', + description: 'The resource to operate on.', + }, + // COMPANY + ...companyOperations, + ...companyFields, + // CONTACT + ...contactOperations, + ...contactFields, + // CONTACT NOTE + ...contactNoteOperations, + ...contactNoteFields, + // CONTACT TAG + ...contactTagOperations, + ...contactTagFields, + // ECOMMERCE ORDER + ...ecommerceOrderOperations, + ...ecommerceOrderFields, + // ECOMMERCE PRODUCT + ...ecommerceProductOperations, + ...ecommerceProductFields, + // EMAIL + ...emailOperations, + ...emailFields, + // FILE + ...fileOperations, + ...fileFields, + ], + }; + + methods = { + loadOptions: { + // Get all the tags to display them to user so that he can + // select them easily + async getTags(this: ILoadOptionsFunctions): Promise { + const returnData: INodePropertyOptions[] = []; + const tags = await infusionsoftApiRequestAllItems.call(this, 'tags', 'GET', '/tags'); + for (const tag of tags) { + const tagName = tag.name; + const tagId = tag.id; + returnData.push({ + name: tagName as string, + value: tagId as string, + }); + } + return returnData; + }, + // Get all the users to display them to user so that he can + // select them easily + async getUsers(this: ILoadOptionsFunctions): Promise { + const returnData: INodePropertyOptions[] = []; + const users = await infusionsoftApiRequestAllItems.call(this, 'users', 'GET', '/users'); + for (const user of users) { + const userName = user.given_name; + const userId = user.id; + returnData.push({ + name: userName as string, + value: userId as string, + }); + } + return returnData; + }, + // Get all the countries to display them to user so that he can + // select them easily + async getCountries(this: ILoadOptionsFunctions): Promise { + const returnData: INodePropertyOptions[] = []; + const { countries } = await infusionsoftApiRequest.call(this, 'GET', '/locales/countries'); + for (const key of Object.keys(countries)) { + const countryName = countries[key]; + const countryId = key; + returnData.push({ + name: countryName as string, + value: countryId as string, + }); + } + return returnData; + }, + // Get all the provinces to display them to user so that he can + // select them easily + async getProvinces(this: ILoadOptionsFunctions): Promise { + const countryCode = this.getCurrentNodeParameter('countryCode') as string; + const returnData: INodePropertyOptions[] = []; + const { provinces } = await infusionsoftApiRequest.call(this, 'GET', `/locales/countries/${countryCode}/provinces`); + for (const key of Object.keys(provinces)) { + const provinceName = provinces[key]; + const provinceId = key; + returnData.push({ + name: provinceName as string, + value: provinceId as string, + }); + } + return returnData; + }, + // Get all the contact types to display them to user so that he can + // select them easily + async getContactTypes(this: ILoadOptionsFunctions): Promise { + const returnData: INodePropertyOptions[] = []; + const types = await infusionsoftApiRequest.call(this, 'GET', '/setting/contact/optionTypes'); + for (const type of types.value.split(',')) { + const typeName = type; + const typeId = type; + returnData.push({ + name: typeName, + value: typeId, + }); + } + return returnData; + }, + // Get all the timezones to display them to user so that he can + // select them easily + async getTimezones(this: ILoadOptionsFunctions): Promise { + const returnData: INodePropertyOptions[] = []; + for (const timezone of moment.tz.names()) { + const timezoneName = timezone; + const timezoneId = timezone; + returnData.push({ + name: timezoneName, + value: timezoneId, + }); + } + return returnData; + }, + }, + }; + + async execute(this: IExecuteFunctions): Promise { + const items = this.getInputData(); + const returnData: IDataObject[] = []; + const length = items.length as unknown as number; + const qs: IDataObject = {}; + let responseData; + const resource = this.getNodeParameter('resource', 0) as string; + const operation = this.getNodeParameter('operation', 0) as string; + for (let i = 0; i < length; i++) { + if (resource === 'company') { + //https://developer.infusionsoft.com/docs/rest/#!/Company/createCompanyUsingPOST + if (operation === 'create') { + const addresses = (this.getNodeParameter('addressesUi', i) as IDataObject).addressesValues as IDataObject[]; + const faxes = (this.getNodeParameter('faxesUi', i) as IDataObject).faxesValues as IDataObject[]; + const phones = (this.getNodeParameter('phonesUi', i) as IDataObject).phonesValues as IDataObject[]; + const companyName = this.getNodeParameter('companyName', i) as string; + const additionalFields = this.getNodeParameter('additionalFields', i) as IDataObject; + const body: ICompany = { + company_name: companyName, + }; + keysToSnakeCase(additionalFields); + Object.assign(body, additionalFields); + if (addresses) { + body.address = keysToSnakeCase(addresses)[0] ; + } + if (faxes) { + body.fax_number = faxes[0]; + } + if (phones) { + body.phone_number = phones[0]; + } + responseData = await infusionsoftApiRequest.call(this, 'POST', '/companies', body); + } + //https://developer.infusionsoft.com/docs/rest/#!/Company/listCompaniesUsingGET + if (operation === 'getAll') { + const returnAll = this.getNodeParameter('returnAll', i) as boolean; + const options = this.getNodeParameter('options', i) as IDataObject; + keysToSnakeCase(options); + Object.assign(qs, options); + if (qs.fields) { + qs.optional_properties = qs.fields; + delete qs.fields; + } + if (returnAll) { + responseData = await infusionsoftApiRequestAllItems.call(this, 'companies', 'GET', '/companies', {}, qs); + } else { + qs.limit = this.getNodeParameter('limit', i) as number; + responseData = await infusionsoftApiRequest.call(this, 'GET', '/companies', {}, qs); + responseData = responseData.companies; + } + } + } + if (resource === 'contact') { + //https://developer.infusionsoft.com/docs/rest/#!/Contact/createOrUpdateContactUsingPUT + if (operation === 'create/update') { + const duplicateOption = this.getNodeParameter('duplicateOption', i) as string; + const addresses = (this.getNodeParameter('addressesUi', i) as IDataObject).addressesValues as IDataObject[]; + const emails = (this.getNodeParameter('emailsUi', i) as IDataObject).emailsValues as IDataObject[]; + const faxes = (this.getNodeParameter('faxesUi', i) as IDataObject).faxesValues as IDataObject[]; + const socialAccounts = (this.getNodeParameter('socialAccountsUi', i) as IDataObject).socialAccountsValues as IDataObject[]; + const phones = (this.getNodeParameter('phonesUi', i) as IDataObject).phonesValues as IDataObject[]; + const additionalFields = this.getNodeParameter('additionalFields', i) as IDataObject; + const body: IContact = { + duplicate_option: pascalCase(duplicateOption), + }; + + if (additionalFields.anniversary) { + body.anniversary = additionalFields.anniversary as string; + } + if (additionalFields.contactType) { + body.contact_type = additionalFields.contactType as string; + } + if (additionalFields.familyName) { + body.family_name = additionalFields.familyName as string; + } + if (additionalFields.givenName) { + body.given_name = additionalFields.givenName as string; + } + if (additionalFields.jobTitle) { + body.job_title = additionalFields.jobTitle as string; + } + if (additionalFields.leadSourceId) { + body.lead_source_id = additionalFields.leadSourceId as number; + } + if (additionalFields.middleName) { + body.middle_name = additionalFields.middleName as string; + } + if (additionalFields.middleName) { + body.middle_name = additionalFields.middleName as string; + } + if (additionalFields.OptInReason) { + body.opt_in_reason = additionalFields.OptInReason as string; + } + if (additionalFields.ownerId) { + body.owner_id = additionalFields.ownerId as number; + } + if (additionalFields.preferredLocale) { + body.preferred_locale = additionalFields.preferredLocale as string; + } + if (additionalFields.preferredName) { + body.preferred_name = additionalFields.preferredName as string; + } + if (additionalFields.sourceType) { + body.source_type = additionalFields.sourceType as string; + } + if (additionalFields.spouseName) { + body.spouse_name = additionalFields.spouseName as string; + } + if (additionalFields.timezone) { + body.time_zone = additionalFields.timezone as string; + } + if (additionalFields.website) { + body.website = additionalFields.website as string; + } + if (additionalFields.ipAddress) { + body.origin = { ip_address: additionalFields.ipAddress as string }; + } + if (additionalFields.companyId) { + body.company = { id: additionalFields.companyId as number }; + } + if (addresses) { + body.addresses = keysToSnakeCase(addresses) as IAddress[]; + } + if (emails) { + body.email_addresses = emails as IEmailContact[]; + } + if (faxes) { + body.fax_numbers = faxes as IFax[]; + } + if (socialAccounts) { + body.social_accounts = socialAccounts as ISocialAccount[]; + } + if (phones) { + body.phone_numbers = phones as IPhone[]; + } + responseData = await infusionsoftApiRequest.call(this, 'PUT', '/contacts', body); + } + //https://developer.infusionsoft.com/docs/rest/#!/Contact/deleteContactUsingDELETE + if (operation === 'delete') { + const contactId = parseInt(this.getNodeParameter('contactId', i) as string, 10); + responseData = await infusionsoftApiRequest.call(this, 'DELETE', `/contacts/${contactId}`); + responseData = { success: true }; + } + //https://developer.infusionsoft.com/docs/rest/#!/Contact/getContactUsingGET + if (operation === 'get') { + const contactId = parseInt(this.getNodeParameter('contactId', i) as string, 10); + const options = this.getNodeParameter('options', i) as IDataObject; + if (options.fields) { + qs.optional_properties = options.fields as string; + } + responseData = await infusionsoftApiRequest.call(this, 'GET', `/contacts/${contactId}`, {}, qs); + } + //https://developer.infusionsoft.com/docs/rest/#!/Contact/listContactsUsingGET + if (operation === 'getAll') { + const returnAll = this.getNodeParameter('returnAll', i) as boolean; + const options = this.getNodeParameter('options', i) as IDataObject; + if (options.email) { + qs.email = options.email as boolean; + } + if (options.givenName) { + qs.given_name = options.givenName as string; + } + if (options.familyName) { + qs.family_name = options.familyName as boolean; + } + if (options.order) { + qs.order = options.order as string; + } + if (options.orderDirection) { + qs.order_direction = options.orderDirection as string; + } + if (options.since) { + qs.since = options.since as string; + } + if (options.until) { + qs.until = options.until as string; + } + if (returnAll) { + responseData = await infusionsoftApiRequestAllItems.call(this, 'contacts', 'GET', '/contacts', {}, qs); + } else { + qs.limit = this.getNodeParameter('limit', i) as number; + responseData = await infusionsoftApiRequest.call(this, 'GET', '/contacts', {}, qs); + responseData = responseData.contacts; + } + } + } + if (resource === 'contactNote') { + //https://developer.infusionsoft.com/docs/rest/#!/Note/createNoteUsingPOST + if (operation === 'create') { + const userId = this.getNodeParameter('userId', i) as number; + const contactId = parseInt(this.getNodeParameter('contactId', i) as string, 10); + const additionalFields = this.getNodeParameter('additionalFields', i) as IDataObject; + const body: INote = { + user_id: userId, + contact_id: contactId, + }; + keysToSnakeCase(additionalFields); + if (additionalFields.type) { + additionalFields.type = pascalCase(additionalFields.type as string); + } + Object.assign(body, additionalFields); + responseData = await infusionsoftApiRequest.call(this, 'POST', '/notes', body); + } + //https://developer.infusionsoft.com/docs/rest/#!/Note/deleteNoteUsingDELETE + if (operation === 'delete') { + const noteId = this.getNodeParameter('noteId', i) as string; + responseData = await infusionsoftApiRequest.call(this, 'DELETE', `/notes/${noteId}`); + responseData = { success: true }; + } + //https://developer.infusionsoft.com/docs/rest/#!/Note/getNoteUsingGET + if (operation === 'get') { + const noteId = this.getNodeParameter('noteId', i) as string; + responseData = await infusionsoftApiRequest.call(this, 'GET', `/notes/${noteId}`); + } + //https://developer.infusionsoft.com/docs/rest/#!/Note/listNotesUsingGET + if (operation === 'getAll') { + const returnAll = this.getNodeParameter('returnAll', i) as boolean; + const filters = this.getNodeParameter('filters', i) as IDataObject; + keysToSnakeCase(filters); + Object.assign(qs, filters); + if (returnAll) { + responseData = await infusionsoftApiRequestAllItems.call(this, 'notes', 'GET', '/notes', {}, qs); + } else { + qs.limit = this.getNodeParameter('limit', i) as number; + responseData = await infusionsoftApiRequest.call(this, 'GET', '/notes', {}, qs); + responseData = responseData.notes; + } + } + //https://developer.infusionsoft.com/docs/rest/#!/Note/updatePropertiesOnNoteUsingPATCH + if (operation === 'update') { + const noteId = this.getNodeParameter('noteId', i) as string; + const additionalFields = this.getNodeParameter('additionalFields', i) as IDataObject; + const body: INote = {}; + keysToSnakeCase(additionalFields); + if (additionalFields.type) { + additionalFields.type = pascalCase(additionalFields.type as string); + } + Object.assign(body, additionalFields); + responseData = await infusionsoftApiRequest.call(this, 'PATCH', `/notes/${noteId}`, body); + } + } + if (resource === 'contactTag') { + //https://developer.infusionsoft.com/docs/rest/#!/Contact/applyTagsToContactIdUsingPOST + if (operation === 'create') { + const contactId = parseInt(this.getNodeParameter('contactId', i) as string, 10); + const tagIds = this.getNodeParameter('tagIds', i) as number[]; + const body: IDataObject = { + tagIds, + }; + responseData = await infusionsoftApiRequest.call(this, 'POST', `/contacts/${contactId}/tags`, body); + } + //https://developer.infusionsoft.com/docs/rest/#!/Contact/removeTagsFromContactUsingDELETE_1 + if (operation === 'delete') { + const contactId = parseInt(this.getNodeParameter('contactId', i) as string, 10); + const tagIds = this.getNodeParameter('tagIds', i) as string; + qs.ids = tagIds; + responseData = await infusionsoftApiRequest.call(this, 'DELETE', `/contacts/${contactId}/tags`, {}, qs); + responseData = { success: true }; + } + //https://developer.infusionsoft.com/docs/rest/#!/Contact/listAppliedTagsUsingGET + if (operation === 'getAll') { + const returnAll = this.getNodeParameter('returnAll', i) as boolean; + const contactId = parseInt(this.getNodeParameter('contactId', i) as string, 10); + if (returnAll) { + responseData = await infusionsoftApiRequestAllItems.call(this, 'tags', 'GET', `/contacts/${contactId}/tags`, {}, qs); + } else { + qs.limit = this.getNodeParameter('limit', i) as number; + responseData = await infusionsoftApiRequest.call(this, 'GET', `/contacts/${contactId}/tags`, {}, qs); + responseData = responseData.tags; + } + } + } + if (resource === 'ecommerceOrder') { + //https://developer.infusionsoft.com/docs/rest/#!/E-Commerce/createOrderUsingPOST + if (operation === 'create') { + const contactId = parseInt(this.getNodeParameter('contactId', i) as string, 10); + const orderDate = this.getNodeParameter('orderDate', i) as string; + const orderTitle = this.getNodeParameter('orderTitle', i) as string; + const orderType = this.getNodeParameter('orderType', i) as string; + const orderItems = (this.getNodeParameter('orderItemsUi', i) as IDataObject).orderItemsValues as IDataObject[]; + const shippingAddress = (this.getNodeParameter('addressUi', i) as IDataObject).addressValues as IDataObject; + const additionalFields = this.getNodeParameter('additionalFields', i) as IDataObject; + const body: IEcommerceOrder = { + contact_id: contactId, + order_date: orderDate, + order_title: orderTitle, + order_type: pascalCase(orderType), + }; + if (additionalFields.promoCodes) { + additionalFields.promoCodes = (additionalFields.promoCodes as string).split(',') as string[]; + } + keysToSnakeCase(additionalFields); + Object.assign(body, additionalFields); + body.order_items = keysToSnakeCase(orderItems) as IItem[]; + if (shippingAddress) { + body.shipping_address = keysToSnakeCase(shippingAddress)[0] as IShippingAddress; + } + responseData = await infusionsoftApiRequest.call(this, 'POST', '/orders', body); + } + //https://developer.infusionsoft.com/docs/rest/#!/E-Commerce/deleteOrderUsingDELETE + if (operation === 'delete') { + const orderId = parseInt(this.getNodeParameter('orderId', i) as string, 10); + responseData = await infusionsoftApiRequest.call(this, 'DELETE', `/orders/${orderId}`); + responseData = { success: true }; + } + //https://developer.infusionsoft.com/docs/rest/#!/E-Commerce/getOrderUsingGET + if (operation === 'get') { + const orderId = parseInt(this.getNodeParameter('orderId', i) as string, 10); + responseData = await infusionsoftApiRequest.call(this, 'get', `/orders/${orderId}`); + } + //https://developer.infusionsoft.com/docs/rest/#!/E-Commerce/listOrdersUsingGET + if (operation === 'getAll') { + const returnAll = this.getNodeParameter('returnAll', i) as boolean; + const options = this.getNodeParameter('options', i) as IDataObject; + keysToSnakeCase(options); + Object.assign(qs, options); + if (returnAll) { + responseData = await infusionsoftApiRequestAllItems.call(this, 'orders', 'GET', '/orders', {}, qs); + } else { + qs.limit = this.getNodeParameter('limit', i) as number; + responseData = await infusionsoftApiRequest.call(this, 'GET', '/orders', {}, qs); + responseData = responseData.orders; + } + } + } + if (resource === 'ecommerceProduct') { + //https://developer.infusionsoft.com/docs/rest/#!/Product/createProductUsingPOST + if (operation === 'create') { + const productName = this.getNodeParameter('productName', i) as string; + const additionalFields = this.getNodeParameter('additionalFields', i) as IDataObject; + const body: IEcommerceProduct = { + product_name: productName, + }; + keysToSnakeCase(additionalFields); + Object.assign(body, additionalFields); + responseData = await infusionsoftApiRequest.call(this, 'POST', '/products', body); + } + //https://developer.infusionsoft.com/docs/rest/#!/Product/deleteProductUsingDELETE + if (operation === 'delete') { + const productId = this.getNodeParameter('productId', i) as string; + responseData = await infusionsoftApiRequest.call(this, 'DELETE', `/products/${productId}`); + responseData = { success: true }; + } + //https://developer.infusionsoft.com/docs/rest/#!/Product/retrieveProductUsingGET + if (operation === 'get') { + const productId = this.getNodeParameter('productId', i) as string; + responseData = await infusionsoftApiRequest.call(this, 'get', `/products/${productId}`); + } + //https://developer.infusionsoft.com/docs/rest/#!/Product/listProductsUsingGET + if (operation === 'getAll') { + const returnAll = this.getNodeParameter('returnAll', i) as boolean; + const filters = this.getNodeParameter('filters', i) as IDataObject; + keysToSnakeCase(filters); + Object.assign(qs, filters); + if (returnAll) { + responseData = await infusionsoftApiRequestAllItems.call(this, 'products', 'GET', '/products', {}, qs); + } else { + qs.limit = this.getNodeParameter('limit', i) as number; + responseData = await infusionsoftApiRequest.call(this, 'GET', '/products', {}, qs); + responseData = responseData.products; + } + } + } + if (resource === 'email') { + //https://developer.infusionsoft.com/docs/rest/#!/Email/createEmailUsingPOST + if (operation === 'createRecord') { + const sentFromAddress = this.getNodeParameter('sentFromAddress', i) as string; + const sendToAddress = this.getNodeParameter('sentToAddress', i) as string; + const additionalFields = this.getNodeParameter('additionalFields', i) as IDataObject; + const body: IDataObject = { + sent_to_address: sendToAddress, + sent_from_address: sentFromAddress, + }; + Object.assign(body, additionalFields); + keysToSnakeCase(body as IDataObject); + responseData = await infusionsoftApiRequest.call(this, 'POST', '/emails', body); + } + //https://developer.infusionsoft.com/docs/rest/#!/Email/deleteEmailUsingDELETE + if (operation === 'deleteRecord') { + const emailRecordId = parseInt(this.getNodeParameter('emailRecordId', i) as string, 10); + responseData = await infusionsoftApiRequest.call(this, 'DELETE', `/emails/${emailRecordId}`); + responseData = { success: true }; + } + //https://developer.infusionsoft.com/docs/rest/#!/Email/listEmailsUsingGET + if (operation === 'getAll') { + const returnAll = this.getNodeParameter('returnAll', i) as boolean; + const filters = this.getNodeParameter('filters', i) as IDataObject; + keysToSnakeCase(filters); + Object.assign(qs, filters); + if (returnAll) { + responseData = await infusionsoftApiRequestAllItems.call(this, 'emails', 'GET', '/emails', {}, qs); + } else { + qs.limit = this.getNodeParameter('limit', i) as number; + responseData = await infusionsoftApiRequest.call(this, 'GET', '/emails', {}, qs); + responseData = responseData.emails; + } + } + //https://developer.infusionsoft.com/docs/rest/#!/Email/deleteEmailUsingDELETE + if (operation === 'send') { + const userId = this.getNodeParameter('userId', i) as number; + const contactIds = ((this.getNodeParameter('contactIds', i) as string).split(',') as string[]).map((e) => (parseInt(e, 10))); + const subject = this.getNodeParameter('subject', i) as string; + const additionalFields = this.getNodeParameter('additionalFields', i) as IDataObject; + const body: IEmail = { + user_id: userId, + contacts: contactIds, + subject, + }; + keysToSnakeCase(additionalFields); + Object.assign(body, additionalFields); + + const attachmentsUi = this.getNodeParameter('attachmentsUi', i) as IDataObject; + let attachments: IAttachment[] = []; + if (attachmentsUi) { + if (attachmentsUi.attachmentsValues) { + keysToSnakeCase(attachmentsUi.attachmentsValues as IDataObject); + attachments = attachmentsUi.attachmentsValues as IAttachment[]; + } + if (attachmentsUi.attachmentsBinary + && (attachmentsUi.attachmentsBinary as IDataObject).length) { + + if (items[i].binary === undefined) { + throw new Error('No binary data exists on item!'); + } + + for (const { property } of attachmentsUi.attachmentsBinary as IDataObject[]) { + + const item = items[i].binary as IBinaryKeyData; + + if (item[property as string] === undefined) { + throw new Error(`Binary data property "${property}" does not exists on item!`); + } + + attachments.push({ + file_data: item[property as string].data, + file_name: item[property as string].fileName, + }); + } + } + body.attachments = attachments; + } + + responseData = await infusionsoftApiRequest.call(this, 'POST', '/emails/queue', body); + responseData = { success: true }; + } + } + if (resource === 'file') { + //https://developer.infusionsoft.com/docs/rest/#!/File/deleteFileUsingDELETE + if (operation === 'delete') { + const fileId = parseInt(this.getNodeParameter('fileId', i) as string, 10); + responseData = await infusionsoftApiRequest.call(this, 'DELETE', `/files/${fileId}`); + responseData = { success: true }; + } + //https://developer.infusionsoft.com/docs/rest/#!/File/listFilesUsingGET + if (operation === 'getAll') { + const returnAll = this.getNodeParameter('returnAll', i) as boolean; + const filters = this.getNodeParameter('filters', i) as IDataObject; + keysToSnakeCase(filters); + Object.assign(qs, filters); + if (qs.permission) { + qs.permission = (qs.permission as string).toUpperCase(); + } + if (qs.type) { + qs.type = titleCase(qs.type as string); + } + if (qs.viewable) { + qs.viewable = (qs.viewable as string).toUpperCase(); + } + if (returnAll) { + responseData = await infusionsoftApiRequestAllItems.call(this, 'files', 'GET', '/files', {}, qs); + } else { + qs.limit = this.getNodeParameter('limit', i) as number; + responseData = await infusionsoftApiRequest.call(this, 'GET', '/files', {}, qs); + responseData = responseData.files; + } + } + //https://developer.infusionsoft.com/docs/rest/#!/File/createFileUsingPOST + if (operation === 'upload') { + const binaryData = this.getNodeParameter('binaryData', i) as boolean; + const fileAssociation = this.getNodeParameter('fileAssociation', i) as string; + const isPublic = this.getNodeParameter('isPublic', i) as boolean; + const body: IFile = { + is_public: isPublic, + file_association: fileAssociation.toUpperCase(), + }; + if (fileAssociation === 'contact') { + const contactId = parseInt(this.getNodeParameter('contactId', i) as string, 10); + body.contact_id = contactId; + } + if (binaryData) { + const binaryPropertyName = this.getNodeParameter('binaryPropertyName', i) as string; + + if (items[i].binary === undefined) { + throw new Error('No binary data exists on item!'); + } + + const item = items[i].binary as IBinaryKeyData; + + if (item[binaryPropertyName as string] === undefined) { + throw new Error(`No binary data property "${binaryPropertyName}" does not exists on item!`); + } + + body.file_data = item[binaryPropertyName as string].data; + body.file_name = item[binaryPropertyName as string].fileName; + + } else { + const fileName = this.getNodeParameter('fileName', i) as string; + const fileData = this.getNodeParameter('fileData', i) as string; + body.file_name = fileName; + body.file_data = fileData; + } + responseData = await infusionsoftApiRequest.call(this, 'POST', '/files', body); + } + } + if (Array.isArray(responseData)) { + returnData.push.apply(returnData, responseData as IDataObject[]); + } else if (responseData !== undefined) { + returnData.push(responseData as IDataObject); + } + } + return [this.helpers.returnJsonArray(returnData)]; + } +} diff --git a/packages/nodes-base/nodes/Infusionsoft/InfusionsoftTrigger.node.ts b/packages/nodes-base/nodes/Infusionsoft/InfusionsoftTrigger.node.ts new file mode 100644 index 000000000..756b170d6 --- /dev/null +++ b/packages/nodes-base/nodes/Infusionsoft/InfusionsoftTrigger.node.ts @@ -0,0 +1,196 @@ +import { + IHookFunctions, + IWebhookFunctions, +} from 'n8n-core'; + +import { + IDataObject, + INodeTypeDescription, + INodeType, + IWebhookResponseData, + ILoadOptionsFunctions, + INodePropertyOptions, +} from 'n8n-workflow'; + +import { + infusionsoftApiRequest, +} from './GenericFunctions'; + +import { + titleCase, + } from 'change-case'; + +export class InfusionsoftTrigger implements INodeType { + description: INodeTypeDescription = { + displayName: 'Infusionsoft Trigger', + name: 'infusionsoftTrigger', + icon: 'file:infusionsoft.png', + group: ['trigger'], + version: 1, + subtitle: '={{$parameter["eventId"]}}', + description: 'Starts the workflow when Infusionsoft events occure.', + defaults: { + name: 'Infusionsoft Trigger', + color: '#79af53', + }, + inputs: [], + outputs: ['main'], + credentials: [ + { + name: 'infusionsoftOAuth2Api', + required: true, + }, + ], + webhooks: [ + { + name: 'default', + httpMethod: 'POST', + responseMode: 'onReceived', + path: 'webhook', + }, + ], + properties: [ + { + displayName: 'Event', + name: 'eventId', + type: 'options', + typeOptions: { + loadOptionsMethod: 'getEvents', + }, + default: '', + required: true, + }, + { + displayName: 'RAW Data', + name: 'rawData', + type: 'boolean', + default: false, + description: `Returns the data exactly in the way it got received from the API.`, + }, + ], + }; + + methods = { + loadOptions: { + // Get all the event types to display them to user so that he can + // select them easily + async getEvents(this: ILoadOptionsFunctions): Promise { + const returnData: INodePropertyOptions[] = []; + const hooks = await infusionsoftApiRequest.call(this, 'GET', '/hooks/event_keys'); + for (const hook of hooks) { + const hookName = hook; + const hookId = hook; + returnData.push({ + name: titleCase((hookName as string).replace('.', ' ')), + value: hookId as string, + }); + } + return returnData; + }, + }, + }; + + // @ts-ignore (because of request) + webhookMethods = { + default: { + async checkExists(this: IHookFunctions): Promise { + const eventId = this.getNodeParameter('eventId') as string; + const webhookUrl = this.getNodeWebhookUrl('default'); + const webhookData = this.getWorkflowStaticData('node'); + + const responseData = await infusionsoftApiRequest.call(this, 'GET', '/hooks', {}); + + for (const existingData of responseData) { + if (existingData.hookUrl === webhookUrl + && existingData.eventKey === eventId + && existingData.status === 'Verified') { + // The webhook exists already + webhookData.webhookId = existingData.key; + return true; + } + } + + return false; + }, + async create(this: IHookFunctions): Promise { + const eventId = this.getNodeParameter('eventId') as string; + const webhookData = this.getWorkflowStaticData('node'); + const webhookUrl = this.getNodeWebhookUrl('default'); + + const body = { + eventKey: eventId, + hookUrl: webhookUrl, + }; + + const responseData = await infusionsoftApiRequest.call(this, 'POST', '/hooks', body); + + if (responseData.key === undefined) { + // Required data is missing so was not successful + return false; + } + + webhookData.webhookId = responseData.key as string; + + return true; + }, + async delete(this: IHookFunctions): Promise { + const webhookData = this.getWorkflowStaticData('node'); + + if (webhookData.webhookId !== undefined) { + + try { + await infusionsoftApiRequest.call(this, 'DELETE', `/hooks/${webhookData.webhookId}`); + } catch (e) { + return false; + } + + // Remove from the static workflow data so that it is clear + // that no webhooks are registred anymore + delete webhookData.webhookId; + } + + return true; + }, + }, + }; + + async webhook(this: IWebhookFunctions): Promise { + const rawData = this.getNodeParameter('rawData') as boolean; + const headers = this.getHeaderData() as IDataObject; + const bodyData = this.getBodyData() as IDataObject; + + if (headers['x-hook-secret']) { + // Is a create webhook confirmation request + const res = this.getResponseObject(); + res.set('x-hook-secret', headers['x-hook-secret'] as string); + res.status(200).end(); + return { + noWebhookResponse: true, + }; + } + + if (rawData) { + return { + workflowData: [ + this.helpers.returnJsonArray(bodyData), + ], + }; + } + + const responseData: IDataObject[] = []; + for (const data of bodyData.object_keys as IDataObject[]) { + responseData.push({ + eventKey: bodyData.event_key, + objectType: bodyData.object_type, + id: data.id, + timestamp: data.timestamp, + apiUrl: data.apiUrl, + }); + } + return { + workflowData: [ + this.helpers.returnJsonArray(responseData), + ], + }; + } +} diff --git a/packages/nodes-base/nodes/Infusionsoft/infusionsoft.png b/packages/nodes-base/nodes/Infusionsoft/infusionsoft.png new file mode 100644 index 0000000000000000000000000000000000000000..4cf546680258f9fd6efd9b203d52103e1864b1ea GIT binary patch literal 4307 zcmY*dc|25Y*gj)7iAt8NL&=sgMk-^?zLRZilQEVt+09^tC@LW)vMZ!y9cvWHR>WAc zFG)nUF!m+XM{nQze&2I`_qng%mW4h;JB`ihUsB8ISM9* z!+Y$>wc^7B!kcQV0pu>e`NK_wvz4xkp&=k~h?xL-&?$iSh;n!VARd7J4+a1|5bwX( z6eRYSLjwT!aRA+4j?G~{S~`busQ#O2vq1kjX3_l1Hp!y>4pfKrwy$_{|M^%KeFV6X}^O3)@ zjN%{W|EvA&qbPmU{Qn&0pGp5z4@Xr7D@yhJO=49S_>h z=>GL?HsDr0{!+}t#inGH3OjuIPXA!yD?@6reCBmVC)L-pW+N}Z3+0=m^OjN7d=6gX>u&b?vap{48k)m$}Tri}PWmAk(Y672|Y zT7*UaTnka|N_7lm^cS4SZ_3%zj)`TPux5;A%HsEX69w(h`BoZEZSM<0n2HjPx5M9! zdtwZ=@Oy~g>Gv;@_n%QKYTZl7X+pe~=dVoBL8X+x*{06M(D%MxQthMAbwB!X(0SRu zesWz!scEgV0>e4YV+q9NC^ge|iKMdw{M7cnx*uk?q1K;!DzF}LA+Fg_3`x?C4tcuG zNozEI;QEzMd=RlNG_iSnR&P0E>8EIb;BJ=GGXYy}W-Te4_c5h(2I3hv?H1+X#0GRw zycM|FVLgLQ{v>C!g5W34g}!P$$P)Dz9vcrq0Oe&I)g3;_ZJ5HxphRsjzv8T8j7L# z!O~JIRiCH16dJB({chW~v|Ib$qvTdw9D>h*w_9bZs)+U*1P(Q#kNZQd?TwRCsY(K^Tec%nYyo$^HxY2Y~iSNUH5Ap_q~R zx(kmNKFUcyS(X#iTnk99O@&UPv7$ zF5RH;$|LXFwbcqM_T{nh6)-sce4|VI{Ion+Gq+{^^N_**&K#rLyO`zc1!w0(-w0c) z$=4Jy|GW<-_yYVy15d>rwZ_kxo;9_v@arK}+S@!=DOy!@RK55gCIj+?PtzY)E?5r8 z-##e(y)(<+XwqGfa?+wW#Tk+f1&n=$LoV-#T$V&yn$1kij|lQC&9A|8AJ3J4>L->t zlq#z044ex4nEKFUkUn+unuqVbCm9h_nIei&(6{mPLi3Xj7L%wnsWta>t~5?Gd`1?P zVkHTKJLJ-l5&{xaG^a~t7dBUTb`=`cm|zGcR)cF8>$fJey$VN^6*n!aUgAxQwR)cv zOC4vs+s-r1@Mr`5)3mvaFphMmr|U_n%3%*KGKQbQsBPg4_#&J+t*XQ>3HgO@Hfvjcj^E$5vpi^U-Xc)}he>W8(VC@j7Xn>R_j>!VH727;5P+j(b~iQpArk?)vc!n#kg zKht~+irp=xcl8skLDAD=2@F@E9RdxCGmq@nbtbEYe9YkW&=ePth?5bl4UzwxF2T0S z!|~Ol#KAX-lk~%JlcDOoH<$VWm)j0;eg-TJ=3tNLp9V8=%=xMP6<`Vh2!JhrEXmL1i2*#XnWn2;OaYLS`Ny+1fC%4K}u(;2l} zA0)7<;0MM@j?lx(teIirCmF29E!BmW3Q?MTL z>1kzL;Ro+icZCF2{R@;Szq9RK>uo}I?fKp)5Am&V5C*s@NmBTvmLA$(3tw6^#;u;A zPir{GJNMX&mpRDQo53w(;+JjuDA=XE4a3n2S1Qy##4ltc_a_ja5F>Z_av=(ZD$mYt zjD7j-RAxrM&oaosHTRBuejbNQ4S!Of1Qd=nw-eI~#xi+E{ZP1*dA&Ps^^0w_`$785 zz@WW%BRzFsXh>nbXCo5(G*F9I?#AQeg&Lu$!EL%>V1$Bx+M-oag2ib=_^f$n^7kAG zoZ-cxVzIvFu{(E}<67~t&&0PQnkR!Ud5mtn%f(4g?%U6HJ^4I1JeGWD&O}>H)ooD6CV=u@*I4_;5`?h%#D;5Pmdm3C!*Rd3~ z`>cC5D8H)8A1UG6#T@o=8+h6-?821v@^c?Wy2{1euHjgSNE(j!f|d$yj!}DMObv>K zEJS`NjYn|i2*of^GH6}#&wA$0+E%#-GR;~F?`BVo;VAMyK=F3WrkUfN645VHDhg-X z*5YNKe7*JFphH%{+ZcCI3&u~OyF!!C#li8tQLWIs!^XyjX$O~FL_h3x?c9*3<%Coj zr_s(MC7(s}Y-SHta=6u(e85}EO#QLikb=NZb5Fj-v?AI=GI*LtL!hw~8=HwWEJ^7+ z7#Hp=H4+?2qPppaD(n+$ z>=UF+G#3%f|7IaP2wA;sUDnO58ee$(Lt+TYZ8LAZV}$)(Ys(JB!bz#Zk{ll5*vv{| zXs*0*d)g)g>4LhsB^=Z=*Ty|n(8SMzEwqybf7&EF`@U;)wdzfKx+{#eaHpQyX zNN%A8;zQpN!_^t_^rPIdkk@LaD2J;bEzA_RMA*rtxsQ#x0&VK;#8>6xFDw%y#X;sU zj@&6K$Hp1$@BKX$57#B7XRtdIbJ@n)SSKQ#taunop*Ca8`r~4o&1ztUFTEEr1A`1 z5piSl5qHHlC5x$6O~(t6qtpPq?f5EmK07s#-@y(ib9B1SY^as4(^**D!9 zdi_$pQ9I2Sh7Y2S+FYNu983@z)&hFlQ4O4JK zh_#t14R>_|=mAZVT7s;Ps_mzCsVf1}=<8r2#rv(8w6qt^b0|8WB@Fi~i6Nd4*RUr) zggyL%5oG~7Mi;(_$mZv_N>i)C8QC}&zt)Eepf63NNrJ)P_DRCW-E&RnC>>`=IQlu7 zhqeQNL6|u#wX@|RuXd=?n#E{MZEf_{;t2lCgcN-TWg@tx_rNJNMGBvcCcUq)nNg=9 zslFCr?E|TWKE*fQp=Txa_L`n!7>TZYRb8EVl7)dQJM=9lr*(2>rb<*UUmKhq?efBz zRbG|r+!BbSSSEW$A1+FHK499HYp8H%Jv7&YI$k#g+0>j9NFZ3D1+TcFMgVr;p zG;~?F<6Tn$^@A6Xl!1W(MxUPb%B8V46$RDR+Y)^jxtifz4e+`v{ukJ{CrZ}!ZqJgO zU5cDo(3ohDLjzoQ28r@ZTx*vsv67RMv!AGWi3{%#1Ug4fa%UuHr8ALBrdi@(3T6W8~c3m!qIl%wBrQoczWM4s6RUs}2yvSI z7v9+@DfPsGl`OW)z1i90Npfiu(wqT&4Owk8dVy^%RiKvSIUS(Z}WIOdGh2}Y-}t& zw;8nAH1e($+S+GlWm32XIs(T4=V>CF_q8@;T{==A Date: Wed, 1 Apr 2020 18:55:40 -0400 Subject: [PATCH 2/3] :zap: small fix --- .../nodes/Infusionsoft/CompanyDescription.ts | 2 +- .../nodes/Infusionsoft/CompanyInterface.ts | 2 +- .../nodes/Infusionsoft/ConctactInterface.ts | 2 +- .../nodes/Infusionsoft/ContactDescription.ts | 2 +- .../Infusionsoft/ContactNoteDescription.ts | 2 +- .../Infusionsoft/ContactTagDescription.ts | 2 +- .../Infusionsoft/EcommerceOrderDescripion.ts | 2 +- .../EcommerceProductDescription.ts | 2 +- .../nodes/Infusionsoft/EmailDescription.ts | 2 +- .../nodes/Infusionsoft/FileDescription.ts | 2 +- packages/nodes-base/package.json | 34 +++++++++---------- 11 files changed, 27 insertions(+), 27 deletions(-) diff --git a/packages/nodes-base/nodes/Infusionsoft/CompanyDescription.ts b/packages/nodes-base/nodes/Infusionsoft/CompanyDescription.ts index b0278d856..edb73d1ce 100644 --- a/packages/nodes-base/nodes/Infusionsoft/CompanyDescription.ts +++ b/packages/nodes-base/nodes/Infusionsoft/CompanyDescription.ts @@ -1,6 +1,6 @@ import { INodeProperties, - } from "n8n-workflow"; + } from 'n8n-workflow'; export const companyOperations = [ { diff --git a/packages/nodes-base/nodes/Infusionsoft/CompanyInterface.ts b/packages/nodes-base/nodes/Infusionsoft/CompanyInterface.ts index c8006e9f4..7bf32c78e 100644 --- a/packages/nodes-base/nodes/Infusionsoft/CompanyInterface.ts +++ b/packages/nodes-base/nodes/Infusionsoft/CompanyInterface.ts @@ -1,4 +1,4 @@ -import { IDataObject } from "n8n-workflow"; +import { IDataObject } from 'n8n-workflow'; export interface ICompany { address?: IDataObject; diff --git a/packages/nodes-base/nodes/Infusionsoft/ConctactInterface.ts b/packages/nodes-base/nodes/Infusionsoft/ConctactInterface.ts index 370888a17..1bbdb6d6f 100644 --- a/packages/nodes-base/nodes/Infusionsoft/ConctactInterface.ts +++ b/packages/nodes-base/nodes/Infusionsoft/ConctactInterface.ts @@ -1,6 +1,6 @@ import { IDataObject, - } from "n8n-workflow"; + } from 'n8n-workflow'; export interface IAddress { country_code?: string; diff --git a/packages/nodes-base/nodes/Infusionsoft/ContactDescription.ts b/packages/nodes-base/nodes/Infusionsoft/ContactDescription.ts index 198224fff..a70e41776 100644 --- a/packages/nodes-base/nodes/Infusionsoft/ContactDescription.ts +++ b/packages/nodes-base/nodes/Infusionsoft/ContactDescription.ts @@ -1,6 +1,6 @@ import { INodeProperties, - } from "n8n-workflow"; + } from 'n8n-workflow'; export const contactOperations = [ { diff --git a/packages/nodes-base/nodes/Infusionsoft/ContactNoteDescription.ts b/packages/nodes-base/nodes/Infusionsoft/ContactNoteDescription.ts index d916702b2..aa625c3b6 100644 --- a/packages/nodes-base/nodes/Infusionsoft/ContactNoteDescription.ts +++ b/packages/nodes-base/nodes/Infusionsoft/ContactNoteDescription.ts @@ -1,6 +1,6 @@ import { INodeProperties, - } from "n8n-workflow"; + } from 'n8n-workflow'; export const contactNoteOperations = [ { diff --git a/packages/nodes-base/nodes/Infusionsoft/ContactTagDescription.ts b/packages/nodes-base/nodes/Infusionsoft/ContactTagDescription.ts index 9643530ad..a212c112a 100644 --- a/packages/nodes-base/nodes/Infusionsoft/ContactTagDescription.ts +++ b/packages/nodes-base/nodes/Infusionsoft/ContactTagDescription.ts @@ -1,6 +1,6 @@ import { INodeProperties, - } from "n8n-workflow"; + } from 'n8n-workflow'; export const contactTagOperations = [ { diff --git a/packages/nodes-base/nodes/Infusionsoft/EcommerceOrderDescripion.ts b/packages/nodes-base/nodes/Infusionsoft/EcommerceOrderDescripion.ts index df601111f..2d43239fb 100644 --- a/packages/nodes-base/nodes/Infusionsoft/EcommerceOrderDescripion.ts +++ b/packages/nodes-base/nodes/Infusionsoft/EcommerceOrderDescripion.ts @@ -1,6 +1,6 @@ import { INodeProperties, - } from "n8n-workflow"; + } from 'n8n-workflow'; export const ecommerceOrderOperations = [ { diff --git a/packages/nodes-base/nodes/Infusionsoft/EcommerceProductDescription.ts b/packages/nodes-base/nodes/Infusionsoft/EcommerceProductDescription.ts index f0b3ef364..09ec6ea09 100644 --- a/packages/nodes-base/nodes/Infusionsoft/EcommerceProductDescription.ts +++ b/packages/nodes-base/nodes/Infusionsoft/EcommerceProductDescription.ts @@ -1,6 +1,6 @@ import { INodeProperties, - } from "n8n-workflow"; + } from 'n8n-workflow'; export const ecommerceProductOperations = [ { diff --git a/packages/nodes-base/nodes/Infusionsoft/EmailDescription.ts b/packages/nodes-base/nodes/Infusionsoft/EmailDescription.ts index f7875c13f..0f7015250 100644 --- a/packages/nodes-base/nodes/Infusionsoft/EmailDescription.ts +++ b/packages/nodes-base/nodes/Infusionsoft/EmailDescription.ts @@ -1,6 +1,6 @@ import { INodeProperties, - } from "n8n-workflow"; + } from 'n8n-workflow'; export const emailOperations = [ { diff --git a/packages/nodes-base/nodes/Infusionsoft/FileDescription.ts b/packages/nodes-base/nodes/Infusionsoft/FileDescription.ts index 9d90f4411..863590a61 100644 --- a/packages/nodes-base/nodes/Infusionsoft/FileDescription.ts +++ b/packages/nodes-base/nodes/Infusionsoft/FileDescription.ts @@ -1,6 +1,6 @@ import { INodeProperties, - } from "n8n-workflow"; + } from 'n8n-workflow'; export const fileOperations = [ { diff --git a/packages/nodes-base/package.json b/packages/nodes-base/package.json index e30a66b89..9cc9d2bc6 100644 --- a/packages/nodes-base/package.json +++ b/packages/nodes-base/package.json @@ -42,15 +42,15 @@ "dist/credentials/GithubApi.credentials.js", "dist/credentials/GithubOAuth2Api.credentials.js", "dist/credentials/GitlabApi.credentials.js", - "dist/credentials/GoogleApi.credentials.js", - "dist/credentials/GoogleOAuth2Api.credentials.js", + "dist/credentials/GoogleApi.credentials.js", + "dist/credentials/GoogleOAuth2Api.credentials.js", "dist/credentials/HttpBasicAuth.credentials.js", "dist/credentials/HttpDigestAuth.credentials.js", "dist/credentials/HttpHeaderAuth.credentials.js", "dist/credentials/HubspotApi.credentials.js", "dist/credentials/Imap.credentials.js", - "dist/credentials/IntercomApi.credentials.js", - "dist/credentials/InfusionsoftOAuth2Api.credentials.js", + "dist/credentials/IntercomApi.credentials.js", + "dist/credentials/InfusionsoftOAuth2Api.credentials.js", "dist/credentials/JiraSoftwareCloudApi.credentials.js", "dist/credentials/LinkFishApi.credentials.js", "dist/credentials/MailchimpApi.credentials.js", @@ -83,8 +83,8 @@ "dist/credentials/TypeformApi.credentials.js", "dist/credentials/TogglApi.credentials.js", "dist/credentials/VeroApi.credentials.js", - "dist/credentials/WordpressApi.credentials.js", - "dist/credentials/ZohoOAuth2Api.credentials.js" + "dist/credentials/WordpressApi.credentials.js", + "dist/credentials/ZohoOAuth2Api.credentials.js" ], "nodes": [ "dist/nodes/ActiveCampaign/ActiveCampaign.node.js", @@ -118,8 +118,8 @@ "dist/nodes/Github/Github.node.js", "dist/nodes/Github/GithubTrigger.node.js", "dist/nodes/Gitlab/Gitlab.node.js", - "dist/nodes/Gitlab/GitlabTrigger.node.js", - "dist/nodes/Google/GoogleCalendar.node.js", + "dist/nodes/Gitlab/GitlabTrigger.node.js", + "dist/nodes/Google/GoogleCalendar.node.js", "dist/nodes/Google/GoogleDrive.node.js", "dist/nodes/Google/GoogleSheets.node.js", "dist/nodes/GraphQL/GraphQL.node.js", @@ -128,9 +128,9 @@ "dist/nodes/Hubspot/Hubspot.node.js", "dist/nodes/If.node.js", "dist/nodes/Interval.node.js", - "dist/nodes/Intercom/Intercom.node.js", - "dist/nodes/Infusionsoft/Infusionsoft.node.js", - "dist/nodes/Infusionsoft/InfusionsoftTrigger.node.js", + "dist/nodes/Intercom/Intercom.node.js", + "dist/nodes/Infusionsoft/Infusionsoft.node.js", + "dist/nodes/Infusionsoft/InfusionsoftTrigger.node.js", "dist/nodes/Jira/JiraSoftwareCloud.node.js", "dist/nodes/LinkFish/LinkFish.node.js", "dist/nodes/Mailchimp/Mailchimp.node.js", @@ -181,8 +181,8 @@ "dist/nodes/WriteBinaryFile.node.js", "dist/nodes/Webhook.node.js", "dist/nodes/Wordpress/Wordpress.node.js", - "dist/nodes/Xml.node.js", - "dist/nodes/Zoho/ZohoCrm.node.js" + "dist/nodes/Xml.node.js", + "dist/nodes/Zoho/ZohoCrm.node.js" ] }, "devDependencies": { @@ -195,8 +195,8 @@ "@types/gm": "^1.18.2", "@types/imap-simple": "^4.2.0", "@types/jest": "^24.0.18", - "@types/lodash.set": "^4.3.6", - "@types/moment-timezone": "^0.5.12", + "@types/lodash.set": "^4.3.6", + "@types/moment-timezone": "^0.5.12", "@types/mongodb": "^3.3.6", "@types/node": "^10.10.1", "@types/nodemailer": "^4.6.5", @@ -222,8 +222,8 @@ "imap-simple": "^4.3.0", "lodash.get": "^4.4.2", "lodash.set": "^4.3.2", - "lodash.unset": "^4.5.2", - "moment-timezone": "0.5.28", + "lodash.unset": "^4.5.2", + "moment-timezone": "0.5.28", "mongodb": "^3.3.2", "mysql2": "^2.0.1", "n8n-core": "~0.20.0", From 3a71e2c978f2c69fb10d12dbc66762fb6d9a1186 Mon Sep 17 00:00:00 2001 From: ricardo Date: Thu, 2 Apr 2020 19:37:40 -0400 Subject: [PATCH 3/3] :zap: Renamed to Keap. --- ...ntials.ts => KeapOAuth2Api.credentials.ts} | 6 +- .../CompanyDescription.ts | 0 .../CompanyInterface.ts | 0 .../ConctactInterface.ts | 0 .../ContactDescription.ts | 20 ++-- .../ContactNoteDescription.ts | 0 .../ContactNoteInterface.ts | 0 .../ContactTagDescription.ts | 0 .../EcommerceOrderDescripion.ts | 0 .../EcommerceOrderInterface.ts | 0 .../EcommerceProductDescription.ts | 0 .../EcommerceProductInterface.ts | 0 .../{Infusionsoft => Keap}/EmaiIInterface.ts | 0 .../EmailDescription.ts | 0 .../{Infusionsoft => Keap}/FileDescription.ts | 0 .../{Infusionsoft => Keap}/FileInterface.ts | 0 .../GenericFunctions.ts | 8 +- .../Keap.node.ts} | 106 +++++++++--------- .../KeapTrigger.node.ts} | 22 ++-- .../infusionsoft.png => Keap/keap.png} | Bin packages/nodes-base/package.json | 6 +- 21 files changed, 84 insertions(+), 84 deletions(-) rename packages/nodes-base/credentials/{InfusionsoftOAuth2Api.credentials.ts => KeapOAuth2Api.credentials.ts} (86%) rename packages/nodes-base/nodes/{Infusionsoft => Keap}/CompanyDescription.ts (100%) rename packages/nodes-base/nodes/{Infusionsoft => Keap}/CompanyInterface.ts (100%) rename packages/nodes-base/nodes/{Infusionsoft => Keap}/ConctactInterface.ts (100%) rename packages/nodes-base/nodes/{Infusionsoft => Keap}/ContactDescription.ts (97%) rename packages/nodes-base/nodes/{Infusionsoft => Keap}/ContactNoteDescription.ts (100%) rename packages/nodes-base/nodes/{Infusionsoft => Keap}/ContactNoteInterface.ts (100%) rename packages/nodes-base/nodes/{Infusionsoft => Keap}/ContactTagDescription.ts (100%) rename packages/nodes-base/nodes/{Infusionsoft => Keap}/EcommerceOrderDescripion.ts (100%) rename packages/nodes-base/nodes/{Infusionsoft => Keap}/EcommerceOrderInterface.ts (100%) rename packages/nodes-base/nodes/{Infusionsoft => Keap}/EcommerceProductDescription.ts (100%) rename packages/nodes-base/nodes/{Infusionsoft => Keap}/EcommerceProductInterface.ts (100%) rename packages/nodes-base/nodes/{Infusionsoft => Keap}/EmaiIInterface.ts (100%) rename packages/nodes-base/nodes/{Infusionsoft => Keap}/EmailDescription.ts (100%) rename packages/nodes-base/nodes/{Infusionsoft => Keap}/FileDescription.ts (100%) rename packages/nodes-base/nodes/{Infusionsoft => Keap}/FileInterface.ts (100%) rename packages/nodes-base/nodes/{Infusionsoft => Keap}/GenericFunctions.ts (68%) rename packages/nodes-base/nodes/{Infusionsoft/Infusionsoft.node.ts => Keap/Keap.node.ts} (85%) rename packages/nodes-base/nodes/{Infusionsoft/InfusionsoftTrigger.node.ts => Keap/KeapTrigger.node.ts} (87%) rename packages/nodes-base/nodes/{Infusionsoft/infusionsoft.png => Keap/keap.png} (100%) diff --git a/packages/nodes-base/credentials/InfusionsoftOAuth2Api.credentials.ts b/packages/nodes-base/credentials/KeapOAuth2Api.credentials.ts similarity index 86% rename from packages/nodes-base/credentials/InfusionsoftOAuth2Api.credentials.ts rename to packages/nodes-base/credentials/KeapOAuth2Api.credentials.ts index 63bd89038..8bbe09d6f 100644 --- a/packages/nodes-base/credentials/InfusionsoftOAuth2Api.credentials.ts +++ b/packages/nodes-base/credentials/KeapOAuth2Api.credentials.ts @@ -7,12 +7,12 @@ const scopes = [ 'full', ]; -export class InfusionsoftOAuth2Api implements ICredentialType { - name = 'infusionsoftOAuth2Api'; +export class KeapOAuth2Api implements ICredentialType { + name = 'keapOAuth2Api'; extends = [ 'oAuth2Api', ]; - displayName = 'Infusionsoft OAuth2 API'; + displayName = 'Keap OAuth2 API'; properties = [ { displayName: 'Authorization URL', diff --git a/packages/nodes-base/nodes/Infusionsoft/CompanyDescription.ts b/packages/nodes-base/nodes/Keap/CompanyDescription.ts similarity index 100% rename from packages/nodes-base/nodes/Infusionsoft/CompanyDescription.ts rename to packages/nodes-base/nodes/Keap/CompanyDescription.ts diff --git a/packages/nodes-base/nodes/Infusionsoft/CompanyInterface.ts b/packages/nodes-base/nodes/Keap/CompanyInterface.ts similarity index 100% rename from packages/nodes-base/nodes/Infusionsoft/CompanyInterface.ts rename to packages/nodes-base/nodes/Keap/CompanyInterface.ts diff --git a/packages/nodes-base/nodes/Infusionsoft/ConctactInterface.ts b/packages/nodes-base/nodes/Keap/ConctactInterface.ts similarity index 100% rename from packages/nodes-base/nodes/Infusionsoft/ConctactInterface.ts rename to packages/nodes-base/nodes/Keap/ConctactInterface.ts diff --git a/packages/nodes-base/nodes/Infusionsoft/ContactDescription.ts b/packages/nodes-base/nodes/Keap/ContactDescription.ts similarity index 97% rename from packages/nodes-base/nodes/Infusionsoft/ContactDescription.ts rename to packages/nodes-base/nodes/Keap/ContactDescription.ts index a70e41776..b1611c518 100644 --- a/packages/nodes-base/nodes/Infusionsoft/ContactDescription.ts +++ b/packages/nodes-base/nodes/Keap/ContactDescription.ts @@ -17,7 +17,7 @@ export const contactOperations = [ options: [ { name: 'Create/Update', - value: 'create/update', + value: 'upsert', description: 'Create/update a contact', }, { @@ -36,7 +36,7 @@ export const contactOperations = [ description: 'Retrieve all contacts', }, ], - default: 'create/update', + default: 'upsert', description: 'The operation to perform.', }, ] as INodeProperties[]; @@ -44,7 +44,7 @@ export const contactOperations = [ export const contactFields = [ /* -------------------------------------------------------------------------- */ -/* contact:create/update */ +/* contact:upsert */ /* -------------------------------------------------------------------------- */ { displayName: 'Duplicate Option', @@ -64,7 +64,7 @@ export const contactFields = [ displayOptions: { show: { operation: [ - 'create/update', + 'upsert', ], resource: [ 'contact', @@ -84,7 +84,7 @@ export const contactFields = [ displayOptions: { show: { operation: [ - 'create/update', + 'upsert', ], resource: [ 'contact', @@ -250,7 +250,7 @@ export const contactFields = [ 'contact', ], operation: [ - 'create/update', + 'upsert', ], }, }, @@ -349,7 +349,7 @@ export const contactFields = [ 'contact', ], operation: [ - 'create/update', + 'upsert', ], }, }, @@ -403,7 +403,7 @@ export const contactFields = [ 'contact', ], operation: [ - 'create/update', + 'upsert', ], }, }, @@ -453,7 +453,7 @@ export const contactFields = [ 'contact', ], operation: [ - 'create/update', + 'upsert', ], }, }, @@ -515,7 +515,7 @@ export const contactFields = [ 'contact', ], operation: [ - 'create/update', + 'upsert', ], }, }, diff --git a/packages/nodes-base/nodes/Infusionsoft/ContactNoteDescription.ts b/packages/nodes-base/nodes/Keap/ContactNoteDescription.ts similarity index 100% rename from packages/nodes-base/nodes/Infusionsoft/ContactNoteDescription.ts rename to packages/nodes-base/nodes/Keap/ContactNoteDescription.ts diff --git a/packages/nodes-base/nodes/Infusionsoft/ContactNoteInterface.ts b/packages/nodes-base/nodes/Keap/ContactNoteInterface.ts similarity index 100% rename from packages/nodes-base/nodes/Infusionsoft/ContactNoteInterface.ts rename to packages/nodes-base/nodes/Keap/ContactNoteInterface.ts diff --git a/packages/nodes-base/nodes/Infusionsoft/ContactTagDescription.ts b/packages/nodes-base/nodes/Keap/ContactTagDescription.ts similarity index 100% rename from packages/nodes-base/nodes/Infusionsoft/ContactTagDescription.ts rename to packages/nodes-base/nodes/Keap/ContactTagDescription.ts diff --git a/packages/nodes-base/nodes/Infusionsoft/EcommerceOrderDescripion.ts b/packages/nodes-base/nodes/Keap/EcommerceOrderDescripion.ts similarity index 100% rename from packages/nodes-base/nodes/Infusionsoft/EcommerceOrderDescripion.ts rename to packages/nodes-base/nodes/Keap/EcommerceOrderDescripion.ts diff --git a/packages/nodes-base/nodes/Infusionsoft/EcommerceOrderInterface.ts b/packages/nodes-base/nodes/Keap/EcommerceOrderInterface.ts similarity index 100% rename from packages/nodes-base/nodes/Infusionsoft/EcommerceOrderInterface.ts rename to packages/nodes-base/nodes/Keap/EcommerceOrderInterface.ts diff --git a/packages/nodes-base/nodes/Infusionsoft/EcommerceProductDescription.ts b/packages/nodes-base/nodes/Keap/EcommerceProductDescription.ts similarity index 100% rename from packages/nodes-base/nodes/Infusionsoft/EcommerceProductDescription.ts rename to packages/nodes-base/nodes/Keap/EcommerceProductDescription.ts diff --git a/packages/nodes-base/nodes/Infusionsoft/EcommerceProductInterface.ts b/packages/nodes-base/nodes/Keap/EcommerceProductInterface.ts similarity index 100% rename from packages/nodes-base/nodes/Infusionsoft/EcommerceProductInterface.ts rename to packages/nodes-base/nodes/Keap/EcommerceProductInterface.ts diff --git a/packages/nodes-base/nodes/Infusionsoft/EmaiIInterface.ts b/packages/nodes-base/nodes/Keap/EmaiIInterface.ts similarity index 100% rename from packages/nodes-base/nodes/Infusionsoft/EmaiIInterface.ts rename to packages/nodes-base/nodes/Keap/EmaiIInterface.ts diff --git a/packages/nodes-base/nodes/Infusionsoft/EmailDescription.ts b/packages/nodes-base/nodes/Keap/EmailDescription.ts similarity index 100% rename from packages/nodes-base/nodes/Infusionsoft/EmailDescription.ts rename to packages/nodes-base/nodes/Keap/EmailDescription.ts diff --git a/packages/nodes-base/nodes/Infusionsoft/FileDescription.ts b/packages/nodes-base/nodes/Keap/FileDescription.ts similarity index 100% rename from packages/nodes-base/nodes/Infusionsoft/FileDescription.ts rename to packages/nodes-base/nodes/Keap/FileDescription.ts diff --git a/packages/nodes-base/nodes/Infusionsoft/FileInterface.ts b/packages/nodes-base/nodes/Keap/FileInterface.ts similarity index 100% rename from packages/nodes-base/nodes/Infusionsoft/FileInterface.ts rename to packages/nodes-base/nodes/Keap/FileInterface.ts diff --git a/packages/nodes-base/nodes/Infusionsoft/GenericFunctions.ts b/packages/nodes-base/nodes/Keap/GenericFunctions.ts similarity index 68% rename from packages/nodes-base/nodes/Infusionsoft/GenericFunctions.ts rename to packages/nodes-base/nodes/Keap/GenericFunctions.ts index 4d942ccd6..c04fb057d 100644 --- a/packages/nodes-base/nodes/Infusionsoft/GenericFunctions.ts +++ b/packages/nodes-base/nodes/Keap/GenericFunctions.ts @@ -17,7 +17,7 @@ import { snakeCase, } from 'change-case'; -export async function infusionsoftApiRequest(this: IWebhookFunctions | IHookFunctions | IExecuteFunctions | ILoadOptionsFunctions, method: string, resource: string, body: any = {}, qs: IDataObject = {}, uri?: string, headers: IDataObject = {}, option: IDataObject = {}): Promise { // tslint:disable-line:no-any +export async function keapApiRequest(this: IWebhookFunctions | IHookFunctions | IExecuteFunctions | ILoadOptionsFunctions, method: string, resource: string, body: any = {}, qs: IDataObject = {}, uri?: string, headers: IDataObject = {}, option: IDataObject = {}): Promise { // tslint:disable-line:no-any let options: OptionsWithUri = { headers: { 'Content-Type': 'application/json', @@ -37,7 +37,7 @@ export async function infusionsoftApiRequest(this: IWebhookFunctions | IHookFunc delete options.body; } //@ts-ignore - return await this.helpers.requestOAuth.call(this, 'infusionsoftOAuth2Api', options); + return await this.helpers.requestOAuth.call(this, 'keapOAuth2Api', options); } catch (error) { if (error.response && error.response.body && error.response.body.message) { // Try to return the error prettier @@ -47,7 +47,7 @@ export async function infusionsoftApiRequest(this: IWebhookFunctions | IHookFunc } } -export async function infusionsoftApiRequestAllItems(this: IHookFunctions| IExecuteFunctions | ILoadOptionsFunctions, propertyName: string, method: string, endpoint: string, body: any = {}, query: IDataObject = {}): Promise { // tslint:disable-line:no-any +export async function keapApiRequestAllItems(this: IHookFunctions| IExecuteFunctions | ILoadOptionsFunctions, propertyName: string, method: string, endpoint: string, body: any = {}, query: IDataObject = {}): Promise { // tslint:disable-line:no-any const returnData: IDataObject[] = []; @@ -56,7 +56,7 @@ export async function infusionsoftApiRequestAllItems(this: IHookFunctions| IExec query.limit = 50; do { - responseData = await infusionsoftApiRequest.call(this, method, endpoint, body, query, uri); + responseData = await keapApiRequest.call(this, method, endpoint, body, query, uri); uri = responseData.next; returnData.push.apply(returnData, responseData[propertyName]); } while ( diff --git a/packages/nodes-base/nodes/Infusionsoft/Infusionsoft.node.ts b/packages/nodes-base/nodes/Keap/Keap.node.ts similarity index 85% rename from packages/nodes-base/nodes/Infusionsoft/Infusionsoft.node.ts rename to packages/nodes-base/nodes/Keap/Keap.node.ts index a2740c0f4..45c755ffd 100644 --- a/packages/nodes-base/nodes/Infusionsoft/Infusionsoft.node.ts +++ b/packages/nodes-base/nodes/Keap/Keap.node.ts @@ -13,8 +13,8 @@ import { } from 'n8n-workflow'; import { - infusionsoftApiRequest, - infusionsoftApiRequestAllItems, + keapApiRequest, + keapApiRequestAllItems, keysToSnakeCase, } from './GenericFunctions'; @@ -101,24 +101,24 @@ import { import * as moment from 'moment-timezone'; -export class Infusionsoft implements INodeType { +export class Keap implements INodeType { description: INodeTypeDescription = { - displayName: 'Infusionsoft', - name: ' infusionsoft', - icon: 'file:infusionsoft.png', + displayName: 'Keap', + name: ' keap', + icon: 'file:keap.png', group: ['input'], version: 1, subtitle: '={{$parameter["operation"] + ": " + $parameter["resource"]}}', - description: 'Consume Infusionsoft API.', + description: 'Consume Keap API.', defaults: { - name: 'Infusionsoft', + name: 'Keap', color: '#79af53', }, inputs: ['main'], outputs: ['main'], credentials: [ { - name: 'infusionsoftOAuth2Api', + name: 'keapOAuth2Api', required: true, }, ], @@ -197,7 +197,7 @@ export class Infusionsoft implements INodeType { // select them easily async getTags(this: ILoadOptionsFunctions): Promise { const returnData: INodePropertyOptions[] = []; - const tags = await infusionsoftApiRequestAllItems.call(this, 'tags', 'GET', '/tags'); + const tags = await keapApiRequestAllItems.call(this, 'tags', 'GET', '/tags'); for (const tag of tags) { const tagName = tag.name; const tagId = tag.id; @@ -212,7 +212,7 @@ export class Infusionsoft implements INodeType { // select them easily async getUsers(this: ILoadOptionsFunctions): Promise { const returnData: INodePropertyOptions[] = []; - const users = await infusionsoftApiRequestAllItems.call(this, 'users', 'GET', '/users'); + const users = await keapApiRequestAllItems.call(this, 'users', 'GET', '/users'); for (const user of users) { const userName = user.given_name; const userId = user.id; @@ -227,7 +227,7 @@ export class Infusionsoft implements INodeType { // select them easily async getCountries(this: ILoadOptionsFunctions): Promise { const returnData: INodePropertyOptions[] = []; - const { countries } = await infusionsoftApiRequest.call(this, 'GET', '/locales/countries'); + const { countries } = await keapApiRequest.call(this, 'GET', '/locales/countries'); for (const key of Object.keys(countries)) { const countryName = countries[key]; const countryId = key; @@ -243,7 +243,7 @@ export class Infusionsoft implements INodeType { async getProvinces(this: ILoadOptionsFunctions): Promise { const countryCode = this.getCurrentNodeParameter('countryCode') as string; const returnData: INodePropertyOptions[] = []; - const { provinces } = await infusionsoftApiRequest.call(this, 'GET', `/locales/countries/${countryCode}/provinces`); + const { provinces } = await keapApiRequest.call(this, 'GET', `/locales/countries/${countryCode}/provinces`); for (const key of Object.keys(provinces)) { const provinceName = provinces[key]; const provinceId = key; @@ -258,7 +258,7 @@ export class Infusionsoft implements INodeType { // select them easily async getContactTypes(this: ILoadOptionsFunctions): Promise { const returnData: INodePropertyOptions[] = []; - const types = await infusionsoftApiRequest.call(this, 'GET', '/setting/contact/optionTypes'); + const types = await keapApiRequest.call(this, 'GET', '/setting/contact/optionTypes'); for (const type of types.value.split(',')) { const typeName = type; const typeId = type; @@ -296,7 +296,7 @@ export class Infusionsoft implements INodeType { const operation = this.getNodeParameter('operation', 0) as string; for (let i = 0; i < length; i++) { if (resource === 'company') { - //https://developer.infusionsoft.com/docs/rest/#!/Company/createCompanyUsingPOST + //https://developer.keap.com/docs/rest/#!/Company/createCompanyUsingPOST if (operation === 'create') { const addresses = (this.getNodeParameter('addressesUi', i) as IDataObject).addressesValues as IDataObject[]; const faxes = (this.getNodeParameter('faxesUi', i) as IDataObject).faxesValues as IDataObject[]; @@ -317,7 +317,7 @@ export class Infusionsoft implements INodeType { if (phones) { body.phone_number = phones[0]; } - responseData = await infusionsoftApiRequest.call(this, 'POST', '/companies', body); + responseData = await keapApiRequest.call(this, 'POST', '/companies', body); } //https://developer.infusionsoft.com/docs/rest/#!/Company/listCompaniesUsingGET if (operation === 'getAll') { @@ -330,17 +330,17 @@ export class Infusionsoft implements INodeType { delete qs.fields; } if (returnAll) { - responseData = await infusionsoftApiRequestAllItems.call(this, 'companies', 'GET', '/companies', {}, qs); + responseData = await keapApiRequestAllItems.call(this, 'companies', 'GET', '/companies', {}, qs); } else { qs.limit = this.getNodeParameter('limit', i) as number; - responseData = await infusionsoftApiRequest.call(this, 'GET', '/companies', {}, qs); + responseData = await keapApiRequest.call(this, 'GET', '/companies', {}, qs); responseData = responseData.companies; } } } if (resource === 'contact') { //https://developer.infusionsoft.com/docs/rest/#!/Contact/createOrUpdateContactUsingPUT - if (operation === 'create/update') { + if (operation === 'upsert') { const duplicateOption = this.getNodeParameter('duplicateOption', i) as string; const addresses = (this.getNodeParameter('addressesUi', i) as IDataObject).addressesValues as IDataObject[]; const emails = (this.getNodeParameter('emailsUi', i) as IDataObject).emailsValues as IDataObject[]; @@ -421,12 +421,12 @@ export class Infusionsoft implements INodeType { if (phones) { body.phone_numbers = phones as IPhone[]; } - responseData = await infusionsoftApiRequest.call(this, 'PUT', '/contacts', body); + responseData = await keapApiRequest.call(this, 'PUT', '/contacts', body); } //https://developer.infusionsoft.com/docs/rest/#!/Contact/deleteContactUsingDELETE if (operation === 'delete') { const contactId = parseInt(this.getNodeParameter('contactId', i) as string, 10); - responseData = await infusionsoftApiRequest.call(this, 'DELETE', `/contacts/${contactId}`); + responseData = await keapApiRequest.call(this, 'DELETE', `/contacts/${contactId}`); responseData = { success: true }; } //https://developer.infusionsoft.com/docs/rest/#!/Contact/getContactUsingGET @@ -436,7 +436,7 @@ export class Infusionsoft implements INodeType { if (options.fields) { qs.optional_properties = options.fields as string; } - responseData = await infusionsoftApiRequest.call(this, 'GET', `/contacts/${contactId}`, {}, qs); + responseData = await keapApiRequest.call(this, 'GET', `/contacts/${contactId}`, {}, qs); } //https://developer.infusionsoft.com/docs/rest/#!/Contact/listContactsUsingGET if (operation === 'getAll') { @@ -464,10 +464,10 @@ export class Infusionsoft implements INodeType { qs.until = options.until as string; } if (returnAll) { - responseData = await infusionsoftApiRequestAllItems.call(this, 'contacts', 'GET', '/contacts', {}, qs); + responseData = await keapApiRequestAllItems.call(this, 'contacts', 'GET', '/contacts', {}, qs); } else { qs.limit = this.getNodeParameter('limit', i) as number; - responseData = await infusionsoftApiRequest.call(this, 'GET', '/contacts', {}, qs); + responseData = await keapApiRequest.call(this, 'GET', '/contacts', {}, qs); responseData = responseData.contacts; } } @@ -487,18 +487,18 @@ export class Infusionsoft implements INodeType { additionalFields.type = pascalCase(additionalFields.type as string); } Object.assign(body, additionalFields); - responseData = await infusionsoftApiRequest.call(this, 'POST', '/notes', body); + responseData = await keapApiRequest.call(this, 'POST', '/notes', body); } //https://developer.infusionsoft.com/docs/rest/#!/Note/deleteNoteUsingDELETE if (operation === 'delete') { const noteId = this.getNodeParameter('noteId', i) as string; - responseData = await infusionsoftApiRequest.call(this, 'DELETE', `/notes/${noteId}`); + responseData = await keapApiRequest.call(this, 'DELETE', `/notes/${noteId}`); responseData = { success: true }; } //https://developer.infusionsoft.com/docs/rest/#!/Note/getNoteUsingGET if (operation === 'get') { const noteId = this.getNodeParameter('noteId', i) as string; - responseData = await infusionsoftApiRequest.call(this, 'GET', `/notes/${noteId}`); + responseData = await keapApiRequest.call(this, 'GET', `/notes/${noteId}`); } //https://developer.infusionsoft.com/docs/rest/#!/Note/listNotesUsingGET if (operation === 'getAll') { @@ -507,10 +507,10 @@ export class Infusionsoft implements INodeType { keysToSnakeCase(filters); Object.assign(qs, filters); if (returnAll) { - responseData = await infusionsoftApiRequestAllItems.call(this, 'notes', 'GET', '/notes', {}, qs); + responseData = await keapApiRequestAllItems.call(this, 'notes', 'GET', '/notes', {}, qs); } else { qs.limit = this.getNodeParameter('limit', i) as number; - responseData = await infusionsoftApiRequest.call(this, 'GET', '/notes', {}, qs); + responseData = await keapApiRequest.call(this, 'GET', '/notes', {}, qs); responseData = responseData.notes; } } @@ -524,7 +524,7 @@ export class Infusionsoft implements INodeType { additionalFields.type = pascalCase(additionalFields.type as string); } Object.assign(body, additionalFields); - responseData = await infusionsoftApiRequest.call(this, 'PATCH', `/notes/${noteId}`, body); + responseData = await keapApiRequest.call(this, 'PATCH', `/notes/${noteId}`, body); } } if (resource === 'contactTag') { @@ -535,14 +535,14 @@ export class Infusionsoft implements INodeType { const body: IDataObject = { tagIds, }; - responseData = await infusionsoftApiRequest.call(this, 'POST', `/contacts/${contactId}/tags`, body); + responseData = await keapApiRequest.call(this, 'POST', `/contacts/${contactId}/tags`, body); } //https://developer.infusionsoft.com/docs/rest/#!/Contact/removeTagsFromContactUsingDELETE_1 if (operation === 'delete') { const contactId = parseInt(this.getNodeParameter('contactId', i) as string, 10); const tagIds = this.getNodeParameter('tagIds', i) as string; qs.ids = tagIds; - responseData = await infusionsoftApiRequest.call(this, 'DELETE', `/contacts/${contactId}/tags`, {}, qs); + responseData = await keapApiRequest.call(this, 'DELETE', `/contacts/${contactId}/tags`, {}, qs); responseData = { success: true }; } //https://developer.infusionsoft.com/docs/rest/#!/Contact/listAppliedTagsUsingGET @@ -550,10 +550,10 @@ export class Infusionsoft implements INodeType { const returnAll = this.getNodeParameter('returnAll', i) as boolean; const contactId = parseInt(this.getNodeParameter('contactId', i) as string, 10); if (returnAll) { - responseData = await infusionsoftApiRequestAllItems.call(this, 'tags', 'GET', `/contacts/${contactId}/tags`, {}, qs); + responseData = await keapApiRequestAllItems.call(this, 'tags', 'GET', `/contacts/${contactId}/tags`, {}, qs); } else { qs.limit = this.getNodeParameter('limit', i) as number; - responseData = await infusionsoftApiRequest.call(this, 'GET', `/contacts/${contactId}/tags`, {}, qs); + responseData = await keapApiRequest.call(this, 'GET', `/contacts/${contactId}/tags`, {}, qs); responseData = responseData.tags; } } @@ -583,18 +583,18 @@ export class Infusionsoft implements INodeType { if (shippingAddress) { body.shipping_address = keysToSnakeCase(shippingAddress)[0] as IShippingAddress; } - responseData = await infusionsoftApiRequest.call(this, 'POST', '/orders', body); + responseData = await keapApiRequest.call(this, 'POST', '/orders', body); } //https://developer.infusionsoft.com/docs/rest/#!/E-Commerce/deleteOrderUsingDELETE if (operation === 'delete') { const orderId = parseInt(this.getNodeParameter('orderId', i) as string, 10); - responseData = await infusionsoftApiRequest.call(this, 'DELETE', `/orders/${orderId}`); + responseData = await keapApiRequest.call(this, 'DELETE', `/orders/${orderId}`); responseData = { success: true }; } //https://developer.infusionsoft.com/docs/rest/#!/E-Commerce/getOrderUsingGET if (operation === 'get') { const orderId = parseInt(this.getNodeParameter('orderId', i) as string, 10); - responseData = await infusionsoftApiRequest.call(this, 'get', `/orders/${orderId}`); + responseData = await keapApiRequest.call(this, 'get', `/orders/${orderId}`); } //https://developer.infusionsoft.com/docs/rest/#!/E-Commerce/listOrdersUsingGET if (operation === 'getAll') { @@ -603,10 +603,10 @@ export class Infusionsoft implements INodeType { keysToSnakeCase(options); Object.assign(qs, options); if (returnAll) { - responseData = await infusionsoftApiRequestAllItems.call(this, 'orders', 'GET', '/orders', {}, qs); + responseData = await keapApiRequestAllItems.call(this, 'orders', 'GET', '/orders', {}, qs); } else { qs.limit = this.getNodeParameter('limit', i) as number; - responseData = await infusionsoftApiRequest.call(this, 'GET', '/orders', {}, qs); + responseData = await keapApiRequest.call(this, 'GET', '/orders', {}, qs); responseData = responseData.orders; } } @@ -621,18 +621,18 @@ export class Infusionsoft implements INodeType { }; keysToSnakeCase(additionalFields); Object.assign(body, additionalFields); - responseData = await infusionsoftApiRequest.call(this, 'POST', '/products', body); + responseData = await keapApiRequest.call(this, 'POST', '/products', body); } //https://developer.infusionsoft.com/docs/rest/#!/Product/deleteProductUsingDELETE if (operation === 'delete') { const productId = this.getNodeParameter('productId', i) as string; - responseData = await infusionsoftApiRequest.call(this, 'DELETE', `/products/${productId}`); + responseData = await keapApiRequest.call(this, 'DELETE', `/products/${productId}`); responseData = { success: true }; } //https://developer.infusionsoft.com/docs/rest/#!/Product/retrieveProductUsingGET if (operation === 'get') { const productId = this.getNodeParameter('productId', i) as string; - responseData = await infusionsoftApiRequest.call(this, 'get', `/products/${productId}`); + responseData = await keapApiRequest.call(this, 'get', `/products/${productId}`); } //https://developer.infusionsoft.com/docs/rest/#!/Product/listProductsUsingGET if (operation === 'getAll') { @@ -641,10 +641,10 @@ export class Infusionsoft implements INodeType { keysToSnakeCase(filters); Object.assign(qs, filters); if (returnAll) { - responseData = await infusionsoftApiRequestAllItems.call(this, 'products', 'GET', '/products', {}, qs); + responseData = await keapApiRequestAllItems.call(this, 'products', 'GET', '/products', {}, qs); } else { qs.limit = this.getNodeParameter('limit', i) as number; - responseData = await infusionsoftApiRequest.call(this, 'GET', '/products', {}, qs); + responseData = await keapApiRequest.call(this, 'GET', '/products', {}, qs); responseData = responseData.products; } } @@ -661,12 +661,12 @@ export class Infusionsoft implements INodeType { }; Object.assign(body, additionalFields); keysToSnakeCase(body as IDataObject); - responseData = await infusionsoftApiRequest.call(this, 'POST', '/emails', body); + responseData = await keapApiRequest.call(this, 'POST', '/emails', body); } //https://developer.infusionsoft.com/docs/rest/#!/Email/deleteEmailUsingDELETE if (operation === 'deleteRecord') { const emailRecordId = parseInt(this.getNodeParameter('emailRecordId', i) as string, 10); - responseData = await infusionsoftApiRequest.call(this, 'DELETE', `/emails/${emailRecordId}`); + responseData = await keapApiRequest.call(this, 'DELETE', `/emails/${emailRecordId}`); responseData = { success: true }; } //https://developer.infusionsoft.com/docs/rest/#!/Email/listEmailsUsingGET @@ -676,10 +676,10 @@ export class Infusionsoft implements INodeType { keysToSnakeCase(filters); Object.assign(qs, filters); if (returnAll) { - responseData = await infusionsoftApiRequestAllItems.call(this, 'emails', 'GET', '/emails', {}, qs); + responseData = await keapApiRequestAllItems.call(this, 'emails', 'GET', '/emails', {}, qs); } else { qs.limit = this.getNodeParameter('limit', i) as number; - responseData = await infusionsoftApiRequest.call(this, 'GET', '/emails', {}, qs); + responseData = await keapApiRequest.call(this, 'GET', '/emails', {}, qs); responseData = responseData.emails; } } @@ -728,7 +728,7 @@ export class Infusionsoft implements INodeType { body.attachments = attachments; } - responseData = await infusionsoftApiRequest.call(this, 'POST', '/emails/queue', body); + responseData = await keapApiRequest.call(this, 'POST', '/emails/queue', body); responseData = { success: true }; } } @@ -736,7 +736,7 @@ export class Infusionsoft implements INodeType { //https://developer.infusionsoft.com/docs/rest/#!/File/deleteFileUsingDELETE if (operation === 'delete') { const fileId = parseInt(this.getNodeParameter('fileId', i) as string, 10); - responseData = await infusionsoftApiRequest.call(this, 'DELETE', `/files/${fileId}`); + responseData = await keapApiRequest.call(this, 'DELETE', `/files/${fileId}`); responseData = { success: true }; } //https://developer.infusionsoft.com/docs/rest/#!/File/listFilesUsingGET @@ -755,10 +755,10 @@ export class Infusionsoft implements INodeType { qs.viewable = (qs.viewable as string).toUpperCase(); } if (returnAll) { - responseData = await infusionsoftApiRequestAllItems.call(this, 'files', 'GET', '/files', {}, qs); + responseData = await keapApiRequestAllItems.call(this, 'files', 'GET', '/files', {}, qs); } else { qs.limit = this.getNodeParameter('limit', i) as number; - responseData = await infusionsoftApiRequest.call(this, 'GET', '/files', {}, qs); + responseData = await keapApiRequest.call(this, 'GET', '/files', {}, qs); responseData = responseData.files; } } @@ -797,7 +797,7 @@ export class Infusionsoft implements INodeType { body.file_name = fileName; body.file_data = fileData; } - responseData = await infusionsoftApiRequest.call(this, 'POST', '/files', body); + responseData = await keapApiRequest.call(this, 'POST', '/files', body); } } if (Array.isArray(responseData)) { diff --git a/packages/nodes-base/nodes/Infusionsoft/InfusionsoftTrigger.node.ts b/packages/nodes-base/nodes/Keap/KeapTrigger.node.ts similarity index 87% rename from packages/nodes-base/nodes/Infusionsoft/InfusionsoftTrigger.node.ts rename to packages/nodes-base/nodes/Keap/KeapTrigger.node.ts index 756b170d6..d7bb614d6 100644 --- a/packages/nodes-base/nodes/Infusionsoft/InfusionsoftTrigger.node.ts +++ b/packages/nodes-base/nodes/Keap/KeapTrigger.node.ts @@ -13,31 +13,31 @@ import { } from 'n8n-workflow'; import { - infusionsoftApiRequest, + keapApiRequest, } from './GenericFunctions'; import { titleCase, } from 'change-case'; -export class InfusionsoftTrigger implements INodeType { +export class KeapTrigger implements INodeType { description: INodeTypeDescription = { - displayName: 'Infusionsoft Trigger', - name: 'infusionsoftTrigger', - icon: 'file:infusionsoft.png', + displayName: 'Keap Trigger', + name: 'keapTrigger', + icon: 'file:keap.png', group: ['trigger'], version: 1, subtitle: '={{$parameter["eventId"]}}', description: 'Starts the workflow when Infusionsoft events occure.', defaults: { - name: 'Infusionsoft Trigger', + name: 'Keap Trigger', color: '#79af53', }, inputs: [], outputs: ['main'], credentials: [ { - name: 'infusionsoftOAuth2Api', + name: 'keapOAuth2Api', required: true, }, ], @@ -76,7 +76,7 @@ export class InfusionsoftTrigger implements INodeType { // select them easily async getEvents(this: ILoadOptionsFunctions): Promise { const returnData: INodePropertyOptions[] = []; - const hooks = await infusionsoftApiRequest.call(this, 'GET', '/hooks/event_keys'); + const hooks = await keapApiRequest.call(this, 'GET', '/hooks/event_keys'); for (const hook of hooks) { const hookName = hook; const hookId = hook; @@ -98,7 +98,7 @@ export class InfusionsoftTrigger implements INodeType { const webhookUrl = this.getNodeWebhookUrl('default'); const webhookData = this.getWorkflowStaticData('node'); - const responseData = await infusionsoftApiRequest.call(this, 'GET', '/hooks', {}); + const responseData = await keapApiRequest.call(this, 'GET', '/hooks', {}); for (const existingData of responseData) { if (existingData.hookUrl === webhookUrl @@ -122,7 +122,7 @@ export class InfusionsoftTrigger implements INodeType { hookUrl: webhookUrl, }; - const responseData = await infusionsoftApiRequest.call(this, 'POST', '/hooks', body); + const responseData = await keapApiRequest.call(this, 'POST', '/hooks', body); if (responseData.key === undefined) { // Required data is missing so was not successful @@ -139,7 +139,7 @@ export class InfusionsoftTrigger implements INodeType { if (webhookData.webhookId !== undefined) { try { - await infusionsoftApiRequest.call(this, 'DELETE', `/hooks/${webhookData.webhookId}`); + await keapApiRequest.call(this, 'DELETE', `/hooks/${webhookData.webhookId}`); } catch (e) { return false; } diff --git a/packages/nodes-base/nodes/Infusionsoft/infusionsoft.png b/packages/nodes-base/nodes/Keap/keap.png similarity index 100% rename from packages/nodes-base/nodes/Infusionsoft/infusionsoft.png rename to packages/nodes-base/nodes/Keap/keap.png diff --git a/packages/nodes-base/package.json b/packages/nodes-base/package.json index 9cc9d2bc6..1242479bf 100644 --- a/packages/nodes-base/package.json +++ b/packages/nodes-base/package.json @@ -50,8 +50,8 @@ "dist/credentials/HubspotApi.credentials.js", "dist/credentials/Imap.credentials.js", "dist/credentials/IntercomApi.credentials.js", - "dist/credentials/InfusionsoftOAuth2Api.credentials.js", "dist/credentials/JiraSoftwareCloudApi.credentials.js", + "dist/credentials/KeapOAuth2Api.credentials.js", "dist/credentials/LinkFishApi.credentials.js", "dist/credentials/MailchimpApi.credentials.js", "dist/credentials/MailgunApi.credentials.js", @@ -129,9 +129,9 @@ "dist/nodes/If.node.js", "dist/nodes/Interval.node.js", "dist/nodes/Intercom/Intercom.node.js", - "dist/nodes/Infusionsoft/Infusionsoft.node.js", - "dist/nodes/Infusionsoft/InfusionsoftTrigger.node.js", "dist/nodes/Jira/JiraSoftwareCloud.node.js", + "dist/nodes/Keap/Keap.node.js", + "dist/nodes/Keap/KeapTrigger.node.js", "dist/nodes/LinkFish/LinkFish.node.js", "dist/nodes/Mailchimp/Mailchimp.node.js", "dist/nodes/Mailchimp/MailchimpTrigger.node.js",