diff --git a/packages/cli/BREAKING-CHANGES.md b/packages/cli/BREAKING-CHANGES.md index e1a52ff9c3..898aae4324 100644 --- a/packages/cli/BREAKING-CHANGES.md +++ b/packages/cli/BREAKING-CHANGES.md @@ -2,6 +2,29 @@ This list shows all the versions which include breaking changes and how to upgrade +## 0.20.0 + +### What changed? + +The node "ActiveCampaign" had to be changed to use v1 of their API. That API is sadly +quite bad but at least it is feature complete which their v3 sadly is not. + +### When is action necessary? + +If a "ActiveCampaign" node gets used in any workflow. + +### How to upgrade: + +After upgrading open all workflows which contain a "Read File From Url" node. +They will have a "?" as icon as they are not known anymore. Create a new +"HTTP Request" node to replace the old one and add the same URL as the previous +node had (in case you do not know it anymore you can select the old node, copy +it and paste it in a text-editor, it will display all the data the node +contained). Then set the "Response Format" to "File". Everything will then +function again like before. + + + ## 0.19.0 ### What changed? @@ -11,7 +34,7 @@ The node "Read File From Url" got removed as its functionality got added to ### When is action necessary? -If the "Read File From Url" gets used in any workflow. +If the "Read File From Url" node gets used in any workflow. ### How to upgrade: diff --git a/packages/editor-ui/package.json b/packages/editor-ui/package.json index a4ffb94ae2..357db000cd 100644 --- a/packages/editor-ui/package.json +++ b/packages/editor-ui/package.json @@ -23,8 +23,7 @@ "test:e2e": "vue-cli-service test:e2e", "test:unit": "vue-cli-service test:unit" }, - "dependencies": { - }, + "dependencies": {}, "devDependencies": { "@beyonk/google-fonts-webpack-plugin": "^1.2.3", "@fortawesome/fontawesome-svg-core": "^1.2.19", diff --git a/packages/nodes-base/nodes/ActiveCampaign/ActiveCampaign.node.ts b/packages/nodes-base/nodes/ActiveCampaign/ActiveCampaign.node.ts index e6efe97457..83682fd1a6 100644 --- a/packages/nodes-base/nodes/ActiveCampaign/ActiveCampaign.node.ts +++ b/packages/nodes-base/nodes/ActiveCampaign/ActiveCampaign.node.ts @@ -183,14 +183,14 @@ export class ActiveCampaign implements INodeType { options: [ { displayName: 'First Name', - name: 'firstName', + name: 'first_name', type: 'string', default: '', description: 'The first name of the contact to create', }, { displayName: 'Last Name', - name: 'lastName', + name: 'last_name', type: 'string', default: '', description: 'The last name of the contact to create', @@ -282,50 +282,68 @@ export class ActiveCampaign implements INodeType { description: 'ID of the contact to get.', }, - // ---------------------------------- - // contact:getAll - // ---------------------------------- - { - displayName: 'Return All', - name: 'returnAll', - type: 'boolean', - displayOptions: { - show: { - operation: [ - 'getAll', - ], - resource: [ - 'contact', - ], - }, - }, - default: false, - description: 'If all results should be returned or only up to a given limit.', - }, - { - displayName: 'Limit', - name: 'limit', - type: 'number', - displayOptions: { - show: { - operation: [ - 'getAll', - ], - resource: [ - 'contact', - ], - returnAll: [ - false, - ], - }, - }, - typeOptions: { - minValue: 1, - maxValue: 500, - }, - default: 100, - description: 'How many results to return.', - }, + // TODO: Does not work as expted so remove for now + // // ---------------------------------- + // // contact:getAll + // // ---------------------------------- + // { + // displayName: 'Full User Data', + // name: 'fullUserData', + // type: 'boolean', + // displayOptions: { + // show: { + // operation: [ + // 'getAll', + // ], + // resource: [ + // 'contact', + // ], + // }, + // }, + // default: false, + // description: 'If all data of the user should be returned or an abbreviated version.', + // }, + // { + // displayName: 'Return All', + // name: 'returnAll', + // type: 'boolean', + // displayOptions: { + // show: { + // operation: [ + // 'getAll', + // ], + // resource: [ + // 'contact', + // ], + // }, + // }, + // default: false, + // description: 'If all results should be returned or only results of a given page.', + // }, + // { + // displayName: 'Page', + // name: 'page', + // type: 'number', + // displayOptions: { + // show: { + // operation: [ + // 'getAll', + // ], + // resource: [ + // 'contact', + // ], + // returnAll: [ + // false, + // ], + // }, + // }, + // typeOptions: { + // minValue: 1, + // maxValue: 500, + // }, + // default: 1, + // description: 'Maximum 20 results per page get returned. Set which page to return.', + // }, // ---------------------------------- // contact:update @@ -375,14 +393,14 @@ export class ActiveCampaign implements INodeType { }, { displayName: 'First Name', - name: 'firstName', + name: 'first_name', type: 'string', default: '', description: 'First name of the contact', }, { displayName: 'Last Name', - name: 'lastName', + name: 'last_name', type: 'string', default: '', description: 'Last name of the contact', @@ -447,17 +465,16 @@ export class ActiveCampaign implements INodeType { let qs: IDataObject; let requestMethod: string; - let endpoint: string; + const endpoint = '/admin/api.php'; let returnAll = false; - let dataKey: string | undefined; + let dataKeys: string[] | undefined; for (let i = 0; i < items.length; i++) { - dataKey = undefined; resource = this.getNodeParameter('resource', 0) as string; operation = this.getNodeParameter('operation', 0) as string; + dataKeys = undefined; requestMethod = 'GET'; - endpoint = ''; body = {} as IDataObject; qs = {} as IDataObject; @@ -471,27 +488,27 @@ export class ActiveCampaign implements INodeType { const updateIfExists = this.getNodeParameter('updateIfExists', i) as boolean; if (updateIfExists === true) { - endpoint = '/api/3/contact/sync'; + qs.api_action = 'contact_sync'; } else { - endpoint = '/api/3/contacts'; + qs.api_action = 'contact_add'; } - dataKey = 'contact'; - body.contact = { - email: this.getNodeParameter('email', i) as string, - } as IDataObject; + dataKeys = ['subscriber_id']; + + body.email = this.getNodeParameter('email', i) as string; const additionalFields = this.getNodeParameter('additionalFields', i) as IDataObject; - addAdditionalFields(body.contact as IDataObject, additionalFields); + addAdditionalFields(body as IDataObject, additionalFields); } else if (operation === 'delete') { // ---------------------------------- // contact:delete // ---------------------------------- - requestMethod = 'DELETE'; + requestMethod = 'GET'; + qs.api_action = 'contact_delete'; const contactId = this.getNodeParameter('contactId', i) as number; - endpoint = `/api/3/contacts/${contactId}`; + qs.id = contactId; } else if (operation === 'get') { // ---------------------------------- @@ -499,39 +516,45 @@ export class ActiveCampaign implements INodeType { // ---------------------------------- requestMethod = 'GET'; + qs.api_action = 'contact_view'; const contactId = this.getNodeParameter('contactId', i) as number; - endpoint = `/api/3/contacts/${contactId}`; + qs.id = contactId; - } else if (operation === 'getAll') { - // ---------------------------------- - // persons:getAll - // ---------------------------------- + // TODO: Does not work as expted so remove for now + // } else if (operation === 'getAll') { + // // ---------------------------------- + // // contact:getAll + // // ---------------------------------- - requestMethod = 'GET'; + // requestMethod = 'GET'; + // qs.api_action = 'contact_list'; + // qs.ids = 'ALL'; - returnAll = this.getNodeParameter('returnAll', i) as boolean; - if (returnAll === false) { - qs.limit = this.getNodeParameter('limit', i) as number; - } + // returnAll = this.getNodeParameter('returnAll', i) as boolean; + // if (returnAll === false) { + // qs.page = this.getNodeParameter('page', i) as number; + // } - dataKey = 'contacts'; - endpoint = `/api/3/contacts`; + // const fullUserData = this.getNodeParameter('fullUserData', i) as boolean; + // qs.full = fullUserData === true ? 1 : 0; } else if (operation === 'update') { // ---------------------------------- // contact:update // ---------------------------------- - requestMethod = 'PUT'; + requestMethod = 'POST'; const contactId = this.getNodeParameter('contactId', i) as number; - endpoint = `/api/3/contacts/${contactId}`; + qs.api_action = 'contact_edit'; + qs.overwrite = 0; - dataKey = 'contact'; - body.contact = {} as IDataObject; + dataKeys = ['subscriber_id']; + + body.id = contactId; const updateFields = this.getNodeParameter('updateFields', i) as IDataObject; - addAdditionalFields(body.contact as IDataObject, updateFields); + addAdditionalFields(body as IDataObject, updateFields); } } else { @@ -540,9 +563,9 @@ export class ActiveCampaign implements INodeType { let responseData; if (returnAll === true) { - responseData = await activeCampaignApiRequestAllItems.call(this, requestMethod, endpoint, body, qs, dataKey); + responseData = await activeCampaignApiRequestAllItems.call(this, requestMethod, endpoint, body, qs, dataKeys); } else { - responseData = await activeCampaignApiRequest.call(this, requestMethod, endpoint, body, qs, dataKey); + responseData = await activeCampaignApiRequest.call(this, requestMethod, endpoint, body, qs, dataKeys); } if (Array.isArray(responseData)) { diff --git a/packages/nodes-base/nodes/ActiveCampaign/GenericFunctions.ts b/packages/nodes-base/nodes/ActiveCampaign/GenericFunctions.ts index 169c9f1146..de4e0884cc 100644 --- a/packages/nodes-base/nodes/ActiveCampaign/GenericFunctions.ts +++ b/packages/nodes-base/nodes/ActiveCampaign/GenericFunctions.ts @@ -19,7 +19,7 @@ import { OptionsWithUri } from 'request'; * @param {object} body * @returns {Promise} */ -export async function activeCampaignApiRequest(this: IHookFunctions | IExecuteFunctions, method: string, endpoint: string, body: IDataObject, query?: IDataObject, dataKey?: string): Promise { // tslint:disable-line:no-any +export async function activeCampaignApiRequest(this: IHookFunctions | IExecuteFunctions, method: string, endpoint: string, body: IDataObject, query?: IDataObject, dataKeys?: string[]): Promise { // tslint:disable-line:no-any const credentials = this.getCredentials('activeCampaignApi'); if (credentials === undefined) { throw new Error('No credentials got returned!'); @@ -29,10 +29,10 @@ export async function activeCampaignApiRequest(this: IHookFunctions | IExecuteFu query = {}; } + query.api_key = credentials.apiKey; + query.api_output = 'json'; + const options: OptionsWithUri = { - headers: { - 'Api-Token': credentials.apiKey, - }, method, qs: query, uri: `${credentials.apiUrl}${endpoint}`, @@ -40,22 +40,27 @@ export async function activeCampaignApiRequest(this: IHookFunctions | IExecuteFu }; if (Object.keys(body).length !== 0) { - options.body = body; + options.form = body; } + const returnData: IDataObject = {}; try { const responseData = await this.helpers.request(options); - if (responseData.success === false) { - throw new Error(`ActiveCampaign error response: ${responseData.error} (${responseData.error_info})`); + if (responseData.result_code === 0) { + throw new Error(`ActiveCampaign error response: ${responseData.result_message}`); } - if (dataKey === undefined) { + if (dataKeys === undefined) { return responseData; - } else { - return responseData[dataKey] as IDataObject; } + for (const dataKey of dataKeys) { + returnData[dataKey] = responseData[dataKey]; + } + + return returnData; + } catch (error) { if (error.statusCode === 403) { // Return a clear error @@ -81,7 +86,7 @@ export async function activeCampaignApiRequest(this: IHookFunctions | IExecuteFu * @param {IDataObject} [query] * @returns {Promise} */ -export async function activeCampaignApiRequestAllItems(this: IHookFunctions | IExecuteFunctions, method: string, endpoint: string, body: IDataObject, query?: IDataObject, dataKey?: string): Promise { // tslint:disable-line:no-any +export async function activeCampaignApiRequestAllItems(this: IHookFunctions | IExecuteFunctions, method: string, endpoint: string, body: IDataObject, query?: IDataObject, dataKeys?: string[]): Promise { // tslint:disable-line:no-any if (query === undefined) { query = {}; @@ -95,15 +100,10 @@ export async function activeCampaignApiRequestAllItems(this: IHookFunctions | IE let itemsReceived = 0; do { - responseData = await activeCampaignApiRequest.call(this, method, endpoint, body, query); + responseData = await activeCampaignApiRequest.call(this, method, endpoint, body, query, dataKeys); - if (dataKey === undefined) { - returnData.push.apply(returnData, responseData); - itemsReceived += returnData.length; - } else { - returnData.push.apply(returnData, responseData[dataKey]); - itemsReceived += responseData[dataKey].length; - } + returnData.push.apply(returnData, responseData); + itemsReceived += returnData.length; query.offset = itemsReceived; } while (