diff --git a/packages/cli/src/Server.ts b/packages/cli/src/Server.ts index 4fe1ef70d7..99cda09412 100644 --- a/packages/cli/src/Server.ts +++ b/packages/cli/src/Server.ts @@ -203,7 +203,7 @@ class App { }); } - jwt.verify(token, getKey, {}, (err: Error, decoded: string) => { + jwt.verify(token, getKey, {}, (err: Error, decoded: object) => { if (err) return ResponseHelper.jwtAuthAuthorizationError(res, "Invalid token"); next(); diff --git a/packages/nodes-base/credentials/HelpScoutOAuth2Api.credentials.ts b/packages/nodes-base/credentials/HelpScoutOAuth2Api.credentials.ts new file mode 100644 index 0000000000..0301bf2c51 --- /dev/null +++ b/packages/nodes-base/credentials/HelpScoutOAuth2Api.credentials.ts @@ -0,0 +1,46 @@ +import { + ICredentialType, + NodePropertyTypes, +} from 'n8n-workflow'; + +export class HelpScoutOAuth2Api implements ICredentialType { + name = 'helpScoutOAuth2Api'; + extends = [ + 'oAuth2Api', + ]; + displayName = 'HelpScout OAuth2 API'; + properties = [ + { + displayName: 'Authorization URL', + name: 'authUrl', + type: 'hidden' as NodePropertyTypes, + default: 'https://secure.helpscout.net/authentication/authorizeClientApplication', + required: true, + }, + { + displayName: 'Access Token URL', + name: 'accessTokenUrl', + type: 'hidden' as NodePropertyTypes, + default: 'https://api.helpscout.net/v2/oauth2/token', + required: true, + }, + { + displayName: 'Scope', + name: 'scope', + type: 'hidden' as NodePropertyTypes, + default: '', + }, + { + 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/HelpScout/ConversationDescription.ts b/packages/nodes-base/nodes/HelpScout/ConversationDescription.ts new file mode 100644 index 0000000000..cce35b096a --- /dev/null +++ b/packages/nodes-base/nodes/HelpScout/ConversationDescription.ts @@ -0,0 +1,598 @@ +import { INodeProperties } from "n8n-workflow"; + +export const conversationOperations = [ + { + displayName: 'Operation', + name: 'operation', + type: 'options', + displayOptions: { + show: { + resource: [ + 'conversation', + ], + }, + }, + options: [ + { + name: 'Create', + value: 'create', + description: 'Create a new conversation', + }, + { + name: 'Delete', + value: 'delete', + description: 'Delete a conversation', + }, + { + name: 'Get', + value: 'get', + description: 'Get a conversation', + }, + { + name: 'Get All', + value: 'getAll', + description: 'Get all conversations', + }, + ], + default: 'create', + description: 'The operation to perform.', + }, +] as INodeProperties[]; + +export const conversationFields = [ +/* -------------------------------------------------------------------------- */ +/* conversation:create */ +/* -------------------------------------------------------------------------- */ + { + displayName: 'Mailbox', + name: 'mailboxId', + type: 'options', + typeOptions: { + loadOptionsMethod: 'getMailboxes', + }, + required: true, + displayOptions: { + show: { + operation: [ + 'create', + ], + resource: [ + 'conversation', + ], + }, + }, + default: '', + description: 'ID of a mailbox where the conversation is being created', + }, + { + displayName: 'Status', + name: 'status', + type: 'options', + required: true, + options: [ + { + name: 'Active', + value: 'active', + }, + { + name: 'Closed', + value: 'closed', + }, + { + name: 'Pending', + value: 'pending', + }, + ], + displayOptions: { + show: { + operation: [ + 'create', + ], + resource: [ + 'conversation', + ], + }, + }, + default: '', + description: 'Conversation status', + }, + { + displayName: 'Subject', + name: 'subject', + type: 'string', + required: true, + typeOptions: { + alwaysOpenEditWindow: true, + }, + displayOptions: { + show: { + operation: [ + 'create', + ], + resource: [ + 'conversation', + ], + }, + }, + default: '', + description: `Conversation’s subject`, + }, + { + displayName: 'Type', + name: 'type', + required: true, + type: 'options', + options: [ + { + name: 'Chat', + value: 'chat', + }, + { + name: 'Email', + value: 'email', + }, + { + name: 'Phone', + value: 'phone', + }, + ], + displayOptions: { + show: { + operation: [ + 'create', + ], + resource: [ + 'conversation', + ], + }, + }, + default: '', + description: 'Conversation type', + }, + { + displayName: 'Resolve Data', + name: 'resolveData', + type: 'boolean', + default: true, + displayOptions: { + show: { + operation: [ + 'create', + ], + resource: [ + 'conversation', + ], + }, + }, + description: 'By default the response only contain the ID to resource
. If this option gets activated it
will resolve the data automatically.', + }, + { + displayName: 'Additional Fields', + name: 'additionalFields', + type: 'collection', + placeholder: 'Add Field', + default: {}, + displayOptions: { + show: { + operation: [ + 'create', + ], + resource: [ + 'conversation', + ], + }, + }, + options: [ + { + displayName: 'Assign To', + name: 'assignTo', + type: 'number', + default: 0, + description: 'The Help Scout user assigned to the conversation.', + }, + { + displayName: 'Auto Reply', + name: 'autoReply', + type: 'boolean', + default: false, + description: `When autoReply is set to true, an auto reply will be sent
+ as long as there is at least one customer thread in the conversation.`, + }, + { + displayName: 'Closed At', + name: 'closedAt', + type: 'dateTime', + default: '', + description: `When the conversation was closed, only applicable for imported conversations`, + }, + { + displayName: 'Created At', + name: 'createdAt', + type: 'dateTime', + default: '', + description: `When this conversation was created - ISO 8601 date time`, + }, + { + displayName: 'Customer Email', + name: 'customerEmail', + type: 'string', + default: '', + }, + { + displayName: 'Customer ID', + name: 'customerId', + type: 'number', + default: 0, + }, + { + displayName: 'Imported', + name: 'imported', + type: 'boolean', + default: false, + description: `When imported is set to true, no outgoing emails or notifications will be generated.`, + }, + { + displayName: 'Tags', + name: 'tags', + type: 'multiOptions', + typeOptions: { + loadOptionsMethod: 'getTags', + }, + default: [], + description: 'List of tags to be be added to the conversation', + }, + { + displayName: 'User ID', + name: 'user', + type: 'number', + default: 0, + description: 'ID of the user who is adding the conversation and threads.', + }, + ] + }, + { + displayName: 'Threads', + name: 'threadsUi', + placeholder: 'Add Thread', + type: 'fixedCollection', + typeOptions: { + multipleValues: true, + }, + displayOptions: { + show: { + operation: [ + 'create', + ], + resource: [ + 'conversation', + ], + }, + }, + default: {}, + options: [ + { + displayName: 'Thread', + name: 'threadsValues', + values: [ + { + displayName: 'Type', + name: 'type', + type: 'options', + options: [ + { + name: 'Chat', + value: 'chat' + }, + { + name: 'Customer', + value: 'customer' + }, + { + name: 'Note', + value: 'note' + }, + { + name: 'Phone', + value: 'phone' + }, + { + name: 'Reply', + value: 'reply' + }, + ], + default: '', + }, + { + displayName: 'Text', + name: 'text', + type: 'string', + typeOptions: { + alwaysOpenEditWindow: true + }, + default: '', + description: 'The message text, ' + }, + { + displayName: 'Bcc', + name: 'bcc', + displayOptions: { + show: { + type: [ + 'customer' + ], + }, + }, + type: 'string', + typeOptions: { + multipleValues: true, + multipleValueButtonText: 'Add Email', + }, + default: [], + description: 'Email addresses.' + }, + { + displayName: 'Cc', + name: 'cc', + displayOptions: { + show: { + type: [ + 'customer' + ], + }, + }, + type: 'string', + typeOptions: { + multipleValues: true, + multipleValueButtonText: 'Add Email', + }, + default: [], + description: 'Email addresses.' + }, + { + displayName: 'Draft', + name: 'draft', + displayOptions: { + show: { + type: [ + 'reply' + ], + }, + }, + type: 'boolean', + default: false, + description: 'If set to true, a draft reply is created', + }, + ], + }, + ], + }, +/* -------------------------------------------------------------------------- */ +/* conversation:get */ +/* -------------------------------------------------------------------------- */ + { + displayName: 'Conversation ID', + name: 'conversationId', + type: 'string', + default: '', + required: true, + displayOptions: { + show: { + resource: [ + 'conversation', + ], + operation: [ + 'get', + ], + }, + }, + description: 'conversation ID', + }, +/* -------------------------------------------------------------------------- */ +/* conversation:delete */ +/* -------------------------------------------------------------------------- */ + { + displayName: 'Conversation ID', + name: 'conversationId', + type: 'string', + default: '', + required: true, + displayOptions: { + show: { + resource: [ + 'conversation', + ], + operation: [ + 'delete', + ], + }, + }, + description: 'conversation ID', + }, +/* -------------------------------------------------------------------------- */ +/* conversation:getAll */ +/* -------------------------------------------------------------------------- */ + { + displayName: 'Options', + name: 'options', + type: 'collection', + placeholder: 'Add Option', + default: {}, + displayOptions: { + show: { + resource: [ + 'conversation', + ], + operation: [ + 'getAll', + ], + }, + }, + options: [ + { + displayName: 'Embed', + name: 'embed', + type: 'options', + options: [ + { + name: 'Threads', + value: 'threads', + }, + ], + default: '', + description: 'Allows embedding/loading of sub-entities', + }, + { + displayName: 'Mailbox ID', + name: 'mailbox', + type: 'string', + default: '', + description: 'Filters conversations from a specific mailbox', + }, + { + displayName: 'Folder ID', + name: 'folder', + type: 'string', + default: '', + description: 'Filters conversations from a specific folder id', + }, + { + displayName: 'Status', + name: 'status', + type: 'options', + options: [ + { + name: 'Active', + value: 'active', + }, + { + name: 'All', + value: 'all', + }, + { + name: 'Closed', + value: 'closed', + }, + { + name: 'Open', + value: 'open', + }, + { + name: 'Pending', + value: 'pending', + }, + { + name: 'Spam', + value: 'spam', + }, + ], + default: 'active', + description: 'Filter conversation by status', + }, + { + displayName: 'Tags', + name: 'tags', + type: 'multiOptions', + typeOptions: { + loadOptionsMethod: 'getTags', + }, + default: [], + description: 'Filter conversation by tags', + }, + { + displayName: 'Assign To', + name: 'assignTo', + type: 'number', + default: 0, + description: 'Filters conversations by assignee id', + }, + { + displayName: 'Modified Since', + name: 'modifiedSince', + type: 'dateTime', + default: '', + description: 'Returns only conversations that were modified after this date', + }, + { + displayName: 'Number', + name: 'number', + type: 'number', + default: 0, + typeOptions: { + minValue: 0, + }, + description: 'Looks up conversation by conversation number', + }, + { + displayName: 'Sort Field', + name: 'sortField', + type: 'options', + options: [ + { + name: 'Created At', + value: 'createdAt', + }, + { + name: 'customer Email', + value: 'customerEmail', + }, + { + name: 'customer Name', + value: 'customerName', + }, + { + name: 'Mailbox ID', + value: 'mailboxid', + }, + { + name: 'Modified At', + value: 'modifiedAt', + }, + { + name: 'Number', + value: 'number', + }, + { + name: 'Score', + value: 'score', + }, + { + name: 'Status', + value: 'status', + }, + { + name: 'Subject', + value: 'subject', + }, + ], + default: '', + description: 'Sorts the result by specified field', + }, + { + displayName: 'Sort Order', + name: 'sortOrder', + type: 'options', + options: [ + { + name: 'ASC', + value: 'asc', + }, + { + name: 'Desc', + value: 'desc', + }, + ], + default: 'desc', + }, + { + displayName: 'Query', + name: 'query', + type: 'string', + typeOptions: { + alwaysOpenEditWindow: true, + }, + default: '', + description: 'Advanced search Examples' + }, + ], + }, +] as INodeProperties[]; diff --git a/packages/nodes-base/nodes/HelpScout/ConversationInterface.ts b/packages/nodes-base/nodes/HelpScout/ConversationInterface.ts new file mode 100644 index 0000000000..4140fda218 --- /dev/null +++ b/packages/nodes-base/nodes/HelpScout/ConversationInterface.ts @@ -0,0 +1,18 @@ +import { IDataObject } from "n8n-workflow"; + +export interface IConversation { + assignTo?: number; + autoReply?: boolean; + closedAt?: string; + createdAt?: string; + customer?: IDataObject; + fields?: IDataObject[]; + imported?: boolean; + mailboxId?: number; // + status?: string; // + subject?: string; // + tags?: IDataObject[]; + threads?: IDataObject[]; + type?: string; // + user?: number; +} diff --git a/packages/nodes-base/nodes/HelpScout/CountriesCodes.ts b/packages/nodes-base/nodes/HelpScout/CountriesCodes.ts new file mode 100644 index 0000000000..653e876beb --- /dev/null +++ b/packages/nodes-base/nodes/HelpScout/CountriesCodes.ts @@ -0,0 +1,1579 @@ +export const countriesCodes = [ + { + "name": "Afghanistan", + "alpha2": "AF", + "alpha3": "AFG", + "numeric": "004" + }, + { + "name": "Åland Islands", + "alpha2": "AX", + "alpha3": "ALA", + "numeric": "248", + "altName": "Aland Islands" + }, + { + "name": "Albania", + "alpha2": "AL", + "alpha3": "ALB", + "numeric": "008" + }, + { + "name": "Algeria", + "alpha2": "DZ", + "alpha3": "DZA", + "numeric": "012" + }, + { + "name": "American Samoa", + "alpha2": "AS", + "alpha3": "ASM", + "numeric": "016" + }, + { + "name": "Andorra", + "alpha2": "AD", + "alpha3": "AND", + "numeric": "020" + }, + { + "name": "Angola", + "alpha2": "AO", + "alpha3": "AGO", + "numeric": "024" + }, + { + "name": "Anguilla", + "alpha2": "AI", + "alpha3": "AIA", + "numeric": "660" + }, + { + "name": "Antarctica", + "alpha2": "AQ", + "alpha3": "ATA", + "numeric": "010" + }, + { + "name": "Antigua and Barbuda", + "alpha2": "AG", + "alpha3": "ATG", + "numeric": "028" + }, + { + "name": "Argentina", + "alpha2": "AR", + "alpha3": "ARG", + "numeric": "032" + }, + { + "name": "Armenia", + "alpha2": "AM", + "alpha3": "ARM", + "numeric": "051" + }, + { + "name": "Aruba", + "alpha2": "AW", + "alpha3": "ABW", + "numeric": "533" + }, + { + "name": "Australia", + "alpha2": "AU", + "alpha3": "AUS", + "numeric": "036" + }, + { + "name": "Austria", + "alpha2": "AT", + "alpha3": "AUT", + "numeric": "040" + }, + { + "name": "Azerbaijan", + "alpha2": "AZ", + "alpha3": "AZE", + "numeric": "031" + }, + { + "name": "Bahamas (the)", + "alpha2": "BS", + "alpha3": "BHS", + "numeric": "044", + "altName": "Bahamas" + }, + { + "name": "Bahrain", + "alpha2": "BH", + "alpha3": "BHR", + "numeric": "048" + }, + { + "name": "Bangladesh", + "alpha2": "BD", + "alpha3": "BGD", + "numeric": "050" + }, + { + "name": "Barbados", + "alpha2": "BB", + "alpha3": "BRB", + "numeric": "052" + }, + { + "name": "Belarus", + "alpha2": "BY", + "alpha3": "BLR", + "numeric": "112" + }, + { + "name": "Belgium", + "alpha2": "BE", + "alpha3": "BEL", + "numeric": "056" + }, + { + "name": "Belize", + "alpha2": "BZ", + "alpha3": "BLZ", + "numeric": "084" + }, + { + "name": "Benin", + "alpha2": "BJ", + "alpha3": "BEN", + "numeric": "204" + }, + { + "name": "Bermuda", + "alpha2": "BM", + "alpha3": "BMU", + "numeric": "060" + }, + { + "name": "Bhutan", + "alpha2": "BT", + "alpha3": "BTN", + "numeric": "064" + }, + { + "name": "Bolivia (Plurinational State of)", + "alpha2": "BO", + "alpha3": "BOL", + "numeric": "068", + "altName": "Bolivia" + }, + { + "name": "Bonaire, Sint Eustatius and Saba", + "alpha2": "BQ", + "alpha3": "BES", + "numeric": "535" + }, + { + "name": "Bosnia and Herzegovina", + "alpha2": "BA", + "alpha3": "BIH", + "numeric": "070" + }, + { + "name": "Botswana", + "alpha2": "BW", + "alpha3": "BWA", + "numeric": "072" + }, + { + "name": "Bouvet Island", + "alpha2": "BV", + "alpha3": "BVT", + "numeric": "074" + }, + { + "name": "Brazil", + "alpha2": "BR", + "alpha3": "BRA", + "numeric": "076" + }, + { + "name": "British Indian Ocean Territory (the)", + "alpha2": "IO", + "alpha3": "IOT", + "numeric": "086", + "altName": "British Indian Ocean Territory" + }, + { + "name": "Brunei Darussalam", + "alpha2": "BN", + "alpha3": "BRN", + "numeric": "096", + "shortName": "Brunei" + }, + { + "name": "Bulgaria", + "alpha2": "BG", + "alpha3": "BGR", + "numeric": "100" + }, + { + "name": "Burkina Faso", + "alpha2": "BF", + "alpha3": "BFA", + "numeric": "854" + }, + { + "name": "Burundi", + "alpha2": "BI", + "alpha3": "BDI", + "numeric": "108" + }, + { + "name": "Cabo Verde", + "alpha2": "CV", + "alpha3": "CPV", + "numeric": "132", + "altName": "Cape Verde" + }, + { + "name": "Cambodia", + "alpha2": "KH", + "alpha3": "KHM", + "numeric": "116" + }, + { + "name": "Cameroon", + "alpha2": "CM", + "alpha3": "CMR", + "numeric": "120" + }, + { + "name": "Canada", + "alpha2": "CA", + "alpha3": "CAN", + "numeric": "124" + }, + { + "name": "Cayman Islands (the)", + "alpha2": "KY", + "alpha3": "CYM", + "numeric": "136", + "altName": "Cayman Islands" + }, + { + "name": "Central African Republic (the)", + "alpha2": "CF", + "alpha3": "CAF", + "numeric": "140", + "altName": "Central African Republic" + }, + { + "name": "Chad", + "alpha2": "TD", + "alpha3": "TCD", + "numeric": "148" + }, + { + "name": "Chile", + "alpha2": "CL", + "alpha3": "CHL", + "numeric": "152" + }, + { + "name": "China", + "alpha2": "CN", + "alpha3": "CHN", + "numeric": "156" + }, + { + "name": "Christmas Island", + "alpha2": "CX", + "alpha3": "CXR", + "numeric": "162" + }, + { + "name": "Cocos (Keeling) Islands (the)", + "alpha2": "CC", + "alpha3": "CCK", + "numeric": "166", + "altName": "Cocos (Keeling) Islands", + "shortName": "Cocos Islands" + }, + { + "name": "Colombia", + "alpha2": "CO", + "alpha3": "COL", + "numeric": "170" + }, + { + "name": "Comoros (the)", + "alpha2": "KM", + "alpha3": "COM", + "numeric": "174", + "altName": "Comoros" + }, + { + "name": "Congo (the Democratic Republic of the)", + "alpha2": "CD", + "alpha3": "COD", + "numeric": "180", + "altName": "Congo, (Kinshasa)", + "shortName": "Democratic Republic of the Congo" + }, + { + "name": "Congo (the)", + "alpha2": "CG", + "alpha3": "COG", + "numeric": "178", + "altName": "Congo (Brazzaville)", + "shortName": "Republic of the Congo" + }, + { + "name": "Cook Islands (the)", + "alpha2": "CK", + "alpha3": "COK", + "numeric": "184", + "altName": "Cook Islands" + }, + { + "name": "Costa Rica", + "alpha2": "CR", + "alpha3": "CRI", + "numeric": "188" + }, + { + "name": "Côte d'Ivoire", + "alpha2": "CI", + "alpha3": "CIV", + "numeric": "384", + "shortName": "Ivory Coast" + }, + { + "name": "Croatia", + "alpha2": "HR", + "alpha3": "HRV", + "numeric": "191" + }, + { + "name": "Cuba", + "alpha2": "CU", + "alpha3": "CUB", + "numeric": "192" + }, + { + "name": "Curaçao", + "alpha2": "CW", + "alpha3": "CUW", + "numeric": "531", + "shortName": "Curacao" + }, + { + "name": "Cyprus", + "alpha2": "CY", + "alpha3": "CYP", + "numeric": "196" + }, + { + "name": "Czechia", + "alpha2": "CZ", + "alpha3": "CZE", + "numeric": "203", + "altName": "Czech Republic" + }, + { + "name": "Denmark", + "alpha2": "DK", + "alpha3": "DNK", + "numeric": "208" + }, + { + "name": "Djibouti", + "alpha2": "DJ", + "alpha3": "DJI", + "numeric": "262" + }, + { + "name": "Dominica", + "alpha2": "DM", + "alpha3": "DMA", + "numeric": "212" + }, + { + "name": "Dominican Republic (the)", + "alpha2": "DO", + "alpha3": "DOM", + "numeric": "214", + "altName": "Dominican Republic" + }, + { + "name": "Ecuador", + "alpha2": "EC", + "alpha3": "ECU", + "numeric": "218" + }, + { + "name": "Egypt", + "alpha2": "EG", + "alpha3": "EGY", + "numeric": "818" + }, + { + "name": "El Salvador", + "alpha2": "SV", + "alpha3": "SLV", + "numeric": "222" + }, + { + "name": "Equatorial Guinea", + "alpha2": "GQ", + "alpha3": "GNQ", + "numeric": "226" + }, + { + "name": "Eritrea", + "alpha2": "ER", + "alpha3": "ERI", + "numeric": "232" + }, + { + "name": "Estonia", + "alpha2": "EE", + "alpha3": "EST", + "numeric": "233" + }, + { + "name": "Ethiopia", + "alpha2": "ET", + "alpha3": "ETH", + "numeric": "231" + }, + { + "name": "Falkland Islands (the) [Malvinas]", + "alpha2": "FK", + "alpha3": "FLK", + "numeric": "238", + "altName": "Falkland Islands (Malvinas)", + "shortName": "Falkland Islands" + }, + { + "name": "Faroe Islands (the)", + "alpha2": "FO", + "alpha3": "FRO", + "numeric": "234", + "altName": "Faroe Islands" + }, + { + "name": "Fiji", + "alpha2": "FJ", + "alpha3": "FJI", + "numeric": "242" + }, + { + "name": "Finland", + "alpha2": "FI", + "alpha3": "FIN", + "numeric": "246" + }, + { + "name": "France", + "alpha2": "FR", + "alpha3": "FRA", + "numeric": "250" + }, + { + "name": "French Guiana", + "alpha2": "GF", + "alpha3": "GUF", + "numeric": "254" + }, + { + "name": "French Polynesia", + "alpha2": "PF", + "alpha3": "PYF", + "numeric": "258" + }, + { + "name": "French Southern Territories (the)", + "alpha2": "TF", + "alpha3": "ATF", + "numeric": "260", + "altName": "French Southern Territories" + }, + { + "name": "Gabon", + "alpha2": "GA", + "alpha3": "GAB", + "numeric": "266" + }, + { + "name": "Gambia (the)", + "alpha2": "GM", + "alpha3": "GMB", + "numeric": "270", + "altName": "Gambia" + }, + { + "name": "Georgia", + "alpha2": "GE", + "alpha3": "GEO", + "numeric": "268" + }, + { + "name": "Germany", + "alpha2": "DE", + "alpha3": "DEU", + "numeric": "276" + }, + { + "name": "Ghana", + "alpha2": "GH", + "alpha3": "GHA", + "numeric": "288" + }, + { + "name": "Gibraltar", + "alpha2": "GI", + "alpha3": "GIB", + "numeric": "292" + }, + { + "name": "Greece", + "alpha2": "GR", + "alpha3": "GRC", + "numeric": "300" + }, + { + "name": "Greenland", + "alpha2": "GL", + "alpha3": "GRL", + "numeric": "304" + }, + { + "name": "Grenada", + "alpha2": "GD", + "alpha3": "GRD", + "numeric": "308" + }, + { + "name": "Guadeloupe", + "alpha2": "GP", + "alpha3": "GLP", + "numeric": "312" + }, + { + "name": "Guam", + "alpha2": "GU", + "alpha3": "GUM", + "numeric": "316" + }, + { + "name": "Guatemala", + "alpha2": "GT", + "alpha3": "GTM", + "numeric": "320" + }, + { + "name": "Guernsey", + "alpha2": "GG", + "alpha3": "GGY", + "numeric": "831" + }, + { + "name": "Guinea", + "alpha2": "GN", + "alpha3": "GIN", + "numeric": "324" + }, + { + "name": "Guinea-Bissau", + "alpha2": "GW", + "alpha3": "GNB", + "numeric": "624" + }, + { + "name": "Guyana", + "alpha2": "GY", + "alpha3": "GUY", + "numeric": "328" + }, + { + "name": "Haiti", + "alpha2": "HT", + "alpha3": "HTI", + "numeric": "332" + }, + { + "name": "Heard Island and McDonald Islands", + "alpha2": "HM", + "alpha3": "HMD", + "numeric": "334", + "altName": "Heard and Mcdonald Islands" + }, + { + "name": "Holy See (the)", + "alpha2": "VA", + "alpha3": "VAT", + "numeric": "336", + "altName": "Holy See (Vatican City State)", + "shortName": "Vatican" + }, + { + "name": "Honduras", + "alpha2": "HN", + "alpha3": "HND", + "numeric": "340" + }, + { + "name": "Hong Kong", + "alpha2": "HK", + "alpha3": "HKG", + "numeric": "344", + "altName": "Hong Kong, SAR China" + }, + { + "name": "Hungary", + "alpha2": "HU", + "alpha3": "HUN", + "numeric": "348" + }, + { + "name": "Iceland", + "alpha2": "IS", + "alpha3": "ISL", + "numeric": "352" + }, + { + "name": "India", + "alpha2": "IN", + "alpha3": "IND", + "numeric": "356" + }, + { + "name": "Indonesia", + "alpha2": "ID", + "alpha3": "IDN", + "numeric": "360" + }, + { + "name": "Iran (Islamic Republic of)", + "alpha2": "IR", + "alpha3": "IRN", + "numeric": "364", + "altName": "Iran, Islamic Republic of", + "shortName": "Iran" + }, + { + "name": "Iraq", + "alpha2": "IQ", + "alpha3": "IRQ", + "numeric": "368" + }, + { + "name": "Ireland", + "alpha2": "IE", + "alpha3": "IRL", + "numeric": "372" + }, + { + "name": "Isle of Man", + "alpha2": "IM", + "alpha3": "IMN", + "numeric": "833" + }, + { + "name": "Israel", + "alpha2": "IL", + "alpha3": "ISR", + "numeric": "376" + }, + { + "name": "Italy", + "alpha2": "IT", + "alpha3": "ITA", + "numeric": "380" + }, + { + "name": "Jamaica", + "alpha2": "JM", + "alpha3": "JAM", + "numeric": "388" + }, + { + "name": "Japan", + "alpha2": "JP", + "alpha3": "JPN", + "numeric": "392" + }, + { + "name": "Jersey", + "alpha2": "JE", + "alpha3": "JEY", + "numeric": "832" + }, + { + "name": "Jordan", + "alpha2": "JO", + "alpha3": "JOR", + "numeric": "400" + }, + { + "name": "Kazakhstan", + "alpha2": "KZ", + "alpha3": "KAZ", + "numeric": "398" + }, + { + "name": "Kenya", + "alpha2": "KE", + "alpha3": "KEN", + "numeric": "404" + }, + { + "name": "Kiribati", + "alpha2": "KI", + "alpha3": "KIR", + "numeric": "296" + }, + { + "name": "Korea (the Democratic People's Republic of)", + "alpha2": "KP", + "alpha3": "PRK", + "numeric": "408", + "altName": "Korea (North)", + "shortName": "North Korea" + }, + { + "name": "Korea (the Republic of)", + "alpha2": "KR", + "alpha3": "KOR", + "numeric": "410", + "altName": "Korea (South)", + "shortName": "South Korea" + }, + { + "name": "Kuwait", + "alpha2": "KW", + "alpha3": "KWT", + "numeric": "414" + }, + { + "name": "Kyrgyzstan", + "alpha2": "KG", + "alpha3": "KGZ", + "numeric": "417" + }, + { + "name": "Lao People's Democratic Republic (the)", + "alpha2": "LA", + "alpha3": "LAO", + "numeric": "418", + "altName": "Lao PDR", + "shortName": "Laos" + }, + { + "name": "Latvia", + "alpha2": "LV", + "alpha3": "LVA", + "numeric": "428" + }, + { + "name": "Lebanon", + "alpha2": "LB", + "alpha3": "LBN", + "numeric": "422" + }, + { + "name": "Lesotho", + "alpha2": "LS", + "alpha3": "LSO", + "numeric": "426" + }, + { + "name": "Liberia", + "alpha2": "LR", + "alpha3": "LBR", + "numeric": "430" + }, + { + "name": "Libya", + "alpha2": "LY", + "alpha3": "LBY", + "numeric": "434" + }, + { + "name": "Liechtenstein", + "alpha2": "LI", + "alpha3": "LIE", + "numeric": "438" + }, + { + "name": "Lithuania", + "alpha2": "LT", + "alpha3": "LTU", + "numeric": "440" + }, + { + "name": "Luxembourg", + "alpha2": "LU", + "alpha3": "LUX", + "numeric": "442" + }, + { + "name": "Macao", + "alpha2": "MO", + "alpha3": "MAC", + "numeric": "446", + "altName": "Macao, SAR China", + "shortName": "Macau" + }, + { + "name": "Macedonia (the former Yugoslav Republic of)", + "alpha2": "MK", + "alpha3": "MKD", + "numeric": "807", + "altName": "Macedonia, Republic of", + "shortName": "Macedonia" + }, + { + "name": "Madagascar", + "alpha2": "MG", + "alpha3": "MDG", + "numeric": "450" + }, + { + "name": "Malawi", + "alpha2": "MW", + "alpha3": "MWI", + "numeric": "454" + }, + { + "name": "Malaysia", + "alpha2": "MY", + "alpha3": "MYS", + "numeric": "458" + }, + { + "name": "Maldives", + "alpha2": "MV", + "alpha3": "MDV", + "numeric": "462" + }, + { + "name": "Mali", + "alpha2": "ML", + "alpha3": "MLI", + "numeric": "466" + }, + { + "name": "Malta", + "alpha2": "MT", + "alpha3": "MLT", + "numeric": "470" + }, + { + "name": "Marshall Islands (the)", + "alpha2": "MH", + "alpha3": "MHL", + "numeric": "584", + "altName": "Marshall Islands" + }, + { + "name": "Martinique", + "alpha2": "MQ", + "alpha3": "MTQ", + "numeric": "474" + }, + { + "name": "Mauritania", + "alpha2": "MR", + "alpha3": "MRT", + "numeric": "478" + }, + { + "name": "Mauritius", + "alpha2": "MU", + "alpha3": "MUS", + "numeric": "480" + }, + { + "name": "Mayotte", + "alpha2": "YT", + "alpha3": "MYT", + "numeric": "175" + }, + { + "name": "Mexico", + "alpha2": "MX", + "alpha3": "MEX", + "numeric": "484" + }, + { + "name": "Micronesia (Federated States of)", + "alpha2": "FM", + "alpha3": "FSM", + "numeric": "583", + "altName": "Micronesia, Federated States of", + "shortName": "Micronesia" + }, + { + "name": "Moldova (the Republic of)", + "alpha2": "MD", + "alpha3": "MDA", + "numeric": "498", + "altName": "Moldova" + }, + { + "name": "Monaco", + "alpha2": "MC", + "alpha3": "MCO", + "numeric": "492" + }, + { + "name": "Mongolia", + "alpha2": "MN", + "alpha3": "MNG", + "numeric": "496" + }, + { + "name": "Montenegro", + "alpha2": "ME", + "alpha3": "MNE", + "numeric": "499" + }, + { + "name": "Montserrat", + "alpha2": "MS", + "alpha3": "MSR", + "numeric": "500" + }, + { + "name": "Morocco", + "alpha2": "MA", + "alpha3": "MAR", + "numeric": "504" + }, + { + "name": "Mozambique", + "alpha2": "MZ", + "alpha3": "MOZ", + "numeric": "508" + }, + { + "name": "Myanmar", + "alpha2": "MM", + "alpha3": "MMR", + "numeric": "104" + }, + { + "name": "Namibia", + "alpha2": "NA", + "alpha3": "NAM", + "numeric": "516" + }, + { + "name": "Nauru", + "alpha2": "NR", + "alpha3": "NRU", + "numeric": "520" + }, + { + "name": "Nepal", + "alpha2": "NP", + "alpha3": "NPL", + "numeric": "524" + }, + { + "name": "Netherlands (the)", + "alpha2": "NL", + "alpha3": "NLD", + "numeric": "528", + "altName": "Netherlands" + }, + { + "name": "New Caledonia", + "alpha2": "NC", + "alpha3": "NCL", + "numeric": "540" + }, + { + "name": "New Zealand", + "alpha2": "NZ", + "alpha3": "NZL", + "numeric": "554" + }, + { + "name": "Nicaragua", + "alpha2": "NI", + "alpha3": "NIC", + "numeric": "558" + }, + { + "name": "Niger (the)", + "alpha2": "NE", + "alpha3": "NER", + "numeric": "562", + "altName": "Niger" + }, + { + "name": "Nigeria", + "alpha2": "NG", + "alpha3": "NGA", + "numeric": "566" + }, + { + "name": "Niue", + "alpha2": "NU", + "alpha3": "NIU", + "numeric": "570" + }, + { + "name": "Norfolk Island", + "alpha2": "NF", + "alpha3": "NFK", + "numeric": "574" + }, + { + "name": "Northern Mariana Islands (the)", + "alpha2": "MP", + "alpha3": "MNP", + "numeric": "580", + "altName": "Northern Mariana Islands" + }, + { + "name": "Norway", + "alpha2": "NO", + "alpha3": "NOR", + "numeric": "578" + }, + { + "name": "Oman", + "alpha2": "OM", + "alpha3": "OMN", + "numeric": "512" + }, + { + "name": "Pakistan", + "alpha2": "PK", + "alpha3": "PAK", + "numeric": "586" + }, + { + "name": "Palau", + "alpha2": "PW", + "alpha3": "PLW", + "numeric": "585" + }, + { + "name": "Palestine, State of", + "alpha2": "PS", + "alpha3": "PSE", + "numeric": "275", + "altName": "Palestinian Territory", + "shortName": "Palestine" + }, + { + "name": "Panama", + "alpha2": "PA", + "alpha3": "PAN", + "numeric": "591" + }, + { + "name": "Papua New Guinea", + "alpha2": "PG", + "alpha3": "PNG", + "numeric": "598" + }, + { + "name": "Paraguay", + "alpha2": "PY", + "alpha3": "PRY", + "numeric": "600" + }, + { + "name": "Peru", + "alpha2": "PE", + "alpha3": "PER", + "numeric": "604" + }, + { + "name": "Philippines (the)", + "alpha2": "PH", + "alpha3": "PHL", + "numeric": "608", + "altName": "Philippines" + }, + { + "name": "Pitcairn", + "alpha2": "PN", + "alpha3": "PCN", + "numeric": "612" + }, + { + "name": "Poland", + "alpha2": "PL", + "alpha3": "POL", + "numeric": "616" + }, + { + "name": "Portugal", + "alpha2": "PT", + "alpha3": "PRT", + "numeric": "620" + }, + { + "name": "Puerto Rico", + "alpha2": "PR", + "alpha3": "PRI", + "numeric": "630" + }, + { + "name": "Qatar", + "alpha2": "QA", + "alpha3": "QAT", + "numeric": "634" + }, + { + "name": "Réunion", + "alpha2": "RE", + "alpha3": "REU", + "numeric": "638", + "shortName": "Reunion" + }, + { + "name": "Romania", + "alpha2": "RO", + "alpha3": "ROU", + "numeric": "642" + }, + { + "name": "Russian Federation (the)", + "alpha2": "RU", + "alpha3": "RUS", + "numeric": "643", + "altName": "Russian Federation", + "shortName": "Russia" + }, + { + "name": "Rwanda", + "alpha2": "RW", + "alpha3": "RWA", + "numeric": "646" + }, + { + "name": "Saint Barthélemy", + "alpha2": "BL", + "alpha3": "BLM", + "numeric": "652", + "altName": "Saint-Barthélemy", + "shortName": "Saint Barthelemy" + }, + { + "name": "Saint Helena, Ascension and Tristan da Cunha", + "alpha2": "SH", + "alpha3": "SHN", + "numeric": "654", + "altName": "Saint Helena" + }, + { + "name": "Saint Kitts and Nevis", + "alpha2": "KN", + "alpha3": "KNA", + "numeric": "659" + }, + { + "name": "Saint Lucia", + "alpha2": "LC", + "alpha3": "LCA", + "numeric": "662" + }, + { + "name": "Saint Martin (French part)", + "alpha2": "MF", + "alpha3": "MAF", + "numeric": "663", + "altName": "Saint-Martin (French part)", + "shortName": "Saint Martin" + }, + { + "name": "Saint Pierre and Miquelon", + "alpha2": "PM", + "alpha3": "SPM", + "numeric": "666" + }, + { + "name": "Saint Vincent and the Grenadines", + "alpha2": "VC", + "alpha3": "VCT", + "numeric": "670", + "altName": "Saint Vincent and Grenadines" + }, + { + "name": "Samoa", + "alpha2": "WS", + "alpha3": "WSM", + "numeric": "882" + }, + { + "name": "San Marino", + "alpha2": "SM", + "alpha3": "SMR", + "numeric": "674" + }, + { + "name": "Sao Tome and Principe", + "alpha2": "ST", + "alpha3": "STP", + "numeric": "678" + }, + { + "name": "Saudi Arabia", + "alpha2": "SA", + "alpha3": "SAU", + "numeric": "682" + }, + { + "name": "Senegal", + "alpha2": "SN", + "alpha3": "SEN", + "numeric": "686" + }, + { + "name": "Serbia", + "alpha2": "RS", + "alpha3": "SRB", + "numeric": "688" + }, + { + "name": "Seychelles", + "alpha2": "SC", + "alpha3": "SYC", + "numeric": "690" + }, + { + "name": "Sierra Leone", + "alpha2": "SL", + "alpha3": "SLE", + "numeric": "694" + }, + { + "name": "Singapore", + "alpha2": "SG", + "alpha3": "SGP", + "numeric": "702" + }, + { + "name": "Sint Maarten (Dutch part)", + "alpha2": "SX", + "alpha3": "SXM", + "numeric": "534", + "shortName": "Sint Maarten" + }, + { + "name": "Slovakia", + "alpha2": "SK", + "alpha3": "SVK", + "numeric": "703" + }, + { + "name": "Slovenia", + "alpha2": "SI", + "alpha3": "SVN", + "numeric": "705" + }, + { + "name": "Solomon Islands", + "alpha2": "SB", + "alpha3": "SLB", + "numeric": "090" + }, + { + "name": "Somalia", + "alpha2": "SO", + "alpha3": "SOM", + "numeric": "706" + }, + { + "name": "South Africa", + "alpha2": "ZA", + "alpha3": "ZAF", + "numeric": "710" + }, + { + "name": "South Georgia and the South Sandwich Islands", + "alpha2": "GS", + "alpha3": "SGS", + "numeric": "239" + }, + { + "name": "South Sudan", + "alpha2": "SS", + "alpha3": "SSD", + "numeric": "728" + }, + { + "name": "Spain", + "alpha2": "ES", + "alpha3": "ESP", + "numeric": "724" + }, + { + "name": "Sri Lanka", + "alpha2": "LK", + "alpha3": "LKA", + "numeric": "144" + }, + { + "name": "Sudan (the)", + "alpha2": "SD", + "alpha3": "SDN", + "numeric": "729", + "altName": "Sudan" + }, + { + "name": "Suriname", + "alpha2": "SR", + "alpha3": "SUR", + "numeric": "740" + }, + { + "name": "Svalbard and Jan Mayen", + "alpha2": "SJ", + "alpha3": "SJM", + "numeric": "744", + "altName": "Svalbard and Jan Mayen Islands" + }, + { + "name": "Swaziland", + "alpha2": "SZ", + "alpha3": "SWZ", + "numeric": "748" + }, + { + "name": "Sweden", + "alpha2": "SE", + "alpha3": "SWE", + "numeric": "752" + }, + { + "name": "Switzerland", + "alpha2": "CH", + "alpha3": "CHE", + "numeric": "756" + }, + { + "name": "Syrian Arab Republic", + "alpha2": "SY", + "alpha3": "SYR", + "numeric": "760", + "altName": "Syrian Arab Republic (Syria)", + "shortName": "Syria" + }, + { + "name": "Taiwan (Province of China)", + "alpha2": "TW", + "alpha3": "TWN", + "numeric": "158", + "altName": "Taiwan, Republic of China", + "shortName": "Taiwan" + }, + { + "name": "Tajikistan", + "alpha2": "TJ", + "alpha3": "TJK", + "numeric": "762" + }, + { + "name": "Tanzania, United Republic of", + "alpha2": "TZ", + "alpha3": "TZA", + "numeric": "834", + "shortName": "Tanzania" + }, + { + "name": "Thailand", + "alpha2": "TH", + "alpha3": "THA", + "numeric": "764" + }, + { + "name": "Timor-Leste", + "alpha2": "TL", + "alpha3": "TLS", + "numeric": "626", + "shortName": "East Timor" + }, + { + "name": "Togo", + "alpha2": "TG", + "alpha3": "TGO", + "numeric": "768" + }, + { + "name": "Tokelau", + "alpha2": "TK", + "alpha3": "TKL", + "numeric": "772" + }, + { + "name": "Tonga", + "alpha2": "TO", + "alpha3": "TON", + "numeric": "776" + }, + { + "name": "Trinidad and Tobago", + "alpha2": "TT", + "alpha3": "TTO", + "numeric": "780" + }, + { + "name": "Tunisia", + "alpha2": "TN", + "alpha3": "TUN", + "numeric": "788" + }, + { + "name": "Turkey", + "alpha2": "TR", + "alpha3": "TUR", + "numeric": "792" + }, + { + "name": "Turkmenistan", + "alpha2": "TM", + "alpha3": "TKM", + "numeric": "795" + }, + { + "name": "Turks and Caicos Islands (the)", + "alpha2": "TC", + "alpha3": "TCA", + "numeric": "796", + "altName": "Turks and Caicos Islands" + }, + { + "name": "Tuvalu", + "alpha2": "TV", + "alpha3": "TUV", + "numeric": "798" + }, + { + "name": "Uganda", + "alpha2": "UG", + "alpha3": "UGA", + "numeric": "800" + }, + { + "name": "Ukraine", + "alpha2": "UA", + "alpha3": "UKR", + "numeric": "804" + }, + { + "name": "United Arab Emirates (the)", + "alpha2": "AE", + "alpha3": "ARE", + "numeric": "784", + "altName": "United Arab Emirates" + }, + { + "name": "United Kingdom of Great Britain and Northern Ireland (the)", + "alpha2": "GB", + "alpha3": "GBR", + "numeric": "826", + "altName": "United Kingdom" + }, + { + "name": "United States Minor Outlying Islands (the)", + "alpha2": "UM", + "alpha3": "UMI", + "numeric": "581", + "altName": "US Minor Outlying Islands" + }, + { + "name": "United States of America (the)", + "alpha2": "US", + "alpha3": "USA", + "numeric": "840", + "altName": "United States of America", + "shortName": "United States" + }, + { + "name": "Uruguay", + "alpha2": "UY", + "alpha3": "URY", + "numeric": "858" + }, + { + "name": "Uzbekistan", + "alpha2": "UZ", + "alpha3": "UZB", + "numeric": "860" + }, + { + "name": "Vanuatu", + "alpha2": "VU", + "alpha3": "VUT", + "numeric": "548" + }, + { + "name": "Venezuela (Bolivarian Republic of)", + "alpha2": "VE", + "alpha3": "VEN", + "numeric": "862", + "altName": "Venezuela (Bolivarian Republic)", + "shortName": "Venezuela" + }, + { + "name": "Viet Nam", + "alpha2": "VN", + "alpha3": "VNM", + "numeric": "704", + "shortName": "Vietnam" + }, + { + "name": "Virgin Islands (British)", + "alpha2": "VG", + "alpha3": "VGB", + "numeric": "092", + "altName": "British Virgin Islands" + }, + { + "name": "Virgin Islands (U.S.)", + "alpha2": "VI", + "alpha3": "VIR", + "numeric": "850", + "altName": "Virgin Islands, US", + "shortName": "U.S. Virgin Islands" + }, + { + "name": "Wallis and Futuna", + "alpha2": "WF", + "alpha3": "WLF", + "numeric": "876", + "altName": "Wallis and Futuna Islands" + }, + { + "name": "Western Sahara*", + "alpha2": "EH", + "alpha3": "ESH", + "numeric": "732", + "altName": "Western Sahara" + }, + { + "name": "Yemen", + "alpha2": "YE", + "alpha3": "YEM", + "numeric": "887" + }, + { + "name": "Zambia", + "alpha2": "ZM", + "alpha3": "ZMB", + "numeric": "894" + }, + { + "name": "Zimbabwe", + "alpha2": "ZW", + "alpha3": "ZWE", + "numeric": "716" + } + ]; diff --git a/packages/nodes-base/nodes/HelpScout/CustomerDescription.ts b/packages/nodes-base/nodes/HelpScout/CustomerDescription.ts new file mode 100644 index 0000000000..9f909a0b6b --- /dev/null +++ b/packages/nodes-base/nodes/HelpScout/CustomerDescription.ts @@ -0,0 +1,811 @@ +import { INodeProperties } from "n8n-workflow"; + +export const customerOperations = [ + { + displayName: 'Operation', + name: 'operation', + type: 'options', + displayOptions: { + show: { + resource: [ + 'customer', + ], + }, + }, + options: [ + { + name: 'Create', + value: 'create', + description: 'Create a new customer', + }, + { + name: 'Properties', + value: 'properties', + description: 'Get customer property definitions', + }, + { + name: 'Get', + value: 'get', + description: 'Get a customer', + }, + { + name: 'Get All', + value: 'getAll', + description: 'Get all customers', + }, + { + name: 'Update', + value: 'update', + description: 'Update a customer', + }, + ], + default: 'create', + description: 'The operation to perform.', + }, +] as INodeProperties[]; + +export const customerFields = [ +/* -------------------------------------------------------------------------- */ +/* customer:create */ +/* -------------------------------------------------------------------------- */ + { + displayName: 'Resolve Data', + name: 'resolveData', + type: 'boolean', + default: true, + displayOptions: { + show: { + operation: [ + 'create', + ], + resource: [ + 'customer', + ], + }, + }, + description: 'By default the response only contain the ID to resource
. If this option gets activated it
will resolve the data automatically.', + }, + { + displayName: 'Additional Fields', + name: 'additionalFields', + type: 'collection', + placeholder: 'Add Field', + default: {}, + displayOptions: { + show: { + operation: [ + 'create', + ], + resource: [ + 'customer', + ], + }, + }, + options: [ + { + displayName: 'Age', + name: 'age', + type: 'number', + typeOptions: { + minValue: 1, + }, + default: 1, + description: `Customer’s age`, + }, + { + displayName: 'Notes', + name: 'background', + type: 'string', + typeOptions: { + alwaysOpenEditWindow: true, + }, + default: '', + description: `Notes`, + }, + { + displayName: 'First Name', + name: 'firstName', + type: 'string', + default: '', + description: `First name of the customer. When defined it must be between 1 and 40 characters.`, + }, + { + displayName: 'Gender', + name: 'gender', + type: 'options', + options: [ + { + name: 'Female', + value: 'female', + }, + { + name: 'Male', + value: 'male', + }, + { + name: 'Unknown', + value: 'unknown', + }, + ], + default: '', + description: 'Gender of this customer.', + }, + { + displayName: 'Job Title', + name: 'jobTitle', + type: 'string', + default: '', + description: 'Job title. Max length 60 characters.', + }, + { + displayName: 'Last Name', + name: 'lastName', + type: 'string', + default: '', + description: 'Last name of the customer', + }, + { + displayName: 'Location', + name: 'location', + type: 'string', + default: '', + description: 'Location of the customer.', + }, + { + displayName: 'Organization', + name: 'organization', + type: 'string', + default: '', + description: 'Organization', + }, + { + displayName: 'Photo Url', + name: 'photoUrl', + type: 'string', + default: '', + description: 'URL of the customer’s photo', + }, + ] + }, + { + displayName: 'Address', + name: 'addressUi', + placeholder: 'Add Address', + type: 'fixedCollection', + displayOptions: { + show: { + operation: [ + 'create', + ], + resource: [ + 'customer', + ], + }, + }, + default: {}, + options: [ + { + displayName: 'Address', + name: 'addressValue', + values: [ + { + displayName: 'Line 1', + name: 'line1', + type: 'string', + default: '', + description: 'line1', + }, + { + displayName: 'Line 2', + name: 'line2', + type: 'string', + default: '', + description: 'line2', + }, + { + displayName: 'City', + name: 'city', + type: 'string', + default: '', + description: 'City', + }, + { + displayName: 'State', + name: 'state', + type: 'string', + default: '', + description: 'State', + }, + { + displayName: 'Country', + name: 'country', + type: 'options', + typeOptions: { + loadOptionsMethod: 'getCountriesCodes', + }, + default: '', + description: 'Country', + }, + { + displayName: 'Postal Code', + name: 'postalCode', + type: 'string', + default: '', + description: 'Postal code', + }, + ], + }, + ], + }, + { + displayName: 'Chat Handles', + name: 'chatsUi', + placeholder: 'Add Chat Handle', + type: 'fixedCollection', + typeOptions: { + multipleValues: true, + }, + displayOptions: { + show: { + operation: [ + 'create', + ], + resource: [ + 'customer', + ], + }, + }, + default: {}, + options: [ + { + displayName: 'Chat Handle', + name: 'chatsValues', + values: [ + { + displayName: 'Type', + name: 'type', + type: 'options', + options: [ + { + name: 'aim', + value: 'aim', + }, + { + name: 'gtalk', + value: 'gtalk', + }, + { + name: 'icq', + value: 'icq', + }, + { + name: 'msn', + value: 'msn', + }, + { + name: 'other', + value: 'other', + }, + { + name: 'qq', + value: 'qq', + }, + { + name: 'skype', + value: 'skype', + }, + { + name: 'xmpp', + value: 'xmpp', + }, + { + name: 'yahoo', + value: 'yahoo', + }, + ], + description: 'Chat type', + default: '', + }, + { + displayName: 'Value', + name: 'value', + type: 'string', + default: '', + description: 'Chat handle', + }, + ], + }, + ], + }, + { + displayName: 'Emails', + name: 'emailsUi', + placeholder: 'Add Email', + type: 'fixedCollection', + typeOptions: { + multipleValues: true, + }, + displayOptions: { + show: { + operation: [ + 'create', + ], + resource: [ + 'customer', + ], + }, + }, + default: {}, + options: [ + { + displayName: 'Email', + name: 'emailsValues', + values: [ + { + displayName: 'Type', + name: 'type', + type: 'options', + options: [ + { + name: 'Home', + value: 'home', + }, + { + name: 'Other', + value: 'other', + }, + { + name: 'Work', + value: 'work', + }, + ], + description: 'Location for this email address', + default: '', + }, + { + displayName: 'Value', + name: 'value', + type: 'string', + default: '', + description: 'Email', + }, + ], + }, + ], + }, + { + displayName: 'Phones', + name: 'phonesUi', + placeholder: 'Add Phone', + type: 'fixedCollection', + typeOptions: { + multipleValues: true, + }, + displayOptions: { + show: { + operation: [ + 'create', + ], + resource: [ + 'customer', + ], + }, + }, + default: {}, + options: [ + { + displayName: 'Email', + name: 'phonesValues', + values: [ + { + displayName: 'Type', + name: 'type', + type: 'options', + options: [ + { + name: 'Fax', + value: 'fax', + }, + { + name: 'Home', + value: 'home', + }, + { + name: 'Other', + value: 'other', + }, + { + name: 'Pager', + value: 'pager', + }, + { + name: 'Work', + value: 'work', + }, + ], + description: 'Location for this phone', + default: '', + }, + { + displayName: 'Value', + name: 'value', + type: 'string', + default: '', + description: 'Phone', + }, + ], + }, + ], + }, + { + displayName: 'Social Profiles', + name: 'socialProfilesUi', + placeholder: 'Add Social Profile', + type: 'fixedCollection', + typeOptions: { + multipleValues: true, + }, + displayOptions: { + show: { + operation: [ + 'create', + ], + resource: [ + 'customer', + ], + }, + }, + default: {}, + options: [ + { + displayName: 'Social Profile', + name: 'socialProfilesValues', + values: [ + { + displayName: 'Type', + name: 'type', + type: 'options', + options: [ + { + name: 'About Me', + value: 'aboutMe', + }, + { + name: 'Facebook', + value: 'facebook', + }, + { + name: 'Flickr', + value: 'flickr', + }, + { + name: 'Forsquare', + value: 'forsquare', + }, + { + name: 'Google', + value: 'google', + }, + { + name: 'Google Plus', + value: 'googleplus', + }, + { + name: 'Linkedin', + value: 'linkedin', + }, + { + name: 'Other', + value: 'other', + }, + { + name: 'Quora', + value: 'quora', + }, + { + name: 'Tungleme', + value: 'tungleme', + }, + { + name: 'Twitter', + value: 'twitter', + }, + { + name: 'Youtube', + value: 'youtube', + }, + ], + description: 'Type of social profile', + default: '', + }, + { + displayName: 'Value', + name: 'value', + type: 'string', + default: '', + description: 'Social Profile handle (url for example)', + }, + ], + }, + ], + }, + { + displayName: 'Websites', + name: 'websitesUi', + placeholder: 'Add Website', + type: 'fixedCollection', + typeOptions: { + multipleValues: true, + }, + displayOptions: { + show: { + operation: [ + 'create', + ], + resource: [ + 'customer', + ], + }, + }, + default: {}, + options: [ + { + displayName: 'Website', + name: 'websitesValues', + values: [ + { + displayName: 'Value', + name: 'value', + type: 'string', + default: '', + description: 'Website URL', + }, + ], + }, + ], + }, +/* -------------------------------------------------------------------------- */ +/* customer:getAll */ +/* -------------------------------------------------------------------------- */ + { + displayName: 'Options', + name: 'options', + type: 'collection', + placeholder: 'Add Option', + default: {}, + displayOptions: { + show: { + resource: [ + 'customer', + ], + operation: [ + 'getAll', + ], + }, + }, + options: [ + { + displayName: 'Mailbox ID', + name: 'mailbox', + type: 'string', + default: '', + description: 'Filters customers from a specific mailbox', + }, + { + displayName: 'First Name', + name: 'firstName', + type: 'string', + default: '', + description: 'Filters customers by first name', + }, + { + displayName: 'Last Name', + name: 'lastName', + type: 'string', + default: '', + description: 'Filters customers by last name', + }, + { + displayName: 'Modified Since', + name: 'modifiedSince', + type: 'dateTime', + default: '', + description: 'Returns only customers that were modified after this date', + }, + { + displayName: 'Sort Field', + name: 'sortField', + type: 'options', + options: [ + { + name: 'Score', + value: 'score', + }, + { + name: 'First Name', + value: 'firstName', + }, + { + name: 'Last Name', + value: 'lastName', + }, + { + name: 'Modified At', + value: 'modifiedAt', + }, + ], + default: 'score', + description: 'Sorts the result by specified field', + }, + { + displayName: 'Sort Order', + name: 'sortOrder', + type: 'options', + options: [ + { + name: 'ASC', + value: 'asc', + }, + { + name: 'Desc', + value: 'desc', + }, + ], + default: 'desc', + }, + { + displayName: 'Query', + name: 'query', + type: 'string', + typeOptions: { + alwaysOpenEditWindow: true, + }, + default: '', + description: 'Advanced search Examples' + }, + ], + }, +/* -------------------------------------------------------------------------- */ +/* customer:get */ +/* -------------------------------------------------------------------------- */ + { + displayName: 'Customer ID', + name: 'customerId', + type: 'string', + default: '', + required: true, + displayOptions: { + show: { + resource: [ + 'customer', + ], + operation: [ + 'get', + ], + }, + }, + description: 'Customer ID', + }, +/* -------------------------------------------------------------------------- */ +/* customer:update */ +/* -------------------------------------------------------------------------- */ + { + displayName: 'Customer ID', + name: 'customerId', + type: 'string', + default: '', + displayOptions: { + show: { + operation: [ + 'update', + ], + resource: [ + 'customer', + ], + }, + }, + description: 'Customer ID', + }, + { + displayName: 'Update Fields', + name: 'updateFields', + type: 'collection', + placeholder: 'Add Field', + default: {}, + displayOptions: { + show: { + operation: [ + 'update', + ], + resource: [ + 'customer', + ], + }, + }, + options: [ + { + displayName: 'Age', + name: 'age', + type: 'number', + typeOptions: { + minValue: 1, + }, + default: 1, + description: `Customer’s age`, + }, + { + displayName: 'Notes', + name: 'background', + type: 'string', + typeOptions: { + alwaysOpenEditWindow: true, + }, + default: '', + description: `Notes`, + }, + { + displayName: 'First Name', + name: 'firstName', + type: 'string', + default: '', + description: `First name of the customer. When defined it must be between 1 and 40 characters.`, + }, + { + displayName: 'Gender', + name: 'gender', + type: 'options', + options: [ + { + name: 'Female', + value: 'female', + }, + { + name: 'Male', + value: 'male', + }, + { + name: 'Unknown', + value: 'unknown', + }, + ], + default: '', + description: 'Gender of this customer.', + }, + { + displayName: 'Job Title', + name: 'jobTitle', + type: 'string', + default: '', + description: 'Job title. Max length 60 characters.', + }, + { + displayName: 'Last Name', + name: 'lastName', + type: 'string', + default: '', + description: 'Last name of the customer', + }, + { + displayName: 'Location', + name: 'location', + type: 'string', + default: '', + description: 'Location of the customer.', + }, + { + displayName: 'Organization', + name: 'organization', + type: 'string', + default: '', + description: 'Organization', + }, + { + displayName: 'Photo Url', + name: 'photoUrl', + type: 'string', + default: '', + description: 'URL of the customer’s photo', + }, + ] + }, +] as INodeProperties[]; diff --git a/packages/nodes-base/nodes/HelpScout/CustomerInterface.ts b/packages/nodes-base/nodes/HelpScout/CustomerInterface.ts new file mode 100644 index 0000000000..569a92c9aa --- /dev/null +++ b/packages/nodes-base/nodes/HelpScout/CustomerInterface.ts @@ -0,0 +1,20 @@ +import { IDataObject } from "n8n-workflow"; + +export interface ICustomer { + address?: IDataObject; + age?: string; + background?: string; + chats?: IDataObject[]; + emails?: IDataObject[]; + firstName?: string; + gender?: string; + jobTitle?: string; + lastName?: string; + location?: string; + organization?: string; + phones?: IDataObject[]; + photoUrl?: string; + properties?: IDataObject; + socialProfiles?: IDataObject[]; + websites?: IDataObject[]; +} diff --git a/packages/nodes-base/nodes/HelpScout/GenericFunctions.ts b/packages/nodes-base/nodes/HelpScout/GenericFunctions.ts new file mode 100644 index 0000000000..c7cda4eac6 --- /dev/null +++ b/packages/nodes-base/nodes/HelpScout/GenericFunctions.ts @@ -0,0 +1,69 @@ +import { OptionsWithUri } from 'request'; +import { + IExecuteFunctions, + IExecuteSingleFunctions, + ILoadOptionsFunctions, + IHookFunctions, +} from 'n8n-core'; +import { + IDataObject, +} from 'n8n-workflow'; + +import { + get, +} from 'lodash'; + +export async function helpscoutApiRequest(this: IExecuteFunctions | IExecuteSingleFunctions | ILoadOptionsFunctions | IHookFunctions, method: string, resource: string, body: any = {}, qs: IDataObject = {}, uri?: string, option: IDataObject = {}): Promise { // tslint:disable-line:no-any + let options: OptionsWithUri = { + headers: { + 'Content-Type': 'application/json', + }, + method, + body, + qs, + uri: uri || `https://api.helpscout.net${resource}`, + json: true + }; + try { + if (Object.keys(option).length !== 0) { + options = Object.assign({}, options, option); + } + if (Object.keys(body).length === 0) { + delete options.body; + } + //@ts-ignore + return await this.helpers.requestOAuth.call(this, 'helpScoutOAuth2Api', options); + } catch (error) { + if (error.response && error.response.body + && error.response.body._embedded + && error.response.body._embedded.errors) { + // Try to return the error prettier + //@ts-ignore + throw new Error(`HelpScout error response [${error.statusCode}]: ${error.response.body.message} - ${error.response.body._embedded.errors.map(error => { + return `${error.path} ${error.message}`; + }).join('-')}`); + } + throw error; + } +} + +export async function helpscoutApiRequestAllItems(this: IExecuteFunctions | ILoadOptionsFunctions | IHookFunctions, propertyName: string ,method: string, endpoint: string, body: any = {}, query: IDataObject = {}): Promise { // tslint:disable-line:no-any + + const returnData: IDataObject[] = []; + + let responseData; + query.size = 50; + let uri; + + do { + responseData = await helpscoutApiRequest.call(this, method, endpoint, body, query, uri); + uri = get(responseData, '_links.next.href'); + returnData.push.apply(returnData, get(responseData, propertyName)); + } while ( + responseData['_links'] !== undefined && + responseData['_links'].next !== undefined && + responseData['_links'].next.href !== undefined + ); + + return returnData; +} diff --git a/packages/nodes-base/nodes/HelpScout/HelpScout.node.ts b/packages/nodes-base/nodes/HelpScout/HelpScout.node.ts new file mode 100644 index 0000000000..27534b2426 --- /dev/null +++ b/packages/nodes-base/nodes/HelpScout/HelpScout.node.ts @@ -0,0 +1,410 @@ +import { + IExecuteFunctions, +} from 'n8n-core'; + +import { + IDataObject, + INodeExecutionData, + INodeTypeDescription, + INodeType, + ILoadOptionsFunctions, + INodePropertyOptions, + IBinaryKeyData, +} from 'n8n-workflow'; + +import { + helpscoutApiRequest, + helpscoutApiRequestAllItems, +} from './GenericFunctions'; + +import { + conversationOperations, + conversationFields, +} from './ConversationDescription'; + +import { + customerOperations, + customerFields, +} from './CustomerDescription'; + +import { + mailboxOperations, + mailboxFields, +} from './MailboxDescription'; + +import { + threadOperations, + threadFields, +} from './ThreadDescription'; + +import { + ICustomer, +} from './CustomerInterface'; + +import { + IConversation, + } from './ConversationInterface'; + + import { + IThread, + IAttachment, + } from './ThreadInterface'; + + import { + countriesCodes +} from './CountriesCodes'; + +export class HelpScout implements INodeType { + description: INodeTypeDescription = { + displayName: 'HelpScout', + name: 'helpScout', + icon: 'file:helpScout.png', + group: ['input'], + version: 1, + subtitle: '={{$parameter["operation"] + ": " + $parameter["resource"]}}', + description: 'Consume Help Scout API.', + defaults: { + name: 'HelpScout', + color: '#1392ee', + }, + inputs: ['main'], + outputs: ['main'], + credentials: [ + { + name: 'helpScoutOAuth2Api', + required: true, + }, + ], + properties: [ + { + displayName: 'Resource', + name: 'resource', + type: 'options', + options: [ + { + name: 'Conversation', + value: 'conversation', + }, + { + name: 'Customer', + value: 'customer', + }, + { + name: 'Mailbox', + value: 'mailbox', + }, + { + name: 'Thread', + value: 'thread', + }, + ], + default: 'conversation', + description: 'The resource to operate on.', + }, + ...conversationOperations, + ...conversationFields, + ...customerOperations, + ...customerFields, + ...mailboxOperations, + ...mailboxFields, + ...threadOperations, + ...threadFields, + ], + }; + + methods = { + loadOptions: { + // Get all the countries codes to display them to user so that he can + // select them easily + async getCountriesCodes(this: ILoadOptionsFunctions): Promise { + const returnData: INodePropertyOptions[] = []; + for (const countryCode of countriesCodes) { + const countryCodeName = `${countryCode.name} - ${countryCode.alpha2}`; + const countryCodeId = countryCode.alpha2; + returnData.push({ + name: countryCodeName, + value: countryCodeId, + }); + } + return returnData; + }, + // 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 helpscoutApiRequestAllItems.call(this, '_embedded.tags', 'GET', '/v2/tags'); + for (const tag of tags) { + const tagName = tag.name; + const tagId = tag.id; + returnData.push({ + name: tagName, + value: tagId, + }); + } + return returnData; + }, + // Get all the mailboxes to display them to user so that he can + // select them easily + async getMailboxes(this: ILoadOptionsFunctions): Promise { + const returnData: INodePropertyOptions[] = []; + const mailboxes = await helpscoutApiRequestAllItems.call(this, '_embedded.mailboxes', 'GET', '/v2/mailboxes'); + for (const mailbox of mailboxes) { + const mailboxName = mailbox.name; + const mailboxId = mailbox.id; + returnData.push({ + name: mailboxName, + value: mailboxId, + }); + } + 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 === 'conversation') { + //https://developer.helpscout.com/mailbox-api/endpoints/conversations/create + if (operation === 'create') { + const mailboxId = this.getNodeParameter('mailboxId', i) as number; + const status = this.getNodeParameter('status', i) as string; + const subject = this.getNodeParameter('subject', i) as string; + const type = this.getNodeParameter('type', i) as string; + const resolveData = this.getNodeParameter('resolveData', i) as boolean; + const additionalFields = this.getNodeParameter('additionalFields', i) as IDataObject; + const threads = (this.getNodeParameter('threadsUi', i) as IDataObject).threadsValues as IDataObject[]; + const body: IConversation = { + mailboxId, + status, + subject, + type, + }; + Object.assign(body, additionalFields); + if (additionalFields.customerId) { + body.customer = { + id: additionalFields.customerId, + }; + //@ts-ignore + delete body.customerId; + } + if (additionalFields.customerEmail) { + body.customer = { + email: additionalFields.customerEmail, + }; + //@ts-ignore + delete body.customerEmail; + } + if (body.customer === undefined) { + throw new Error('Either customer email or customer ID must be set'); + } + if (threads) { + for (let i = 0; i < threads.length; i++) { + if (threads[i].type === '' || threads[i].text === '') { + throw new Error('Chat Threads cannot be empty'); + } + if (threads[i].type !== 'note') { + threads[i].customer = body.customer; + } + } + body.threads = threads; + } + responseData = await helpscoutApiRequest.call(this, 'POST', '/v2/conversations', body, qs, undefined, { resolveWithFullResponse: true }); + const id = responseData.headers['resource-id']; + const uri = responseData.headers.location; + if (resolveData) { + responseData = await helpscoutApiRequest.call(this, 'GET', '', {}, {}, uri); + } else { + responseData = { + id, + uri, + }; + } + } + //https://developer.helpscout.com/mailbox-api/endpoints/conversations/delete + if (operation === 'delete') { + const conversationId = this.getNodeParameter('conversationId', i) as string; + responseData = await helpscoutApiRequest.call(this, 'DELETE', `/v2/conversations/${conversationId}`); + responseData = { success: true }; + } + //https://developer.helpscout.com/mailbox-api/endpoints/conversations/get + if (operation === 'get') { + const conversationId = this.getNodeParameter('conversationId', i) as string; + responseData = await helpscoutApiRequest.call(this, 'GET', `/v2/conversations/${conversationId}`); + } + //https://developer.helpscout.com/mailbox-api/endpoints/conversations/list + if (operation === 'getAll') { + const options = this.getNodeParameter('options', i) as IDataObject; + Object.assign(qs, options); + responseData = await helpscoutApiRequestAllItems.call(this, '_embedded.conversations', 'GET', '/v2/conversations', {}, qs); + } + } + if (resource === 'customer') { + //https://developer.helpscout.com/mailbox-api/endpoints/customers/create + if (operation === 'create') { + const resolveData = this.getNodeParameter('resolveData', i) as boolean; + const additionalFields = this.getNodeParameter('additionalFields', i) as IDataObject; + const chats = (this.getNodeParameter('chatsUi', i) as IDataObject).chatsValues as IDataObject[]; + const address = (this.getNodeParameter('addressUi', i) as IDataObject).addressValue as IDataObject; + const emails = (this.getNodeParameter('emailsUi', i) as IDataObject).emailsValues as IDataObject[]; + const phones = (this.getNodeParameter('phonesUi', i) as IDataObject).phonesValues as IDataObject[]; + const socialProfiles = (this.getNodeParameter('socialProfilesUi', i) as IDataObject).socialProfilesValues as IDataObject[]; + const websites = (this.getNodeParameter('websitesUi', i) as IDataObject).websitesValues as IDataObject[]; + let body: ICustomer = {}; + body = Object.assign({}, additionalFields); + if (body.age) { + body.age = body.age.toString(); + } + if (chats) { + body.chats = chats; + } + if (address) { + body.address = address; + body.address.lines = [address.line1, address.line2]; + } + if (emails) { + body.emails = emails; + } + if (phones) { + body.phones = phones; + } + if (socialProfiles) { + body.socialProfiles = socialProfiles; + } + if (websites) { + body.websites = websites; + } + if (Object.keys(body).length === 0) { + throw new Error('You have to set at least one field'); + } + responseData = await helpscoutApiRequest.call(this, 'POST', '/v2/customers', body, qs, undefined, { resolveWithFullResponse: true }); + const id = responseData.headers['resource-id']; + const uri = responseData.headers.location; + if (resolveData) { + responseData = await helpscoutApiRequest.call(this, 'GET', '', {}, {}, uri); + } else { + responseData = { + id, + uri, + }; + } + } + //https://developer.helpscout.com/mailbox-api/endpoints/customer_properties/list + if (operation === 'properties') { + responseData = await helpscoutApiRequestAllItems.call(this, '_embedded.customer-properties', 'GET', '/v2/customer-properties', {}, qs); + } + //https://developer.helpscout.com/mailbox-api/endpoints/customers/get + if (operation === 'get') { + const customerId = this.getNodeParameter('customerId', i) as string; + responseData = await helpscoutApiRequest.call(this, 'GET', `/v2/customers/${customerId}`); + } + //https://developer.helpscout.com/mailbox-api/endpoints/customers/list + if (operation === 'getAll') { + const options = this.getNodeParameter('options', i) as IDataObject; + Object.assign(qs, options); + responseData = await helpscoutApiRequestAllItems.call(this, '_embedded.customers', 'GET', '/v2/customers', {}, qs); + } + //https://developer.helpscout.com/mailbox-api/endpoints/customers/overwrite/ + if (operation === 'update') { + const customerId = this.getNodeParameter('customerId', i) as string; + const updateFields = this.getNodeParameter('updateFields', i) as IDataObject; + let body: ICustomer = {}; + body = Object.assign({}, updateFields); + if (body.age) { + body.age = body.age.toString(); + } + if (Object.keys(body).length === 0) { + throw new Error('You have to set at least one field'); + } + responseData = await helpscoutApiRequest.call(this, 'PUT', `/v2/customers/${customerId}`, body, qs, undefined, { resolveWithFullResponse: true }); + responseData = { success: true }; + } + } + if (resource === 'mailbox') { + //https://developer.helpscout.com/mailbox-api/endpoints/mailboxes/get + if (operation === 'get') { + const mailboxId = this.getNodeParameter('mailboxId', i) as string; + responseData = await helpscoutApiRequest.call(this, 'GET', `/v2/mailboxes/${mailboxId}`, {}, qs); + } + //https://developer.helpscout.com/mailbox-api/endpoints/mailboxes/list + if (operation === 'getAll') { + responseData = await helpscoutApiRequestAllItems.call(this, '_embedded.mailboxes', 'GET', '/v2/mailboxes', {}, qs); + } + } + if (resource === 'thread') { + //https://developer.helpscout.com/mailbox-api/endpoints/conversations/threads/chat + if (operation === 'create') { + const conversationId = this.getNodeParameter('conversationId', i) as string; + const type = this.getNodeParameter('type', i) as string; + const text = this.getNodeParameter('text', i) as string; + const additionalFields = this.getNodeParameter('additionalFields', i) as IDataObject; + const attachments = this.getNodeParameter('attachmentsUi', i) as IDataObject; + const body: IThread = { + text, + attachments: [], + }; + Object.assign(body, additionalFields); + if (additionalFields.customerId) { + body.customer = { + id: additionalFields.customerId, + }; + //@ts-ignore + delete body.customerId; + } + if (additionalFields.customerEmail) { + body.customer = { + email: additionalFields.customerEmail, + }; + //@ts-ignore + delete body.customerEmail; + } + if (body.customer === undefined) { + throw new Error('Either customer email or customer ID must be set'); + } + if (attachments) { + if (attachments.attachmentsValues + && (attachments.attachmentsValues as IDataObject[]).length !== 0) { + body.attachments?.push.apply(body.attachments, attachments.attachmentsValues as IAttachment[]); + } + if (attachments.attachmentsBinary + && (attachments.attachmentsBinary as IDataObject[]).length !== 0 + && items[i].binary) { + const mapFunction = (value: IDataObject): IAttachment => { + const binaryProperty = (items[i].binary as IBinaryKeyData)[value.property as string]; + if (binaryProperty) { + return { + fileName: binaryProperty.fileName || 'unknown', + data: binaryProperty.data, + mimeType: binaryProperty.mimeType, + }; + } else { + throw new Error(`Binary property ${value.property} does not exist on input`); + } + }; + body.attachments?.push.apply(body.attachments, (attachments.attachmentsBinary as IDataObject[]).map(mapFunction) as IAttachment[]); + } + } + responseData = await helpscoutApiRequest.call(this, 'POST', `/v2/conversations/${conversationId}/chats`, body); + responseData = { success: true }; + } + //https://developer.helpscout.com/mailbox-api/endpoints/conversations/threads/list + if (operation === 'getAll') { + const conversationId = this.getNodeParameter('conversationId', i) as string; + responseData = await helpscoutApiRequestAllItems.call(this, '_embedded.threads', 'GET', `/v2/conversations/${conversationId}/threads`); + } + } + } + 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/HelpScout/HelpScoutTrigger.node.ts b/packages/nodes-base/nodes/HelpScout/HelpScoutTrigger.node.ts new file mode 100644 index 0000000000..ecb7e2a12a --- /dev/null +++ b/packages/nodes-base/nodes/HelpScout/HelpScoutTrigger.node.ts @@ -0,0 +1,202 @@ +import { + IHookFunctions, + IWebhookFunctions, +} from 'n8n-core'; + +import { + INodeTypeDescription, + INodeType, + IWebhookResponseData, + IDataObject, +} from 'n8n-workflow'; + +import { + helpscoutApiRequest, + helpscoutApiRequestAllItems, +} from './GenericFunctions'; + +import { createHmac } from 'crypto'; + +export class HelpScoutTrigger implements INodeType { + description: INodeTypeDescription = { + displayName: 'HelpScout Trigger', + name: 'helpScoutTrigger', + icon: 'file:helpScout.png', + group: ['trigger'], + version: 1, + description: 'Starts the workflow when HelpScout events occure.', + defaults: { + name: 'HelpScout Trigger', + color: '#1392ee', + }, + inputs: [], + outputs: ['main'], + credentials: [ + { + name: 'helpScoutOAuth2Api', + required: true, + }, + ], + webhooks: [ + { + name: 'default', + httpMethod: 'POST', + responseMode: 'onReceived', + path: 'webhook', + }, + ], + properties: [ + { + displayName: 'Events', + name: 'events', + type: 'multiOptions', + options: [ + { + name: 'convo.agent.reply.created', + value: 'convo.agent.reply.created', + }, + { + name: 'convo.assigned', + value: 'convo.assigned', + }, + { + name: 'convo.created', + value: 'convo.created', + }, + { + name: 'convo.customer.reply.created', + value: 'convo.customer.reply.created', + }, + { + name: 'convo.deleted', + value: 'convo.deleted', + }, + { + name: 'convo.merged', + value: 'convo.merged', + }, + { + name: 'convo.moved', + value: 'convo.moved', + }, + { + name: 'convo.note.created', + value: 'convo.note.created', + }, + { + name: 'convo.status', + value: 'convo.status', + }, + { + name: 'convo.tags', + value: 'convo.tags', + }, + { + name: 'customer.created', + value: 'customer.created', + }, + { + name: 'satisfaction.ratings', + value: 'satisfaction.ratings', + }, + ], + default: [], + required: true, + }, + ], + + }; + + // @ts-ignore (because of request) + webhookMethods = { + default: { + async checkExists(this: IHookFunctions): Promise { + const webhookUrl = this.getNodeWebhookUrl('default'); + const webhookData = this.getWorkflowStaticData('node'); + const events = this.getNodeParameter('events') as string; + + // Check all the webhooks which exist already if it is identical to the + // one that is supposed to get created. + const endpoint = '/v2/webhooks'; + const data = await helpscoutApiRequestAllItems.call(this, '_embedded.webhooks', 'GET', endpoint, {}); + + for (const webhook of data) { + if (webhook.url === webhookUrl) { + for (const event of events) { + if (!webhook.events.includes(event) + && webhook.state === 'enabled') { + return false; + } + } + } + // Set webhook-id to be sure that it can be deleted + webhookData.webhookId = webhook.id as string; + return true; + } + return false; + }, + async create(this: IHookFunctions): Promise { + const webhookData = this.getWorkflowStaticData('node'); + const webhookUrl = this.getNodeWebhookUrl('default'); + const events = this.getNodeParameter('events') as string; + + const endpoint = '/v2/webhooks'; + + const body = { + url: webhookUrl, + events, + secret: Math.random().toString(36).substring(2, 15), + }; + + const responseData = await helpscoutApiRequest.call(this, 'POST', endpoint, body, {}, undefined, { resolveWithFullResponse: true }); + + if (responseData.headers['resource-id'] === undefined) { + // Required data is missing so was not successful + return false; + } + + webhookData.webhookId = responseData.headers['resource-id'] as string; + webhookData.secret = body.secret; + return true; + }, + async delete(this: IHookFunctions): Promise { + const webhookData = this.getWorkflowStaticData('node'); + if (webhookData.webhookId !== undefined) { + + const endpoint = `/v2/webhooks/${webhookData.webhookId}`; + try { + await helpscoutApiRequest.call(this, 'DELETE', endpoint); + } catch (e) { + return false; + } + + // Remove from the static workflow data so that it is clear + // that no webhooks are registred anymore + delete webhookData.webhookId; + delete webhookData.secret; + } + return true; + }, + }, + }; + + async webhook(this: IWebhookFunctions): Promise { + const req = this.getRequestObject(); + const bodyData = this.getBodyData(); + const headerData = this.getHeaderData() as IDataObject; + const webhookData = this.getWorkflowStaticData('node'); + if (headerData['x-helpscout-signature'] === undefined) { + return {}; + } + //@ts-ignore + const computedSignature = createHmac('sha1', webhookData.secret as string).update(req.rawBody).digest('base64'); + if (headerData['x-helpscout-signature'] !== computedSignature) { + return {}; + } + return { + workflowData: [ + this.helpers.returnJsonArray(bodyData), + ], + }; + } +} diff --git a/packages/nodes-base/nodes/HelpScout/MailboxDescription.ts b/packages/nodes-base/nodes/HelpScout/MailboxDescription.ts new file mode 100644 index 0000000000..923b01677f --- /dev/null +++ b/packages/nodes-base/nodes/HelpScout/MailboxDescription.ts @@ -0,0 +1,54 @@ +import { INodeProperties } from "n8n-workflow"; + +export const mailboxOperations = [ + { + displayName: 'Operation', + name: 'operation', + type: 'options', + displayOptions: { + show: { + resource: [ + 'mailbox', + ], + }, + }, + options: [ + { + name: 'Get', + value: 'get', + description: 'Get data of a mailbox', + }, + { + name: 'Get All', + value: 'getAll', + description: 'Get all mailboxes', + }, + ], + default: 'get', + description: 'The operation to perform.', + }, +] as INodeProperties[]; + +export const mailboxFields = [ + +/* -------------------------------------------------------------------------- */ +/* mailbox:get */ +/* -------------------------------------------------------------------------- */ + { + displayName: 'Mailbox ID', + name: 'mailboxId', + type: 'string', + default: '', + required: true, + displayOptions: { + show: { + resource: [ + 'mailbox', + ], + operation: [ + 'get', + ], + }, + }, + }, +] as INodeProperties[]; diff --git a/packages/nodes-base/nodes/HelpScout/ThreadDescription.ts b/packages/nodes-base/nodes/HelpScout/ThreadDescription.ts new file mode 100644 index 0000000000..230b99ce56 --- /dev/null +++ b/packages/nodes-base/nodes/HelpScout/ThreadDescription.ts @@ -0,0 +1,257 @@ +import { INodeProperties } from "n8n-workflow"; + +export const threadOperations = [ + { + displayName: 'Operation', + name: 'operation', + type: 'options', + displayOptions: { + show: { + resource: [ + 'thread', + ], + }, + }, + options: [ + { + name: 'Create', + value: 'create', + description: 'Create a new chat thread', + }, + { + name: 'Get All', + value: 'getAll', + description: 'Get all chat threads', + }, + ], + default: 'create', + description: 'The operation to perform.', + }, +] as INodeProperties[]; + +export const threadFields = [ +/* -------------------------------------------------------------------------- */ +/* thread:create */ +/* -------------------------------------------------------------------------- */ + { + displayName: 'Conversation ID', + name: 'conversationId', + type: 'string', + default: '', + required: true, + displayOptions: { + show: { + resource: [ + 'thread', + ], + operation: [ + 'create', + ], + }, + }, + description: 'conversation ID', + }, + { + displayName: 'Type', + name: 'type', + type: 'options', + required: true, + displayOptions: { + show: { + resource: [ + 'thread', + ], + operation: [ + 'create', + ], + }, + }, + options: [ + { + name: 'Chat', + value: 'chat' + }, + { + name: 'Customer', + value: 'customer' + }, + { + name: 'Note', + value: 'note' + }, + { + name: 'Phone', + value: 'phone' + }, + { + name: 'Reply', + value: 'reply' + }, + ], + default: '', + }, + { + displayName: 'Text', + name: 'text', + type: 'string', + default: '', + typeOptions: { + alwaysOpenEditWindow: true, + }, + required: true, + displayOptions: { + show: { + resource: [ + 'thread', + ], + operation: [ + 'create', + ], + }, + }, + description: 'The chat text', + }, + { + displayName: 'Additional Fields', + name: 'additionalFields', + type: 'collection', + placeholder: 'Add Field', + default: {}, + displayOptions: { + show: { + operation: [ + 'create', + ], + resource: [ + 'thread', + ], + }, + }, + options: [ + { + displayName: 'Customer Email', + name: 'customerEmail', + type: 'string', + default: '', + }, + { + displayName: 'Customer ID', + name: 'customerId', + type: 'number', + default: 0, + }, + { + displayName: 'Draft', + name: 'draft', + type: 'boolean', + default: false, + displayOptions: { + show: { + '/type': [ + 'note', + ], + }, + }, + description: 'If set to true, a draft reply is created', + }, + { + displayName: 'Imported', + name: 'imported', + type: 'boolean', + default: false, + description: 'When imported is set to true, no outgoing emails or notifications will be generated.', + }, + { + displayName: 'Created At', + name: 'createdAt', + type: 'dateTime', + default: '', + }, + ] + }, + { + displayName: 'Attachments', + name: 'attachmentsUi', + placeholder: 'Add Attachments', + type: 'fixedCollection', + typeOptions: { + multipleValues: true, + }, + displayOptions: { + show: { + operation: [ + 'create', + ], + resource: [ + 'thread', + ], + }, + }, + options: [ + { + name: 'attachmentsValues', + displayName: 'Attachments Values', + values: [ + { + displayName: 'FileName', + name: 'fileName', + type: 'string', + default: '', + description: 'Attachment’s file name', + }, + { + displayName: 'Mime Type', + name: 'mimeType', + type: 'string', + default: '', + description: 'Attachment’s mime type', + }, + { + displayName: 'Data', + name: 'data', + type: 'string', + default: '', + placeholder: 'ZXhhbXBsZSBmaWxl', + description: 'Base64-encoded stream of data.', + }, + ], + }, + { + name: 'attachmentsBinary', + displayName: 'Attachments Binary', + values: [ + { + displayName: 'Property', + name: 'property', + type: 'string', + default: 'data', + description: 'Name of the binary properties which contain data which should be added to email as attachment', + }, + ], + }, + ], + default: '', + description: 'Array of supported attachments to add to the message.', + }, +/* -------------------------------------------------------------------------- */ +/* thread:getAll */ +/* -------------------------------------------------------------------------- */ + { + displayName: 'Conversation ID', + name: 'conversationId', + type: 'string', + default: '', + required: true, + displayOptions: { + show: { + resource: [ + 'thread', + ], + operation: [ + 'getAll', + ], + }, + }, + description: 'conversation ID', + }, +] as INodeProperties[]; diff --git a/packages/nodes-base/nodes/HelpScout/ThreadInterface.ts b/packages/nodes-base/nodes/HelpScout/ThreadInterface.ts new file mode 100644 index 0000000000..b6cbbda213 --- /dev/null +++ b/packages/nodes-base/nodes/HelpScout/ThreadInterface.ts @@ -0,0 +1,15 @@ +import { IDataObject } from "n8n-workflow"; + +export interface IAttachment { + fileName?: string; + mimeType?: string; + data?: string; +} + +export interface IThread { + createdAt?: string; + customer?: IDataObject; + imported?: boolean; + text?: string; + attachments?: IAttachment[]; +} diff --git a/packages/nodes-base/nodes/HelpScout/helpScout.png b/packages/nodes-base/nodes/HelpScout/helpScout.png new file mode 100644 index 0000000000..a2be95ba7f Binary files /dev/null and b/packages/nodes-base/nodes/HelpScout/helpScout.png differ diff --git a/packages/nodes-base/package.json b/packages/nodes-base/package.json index 746f91f723..282af6371b 100644 --- a/packages/nodes-base/package.json +++ b/packages/nodes-base/package.json @@ -42,7 +42,8 @@ "dist/credentials/GithubApi.credentials.js", "dist/credentials/GithubOAuth2Api.credentials.js", "dist/credentials/GitlabApi.credentials.js", - "dist/credentials/GoogleApi.credentials.js", + "dist/credentials/GoogleApi.credentials.js", + "dist/credentials/HelpScoutOAuth2Api.credentials.js", "dist/credentials/HttpBasicAuth.credentials.js", "dist/credentials/HttpDigestAuth.credentials.js", "dist/credentials/HttpHeaderAuth.credentials.js", @@ -118,7 +119,9 @@ "dist/nodes/Gitlab/GitlabTrigger.node.js", "dist/nodes/Google/GoogleDrive.node.js", "dist/nodes/Google/GoogleSheets.node.js", - "dist/nodes/GraphQL/GraphQL.node.js", + "dist/nodes/GraphQL/GraphQL.node.js", + "dist/nodes/HelpScout/HelpScout.node.js", + "dist/nodes/HelpScout/HelpScoutTrigger.node.js", "dist/nodes/HtmlExtract/HtmlExtract.node.js", "dist/nodes/HttpRequest.node.js", "dist/nodes/Hubspot/Hubspot.node.js",