diff --git a/packages/nodes-base/credentials/GetResponseApi.credentials.ts b/packages/nodes-base/credentials/GetResponseApi.credentials.ts new file mode 100644 index 0000000000..1494a01930 --- /dev/null +++ b/packages/nodes-base/credentials/GetResponseApi.credentials.ts @@ -0,0 +1,17 @@ +import { + ICredentialType, + NodePropertyTypes, +} from 'n8n-workflow'; + +export class GetResponseApi implements ICredentialType { + name = 'getResponseApi'; + displayName = 'GetResponse API'; + properties = [ + { + displayName: 'API Key', + name: 'apiKey', + type: 'string' as NodePropertyTypes, + default: '', + }, + ]; +} diff --git a/packages/nodes-base/credentials/GetResponseOAuth2Api.credentials.ts b/packages/nodes-base/credentials/GetResponseOAuth2Api.credentials.ts new file mode 100644 index 0000000000..76ce3acb0f --- /dev/null +++ b/packages/nodes-base/credentials/GetResponseOAuth2Api.credentials.ts @@ -0,0 +1,47 @@ +import { + ICredentialType, + NodePropertyTypes, +} from 'n8n-workflow'; + +export class GetResponseOAuth2Api implements ICredentialType { + name = 'getResponseOAuth2Api'; + extends = [ + 'oAuth2Api', + ]; + displayName = 'GetResponse OAuth2 API'; + properties = [ + { + displayName: 'Authorization URL', + name: 'authUrl', + type: 'hidden' as NodePropertyTypes, + default: 'https://app.getresponse.com/oauth2_authorize.html', + required: true, + }, + { + displayName: 'Access Token URL', + name: 'accessTokenUrl', + type: 'hidden' as NodePropertyTypes, + default: 'https://api.getresponse.com/v3/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: 'header', + description: 'Resource to consume.', + }, + ]; +} diff --git a/packages/nodes-base/nodes/GetResponse/ContactDescription.ts b/packages/nodes-base/nodes/GetResponse/ContactDescription.ts new file mode 100644 index 0000000000..ba173edde4 --- /dev/null +++ b/packages/nodes-base/nodes/GetResponse/ContactDescription.ts @@ -0,0 +1,646 @@ +import { + INodeProperties, +} from 'n8n-workflow'; + +export const contactOperations = [ + { + displayName: 'Operation', + name: 'operation', + type: 'options', + displayOptions: { + show: { + resource: [ + 'contact', + ], + }, + }, + options: [ + { + name: 'Create', + value: 'create', + description: 'Create a new contact', + }, + { + name: 'Delete', + value: 'delete', + description: 'Delete a contact', + }, + { + name: 'Get', + value: 'get', + description: 'Get a contact', + }, + { + name: 'Get All', + value: 'getAll', + description: 'Get all contacts', + }, + { + name: 'Update', + value: 'update', + description: 'Update contact properties', + }, + ], + default: 'get', + description: 'The operation to perform.', + }, +] as INodeProperties[]; + +export const contactFields = [ + /* -------------------------------------------------------------------------- */ + /* contact:create */ + /* -------------------------------------------------------------------------- */ + { + displayName: 'Email', + name: 'email', + type: 'string', + displayOptions: { + show: { + resource: [ + 'contact', + ], + operation: [ + 'create', + ], + }, + }, + default: '', + description: '', + }, + { + displayName: 'Campaign ID', + name: 'campaignId', + type: 'options', + typeOptions: { + loadOptionsMethod: 'getCampaigns', + }, + displayOptions: { + show: { + resource: [ + 'contact', + ], + operation: [ + 'create', + ], + }, + }, + default: '', + description: '', + }, + { + displayName: 'Additional Fields', + name: 'additionalFields', + type: 'collection', + placeholder: 'Add Field', + default: {}, + displayOptions: { + show: { + resource: [ + 'contact', + ], + operation: [ + 'create', + ], + }, + }, + options: [ + { + displayName: 'Custom Fields', + name: 'customFieldsUi', + type: 'fixedCollection', + default: '', + placeholder: 'Add Custom Field', + typeOptions: { + multipleValues: true, + }, + options: [ + { + name: 'customFieldValues', + displayName: 'Custom Field', + values: [ + { + displayName: 'Field ID', + name: 'customFieldId', + type: 'options', + typeOptions: { + loadOptionsMethod: 'getCustomFields', + }, + description: 'The end user specified key of the user defined data.', + default: '', + }, + { + displayName: 'Value', + name: 'value', + type: 'string', + description: 'The end user specified value of the user defined data.', + default: '', + }, + ], + }, + ], + }, + { + displayName: 'Day Of Cycle', + name: 'dayOfCycle', + type: 'string', + description: `The day on which the contact is in the Autoresponder cycle. null indicates the contacts is not in the cycle.`, + default: '', + }, + { + displayName: 'IP Address', + name: 'ipAddress', + type: 'string', + description: `The contact's IP address. IPv4 and IPv6 formats are accepted.`, + default: '', + }, + { + displayName: 'Name', + name: 'name', + type: 'string', + default: '', + }, + { + displayName: 'Note', + name: 'note', + type: 'string', + default: '', + }, + { + displayName: 'Scoring', + name: 'scoring', + type: 'number', + default: '', + description: 'Contact scoring, pass null to remove the score from a contact', + typeOptions: { + minValue: 0, + }, + }, + { + displayName: 'Tag IDs', + name: 'tags', + type: 'multiOptions', + typeOptions: { + loadOptionsMethod: 'getTags', + }, + default: '', + }, + ], + }, + + /* -------------------------------------------------------------------------- */ + /* contact:delete */ + /* -------------------------------------------------------------------------- */ + { + displayName: 'Contact ID', + name: 'contactId', + type: 'string', + required: true, + displayOptions: { + show: { + resource: [ + 'contact', + ], + operation: [ + 'delete', + ], + }, + }, + default: '', + description: 'Id of contact to delete.', + }, + { + displayName: 'Options', + name: 'options', + type: 'collection', + placeholder: 'Add Field', + default: {}, + displayOptions: { + show: { + resource: [ + 'contact', + ], + operation: [ + 'delete', + ], + }, + }, + options: [ + { + displayName: 'IP Address', + name: 'ipAddress', + type: 'string', + description: `This makes it possible to pass the IP from which the contact unsubscribed. Used only if the messageId was send.`, + default: '', + }, + { + displayName: 'Message ID', + name: 'messageId', + type: 'string', + description: `The ID of a message (such as a newsletter, an autoresponder, or an RSS-newsletter). When passed, this method will simulate the unsubscribe process, as if the contact clicked the unsubscribe link in a given message.`, + default: '', + }, + ], + }, + /* -------------------------------------------------------------------------- */ + /* contact:get */ + /* -------------------------------------------------------------------------- */ + { + displayName: 'Contact ID', + name: 'contactId', + type: 'string', + required: true, + displayOptions: { + show: { + resource: [ + 'contact', + ], + operation: [ + 'get', + ], + }, + }, + default: '', + description: 'Unique identifier for a particular contact', + }, + { + displayName: 'Options', + name: 'options', + type: 'collection', + placeholder: 'Add Field', + default: {}, + displayOptions: { + show: { + resource: [ + 'contact', + ], + operation: [ + 'get', + ], + }, + }, + options: [ + { + displayName: 'Fields', + name: 'fields', + type: 'string', + description: `List of fields that should be returned. Id is always returned. Fields should be separated by comma`, + default: '', + }, + ], + }, + /* -------------------------------------------------------------------------- */ + /* contact:getAll */ + /* -------------------------------------------------------------------------- */ + { + displayName: 'Return All', + name: 'returnAll', + type: 'boolean', + displayOptions: { + show: { + resource: [ + 'contact', + ], + operation: [ + 'getAll', + ], + }, + }, + default: false, + description: 'If all results should be returned or only up to a given limit.', + }, + { + displayName: 'Limit', + name: 'limit', + type: 'number', + default: 20, + displayOptions: { + show: { + resource: [ + 'contact', + ], + operation: [ + 'getAll', + ], + returnAll: [ + false, + ], + }, + }, + }, + { + displayName: 'Options', + name: 'options', + type: 'collection', + placeholder: 'Add Field', + default: {}, + displayOptions: { + show: { + resource: [ + 'contact', + ], + operation: [ + 'getAll', + ], + }, + }, + options: [ + { + displayName: 'Campaign ID', + name: 'campaignId', + type: 'string', + description: `Search contacts by campaign ID`, + default: '', + }, + { + displayName: 'Change On From', + name: 'changeOnFrom', + type: 'dateTime', + default: '', + description: `Search contacts edited from this date`, + }, + { + displayName: 'Change On To', + name: 'changeOnTo', + type: 'dateTime', + default: '', + description: `Search contacts edited to this date`, + }, + { + displayName: 'Created On From', + name: 'createdOnFrom', + type: 'dateTime', + default: '', + description: `Count data from this date`, + }, + { + displayName: 'Created On To', + name: 'createdOnTo', + type: 'dateTime', + default: '', + description: `Count data from this date`, + }, + { + displayName: 'Exact Match', + name: 'exactMatch', + type: 'boolean', + default: false, + description: `When set to true it will search for contacts with the exact value
+ of the email and name provided in the query string. Without this flag, matching is done via a standard 'like' comparison,
+ which may sometimes be slow.`, + }, + { + displayName: 'Fields', + name: 'fields', + type: 'string', + description: `List of fields that should be returned. Id is always returned. Fields should be separated by comma`, + default: '', + }, + { + displayName: 'Name', + name: 'name', + type: 'string', + description: `Search contacts by name`, + default: '', + }, + { + displayName: 'Origin', + name: 'origin', + type: 'options', + options: [ + { + name: 'API', + value: 'api', + }, + { + name: 'Copy', + value: 'copy', + }, + { + name: 'Email', + value: 'email', + }, + { + name: 'Forward', + value: 'forward', + }, + { + name: 'import', + value: 'import', + }, + { + name: 'Iphone', + value: 'iphone', + }, + { + name: 'Landing Page', + value: 'landing_page', + }, + { + name: 'Leads', + value: 'leads', + }, + { + name: 'Panel', + value: 'panel', + }, + { + name: 'Sale', + value: 'sale', + }, + { + name: 'Survey', + value: 'survey', + }, + { + name: 'Webinar', + value: 'webinar', + }, + { + name: 'WWW', + value: 'www', + }, + ], + description: `Search contacts by origin`, + default: '', + }, + { + displayName: 'Sort By', + name: 'sortBy', + type: 'options', + options: [ + { + name: 'Campaign ID', + value: 'campaignId', + }, + { + name: 'Changed On', + value: 'changedOn', + }, + { + name: 'Created On', + value: 'createdOn', + }, + { + name: 'Email', + value: 'email', + }, + ], + default: '', + }, + { + displayName: 'Sort Order', + name: 'sortOrder', + type: 'options', + options: [ + { + name: 'ASC', + value: 'ASC', + }, + { + name: 'DESC', + value: 'DESC', + }, + ], + default: '', + }, + ], + }, + /* -------------------------------------------------------------------------- */ + /* contact:update */ + /* -------------------------------------------------------------------------- */ + { + displayName: 'Contact ID', + name: 'contactId', + type: 'string', + required: true, + displayOptions: { + show: { + resource: [ + 'contact', + ], + operation: [ + 'update', + ], + }, + }, + default: '', + description: 'Unique identifier for a particular contact', + }, + { + displayName: 'Update Fields', + name: 'updateFields', + type: 'collection', + placeholder: 'Add Field', + default: {}, + displayOptions: { + show: { + resource: [ + 'contact', + ], + operation: [ + 'update', + ], + }, + }, + options: [ + { + displayName: 'Campaign ID', + name: 'campaignId', + type: 'options', + typeOptions: { + loadOptionsMethod: 'getCampaigns', + }, + default: '', + description: '', + }, + { + displayName: 'Custom Fields', + name: 'customFieldsUi', + type: 'fixedCollection', + default: '', + placeholder: 'Add Custom Field', + typeOptions: { + multipleValues: true, + }, + options: [ + { + name: 'customFieldValues', + displayName: 'Custom Field', + values: [ + { + displayName: 'Field ID', + name: 'customFieldId', + type: 'options', + typeOptions: { + loadOptionsMethod: 'getCustomFields', + }, + description: 'The end user specified key of the user defined data.', + default: '', + }, + { + displayName: 'Value', + name: 'value', + type: 'string', + description: 'The end user specified value of the user defined data.', + default: '', + }, + ], + }, + ], + }, + { + displayName: 'Day Of Cycle', + name: 'dayOfCycle', + type: 'string', + description: `The day on which the contact is in the Autoresponder cycle. null indicates the contacts is not in the cycle.`, + default: '', + }, + { + displayName: 'Email', + name: 'email', + type: 'string', + default: '', + description: '', + }, + { + displayName: 'IP Address', + name: 'ipAddress', + type: 'string', + description: `The contact's IP address. IPv4 and IPv6 formats are accepted.`, + default: '', + }, + { + displayName: 'Name', + name: 'name', + type: 'string', + default: '', + }, + { + displayName: 'Note', + name: 'note', + type: 'string', + default: '', + }, + { + displayName: 'Scoring', + name: 'scoring', + type: 'number', + default: '', + description: 'Contact scoring, pass null to remove the score from a contact', + typeOptions: { + minValue: 0, + }, + }, + { + displayName: 'Tag IDs', + name: 'tags', + type: 'multiOptions', + typeOptions: { + loadOptionsMethod: 'getTags', + }, + default: '', + }, + ], + }, + +] as INodeProperties[]; diff --git a/packages/nodes-base/nodes/GetResponse/GenericFunctions.ts b/packages/nodes-base/nodes/GetResponse/GenericFunctions.ts new file mode 100644 index 0000000000..eaae8d4df1 --- /dev/null +++ b/packages/nodes-base/nodes/GetResponse/GenericFunctions.ts @@ -0,0 +1,71 @@ +import { + OptionsWithUri, + } from 'request'; + +import { + IExecuteFunctions, + IHookFunctions, + ILoadOptionsFunctions, + IWebhookFunctions, +} from 'n8n-core'; + +import { + IDataObject +} from 'n8n-workflow'; + +export async function getresponseApiRequest(this: IWebhookFunctions | IHookFunctions | IExecuteFunctions | ILoadOptionsFunctions, method: string, resource: string, body: any = {}, qs: IDataObject = {}, uri?: string, option: IDataObject = {}): Promise { // tslint:disable-line:no-any + + const authentication = this.getNodeParameter('authentication', 0, 'apiKey') as string; + + let options: OptionsWithUri = { + headers: { + 'Content-Type': 'application/json', + }, + method, + body, + qs, + uri: uri || `https://api.getresponse.com/v3${resource}`, + json: true, + }; + try { + options = Object.assign({}, options, option); + if (Object.keys(body).length === 0) { + delete options.body; + } + + if (authentication === 'apiKey') { + const credentials = this.getCredentials('getResponseApi') as IDataObject; + options!.headers!['X-Auth-Token'] = `api-key ${credentials.apiKey}`; + //@ts-ignore + return await this.helpers.request.call(this, options); + } else { + //@ts-ignore + return await this.helpers.requestOAuth2.call(this, 'getResponseOAuth2Api', options); + } + } catch (error) { + if (error.response && error.response.body && error.response.body.message) { + // Try to return the error prettier + throw new Error(`GetResponse error response [${error.statusCode}]: ${error.response.body.message}`); + } + throw error; + } +} + +export async function getResponseApiRequestAllItems(this: IExecuteFunctions | ILoadOptionsFunctions, method: string, endpoint: string, body: any = {}, query: IDataObject = {}): Promise { // tslint:disable-line:no-any + + const returnData: IDataObject[] = []; + + let responseData; + query.page = 1; + + do { + responseData = await getresponseApiRequest.call(this, method, endpoint, body, query, undefined, { resolveWithFullResponse: true }); + query.page++; + returnData.push.apply(returnData, responseData.body); + } while ( + responseData.headers.TotalPages !== responseData.headers.CurrentPage + ); + + return returnData; +} + diff --git a/packages/nodes-base/nodes/GetResponse/GetResponse.node.ts b/packages/nodes-base/nodes/GetResponse/GetResponse.node.ts new file mode 100644 index 0000000000..01b069b79c --- /dev/null +++ b/packages/nodes-base/nodes/GetResponse/GetResponse.node.ts @@ -0,0 +1,320 @@ +import { + IExecuteFunctions, +} from 'n8n-core'; + +import { + IDataObject, + ILoadOptionsFunctions, + INodeExecutionData, + INodePropertyOptions, + INodeType, + INodeTypeDescription, +} from 'n8n-workflow'; + +import { + getresponseApiRequest, + getResponseApiRequestAllItems, +} from './GenericFunctions'; + +import { + contactFields, + contactOperations, +} from './ContactDescription'; + +import * as moment from 'moment-timezone'; + +export class GetResponse implements INodeType { + description: INodeTypeDescription = { + displayName: 'GetResponse', + name: 'getResponse', + icon: 'file:getResponse.png', + group: ['input'], + version: 1, + subtitle: '={{$parameter["operation"] + ": " + $parameter["resource"]}}', + description: 'Consume GetResponse API.', + defaults: { + name: 'GetResponse', + color: '#00afec', + }, + inputs: ['main'], + outputs: ['main'], + credentials: [ + { + name: 'getResponseApi', + required: true, + displayOptions: { + show: { + authentication: [ + 'apiKey', + ], + }, + }, + }, + { + name: 'getResponseOAuth2Api', + required: true, + displayOptions: { + show: { + authentication: [ + 'oAuth2', + ], + }, + }, + }, + ], + properties: [ + { + displayName: 'Authentication', + name: 'authentication', + type: 'options', + options: [ + { + name: 'API Key', + value: 'apiKey', + }, + { + name: 'OAuth2', + value: 'oAuth2', + }, + ], + default: 'apiKey', + description: 'The resource to operate on.', + }, + { + displayName: 'Resource', + name: 'resource', + type: 'options', + options: [ + { + name: 'Contact', + value: 'contact', + }, + ], + default: 'contact', + description: 'The resource to operate on.', + }, + ...contactOperations, + ...contactFields, + ], + }; + + methods = { + loadOptions: { + // Get all the campaigns to display them to user so that he can + // select them easily + async getCampaigns( + this: ILoadOptionsFunctions, + ): Promise { + const returnData: INodePropertyOptions[] = []; + const campaigns = await getresponseApiRequest.call( + this, + 'GET', + `/campaigns`, + ); + for (const campaign of campaigns) { + returnData.push({ + name: campaign.name as string, + value: campaign.campaignId, + }); + } + return returnData; + }, + // Get all the tagd to display them to user so that he can + // select them easily + async getTags( + this: ILoadOptionsFunctions, + ): Promise { + const returnData: INodePropertyOptions[] = []; + const tags = await getresponseApiRequest.call( + this, + 'GET', + `/tags`, + ); + for (const tag of tags) { + returnData.push({ + name: tag.name as string, + value: tag.tagId, + }); + } + return returnData; + }, + // Get all the custom fields to display them to user so that he can + // select them easily + async getCustomFields( + this: ILoadOptionsFunctions, + ): Promise { + const returnData: INodePropertyOptions[] = []; + const customFields = await getresponseApiRequest.call( + this, + 'GET', + `/custom-fields`, + ); + for (const customField of customFields) { + returnData.push({ + name: customField.name as string, + value: customField.customFieldId, + }); + } + 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 === 'contact') { + //https://apireference.getresponse.com/#operation/createContact + if (operation === 'create') { + const email = this.getNodeParameter('email', i) as string; + + const campaignId = this.getNodeParameter('campaignId', i) as string; + + const additionalFields = this.getNodeParameter('additionalFields', i) as IDataObject; + + const body: IDataObject = { + email, + campaign: { + campaignId, + }, + }; + + Object.assign(body, additionalFields); + + if (additionalFields.customFieldsUi) { + const customFieldValues = (additionalFields.customFieldsUi as IDataObject).customFieldValues as IDataObject[]; + if (customFieldValues) { + body.customFieldValues = customFieldValues; + for (let i = 0; i < customFieldValues.length; i++) { + if (!Array.isArray(customFieldValues[i].value)) { + customFieldValues[i].value = [customFieldValues[i].value]; + } + } + delete body.customFieldsUi; + } + } + + responseData = await getresponseApiRequest.call(this, 'POST', '/contacts', body); + + responseData = { success: true }; + } + //https://apireference.getresponse.com/?_ga=2.160836350.2102802044.1604719933-1897033509.1604598019#operation/deleteContact + if (operation === 'delete') { + const contactId = this.getNodeParameter('contactId', i) as string; + + const options = this.getNodeParameter('options', i) as IDataObject; + + Object.assign(qs, options); + + responseData = await getresponseApiRequest.call(this, 'DELETE', `/contacts/${contactId}`, {}, qs); + + responseData = { success: true }; + } + //https://apireference.getresponse.com/?_ga=2.160836350.2102802044.1604719933-1897033509.1604598019#operation/getContactById + if (operation === 'get') { + const contactId = this.getNodeParameter('contactId', i) as string; + + const options = this.getNodeParameter('options', i) as IDataObject; + + Object.assign(qs, options); + + responseData = await getresponseApiRequest.call(this, 'GET', `/contacts/${contactId}`, {}, qs); + } + //https://apireference.getresponse.com/?_ga=2.160836350.2102802044.1604719933-1897033509.1604598019#operation/getContactList + if (operation === 'getAll') { + const returnAll = this.getNodeParameter('returnAll', i) as boolean; + + const options = this.getNodeParameter('options', i) as IDataObject; + + const timezone = this.getTimezone(); + + Object.assign(qs, options); + + const isNotQuery = [ + 'sortBy', + 'sortOrder', + 'additionalFlags', + 'fields', + 'exactMatch', + ]; + + const isDate = [ + 'createdOnFrom', + 'createdOnTo', + 'changeOnFrom', + 'changeOnTo', + ]; + + const dateMapToKey: { [key: string]: string; } = { + 'createdOnFrom': '[createdOn][from]', + 'createdOnTo': '[createdOn][to]', + 'changeOnFrom': '[changeOn][from]', + 'changeOnTo': '[changeOn][to]', + }; + + for (const key of Object.keys(qs)) { + if (!isNotQuery.includes(key)) { + if (isDate.includes(key)) { + qs[`query${dateMapToKey[key]}`] = moment.tz(qs[key], timezone).format('YYYY-MM-DDTHH:mm:ssZZ'); + } else { + qs[`query[${key}]`] = qs[key]; + } + delete qs[key]; + } + } + + if (qs.sortBy) { + qs[`sort[${qs.sortBy}]`] = qs.sortOrder || 'ASC'; + } + + if (qs.exactMatch === true) { + qs['additionalFlags'] = 'exactMatch'; + delete qs.exactMatch; + } + + if (returnAll) { + responseData = await getResponseApiRequestAllItems.call(this, 'GET', `/contacts`, {}, qs); + } else { + qs.perPage = this.getNodeParameter('limit', i) as number; + responseData = await getresponseApiRequest.call(this, 'GET', `/contacts`, {}, qs); + } + } + //https://apireference.getresponse.com/?_ga=2.160836350.2102802044.1604719933-1897033509.1604598019#operation/updateContact + if (operation === 'update') { + + const contactId = this.getNodeParameter('contactId', i) as string; + + const updateFields = this.getNodeParameter('updateFields', i) as IDataObject; + + const body: IDataObject = {}; + + Object.assign(body, updateFields); + + if (updateFields.customFieldsUi) { + const customFieldValues = (updateFields.customFieldsUi as IDataObject).customFieldValues as IDataObject[]; + if (customFieldValues) { + body.customFieldValues = customFieldValues; + delete body.customFieldsUi; + } + } + + responseData = await getresponseApiRequest.call(this, 'POST', `/contacts/${contactId}`, 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/GetResponse/getResponse.png b/packages/nodes-base/nodes/GetResponse/getResponse.png new file mode 100644 index 0000000000..78a1717949 Binary files /dev/null and b/packages/nodes-base/nodes/GetResponse/getResponse.png differ diff --git a/packages/nodes-base/package.json b/packages/nodes-base/package.json index 29a0b90bb7..b88362d29f 100644 --- a/packages/nodes-base/package.json +++ b/packages/nodes-base/package.json @@ -71,6 +71,8 @@ "dist/credentials/FileMaker.credentials.js", "dist/credentials/FlowApi.credentials.js", "dist/credentials/Ftp.credentials.js", + "dist/credentials/GetResponseApi.credentials.js", + "dist/credentials/GetResponseOAuth2Api.credentials.js", "dist/credentials/GithubApi.credentials.js", "dist/credentials/GithubOAuth2Api.credentials.js", "dist/credentials/GitlabApi.credentials.js", @@ -276,6 +278,7 @@ "dist/nodes/Flow/FlowTrigger.node.js", "dist/nodes/Function.node.js", "dist/nodes/FunctionItem.node.js", + "dist/nodes/GetResponse/GetResponse.node.js", "dist/nodes/Github/Github.node.js", "dist/nodes/Github/GithubTrigger.node.js", "dist/nodes/Gitlab/Gitlab.node.js",