From c73a5f76dc81f60971b962de2142d3829950a443 Mon Sep 17 00:00:00 2001 From: Oliver Trajceski Date: Thu, 28 Oct 2021 17:59:09 +0200 Subject: [PATCH 01/17] :bug: Rework expression for renaming node for dotted expressions (#2380) --- packages/workflow/src/Workflow.ts | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/packages/workflow/src/Workflow.ts b/packages/workflow/src/Workflow.ts index 7152cbac7b..860eaea0dd 100644 --- a/packages/workflow/src/Workflow.ts +++ b/packages/workflow/src/Workflow.ts @@ -415,8 +415,7 @@ export class Workflow { const currentNameEscaped = currentName.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'); parameterValue = parameterValue.replace( - // eslint-disable-next-line no-useless-escape - new RegExp(`(\\$node(\.|\\["|\\[\'))${currentNameEscaped}((\s/g|"\\]|\'\\]))`, 'g'), + new RegExp(`(\\$node(\\.|\\["|\\['))${currentNameEscaped}((\\.|"\\]|'\\]))`, 'g'), `$1${newName}$3`, ); } From c97ceba86d074ebcc1c7566e19280e9d7dcb03c8 Mon Sep 17 00:00:00 2001 From: Omar Ajoue Date: Thu, 28 Oct 2021 18:07:09 +0200 Subject: [PATCH 02/17] :bug: Fixed the way proxies are declared with axios (#2384) --- packages/core/src/NodeExecuteFunctions.ts | 60 ++++++++++++++++++++++- 1 file changed, 58 insertions(+), 2 deletions(-) diff --git a/packages/core/src/NodeExecuteFunctions.ts b/packages/core/src/NodeExecuteFunctions.ts index 6ef54f37ac..b34f2ed02d 100644 --- a/packages/core/src/NodeExecuteFunctions.ts +++ b/packages/core/src/NodeExecuteFunctions.ts @@ -71,7 +71,7 @@ import { fromBuffer } from 'file-type'; import { lookup } from 'mime-types'; import axios, { AxiosProxyConfig, AxiosRequestConfig, Method } from 'axios'; -import { URLSearchParams } from 'url'; +import { URL, URLSearchParams } from 'url'; // eslint-disable-next-line import/no-cycle import { BINARY_ENCODING, @@ -338,7 +338,63 @@ async function parseRequestObject(requestObject: IDataObject) { } if (requestObject.proxy !== undefined) { - axiosConfig.proxy = requestObject.proxy as AxiosProxyConfig; + // try our best to parse the url provided. + if (typeof requestObject.proxy === 'string') { + try { + const url = new URL(requestObject.proxy); + axiosConfig.proxy = { + host: url.hostname, + port: parseInt(url.port, 10), + protocol: url.protocol, + }; + if (!url.port) { + // Sets port to a default if not informed + if (url.protocol === 'http') { + axiosConfig.proxy.port = 80; + } else if (url.protocol === 'https') { + axiosConfig.proxy.port = 443; + } + } + if (url.username || url.password) { + axiosConfig.proxy.auth = { + username: url.username, + password: url.password, + }; + } + } catch (error) { + // Not a valid URL. We will try to simply parse stuff + // such as user:pass@host:port without protocol (we'll assume http) + if (requestObject.proxy.includes('@')) { + const [userpass, hostport] = requestObject.proxy.split('@'); + const [username, password] = userpass.split(':'); + const [hostname, port] = hostport.split(':'); + axiosConfig.proxy = { + host: hostname, + port: parseInt(port, 10), + protocol: 'http', + auth: { + username, + password, + }, + }; + } else if (requestObject.proxy.includes(':')) { + const [hostname, port] = requestObject.proxy.split(':'); + axiosConfig.proxy = { + host: hostname, + port: parseInt(port, 10), + protocol: 'http', + }; + } else { + axiosConfig.proxy = { + host: requestObject.proxy, + port: 80, + protocol: 'http', + }; + } + } + } else { + axiosConfig.proxy = requestObject.proxy as AxiosProxyConfig; + } } if (requestObject.encoding === null) { From e39678b54f18c424175ef0c0f8f37c55981b790b Mon Sep 17 00:00:00 2001 From: Omar Ajoue Date: Thu, 28 Oct 2021 18:09:25 +0200 Subject: [PATCH 03/17] :bug: Fixed url params serializing for OAuth1 requests (#2381) --- packages/core/src/NodeExecuteFunctions.ts | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/packages/core/src/NodeExecuteFunctions.ts b/packages/core/src/NodeExecuteFunctions.ts index b34f2ed02d..d6dc5da8ad 100644 --- a/packages/core/src/NodeExecuteFunctions.ts +++ b/packages/core/src/NodeExecuteFunctions.ts @@ -86,6 +86,12 @@ import { axios.defaults.timeout = 300000; // Prevent axios from adding x-form-www-urlencoded headers by default axios.defaults.headers.post = {}; +axios.defaults.paramsSerializer = (params) => { + if (params instanceof URLSearchParams) { + return params.toString(); + } + return stringify(params, { arrayFormat: 'indices' }); +}; const requestPromiseWithDefaults = requestPromise.defaults({ timeout: 300000, // 5 minutes @@ -413,6 +419,7 @@ async function parseRequestObject(requestObject: IDataObject) { if ( requestObject.json !== false && axiosConfig.data !== undefined && + axiosConfig.data !== '' && !(axiosConfig.data instanceof Buffer) && !allHeaders.some((headerKey) => headerKey.toLowerCase() === 'content-type') ) { @@ -462,6 +469,11 @@ async function proxyRequestToAxios( axiosConfig = Object.assign(axiosConfig, await parseRequestObject(configObject)); + Logger.debug('Proxying request to axios', { + originalConfig: configObject, + parsedConfig: axiosConfig, + }); + return new Promise((resolve, reject) => { axios(axiosConfig) .then((response) => { From a798c6c0f6bce3feab4113412c081c445e1ff6e3 Mon Sep 17 00:00:00 2001 From: Ricardo Espinoza Date: Thu, 28 Oct 2021 20:00:25 -0400 Subject: [PATCH 04/17] :sparkles: Add Microsoft Dynamics CRM Node (#2292) * :sparkles: Microsoft Dynamics CRM * :zap: Improvements * :zap: Improvements * :zap: Minor improvements Co-authored-by: Jan Oberhauser --- .../MicrosoftDynamicsOAuth2Api.credentials.ts | 30 ++ .../Microsoft/Dynamics/GenericFunctions.ts | 502 ++++++++++++++++++ .../Dynamics/MicrosoftDynamicsCrm.node.ts | 249 +++++++++ .../descriptions/AccountDescription.ts | 274 ++++++++++ .../Microsoft/Dynamics/descriptions/index.ts | 1 + .../nodes/Microsoft/Dynamics/dynamicsCrm.svg | 1 + packages/nodes-base/package.json | 2 + 7 files changed, 1059 insertions(+) create mode 100644 packages/nodes-base/credentials/MicrosoftDynamicsOAuth2Api.credentials.ts create mode 100644 packages/nodes-base/nodes/Microsoft/Dynamics/GenericFunctions.ts create mode 100644 packages/nodes-base/nodes/Microsoft/Dynamics/MicrosoftDynamicsCrm.node.ts create mode 100644 packages/nodes-base/nodes/Microsoft/Dynamics/descriptions/AccountDescription.ts create mode 100644 packages/nodes-base/nodes/Microsoft/Dynamics/descriptions/index.ts create mode 100644 packages/nodes-base/nodes/Microsoft/Dynamics/dynamicsCrm.svg diff --git a/packages/nodes-base/credentials/MicrosoftDynamicsOAuth2Api.credentials.ts b/packages/nodes-base/credentials/MicrosoftDynamicsOAuth2Api.credentials.ts new file mode 100644 index 0000000000..f94722ee2c --- /dev/null +++ b/packages/nodes-base/credentials/MicrosoftDynamicsOAuth2Api.credentials.ts @@ -0,0 +1,30 @@ +import { + ICredentialType, + INodeProperties, +} from 'n8n-workflow'; + +export class MicrosoftDynamicsOAuth2Api implements ICredentialType { + name = 'microsoftDynamicsOAuth2Api'; + extends = [ + 'microsoftOAuth2Api', + ]; + displayName = 'Microsoft Dynamics OAuth2 API'; + documentationUrl = 'microsoft'; + properties: INodeProperties[] = [ + //https://docs.microsoft.com/en-us/azure/active-directory/develop/v2-permissions-and-consent + { + displayName: 'Subdomain', + name: 'subdomain', + type: 'string', + required: true, + placeholder: 'organization', + default: '', + }, + { + displayName: 'Scope', + name: 'scope', + type: 'hidden', + default: '=openid offline_access https://{{$self.subdomain}}.crm.dynamics.com/.default', + }, + ]; +} diff --git a/packages/nodes-base/nodes/Microsoft/Dynamics/GenericFunctions.ts b/packages/nodes-base/nodes/Microsoft/Dynamics/GenericFunctions.ts new file mode 100644 index 0000000000..006d7bd741 --- /dev/null +++ b/packages/nodes-base/nodes/Microsoft/Dynamics/GenericFunctions.ts @@ -0,0 +1,502 @@ +import { + OptionsWithUri, +} from 'request'; + +import { + IExecuteFunctions, + IExecuteSingleFunctions, + ILoadOptionsFunctions, +} from 'n8n-core'; + +import { + IDataObject, + INodePropertyOptions, + NodeApiError, +} from 'n8n-workflow'; + +export async function microsoftApiRequest(this: IExecuteFunctions | IExecuteSingleFunctions | ILoadOptionsFunctions, method: string, resource: string, body: any = {}, qs: IDataObject = {}, uri?: string, option: IDataObject = {}): Promise { // tslint:disable-line:no-any + const credenitals = await this.getCredentials('microsoftDynamicsOAuth2Api') as { domain: string }; + + let options: OptionsWithUri = { + headers: { + 'Content-Type': 'application/json', + 'accept': 'application/json', + 'Prefer': 'return=representation', + }, + method, + body, + qs, + uri: uri || `https://${credenitals.subdomain}.crm.dynamics.com/api/data/v9.2${resource}`, + json: true, + }; + + try { + if (Object.keys(option).length !== 0) { + options = Object.assign({}, options, option); + } + //@ts-ignore + return await this.helpers.requestOAuth2.call(this, 'microsoftDynamicsOAuth2Api', options, { property: 'id_token' }); + } catch (error) { + throw new NodeApiError(this.getNode(), error); + } +} + +export async function microsoftApiRequestAllItems(this: IExecuteFunctions | ILoadOptionsFunctions, propertyName: string, method: string, endpoint: string, body: any = {}, query: IDataObject = {}): Promise { // tslint:disable-line:no-any + + const returnData: IDataObject[] = []; + + let responseData; + let uri: string | undefined; + query['$top'] = 100; + + do { + responseData = await microsoftApiRequest.call(this, method, endpoint, body, query, uri); + uri = responseData['@odata.nextLink']; + returnData.push.apply(returnData, responseData[propertyName]); + } while ( + responseData['@odata.nextLink'] !== undefined + ); + + return returnData; +} + +export async function getPicklistOptions(this: ILoadOptionsFunctions, entityName: string, attributeName: string): Promise { + const returnData: INodePropertyOptions[] = []; + const endpoint = `/EntityDefinitions(LogicalName='${entityName}')/Attributes(LogicalName='${attributeName}')/Microsoft.Dynamics.CRM.PicklistAttributeMetadata?$select=LogicalName&$expand=OptionSet($select=Options),GlobalOptionSet($select=Options)`; + const { OptionSet: { Options: options } } = await microsoftApiRequest.call(this, 'GET', endpoint); + for (const option of options) { + returnData.push({ + name: option.Label.UserLocalizedLabel.Label, + value: option.Value, + }); + } + return returnData; +} + +export async function getEntityFields(this: ILoadOptionsFunctions, entityName: string): Promise { + const endpoint = `/EntityDefinitions(LogicalName='${entityName}')/Attributes`; + const { value } = await microsoftApiRequest.call(this, 'GET', endpoint); + return value; +} + +export function adjustAddresses(addresses: [{ [key: string]: string }]) { + // tslint:disable-next-line: no-any + const results: { [key: string]: any } = {}; + for (const [index, address] of addresses.entries()) { + for (const key of Object.keys(address)) { + if (address[key] !== '') { + results[`address${index + 1}_${key}`] = address[key]; + } + } + } + return results; +} + +export function getAccountFields() { + return [ + { + displayName: 'Account Category', + name: 'accountcategorycode', + type: 'options', + typeOptions: { + loadOptionsMethod: 'getAccountCategories', + }, + default: '', + description: 'Category to indicate whether the customer account is standard or preferred', + }, + { + displayName: 'Account Rating', + name: 'accountratingcode', + type: 'options', + typeOptions: { + loadOptionsMethod: 'getAccountRatingCodes', + }, + default: '', + }, + { + displayName: 'Address', + name: 'addresses', + type: 'fixedCollection', + default: {}, + typeOptions: { + multipleValues: true, + }, + placeholder: 'Add Address Field', + options: [ + { + displayName: 'Address Fields', + name: 'address', + values: [ + { + displayName: 'Address Type', + name: 'addresstypecode', + type: 'options', + typeOptions: { + loadOptionsMethod: 'getAddressTypes', + }, + default: '', + }, + { + displayName: 'Line1', + name: 'line1', + type: 'string', + default: '', + }, + { + displayName: 'Line2', + name: 'line2', + type: 'string', + default: '', + }, + { + displayName: 'Line3', + name: 'line3', + type: 'string', + default: '', + }, + { + displayName: 'City', + name: 'city', + type: 'string', + default: '', + }, + { + displayName: 'State or Province', + name: 'stateorprovince', + type: 'string', + default: '', + }, + { + displayName: 'Country', + name: 'country', + type: 'string', + default: '', + }, + { + displayName: 'Name', + name: 'name', + type: 'string', + default: '', + }, + { + displayName: 'Postalcode', + name: 'postalcode', + type: 'string', + default: '', + }, + { + displayName: 'Primary Contact Name', + name: 'primarycontactname', + type: 'string', + default: '', + }, + { + displayName: 'Telephone1', + name: 'telephone1', + type: 'string', + default: '', + }, + { + displayName: 'Telephone2', + name: 'telephone2', + type: 'string', + default: '', + }, + { + displayName: 'Fax', + name: 'fax', + type: 'string', + default: '', + }, + ], + }, + ], + }, + { + displayName: 'Business Type', + name: 'businesstypecode', + type: 'options', + typeOptions: { + loadOptionsMethod: 'getBusinessTypes', + }, + default: '', + description: 'The legal designation or other business type of the account for contracts or reporting purposes', + }, + { + displayName: 'Customer Size', + name: 'customersizecode', + type: 'options', + typeOptions: { + loadOptionsMethod: 'getCustomerSizeCodes', + }, + default: '', + }, + { + displayName: 'Customer Type', + name: 'customertypecode', + type: 'options', + typeOptions: { + loadOptionsMethod: 'getCustomerTypeCodes', + }, + default: '', + }, + { + displayName: 'Description', + name: 'description', + type: 'string', + default: '', + description: 'Additional information to describe the account, such as an excerpt from the company’s website', + }, + { + displayName: 'Email Address 1', + name: 'emailaddress1', + type: 'string', + default: '', + description: 'The primary email address for the account', + }, + { + displayName: 'Email Address 2', + name: 'emailaddress2', + type: 'string', + default: '', + description: 'The secondary email address for the account', + }, + { + displayName: 'Email Address 3', + name: 'emailaddress3', + type: 'string', + default: '', + description: 'Alternate email address for the account', + }, + { + displayName: 'Fax', + name: 'fax', + type: 'string', + default: '', + description: '', + }, + { + displayName: 'FTP site URL', + name: 'ftpsiteurl', + type: 'string', + default: '', + description: 'URL for the account’s FTP site to enable users to access data and share documents', + }, + { + displayName: 'Industry', + name: 'industrycode', + type: 'options', + typeOptions: { + loadOptionsMethod: 'getIndustryCodes', + }, + default: '', + description: 'The account’s primary industry for use in marketing segmentation and demographic analysis', + }, + { + displayName: 'Name', + name: 'name', + type: 'string', + default: '', + displayOptions: { + show: { + '/resource': [ + 'account', + ], + '/operation': [ + 'update', + ], + }, + }, + description: 'Company o business name', + }, + { + displayName: 'Credit Limit', + name: 'creditlimit', + type: 'number', + default: '', + description: 'Credit limit of the account. This is a useful reference when you address invoice and accounting issues with the customer', + }, + { + displayName: 'Number Of Employees', + name: 'numberofemployees', + type: 'number', + default: 0, + description: 'Number of employees that work at the account for use in marketing segmentation and demographic analysis', + }, + { + displayName: 'Payment Terms', + name: 'paymenttermscode', + type: 'options', + typeOptions: { + loadOptionsMethod: 'getPaymentTermsCodes', + }, + default: '', + description: 'The payment terms to indicate when the customer needs to pay the total amount', + }, + { + displayName: 'Preferred Appointment Day', + name: 'preferredappointmentdaycode', + type: 'options', + typeOptions: { + loadOptionsMethod: 'getPreferredAppointmentDayCodes', + }, + default: '', + description: '', + }, + { + displayName: 'Preferred Appointment Time', + name: 'preferredappointmenttimecode', + type: 'options', + typeOptions: { + loadOptionsMethod: 'getPreferredAppointmentTimeCodes', + }, + default: '', + description: '', + }, + { + displayName: 'Preferred Contact Method', + name: 'preferredcontactmethodcode', + type: 'options', + typeOptions: { + loadOptionsMethod: 'getPreferredContactMethodCodes', + }, + default: '', + description: '', + }, + { + displayName: 'Primary Satori ID', + name: 'primarysatoriid', + type: 'string', + default: '', + description: '', + }, + { + displayName: 'Primary Twitter ID', + name: 'primarytwitterid', + type: 'string', + default: '', + description: '', + }, + { + displayName: 'Revenue', + name: 'revenue', + type: 'number', + default: '', + description: 'The annual revenue for the account, used as an indicator in financial performance analysis', + }, + { + displayName: 'Shares Outstanding', + name: 'sharesoutstanding', + type: 'number', + default: '', + description: 'The number of shares available to the public for the account. This number is used as an indicator in financial performance analysis', + }, + { + displayName: 'Shipping Method', + name: 'shippingmethodcode', + type: 'options', + typeOptions: { + loadOptionsMethod: 'getShippingMethodCodes', + }, + default: '', + description: 'Shipping method for deliveries sent to the account’s address to designate the preferred carrier or other delivery option', + }, + { + displayName: 'SIC', + name: 'sic', + type: 'string', + default: '', + description: 'The Standard Industrial Classification (SIC) code that indicates the account’s primary industry of business, for use in marketing segmentation and demographic analysis', + }, + { + displayName: 'Stage ID', + name: 'stageid', + type: 'string', + default: '', + description: '', + }, + { + displayName: 'Stock Exchange', + name: 'stockexchange', + type: 'string', + default: '', + description: 'The stock exchange at which the account is listed to track their stock and financial performance of the company', + }, + { + displayName: 'Telephone 1', + name: 'telephone1', + type: 'string', + default: '', + description: 'The main phone number for this account', + }, + { + displayName: 'Telephone 2', + name: 'telephone2', + type: 'string', + default: '', + description: 'The second phone number for this account', + }, + { + displayName: 'Telephone 3', + name: 'telephone3', + type: 'string', + default: '', + description: 'The third phone number for this account', + }, + { + displayName: 'Territory', + name: 'territorycode', + type: 'options', + typeOptions: { + loadOptionsMethod: 'getTerritoryCodes', + }, + default: '', + description: 'Region or territory for the account for use in segmentation and analysis', + }, + { + displayName: 'Ticker Symbol', + name: 'tickersymbol', + type: 'string', + default: '', + description: 'Type the stock exchange symbol for the account to track financial performance of the company. You can click the code entered in this field to access the latest trading information from MSN Money', + }, + { + displayName: 'Website URL', + name: 'websiteurl', + type: 'string', + default: '', + description: 'The account’s website URL to get quick details about the company profile', + }, + { + displayName: 'Yomi Name', + name: 'yominame', + type: 'string', + default: '', + description: 'The phonetic spelling of the company name, if specified in Japanese, to make sure the name is pronounced correctly in phone calls and other communications', + }, + ]; +} + +export const sort = (a: { name: string }, b: { name: string }) => { + if (a.name < b.name) { return -1; } + if (a.name > b.name) { return 1; } + return 0; +}; + +export interface IField { + IsRetrievable: boolean; + LogicalName: string; + IsSearchable: string; + IsValidODataAttribute: string; + IsValidForRead: string; + CanBeSecuredForRead: string; + AttributeType: string; + IsSortableEnabled: { + Value: boolean, + }; + DisplayName: { + UserLocalizedLabel: { + Label: string + } + }; +} diff --git a/packages/nodes-base/nodes/Microsoft/Dynamics/MicrosoftDynamicsCrm.node.ts b/packages/nodes-base/nodes/Microsoft/Dynamics/MicrosoftDynamicsCrm.node.ts new file mode 100644 index 0000000000..675e9f953a --- /dev/null +++ b/packages/nodes-base/nodes/Microsoft/Dynamics/MicrosoftDynamicsCrm.node.ts @@ -0,0 +1,249 @@ +import { + IExecuteFunctions, +} from 'n8n-core'; + +import { + IDataObject, + ILoadOptionsFunctions, + INodeExecutionData, + INodePropertyOptions, + INodeType, + INodeTypeDescription, +} from 'n8n-workflow'; + +import { + adjustAddresses, + getEntityFields, + getPicklistOptions, + IField, + microsoftApiRequest, + microsoftApiRequestAllItems, + sort, +} from './GenericFunctions'; + +import { + accountFields, + accountOperations, +} from './descriptions'; + +export class MicrosoftDynamicsCrm implements INodeType { + description: INodeTypeDescription = { + displayName: 'Microsoft Dynamics CRM', + name: 'microsoftDynamicsCrm', + icon: 'file:dynamicsCrm.svg', + group: ['input'], + version: 1, + subtitle: '={{$parameter["operation"] + ": " + $parameter["resource"]}}', + description: 'Consume Microsoft Dynamics CRM API', + defaults: { + name: 'Microsoft Dynamics CRM', + color: '#000000', + }, + inputs: ['main'], + outputs: ['main'], + credentials: [ + { + name: 'microsoftDynamicsOAuth2Api', + required: true, + }, + ], + properties: [ + { + displayName: 'Resource', + name: 'resource', + type: 'options', + options: [ + { + name: 'Account', + value: 'account', + }, + ], + default: 'account', + description: 'The resource to operate on', + }, + ...accountOperations, + ...accountFields, + ], + }; + + methods = { + loadOptions: { + async getAccountCategories(this: ILoadOptionsFunctions): Promise { + return await getPicklistOptions.call(this, 'account', 'accountcategorycode'); + }, + async getAccountRatingCodes(this: ILoadOptionsFunctions): Promise { + return await getPicklistOptions.call(this, 'account', 'accountratingcode'); + }, + async getAddressTypes(this: ILoadOptionsFunctions): Promise { + return await getPicklistOptions.call(this, 'account', 'address1_addresstypecode'); + }, + async getBusinessTypes(this: ILoadOptionsFunctions): Promise { + return await getPicklistOptions.call(this, 'account', 'businesstypecode'); + }, + async getCustomerSizeCodes(this: ILoadOptionsFunctions): Promise { + return await getPicklistOptions.call(this, 'account', 'customersizecode'); + }, + async getCustomerTypeCodes(this: ILoadOptionsFunctions): Promise { + return await getPicklistOptions.call(this, 'account', 'customertypecode'); + }, + async getIndustryCodes(this: ILoadOptionsFunctions): Promise { + return await getPicklistOptions.call(this, 'account', 'industrycode'); + }, + async getPaymentTermsCodes(this: ILoadOptionsFunctions): Promise { + return await getPicklistOptions.call(this, 'account', 'paymenttermscode'); + }, + async getPreferredAppointmentDayCodes(this: ILoadOptionsFunctions): Promise { + return await getPicklistOptions.call(this, 'account', 'preferredappointmentdaycode'); + }, + async getPreferredAppointmentTimeCodes(this: ILoadOptionsFunctions): Promise { + return await getPicklistOptions.call(this, 'account', 'preferredappointmenttimecode'); + }, + async getPreferredContactMethodCodes(this: ILoadOptionsFunctions): Promise { + return await getPicklistOptions.call(this, 'account', 'preferredcontactmethodcode'); + }, + async getShippingMethodCodes(this: ILoadOptionsFunctions): Promise { + return await getPicklistOptions.call(this, 'account', 'shippingmethodcode'); + }, + async getTerritoryCodes(this: ILoadOptionsFunctions): Promise { + return await getPicklistOptions.call(this, 'account', 'territorycode'); + }, + async getAccountFields(this: ILoadOptionsFunctions): Promise { + const fields = await getEntityFields.call(this, 'account'); + const isSelectable = (field: IField) => (field.IsValidForRead && field.CanBeSecuredForRead && field.IsValidODataAttribute && field.LogicalName !== 'slaid'); + return fields.filter(isSelectable).filter(field => field.DisplayName.UserLocalizedLabel?.Label).map(field => ({ name: field.DisplayName.UserLocalizedLabel.Label, value: field.LogicalName })).sort(sort); + }, + async getExpandableAccountFields(this: ILoadOptionsFunctions): Promise { + const fields = await getEntityFields.call(this, 'account'); + const isSelectable = (field: IField) => (field.IsValidForRead && field.CanBeSecuredForRead && field.IsValidODataAttribute && field.AttributeType === 'Lookup' && field.LogicalName !== 'slaid'); + return fields.filter(isSelectable).map(field => ({ name: field.DisplayName.UserLocalizedLabel.Label, value: field.LogicalName })).sort(sort); + }, + }, + }; + + 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++) { + try { + if (resource === 'account') { + //https://docs.microsoft.com/en-us/powerapps/developer/data-platform/webapi/create-entity-web-api + if (operation === 'create') { + const name = this.getNodeParameter('name', i) as string; + // tslint:disable-next-line: no-any + const additionalFields = this.getNodeParameter('additionalFields', i) as { addresses: { address: [{ [key: string]: any }] } }; + const options = this.getNodeParameter('options', i) as { returnFields: string[] }; + + const body = { + name, + ...additionalFields, + }; + + if (body?.addresses?.address) { + Object.assign(body, adjustAddresses(body.addresses.address)); + //@ts-ignore + delete body?.addresses; + } + + if (options.returnFields) { + options.returnFields.push('accountid'); + qs['$select'] = options.returnFields.join(','); + } else { + qs['$select'] = 'accountid'; + } + + responseData = await microsoftApiRequest.call(this, 'POST', `/accounts`, body, qs); + } + + if (operation === 'delete') { + //https://docs.microsoft.com/en-us/powerapps/developer/data-platform/webapi/update-delete-entities-using-web-api#basic-delete + const accountId = this.getNodeParameter('accountId', i) as string; + await microsoftApiRequest.call(this, 'DELETE', `/accounts(${accountId})`, {}, qs); + responseData = { success: true }; + } + + if (operation === 'get') { + //https://docs.microsoft.com/en-us/powerapps/developer/data-platform/webapi/retrieve-entity-using-web-api + const accountId = this.getNodeParameter('accountId', i) as string; + const options = this.getNodeParameter('options', i) as IDataObject; + if (options.returnFields) { + qs['$select'] = (options.returnFields as string[]).join(','); + } + if (options.expandFields) { + qs['$expand'] = (options.expandFields as string[]).join(','); + } + responseData = await microsoftApiRequest.call(this, 'GET', `/accounts(${accountId})`, {}, qs); + } + + if (operation === 'getAll') { + //https://docs.microsoft.com/en-us/powerapps/developer/data-platform/webapi/query-data-web-api + const returnAll = this.getNodeParameter('returnAll', i) as boolean; + const options = this.getNodeParameter('options', i) as IDataObject; + const filters = this.getNodeParameter('filters', i) as IDataObject; + if (options.returnFields) { + qs['$select'] = (options.returnFields as string[]).join(','); + } + if (options.expandFields) { + qs['$expand'] = (options.expandFields as string[]).join(','); + } + if (filters.query) { + qs['$filter'] = filters.query as string; + } + if (returnAll) { + responseData = await microsoftApiRequestAllItems.call(this, 'value', 'GET', `/accounts`, {}, qs); + } else { + qs['$top'] = this.getNodeParameter('limit', 0) as number; + responseData = await microsoftApiRequest.call(this, 'GET', `/accounts`, {}, qs); + responseData = responseData.value; + } + } + + if (operation === 'update') { + const accountId = this.getNodeParameter('accountId', i) as string; + // tslint:disable-next-line: no-any + const updateFields = this.getNodeParameter('updateFields', i) as { addresses: { address: [{ [key: string]: any }] } }; + const options = this.getNodeParameter('options', i) as { returnFields: string[] }; + + const body = { + ...updateFields, + }; + + if (body?.addresses?.address) { + Object.assign(body, adjustAddresses(body.addresses.address)); + //@ts-ignore + delete body?.addresses; + } + + if (options.returnFields) { + options.returnFields.push('accountid'); + qs['$select'] = options.returnFields.join(','); + } else { + qs['$select'] = 'accountid'; + } + + responseData = await microsoftApiRequest.call(this, 'PATCH', `/accounts(${accountId})`, body, qs); + } + } + + if (Array.isArray(responseData)) { + returnData.push(...responseData); + } else { + returnData.push(responseData as IDataObject); + } + } catch (error) { + if (this.continueOnFail()) { + returnData.push({ error: error.message }); + continue; + } + throw error; + } + } + + return [this.helpers.returnJsonArray(returnData)]; + } +} diff --git a/packages/nodes-base/nodes/Microsoft/Dynamics/descriptions/AccountDescription.ts b/packages/nodes-base/nodes/Microsoft/Dynamics/descriptions/AccountDescription.ts new file mode 100644 index 0000000000..40bb6b51cd --- /dev/null +++ b/packages/nodes-base/nodes/Microsoft/Dynamics/descriptions/AccountDescription.ts @@ -0,0 +1,274 @@ +import { + INodeProperties, +} from 'n8n-workflow'; + +import { + getAccountFields, +} from '../GenericFunctions'; + +export const accountOperations = [ + { + displayName: 'Operation', + name: 'operation', + type: 'options', + displayOptions: { + show: { + resource: [ + 'account', + ], + }, + }, + options: [ + { + name: 'Create', + value: 'create', + }, + { + name: 'Delete', + value: 'delete', + }, + { + name: 'Get', + value: 'get', + }, + { + name: 'Get All', + value: 'getAll', + }, + { + name: 'Update', + value: 'update', + }, + ], + default: 'create', + description: 'Operation to perform', + }, +] as INodeProperties[]; + +export const accountFields = [ + // ---------------------------------------- + // account:create + // ---------------------------------------- + { + displayName: 'Name', + name: 'name', + description: 'Company or business name', + type: 'string', + required: true, + default: '', + displayOptions: { + show: { + resource: [ + 'account', + ], + operation: [ + 'create', + ], + }, + }, + }, + { + displayName: 'Additional Fields', + name: 'additionalFields', + type: 'collection', + placeholder: 'Add Field', + default: {}, + displayOptions: { + show: { + resource: [ + 'account', + ], + operation: [ + 'create', + ], + }, + }, + options: [ + ...getAccountFields(), + ], + }, + // ---------------------------------------- + // account:get + // ---------------------------------------- + { + displayName: 'Account ID', + name: 'accountId', + type: 'string', + required: true, + default: '', + displayOptions: { + show: { + resource: [ + 'account', + ], + operation: [ + 'delete', + 'get', + 'update', + ], + }, + }, + }, + // ---------------------------------------- + // account:getAll + // ---------------------------------------- + { + displayName: 'Return All', + name: 'returnAll', + type: 'boolean', + displayOptions: { + show: { + resource: [ + 'account', + ], + operation: [ + 'getAll', + ], + }, + }, + default: false, + description: 'If all results should be returned or only up to a given limit.', + }, + { + displayName: 'Limit', + name: 'limit', + type: 'number', + displayOptions: { + show: { + resource: [ + 'account', + ], + operation: [ + 'getAll', + ], + returnAll: [ + false, + ], + }, + }, + typeOptions: { + minValue: 1, + maxValue: 10, + }, + default: 5, + description: 'How many results to return.', + }, + { + displayName: 'Options', + name: 'options', + type: 'collection', + placeholder: 'Add Option', + default: {}, + displayOptions: { + show: { + resource: [ + 'account', + ], + operation: [ + 'get', + 'getAll', + ], + }, + }, + options: [ + { + displayName: 'Return Fields', + name: 'returnFields', + type: 'multiOptions', + typeOptions: { + loadOptionsMethod: 'getAccountFields', + }, + default: '', + }, + { + displayName: 'Expand Fields', + name: 'expandFields', + type: 'multiOptions', + typeOptions: { + loadOptionsMethod: 'getExpandableAccountFields', + }, + default: '', + }, + ], + }, + { + displayName: 'Filters', + name: 'filters', + type: 'collection', + placeholder: 'Add Filter', + default: {}, + displayOptions: { + show: { + resource: [ + 'account', + ], + operation: [ + 'getAll', + ], + }, + }, + options: [ + { + displayName: 'Query', + name: 'query', + type: 'string', + default: '', + description: 'Query to filter the results. Check filters', + }, + ], + }, + + // ---------------------------------------- + // account:update + // ---------------------------------------- + { + displayName: 'Update Fields', + name: 'updateFields', + type: 'collection', + placeholder: 'Add Field', + default: {}, + displayOptions: { + show: { + resource: [ + 'account', + ], + operation: [ + 'update', + ], + }, + }, + options: [ + ...getAccountFields(), + ], + }, + { + displayName: 'Options', + name: 'options', + type: 'collection', + placeholder: 'Add Option', + default: {}, + displayOptions: { + show: { + resource: [ + 'account', + ], + operation: [ + 'create', + 'update', + ], + }, + }, + options: [ + { + displayName: 'Return Fields', + name: 'returnFields', + type: 'multiOptions', + typeOptions: { + loadOptionsMethod: 'getAccountFields', + }, + default: '', + description: 'Fields the response will include', + }, + ], + }, +] as INodeProperties[]; diff --git a/packages/nodes-base/nodes/Microsoft/Dynamics/descriptions/index.ts b/packages/nodes-base/nodes/Microsoft/Dynamics/descriptions/index.ts new file mode 100644 index 0000000000..6bd434939a --- /dev/null +++ b/packages/nodes-base/nodes/Microsoft/Dynamics/descriptions/index.ts @@ -0,0 +1 @@ +export * from './AccountDescription'; \ No newline at end of file diff --git a/packages/nodes-base/nodes/Microsoft/Dynamics/dynamicsCrm.svg b/packages/nodes-base/nodes/Microsoft/Dynamics/dynamicsCrm.svg new file mode 100644 index 0000000000..7df712843c --- /dev/null +++ b/packages/nodes-base/nodes/Microsoft/Dynamics/dynamicsCrm.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/packages/nodes-base/package.json b/packages/nodes-base/package.json index c2135d5edf..71c29f513a 100644 --- a/packages/nodes-base/package.json +++ b/packages/nodes-base/package.json @@ -172,6 +172,7 @@ "dist/credentials/MediumApi.credentials.js", "dist/credentials/MediumOAuth2Api.credentials.js", "dist/credentials/MessageBirdApi.credentials.js", + "dist/credentials/MicrosoftDynamicsOAuth2Api.credentials.js", "dist/credentials/MicrosoftExcelOAuth2Api.credentials.js", "dist/credentials/MicrosoftOAuth2Api.credentials.js", "dist/credentials/MicrosoftOneDriveOAuth2Api.credentials.js", @@ -486,6 +487,7 @@ "dist/nodes/Medium/Medium.node.js", "dist/nodes/Merge.node.js", "dist/nodes/MessageBird/MessageBird.node.js", + "dist/nodes/Microsoft/Dynamics/MicrosoftDynamicsCrm.node.js", "dist/nodes/Microsoft/Excel/MicrosoftExcel.node.js", "dist/nodes/Microsoft/OneDrive/MicrosoftOneDrive.node.js", "dist/nodes/Microsoft/Outlook/MicrosoftOutlook.node.js", From 8ca388f1689d2f70eb3c98370a5c54906030ab17 Mon Sep 17 00:00:00 2001 From: Jan Oberhauser Date: Thu, 28 Oct 2021 19:08:32 -0500 Subject: [PATCH 05/17] :shirt: Fix lint issue --- .../nodes-base/nodes/Microsoft/Dynamics/GenericFunctions.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/nodes-base/nodes/Microsoft/Dynamics/GenericFunctions.ts b/packages/nodes-base/nodes/Microsoft/Dynamics/GenericFunctions.ts index 006d7bd741..67800133c9 100644 --- a/packages/nodes-base/nodes/Microsoft/Dynamics/GenericFunctions.ts +++ b/packages/nodes-base/nodes/Microsoft/Dynamics/GenericFunctions.ts @@ -15,7 +15,7 @@ import { } from 'n8n-workflow'; export async function microsoftApiRequest(this: IExecuteFunctions | IExecuteSingleFunctions | ILoadOptionsFunctions, method: string, resource: string, body: any = {}, qs: IDataObject = {}, uri?: string, option: IDataObject = {}): Promise { // tslint:disable-line:no-any - const credenitals = await this.getCredentials('microsoftDynamicsOAuth2Api') as { domain: string }; + const credenitals = await this.getCredentials('microsoftDynamicsOAuth2Api') as { subdomain: string }; let options: OptionsWithUri = { headers: { From 89fee87a88f27230cab9a3a9dbdfd882a44e39ad Mon Sep 17 00:00:00 2001 From: Jan Oberhauser Date: Fri, 29 Oct 2021 00:13:50 +0000 Subject: [PATCH 06/17] =?UTF-8?q?:bookmark:=20Release=C2=A0n8n-workflow@0.?= =?UTF-8?q?74.0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- packages/workflow/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/workflow/package.json b/packages/workflow/package.json index 61d915a59b..a86acf16d1 100644 --- a/packages/workflow/package.json +++ b/packages/workflow/package.json @@ -1,6 +1,6 @@ { "name": "n8n-workflow", - "version": "0.73.0", + "version": "0.74.0", "description": "Workflow base code of n8n", "license": "SEE LICENSE IN LICENSE.md", "homepage": "https://n8n.io", From 95fd11dac2bbc3e582e2c326f600a52b3406e695 Mon Sep 17 00:00:00 2001 From: Jan Oberhauser Date: Fri, 29 Oct 2021 00:14:01 +0000 Subject: [PATCH 07/17] :arrow_up: Set n8n-workflow@0.74.0 on n8n-core --- packages/core/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/core/package.json b/packages/core/package.json index 88bfc1170b..f3f6111615 100644 --- a/packages/core/package.json +++ b/packages/core/package.json @@ -50,7 +50,7 @@ "form-data": "^4.0.0", "lodash.get": "^4.4.2", "mime-types": "^2.1.27", - "n8n-workflow": "~0.73.0", + "n8n-workflow": "~0.74.0", "oauth-1.0a": "^2.2.6", "p-cancelable": "^2.0.0", "qs": "^6.10.1", From 783d48e3b11fa30bac94253b37ab328d92c2ca67 Mon Sep 17 00:00:00 2001 From: Jan Oberhauser Date: Fri, 29 Oct 2021 00:14:01 +0000 Subject: [PATCH 08/17] =?UTF-8?q?:bookmark:=20Release=C2=A0n8n-core@0.91.0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- packages/core/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/core/package.json b/packages/core/package.json index f3f6111615..853d398478 100644 --- a/packages/core/package.json +++ b/packages/core/package.json @@ -1,6 +1,6 @@ { "name": "n8n-core", - "version": "0.90.0", + "version": "0.91.0", "description": "Core functionality of n8n", "license": "SEE LICENSE IN LICENSE.md", "homepage": "https://n8n.io", From 3bfea67086c5d5e8244d701d244b09d8c60dac50 Mon Sep 17 00:00:00 2001 From: Jan Oberhauser Date: Fri, 29 Oct 2021 00:14:10 +0000 Subject: [PATCH 09/17] :arrow_up: Set n8n-core@0.91.0 and n8n-workflow@0.74.0 on n8n-node-dev --- packages/node-dev/package.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/node-dev/package.json b/packages/node-dev/package.json index 93145b731a..75dff9d534 100644 --- a/packages/node-dev/package.json +++ b/packages/node-dev/package.json @@ -60,8 +60,8 @@ "change-case": "^4.1.1", "copyfiles": "^2.1.1", "inquirer": "^7.0.1", - "n8n-core": "~0.90.0", - "n8n-workflow": "~0.73.0", + "n8n-core": "~0.91.0", + "n8n-workflow": "~0.74.0", "oauth-1.0a": "^2.2.6", "replace-in-file": "^6.0.0", "request": "^2.88.2", From 562871bac5c33806700b16cfab362a3c216b6875 Mon Sep 17 00:00:00 2001 From: Jan Oberhauser Date: Fri, 29 Oct 2021 00:14:11 +0000 Subject: [PATCH 10/17] =?UTF-8?q?:bookmark:=20Release=C2=A0n8n-node-dev@0.?= =?UTF-8?q?31.0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- packages/node-dev/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/node-dev/package.json b/packages/node-dev/package.json index 75dff9d534..4d45c28310 100644 --- a/packages/node-dev/package.json +++ b/packages/node-dev/package.json @@ -1,6 +1,6 @@ { "name": "n8n-node-dev", - "version": "0.30.0", + "version": "0.31.0", "description": "CLI to simplify n8n credentials/node development", "license": "SEE LICENSE IN LICENSE.md", "homepage": "https://n8n.io", From 0e02d13ba57dbcba6fd0dbc1a44863335611e0f3 Mon Sep 17 00:00:00 2001 From: Jan Oberhauser Date: Fri, 29 Oct 2021 00:14:20 +0000 Subject: [PATCH 11/17] :arrow_up: Set n8n-core@0.91.0 and n8n-workflow@0.74.0 on n8n-nodes-base --- packages/nodes-base/package.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/nodes-base/package.json b/packages/nodes-base/package.json index 71c29f513a..453bdd28af 100644 --- a/packages/nodes-base/package.json +++ b/packages/nodes-base/package.json @@ -667,7 +667,7 @@ "@types/xml2js": "^0.4.3", "gulp": "^4.0.0", "jest": "^26.4.2", - "n8n-workflow": "~0.73.0", + "n8n-workflow": "~0.74.0", "nodelinter": "^0.1.9", "ts-jest": "^26.3.0", "tslint": "^6.1.2", @@ -707,7 +707,7 @@ "mssql": "^6.2.0", "mysql2": "~2.3.0", "node-ssh": "^12.0.0", - "n8n-core": "~0.90.0", + "n8n-core": "~0.91.0", "nodemailer": "^6.5.0", "pdf-parse": "^1.1.1", "pg": "^8.3.0", From 55e9d15daa12da43ab4574c08d2db257909ad008 Mon Sep 17 00:00:00 2001 From: Jan Oberhauser Date: Fri, 29 Oct 2021 00:14:20 +0000 Subject: [PATCH 12/17] =?UTF-8?q?:bookmark:=20Release=C2=A0n8n-nodes-base@?= =?UTF-8?q?0.143.0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- packages/nodes-base/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/nodes-base/package.json b/packages/nodes-base/package.json index 453bdd28af..2760b61294 100644 --- a/packages/nodes-base/package.json +++ b/packages/nodes-base/package.json @@ -1,6 +1,6 @@ { "name": "n8n-nodes-base", - "version": "0.142.0", + "version": "0.143.0", "description": "Base nodes of n8n", "license": "SEE LICENSE IN LICENSE.md", "homepage": "https://n8n.io", From b9b666ee55d35b853f41bba6de0d23762764ebce Mon Sep 17 00:00:00 2001 From: Jan Oberhauser Date: Fri, 29 Oct 2021 00:15:03 +0000 Subject: [PATCH 13/17] =?UTF-8?q?:bookmark:=20Release=C2=A0n8n-design-syst?= =?UTF-8?q?em@0.6.0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- packages/design-system/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/design-system/package.json b/packages/design-system/package.json index 13fff02107..7a1b8c79ad 100644 --- a/packages/design-system/package.json +++ b/packages/design-system/package.json @@ -1,6 +1,6 @@ { "name": "n8n-design-system", - "version": "0.5.0", + "version": "0.6.0", "license": "SEE LICENSE IN LICENSE.md", "homepage": "https://n8n.io", "author": { From a6a40d8be46e2f26aaaa8b8da7ca2d1c9e4cede2 Mon Sep 17 00:00:00 2001 From: Jan Oberhauser Date: Fri, 29 Oct 2021 00:15:11 +0000 Subject: [PATCH 14/17] :arrow_up: Set n8n-design-system@0.6.0 and n8n-workflow@0.74.0 on n8n-editor-ui --- packages/editor-ui/package.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/editor-ui/package.json b/packages/editor-ui/package.json index 2f381b5740..2c89590d96 100644 --- a/packages/editor-ui/package.json +++ b/packages/editor-ui/package.json @@ -26,7 +26,7 @@ }, "dependencies": { "@fontsource/open-sans": "^4.5.0", - "n8n-design-system": "~0.5.0", + "n8n-design-system": "~0.6.0", "timeago.js": "^4.0.2", "v-click-outside": "^3.1.2", "vue-fragment": "^1.5.2" @@ -71,7 +71,7 @@ "lodash.debounce": "^4.0.8", "lodash.get": "^4.4.2", "lodash.set": "^4.3.2", - "n8n-workflow": "~0.73.0", + "n8n-workflow": "~0.74.0", "sass": "^1.26.5", "normalize-wheel": "^1.0.1", "prismjs": "^1.17.1", From a7a6e77598fc93c7fec4d3fb94c7261e09ef1dec Mon Sep 17 00:00:00 2001 From: Jan Oberhauser Date: Fri, 29 Oct 2021 00:15:11 +0000 Subject: [PATCH 15/17] =?UTF-8?q?:bookmark:=20Release=C2=A0n8n-editor-ui@0?= =?UTF-8?q?.114.0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- packages/editor-ui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/editor-ui/package.json b/packages/editor-ui/package.json index 2c89590d96..60e7485c89 100644 --- a/packages/editor-ui/package.json +++ b/packages/editor-ui/package.json @@ -1,6 +1,6 @@ { "name": "n8n-editor-ui", - "version": "0.113.0", + "version": "0.114.0", "description": "Workflow Editor UI for n8n", "license": "SEE LICENSE IN LICENSE.md", "homepage": "https://n8n.io", From 59b58b32453c7bf9d781c04ac40697d5b4073395 Mon Sep 17 00:00:00 2001 From: Jan Oberhauser Date: Fri, 29 Oct 2021 00:15:47 +0000 Subject: [PATCH 16/17] :arrow_up: Set n8n-core@0.91.0, n8n-editor-ui@0.114.0, n8n-nodes-base@0.143.0 and n8n-workflow@0.74.0 on n8n --- packages/cli/package.json | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/packages/cli/package.json b/packages/cli/package.json index 2ab8191e77..a5f8004e14 100644 --- a/packages/cli/package.json +++ b/packages/cli/package.json @@ -110,10 +110,10 @@ "localtunnel": "^2.0.0", "lodash.get": "^4.4.2", "mysql2": "~2.3.0", - "n8n-core": "~0.90.0", - "n8n-editor-ui": "~0.113.0", - "n8n-nodes-base": "~0.142.0", - "n8n-workflow": "~0.73.0", + "n8n-core": "~0.91.0", + "n8n-editor-ui": "~0.114.0", + "n8n-nodes-base": "~0.143.0", + "n8n-workflow": "~0.74.0", "oauth-1.0a": "^2.2.6", "open": "^7.0.0", "pg": "^8.3.0", From ebdd86a5f5f5c86d7b8647ada464dcc1bf45e0f1 Mon Sep 17 00:00:00 2001 From: Jan Oberhauser Date: Fri, 29 Oct 2021 00:15:48 +0000 Subject: [PATCH 17/17] =?UTF-8?q?:bookmark:=20Release=C2=A0n8n@0.146.0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- packages/cli/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/cli/package.json b/packages/cli/package.json index a5f8004e14..4b0d0526d7 100644 --- a/packages/cli/package.json +++ b/packages/cli/package.json @@ -1,6 +1,6 @@ { "name": "n8n", - "version": "0.145.0", + "version": "0.146.0", "description": "n8n Workflow Automation Tool", "license": "SEE LICENSE IN LICENSE.md", "homepage": "https://n8n.io",