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",