From dab58e7704712641359bb4d2391ee08fb8527463 Mon Sep 17 00:00:00 2001 From: Jan Oberhauser Date: Wed, 9 Sep 2020 13:37:44 +0200 Subject: [PATCH 001/284] :bookmark: Release n8n-nodes-base@0.76.0 --- 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 e545e02f5..b84b29fde 100644 --- a/packages/nodes-base/package.json +++ b/packages/nodes-base/package.json @@ -1,6 +1,6 @@ { "name": "n8n-nodes-base", - "version": "0.75.0", + "version": "0.76.0", "description": "Base nodes of n8n", "license": "SEE LICENSE IN LICENSE.md", "homepage": "https://n8n.io", From b2f77b70eb46ab6cf0654c35dab6ababf02967ee Mon Sep 17 00:00:00 2001 From: Jan Oberhauser Date: Wed, 9 Sep 2020 13:38:53 +0200 Subject: [PATCH 002/284] :arrow_up: Set n8n-nodes-base@0.76.0 on n8n --- 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 049af1d77..20dde22b3 100644 --- a/packages/cli/package.json +++ b/packages/cli/package.json @@ -102,7 +102,7 @@ "mysql2": "^2.0.1", "n8n-core": "~0.44.0", "n8n-editor-ui": "~0.55.0", - "n8n-nodes-base": "~0.75.0", + "n8n-nodes-base": "~0.76.0", "n8n-workflow": "~0.39.0", "oauth-1.0a": "^2.2.6", "open": "^7.0.0", From 3295e46b3a82b70c15a197166c4617f748f32880 Mon Sep 17 00:00:00 2001 From: Jan Oberhauser Date: Wed, 9 Sep 2020 13:39:27 +0200 Subject: [PATCH 003/284] :bookmark: Release n8n@0.81.0 --- 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 20dde22b3..6f7bb04e7 100644 --- a/packages/cli/package.json +++ b/packages/cli/package.json @@ -1,6 +1,6 @@ { "name": "n8n", - "version": "0.80.0", + "version": "0.81.0", "description": "n8n Workflow Automation Tool", "license": "SEE LICENSE IN LICENSE.md", "homepage": "https://n8n.io", From b7d46262daba867dc81509ff1bf0268cb0c85950 Mon Sep 17 00:00:00 2001 From: smamudhan <48641776+smamudhan@users.noreply.github.com> Date: Thu, 10 Sep 2020 13:18:02 +0530 Subject: [PATCH 004/284] :zap: Add documentationUrls for several nodes (#940) --- packages/nodes-base/credentials/ContentfulApi.credentials.ts | 1 + packages/nodes-base/credentials/ConvertKitApi.credentials.ts | 1 + packages/nodes-base/credentials/SentryIoApi.credentials.ts | 1 + packages/nodes-base/credentials/SentryIoOAuth2Api.credentials.ts | 1 + 4 files changed, 4 insertions(+) diff --git a/packages/nodes-base/credentials/ContentfulApi.credentials.ts b/packages/nodes-base/credentials/ContentfulApi.credentials.ts index 8ce49fb14..59680f100 100644 --- a/packages/nodes-base/credentials/ContentfulApi.credentials.ts +++ b/packages/nodes-base/credentials/ContentfulApi.credentials.ts @@ -7,6 +7,7 @@ import { export class ContentfulApi implements ICredentialType { name = 'contentfulApi'; displayName = 'Contenful API'; + documentationUrl = 'contentful'; properties = [ { displayName: 'Space ID', diff --git a/packages/nodes-base/credentials/ConvertKitApi.credentials.ts b/packages/nodes-base/credentials/ConvertKitApi.credentials.ts index d7e869755..94195ec0f 100644 --- a/packages/nodes-base/credentials/ConvertKitApi.credentials.ts +++ b/packages/nodes-base/credentials/ConvertKitApi.credentials.ts @@ -7,6 +7,7 @@ import { export class ConvertKitApi implements ICredentialType { name = 'convertKitApi'; displayName = 'ConvertKit API'; + documentationUrl = 'convertKit'; properties = [ { displayName: 'API Secret', diff --git a/packages/nodes-base/credentials/SentryIoApi.credentials.ts b/packages/nodes-base/credentials/SentryIoApi.credentials.ts index 576f27f2f..4530d5027 100644 --- a/packages/nodes-base/credentials/SentryIoApi.credentials.ts +++ b/packages/nodes-base/credentials/SentryIoApi.credentials.ts @@ -6,6 +6,7 @@ import { export class SentryIoApi implements ICredentialType { name = 'sentryIoApi'; displayName = 'Sentry.io API'; + documentationUrl = 'sentryIo'; properties = [ { displayName: 'Token', diff --git a/packages/nodes-base/credentials/SentryIoOAuth2Api.credentials.ts b/packages/nodes-base/credentials/SentryIoOAuth2Api.credentials.ts index 3948be6c6..9c0ee4dd2 100644 --- a/packages/nodes-base/credentials/SentryIoOAuth2Api.credentials.ts +++ b/packages/nodes-base/credentials/SentryIoOAuth2Api.credentials.ts @@ -9,6 +9,7 @@ export class SentryIoOAuth2Api implements ICredentialType { 'oAuth2Api', ]; displayName = 'Sentry.io OAuth2 API'; + documentationUrl = 'sentryIo'; properties = [ { displayName: 'Authorization URL', From 013a7b8cf9fc5ed2ceb10ef5caca405161d0a946 Mon Sep 17 00:00:00 2001 From: Jan Oberhauser Date: Thu, 10 Sep 2020 10:16:24 +0200 Subject: [PATCH 005/284] :zap: Add hooks for oauth-authentication --- packages/cli/src/Server.ts | 24 ++++++++++++++++-------- 1 file changed, 16 insertions(+), 8 deletions(-) diff --git a/packages/cli/src/Server.ts b/packages/cli/src/Server.ts index 43886323a..46a2351a9 100644 --- a/packages/cli/src/Server.ts +++ b/packages/cli/src/Server.ts @@ -1009,7 +1009,7 @@ class App { const signatureMethod = _.get(oauthCredentials, 'signatureMethod') as string; - const oauth = new clientOAuth1({ + const oAuthOptions: clientOAuth1.Options = { consumer: { key: _.get(oauthCredentials, 'consumerKey') as string, secret: _.get(oauthCredentials, 'consumerSecret') as string, @@ -1021,16 +1021,20 @@ class App { .update(base) .digest('base64'); }, - }); + }; - const callback = `${WebhookHelpers.getWebhookBaseUrl()}${this.restEndpoint}/oauth1-credential/callback?cid=${req.query.id}`; + const oauthRequestData = { + oauth_callback: `${WebhookHelpers.getWebhookBaseUrl()}${this.restEndpoint}/oauth1-credential/callback?cid=${req.query.id}` + }; + + await this.externalHooks.run('oauth1.authenticate', [oAuthOptions, oauthRequestData]); + + const oauth = new clientOAuth1(oAuthOptions); const options: RequestOptions = { method: 'POST', url: (_.get(oauthCredentials, 'requestTokenUrl') as string), - data: { - oauth_callback: callback, - }, + data: oauthRequestData, }; const data = oauth.toHeader(oauth.authorize(options as RequestOptions)); @@ -1173,7 +1177,7 @@ class App { }; const stateEncodedStr = Buffer.from(JSON.stringify(state)).toString('base64') as string; - const oAuthObj = new clientOAuth2({ + const oAuthOptions: clientOAuth2.Options = { clientId: _.get(oauthCredentials, 'clientId') as string, clientSecret: _.get(oauthCredentials, 'clientSecret', '') as string, accessTokenUri: _.get(oauthCredentials, 'accessTokenUrl', '') as string, @@ -1181,7 +1185,11 @@ class App { redirectUri: `${WebhookHelpers.getWebhookBaseUrl()}${this.restEndpoint}/oauth2-credential/callback`, scopes: _.split(_.get(oauthCredentials, 'scope', 'openid,') as string, ','), state: stateEncodedStr, - }); + }; + + await this.externalHooks.run('oauth2.authenticate', [oAuthOptions]); + + const oAuthObj = new clientOAuth2(oAuthOptions); // Encrypt the data const credentials = new Credentials(result.name, result.type, result.nodesAccess); From fccbd48937bdc5eb0838e324453eff34d545ee7b Mon Sep 17 00:00:00 2001 From: Jan Oberhauser Date: Thu, 10 Sep 2020 15:55:42 +0200 Subject: [PATCH 006/284] :zap: Run requests in HTTP Request Node in parallel --- packages/nodes-base/nodes/HttpRequest.node.ts | 69 ++++++++++++------- 1 file changed, 45 insertions(+), 24 deletions(-) diff --git a/packages/nodes-base/nodes/HttpRequest.node.ts b/packages/nodes-base/nodes/HttpRequest.node.ts index e30896252..e37e2bd57 100644 --- a/packages/nodes-base/nodes/HttpRequest.node.ts +++ b/packages/nodes-base/nodes/HttpRequest.node.ts @@ -620,8 +620,8 @@ export class HttpRequest implements INodeType { }, }; - let response: any; // tslint:disable-line:no-any const returnItems: INodeExecutionData[] = []; + const requestPromises = []; for (let itemIndex = 0; itemIndex < items.length; itemIndex++) { const options = this.getNodeParameter('options', itemIndex, {}) as IDataObject; const url = this.getNodeParameter('url', itemIndex) as string; @@ -814,27 +814,49 @@ export class HttpRequest implements INodeType { } else { requestOptions.json = true; } - try { - // Now that the options are all set make the actual http request - if (oAuth1Api !== undefined) { - //@ts-ignore - response = await this.helpers.requestOAuth1.call(this, 'oAuth1Api', requestOptions); - } - else if (oAuth2Api !== undefined) { - //@ts-ignore - response = await this.helpers.requestOAuth2.call(this, 'oAuth2Api', requestOptions, { tokenType: 'Bearer' }); + + // Now that the options are all set make the actual http request + if (oAuth1Api !== undefined) { + requestPromises.push(this.helpers.requestOAuth1.call(this, 'oAuth1Api', requestOptions)); + } else if (oAuth2Api !== undefined) { + requestPromises.push(this.helpers.requestOAuth2.call(this, 'oAuth2Api', requestOptions, { tokenType: 'Bearer' })); + } else { + requestPromises.push(this.helpers.request(requestOptions)); + } + } + + // @ts-ignore + const promisesResponses = await Promise.allSettled(requestPromises); + + let response: any; // tslint:disable-line:no-any + for (let itemIndex = 0; itemIndex < items.length; itemIndex++) { + // @ts-ignore + response = promisesResponses.shift(); + + if (response!.status !== 'fulfilled') { + if (this.continueOnFail() !== true) { + // throw error; + throw new Error(response!.reason); } else { - response = await this.helpers.request(requestOptions); - } - } catch (error) { - if (this.continueOnFail() === true) { - returnItems.push({ json: { error } }); + // Return the actual reason as error + returnItems.push( + { + json: { + error: response.reason, + }, + } + ); continue; } - - throw error; } + response = response.value; + + const options = this.getNodeParameter('options', itemIndex, {}) as IDataObject; + const url = this.getNodeParameter('url', itemIndex) as string; + + const fullResponse = !!options.fullResponse as boolean; + if (responseFormat === 'file') { const dataPropertyName = this.getNodeParameter('dataPropertyName', 0) as string; @@ -853,23 +875,22 @@ export class HttpRequest implements INodeType { const fileName = (url).split('/').pop(); - if (fullResponse === true) { const returnItem: IDataObject = {}; for (const property of fullReponseProperties) { if (property === 'body') { continue; } - returnItem[property] = response[property]; + returnItem[property] = response![property]; } newItem.json = returnItem; - newItem.binary![dataPropertyName] = await this.helpers.prepareBinaryData(response.body, fileName); + newItem.binary![dataPropertyName] = await this.helpers.prepareBinaryData(response!.body, fileName); } else { newItem.json = items[itemIndex].json; - newItem.binary![dataPropertyName] = await this.helpers.prepareBinaryData(response, fileName); + newItem.binary![dataPropertyName] = await this.helpers.prepareBinaryData(response!, fileName); } items[itemIndex] = newItem; @@ -880,11 +901,11 @@ export class HttpRequest implements INodeType { const returnItem: IDataObject = {}; for (const property of fullReponseProperties) { if (property === 'body') { - returnItem[dataPropertyName] = response[property]; + returnItem[dataPropertyName] = response![property]; continue; } - returnItem[property] = response[property]; + returnItem[property] = response![property]; } returnItems.push({ json: returnItem }); } else { @@ -899,7 +920,7 @@ export class HttpRequest implements INodeType { if (fullResponse === true) { const returnItem: IDataObject = {}; for (const property of fullReponseProperties) { - returnItem[property] = response[property]; + returnItem[property] = response![property]; } if (responseFormat === 'json' && typeof returnItem.body === 'string') { From 2a6f4ebf867ef2fd3de9b4eb2c6ae06670f6421f Mon Sep 17 00:00:00 2001 From: Ahsan Virani Date: Thu, 10 Sep 2020 17:49:44 +0200 Subject: [PATCH 007/284] :sparkles: Add hash support in basic auth (#943) --- packages/cli/config/index.ts | 6 ++++++ packages/cli/package.json | 6 ++++-- packages/cli/src/Server.ts | 5 ++++- 3 files changed, 14 insertions(+), 3 deletions(-) diff --git a/packages/cli/config/index.ts b/packages/cli/config/index.ts index 32dec2438..5d66a1b3b 100644 --- a/packages/cli/config/index.ts +++ b/packages/cli/config/index.ts @@ -319,6 +319,12 @@ const config = convict({ env: 'N8N_BASIC_AUTH_PASSWORD', doc: 'The password of the basic auth user' }, + hash: { + format: 'Boolean', + default: false, + env: 'N8N_BASIC_AUTH_HASH', + doc: 'If password for basic auth is hashed' + } }, jwtAuth: { active: { diff --git a/packages/cli/package.json b/packages/cli/package.json index 6f7bb04e7..1caed60c0 100644 --- a/packages/cli/package.json +++ b/packages/cli/package.json @@ -54,6 +54,7 @@ "devDependencies": { "@oclif/dev-cli": "^1.22.2", "@types/basic-auth": "^1.1.2", + "@types/bcrypt": "^3.0.0", "@types/compression": "1.0.1", "@types/connect-history-api-fallback": "^1.3.1", "@types/convict": "^4.2.1", @@ -72,15 +73,16 @@ "p-cancelable": "^2.0.0", "run-script-os": "^1.0.7", "ts-jest": "^25.4.0", + "ts-node": "^8.9.1", "tslint": "^6.1.2", - "typescript": "~3.7.4", - "ts-node": "^8.9.1" + "typescript": "~3.7.4" }, "dependencies": { "@oclif/command": "^1.5.18", "@oclif/errors": "^1.2.2", "@types/jsonwebtoken": "^8.3.4", "basic-auth": "^2.0.1", + "bcrypt": "^5.0.0", "body-parser": "^1.18.3", "body-parser-xml": "^1.1.0", "client-oauth2": "^4.2.5", diff --git a/packages/cli/src/Server.ts b/packages/cli/src/Server.ts index 46a2351a9..4f23a624c 100644 --- a/packages/cli/src/Server.ts +++ b/packages/cli/src/Server.ts @@ -20,6 +20,7 @@ import { RequestOptions } from 'oauth-1.0a'; import * as csrf from 'csrf'; import * as requestPromise from 'request-promise-native'; import { createHmac } from 'crypto'; +import { compareSync } from 'bcrypt'; import { ActiveExecutions, @@ -186,6 +187,8 @@ class App { throw new Error('Basic auth is activated but no password got defined. Please set one!'); } + const basicAuthHashEnabled = await GenericHelpers.getConfigValue('security.basicAuth.hash') as boolean; + this.app.use((req: express.Request, res: express.Response, next: express.NextFunction) => { if (req.url.match(authIgnoreRegex)) { return next(); @@ -198,7 +201,7 @@ class App { return ResponseHelper.basicAuthAuthorizationError(res, realm, 'Authorization is required!'); } - if (basicAuthData.name !== basicAuthUser || basicAuthData.pass !== basicAuthPassword) { + if (basicAuthData.name !== basicAuthUser || (!basicAuthHashEnabled && basicAuthData.pass !== basicAuthPassword) || (basicAuthHashEnabled && compareSync(basicAuthData.pass, basicAuthPassword) === false)) { // Provided authentication data is wrong return ResponseHelper.basicAuthAuthorizationError(res, realm, 'Authorization data is wrong!'); } From f8223c6e98bebd0ad788f18d2af3101609486c2b Mon Sep 17 00:00:00 2001 From: Ricardo Espinoza Date: Thu, 10 Sep 2020 15:56:15 -0400 Subject: [PATCH 008/284] :zap: Add Freshdesk contacts resource (#938) * Freshdesk - add contact integration * fix linter errors * :zap: Small improvements Co-authored-by: Yonatan Rosemarin --- .../nodes/Freshdesk/ContactDescription.ts | 431 ++++++++++++++++++ .../nodes/Freshdesk/ContactInterface.ts | 24 + .../nodes/Freshdesk/Freshdesk.node.ts | 80 +++- .../nodes/Freshdesk/GenericFunctions.ts | 8 +- 4 files changed, 538 insertions(+), 5 deletions(-) create mode 100644 packages/nodes-base/nodes/Freshdesk/ContactDescription.ts create mode 100644 packages/nodes-base/nodes/Freshdesk/ContactInterface.ts diff --git a/packages/nodes-base/nodes/Freshdesk/ContactDescription.ts b/packages/nodes-base/nodes/Freshdesk/ContactDescription.ts new file mode 100644 index 000000000..e5dd39e3d --- /dev/null +++ b/packages/nodes-base/nodes/Freshdesk/ContactDescription.ts @@ -0,0 +1,431 @@ +import { + INodeProperties, +} from 'n8n-workflow'; + +export const contactOperations = [ + { + displayName: 'Operation', + name: 'operation', + type: 'options', + required: true, + 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 a contact', + }, + ], + default: 'create', + description: 'The operation to perform.', + }, +] as INodeProperties[]; + +export const contactFields = [ + + /* -------------------------------------------------------------------------- */ + /* contact:create/update */ + /* -------------------------------------------------------------------------- */ + { + displayName: 'Name', + name: 'name', + type: 'string', + placeholder: '', + displayOptions: { + show: { + operation: [ + 'create', + ], + resource: [ + 'contact', + ], + }, + }, + default: '', + description: 'Name of the contact.', + required: true, + }, + { + displayName: 'Email', + name: 'email', + type: 'string', + default: '', + displayOptions: { + show: { + operation: [ + 'create', + ], + resource: [ + 'contact', + ], + }, + }, + description: `Primary email address of the contact. If you want to associate
+ additional email(s) with this contact, use the other_emails attribute.`, + }, + { + displayName: 'Contact ID', + name: 'contactId', + type: 'string', + default: '', + displayOptions: { + show: { + resource: [ + 'contact', + ], + operation: [ + 'update', + ], + }, + }, + required: true, + }, + { + displayName: 'Additional Fields', + name: 'additionalFields', + type: 'collection', + placeholder: 'Add Field', + displayOptions: { + show: { + operation: [ + 'create', + 'update', + ], + resource: [ + 'contact', + ], + }, + }, + default: {}, + options: [ + { + displayName: 'Address', + name: 'address', + type: 'string', + default: '', + description: 'Address of the contact.', + }, + // { + // displayName: 'Avatar', + // name: 'avatar', + // type: '', + // default: '', + // description: `Avatar image of the contact The maximum file size is 5MB
+ // and the supported file types are .jpg, .jpeg, .jpe, and .png.`, + // }, + { + displayName: 'Company ID', + name: 'company_id', + type: 'number', + default: '', + description: 'ID of the primary company to which this contact belongs.', + }, + { + displayName: 'Custom Fields', + name: 'customFields', + type: 'fixedCollection', + placeholder: 'Add Custom Field', + typeOptions: { + multipleValues: true, + }, + description: `Key value pairs containing the name and value of the custom field.
+ Only dates in the format YYYY-MM-DD are accepted as input for custom date fields.`, + default: {}, + options: [ + { + displayName: 'Custom Field', + name: 'customField', + values: [ + { + displayName: 'Name', + name: 'name', + type: 'string', + default: '', + description: `Custom Field\'s name.`, + }, + { + displayName: 'Value', + name: 'value', + type: 'string', + default: '', + description: `Custom Field\'s values.`, + }, + ] + }, + ], + }, + { + displayName: 'Description', + name: 'description', + type: 'string', + default: '', + description: 'A small description of the contact.', + }, + { + displayName: 'Email', + name: 'email', + type: 'string', + default: '', + displayOptions: { + show: { + '/operation': [ + 'update', + ], + }, + }, + description: `Primary email address of the contact. If you want to associate
+ additional email(s) with this contact, use the other_emails attribute.`, + }, + { + displayName: 'Job Title', + name: 'job_title', + type: 'string', + default: '', + description: 'Job title of the contact.', + }, + { + displayName: 'Language', + name: 'language', + type: 'string', + default: '', + description: `Language of the contact. Default language is "en".
+ This attribute can only be set if the Multiple Language feature is
+ enabled (Garden plan and above).`, + }, + { + displayName: 'Mobile', + name: 'mobile', + type: 'string', + default: '', + description: 'Mobile number of the contact.', + }, + { + displayName: 'Name', + name: 'name', + type: 'string', + default: '', + displayOptions: { + show: { + '/operation': [ + 'update', + ], + }, + }, + description: 'Name of the contact.', + }, + { + displayName: 'Other Companies', + name: 'other_companies', + type: 'string', + default: '', + typeOptions: { + multipleValues: true, + }, + placeholder: 'Add Company', + description: `Additional companies associated with the contact.
+ This attribute can only be set if the Multiple Companies feature
+ is enabled (Estate plan and above).`, + }, + { + displayName: 'Other Emails', + name: 'other_emails', + type: 'string', + default: '', + typeOptions: { + multipleValues: true, + }, + placeholder: 'Add Email', + description: 'Additional emails associated with the contact.', + }, + { + displayName: 'Phone', + name: 'phone', + type: 'string', + default: '', + description: 'Telephone number of the contact.', + }, + { + displayName: 'Tags', + name: 'tags', + type: 'string', + default: '', + typeOptions: { + multipleValues: true, + }, + description: 'Tags associated with this contact.', + }, + { + displayName: 'Time Zone', + name: 'time_zone', + type: 'string', + default: '', + description: `Time zone of the contact. Default value is the time zone of the domain.
+ This attribute can only be set if the Multiple Time Zone feature is enabled (Garden plan and above)];`, + }, + { + displayName: 'Twitter ID', + name: 'twitter_id', + type: 'string', + default: '', + description: 'Twitter handle of the contact.', + }, + { + displayName: 'Unique External ID', + name: 'unique_external_id', + type: 'string', + default: '', + description: 'External ID of the contact.', + }, + { + displayName: 'View All Tickets', + name: 'view_all_tickets', + type: 'boolean', + default: '', + description: `Set to true if the contact can see all the tickets
+ that are associated with the company to which he belong.`, + }, + ], + }, + /* -------------------------------------------------------------------------- */ + /* contact:delete */ + /* -------------------------------------------------------------------------- */ + { + displayName: 'Contact ID', + name: 'contactId', + type: 'string', + default: '', + displayOptions: { + show: { + resource: [ + 'contact', + ], + operation: [ + 'delete', + ], + }, + }, + required: true, + }, + /* -------------------------------------------------------------------------- */ + /* contact:get */ + /* -------------------------------------------------------------------------- */ + { + displayName: 'Contact ID', + name: 'contactId', + type: 'string', + default: '', + displayOptions: { + show: { + resource: [ + 'contact', + ], + operation: [ + 'get', + ], + }, + }, + required: true, + }, + /* -------------------------------------------------------------------------- */ + /* contact:getAll */ + /* -------------------------------------------------------------------------- */ + { + displayName: 'Filters', + name: 'filters', + type: 'collection', + placeholder: 'Add Filter', + default: {}, + displayOptions: { + show: { + operation: [ + 'getAll', + ], + resource: [ + 'contact', + ], + }, + }, + options: [ + { + displayName: 'Company ID', + name: 'company_id', + type: 'number', + default: '', + }, + { + displayName: 'Email', + name: 'email', + type: 'string', + default: '', + }, + { + displayName: 'Mobile', + name: 'mobile', + type: 'string', + default: '', + }, + { + displayName: 'Phone', + name: 'phone', + type: 'string', + default: '', + }, + { + displayName: 'State', + name: 'state', + type: 'options', + default: '', + options: [ + { + name: 'Blocked', + value: 'blocked', + }, + { + name: 'Deleted', + value: 'deleted', + }, + { + name: 'Unverified', + value: 'unverified', + }, + { + name: 'Verified', + value: 'verified', + }, + ], + }, + { + displayName: 'Updated Since', + name: 'updated_since', + type: 'dateTime', + default: '', + }, + ], + }, +] as INodeProperties[]; diff --git a/packages/nodes-base/nodes/Freshdesk/ContactInterface.ts b/packages/nodes-base/nodes/Freshdesk/ContactInterface.ts new file mode 100644 index 000000000..431f1589f --- /dev/null +++ b/packages/nodes-base/nodes/Freshdesk/ContactInterface.ts @@ -0,0 +1,24 @@ +import { + IDataObject, +} from 'n8n-workflow'; + +export interface ICreateContactBody { + address?: string; + // avatar?: object; + company_id?: number; + custom_fields?: IDataObject; + description?: string; + email?: string; + job_title?: string; + language?: string; + mobile?: string; + name?: string; + other_companies?: string[]; + other_emails?: string[]; + phone?: string; + tags?: string[]; + time_zone?: string; + twitter_id?: string; + unique_external_id?: string; + view_all_tickets?: boolean; +} diff --git a/packages/nodes-base/nodes/Freshdesk/Freshdesk.node.ts b/packages/nodes-base/nodes/Freshdesk/Freshdesk.node.ts index 3e9217d3a..eba94626b 100644 --- a/packages/nodes-base/nodes/Freshdesk/Freshdesk.node.ts +++ b/packages/nodes-base/nodes/Freshdesk/Freshdesk.node.ts @@ -6,9 +6,11 @@ import { ILoadOptionsFunctions, INodePropertyOptions, } from 'n8n-workflow'; + import { IExecuteFunctions, } from 'n8n-core'; + import { freshdeskApiRequest, freshdeskApiRequestAllItems, @@ -16,6 +18,18 @@ import { capitalize } from './GenericFunctions'; +import { + ICreateContactBody, +} from './ContactInterface'; + +import { + contactFields, + contactOperations, +} from './ContactDescription'; + +import * as moment from 'moment-timezone'; +import { response } from 'express'; + enum Status { Open = 2, Pending = 3, @@ -77,7 +91,7 @@ export class Freshdesk implements INodeType { description: 'Consume Freshdesk API', defaults: { name: 'Freshdesk', - color: '#c02428', + color: '#25c10b', }, inputs: ['main'], outputs: ['main'], @@ -94,6 +108,10 @@ export class Freshdesk implements INodeType { type: 'options', required: true, options: [ + { + name: 'Contact', + value: 'contact', + }, { name: 'Ticket', value: 'ticket', @@ -1046,6 +1064,9 @@ export class Freshdesk implements INodeType { default: '', description: 'Ticket ID', }, + // CONTACTS + ...contactOperations, + ...contactFields, ] }; @@ -1342,14 +1363,67 @@ export class Freshdesk implements INodeType { const ticketId = this.getNodeParameter('ticketId', i) as string; responseData = await freshdeskApiRequest.call(this, 'DELETE', `/tickets/${ticketId}`); } + } else if (resource === 'contact') { + //https://developers.freshdesk.com/api/#create_contact + if (operation === 'create') { + const name = this.getNodeParameter('name', i) as string; + const email = this.getNodeParameter('email', i) as string; + const additionalFields = this.getNodeParameter('additionalFields', i, {}) as IDataObject; + + if (additionalFields.customFields) { + const metadata = (additionalFields.customFields as IDataObject).customField as IDataObject[]; + additionalFields.custom_fields = {}; + for (const data of metadata) { + //@ts-ignore + additionalFields.custom_fields[data.name as string] = data.value; + } + delete additionalFields.customFields; + } + + const body: ICreateContactBody = additionalFields; + body.name = name; + if (email) { + body.email = email; + } + responseData = await freshdeskApiRequest.call(this, 'POST', '/contacts', body); + //https://developers.freshdesk.com/api/#delete_contact + } else if (operation === 'delete') { + const contactId = this.getNodeParameter('contactId', i) as string; + responseData = await freshdeskApiRequest.call(this, 'DELETE', `/contacts/${contactId}`, {}); + } else if (operation === 'get') { + const contactId = this.getNodeParameter('contactId', i) as string; + responseData = await freshdeskApiRequest.call(this, 'GET', `/contacts/${contactId}`, {}); + //https://developers.freshdesk.com/api/#list_all_contacts + } else if (operation === 'getAll') { + const qs = this.getNodeParameter('filters', i, {}) as IDataObject; + responseData = await freshdeskApiRequest.call(this, 'GET', '/contacts', {}, qs); + //https://developers.freshdesk.com/api/#update_contact + } else if (operation === 'update') { + const contactId = this.getNodeParameter('contactId', i) as string; + const additionalFields = this.getNodeParameter('additionalFields', i, {}) as IDataObject; + + if (additionalFields.customFields) { + const metadata = (additionalFields.customFields as IDataObject).customField as IDataObject[]; + additionalFields.custom_fields = {}; + for (const data of metadata) { + //@ts-ignore + additionalFields.custom_fields[data.name as string] = data.value; + } + delete additionalFields.customFields; + } + + const body: ICreateContactBody = additionalFields; + responseData = await freshdeskApiRequest.call(this, 'PUT', `/contacts/${contactId}`, body); + } } + if (Array.isArray(responseData)) { returnData.push.apply(returnData, responseData as IDataObject[]); } else { if (responseData === undefined) { - responseData = { json: { + responseData = { success: true, - } }; + }; } returnData.push(responseData as IDataObject); diff --git a/packages/nodes-base/nodes/Freshdesk/GenericFunctions.ts b/packages/nodes-base/nodes/Freshdesk/GenericFunctions.ts index 50089fdcc..e52debfd6 100644 --- a/packages/nodes-base/nodes/Freshdesk/GenericFunctions.ts +++ b/packages/nodes-base/nodes/Freshdesk/GenericFunctions.ts @@ -1,4 +1,6 @@ -import { OptionsWithUri } from 'request'; +import { + OptionsWithUri, +} from 'request'; import { IExecuteFunctions, @@ -6,7 +8,9 @@ import { BINARY_ENCODING } from 'n8n-core'; -import { IDataObject } from 'n8n-workflow'; +import { + IDataObject, +} from 'n8n-workflow'; export async function freshdeskApiRequest(this: IExecuteFunctions | ILoadOptionsFunctions, method: string, resource: string, body: any = {}, query: IDataObject = {}, uri?: string, option: IDataObject = {}): Promise { // tslint:disable-line:no-any From 5751a645aab8194dd0a7c1018c4d34c2a7575d5f Mon Sep 17 00:00:00 2001 From: Jan Oberhauser Date: Thu, 10 Sep 2020 21:56:31 +0200 Subject: [PATCH 009/284] :zap: Fix minor issues on Freshdesk-Node --- .../nodes-base/nodes/Freshdesk/ContactDescription.ts | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/packages/nodes-base/nodes/Freshdesk/ContactDescription.ts b/packages/nodes-base/nodes/Freshdesk/ContactDescription.ts index e5dd39e3d..446babb5f 100644 --- a/packages/nodes-base/nodes/Freshdesk/ContactDescription.ts +++ b/packages/nodes-base/nodes/Freshdesk/ContactDescription.ts @@ -156,7 +156,7 @@ export const contactFields = [ }, description: `Key value pairs containing the name and value of the custom field.
Only dates in the format YYYY-MM-DD are accepted as input for custom date fields.`, - default: {}, + default: [], options: [ { displayName: 'Custom Field', @@ -243,7 +243,7 @@ export const contactFields = [ displayName: 'Other Companies', name: 'other_companies', type: 'string', - default: '', + default: [], typeOptions: { multipleValues: true, }, @@ -256,7 +256,7 @@ export const contactFields = [ displayName: 'Other Emails', name: 'other_emails', type: 'string', - default: '', + default: [], typeOptions: { multipleValues: true, }, @@ -274,7 +274,7 @@ export const contactFields = [ displayName: 'Tags', name: 'tags', type: 'string', - default: '', + default: [], typeOptions: { multipleValues: true, }, @@ -306,7 +306,7 @@ export const contactFields = [ displayName: 'View All Tickets', name: 'view_all_tickets', type: 'boolean', - default: '', + default: false, description: `Set to true if the contact can see all the tickets
that are associated with the company to which he belong.`, }, From 279af6251be7ec49a017b7b86ce7d7cd85bc54fc Mon Sep 17 00:00:00 2001 From: Ben Hesseldieck <1849459+BHesseldieck@users.noreply.github.com> Date: Fri, 11 Sep 2020 12:15:06 +0200 Subject: [PATCH 010/284] :zap: Adjustions for utilizing an external OAuth Hook (#945) * :construction: add oauth redirect env variable, add host to oauth state * :art: format * :construction: reset changes * :construction: add hook * :construction: remove host from inital state --- packages/cli/src/Server.ts | 7 ++++--- packages/cli/templates/oauth-callback.html | 12 ++++++------ 2 files changed, 10 insertions(+), 9 deletions(-) diff --git a/packages/cli/src/Server.ts b/packages/cli/src/Server.ts index 4f23a624c..d09855bcd 100644 --- a/packages/cli/src/Server.ts +++ b/packages/cli/src/Server.ts @@ -1176,7 +1176,7 @@ class App { const csrfSecret = token.secretSync(); const state = { token: token.create(csrfSecret), - cid: req.query.id + cid: req.query.id, }; const stateEncodedStr = Buffer.from(JSON.stringify(state)).toString('base64') as string; @@ -1294,13 +1294,14 @@ class App { }; delete oAuth2Parameters.clientSecret; } - const redirectUri = `${WebhookHelpers.getWebhookBaseUrl()}${this.restEndpoint}/oauth2-credential/callback`; + + await this.externalHooks.run('oauth2.callback', [oAuth2Parameters]); const oAuthObj = new clientOAuth2(oAuth2Parameters); const queryParameters = req.originalUrl.split('?').splice(1, 1).join(''); - const oauthToken = await oAuthObj.code.getToken(`${redirectUri}?${queryParameters}`, options); + const oauthToken = await oAuthObj.code.getToken(`${oAuth2Parameters.redirectUri}?${queryParameters}`, options); if (oauthToken === undefined) { const errorResponse = new ResponseHelper.ResponseError('Unable to get access tokens!', undefined, 404); diff --git a/packages/cli/templates/oauth-callback.html b/packages/cli/templates/oauth-callback.html index e479c5ea9..5f7b736d9 100644 --- a/packages/cli/templates/oauth-callback.html +++ b/packages/cli/templates/oauth-callback.html @@ -1,9 +1,9 @@ - + -Got connected. The window can be closed now. + Got connected. The window can be closed now. From f00c6c10a335ddf22e6efd8c7d2d58f5d2edaa3b Mon Sep 17 00:00:00 2001 From: Jan Oberhauser Date: Fri, 11 Sep 2020 16:59:41 +0200 Subject: [PATCH 011/284] :zap: Increase default space between new nodes --- packages/editor-ui/src/views/NodeView.vue | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/editor-ui/src/views/NodeView.vue b/packages/editor-ui/src/views/NodeView.vue index dd4c75655..742e0ae70 100644 --- a/packages/editor-ui/src/views/NodeView.vue +++ b/packages/editor-ui/src/views/NodeView.vue @@ -940,7 +940,7 @@ export default mixins( // If a node is active then add the new node directly after the current one // newNodeData.position = [activeNode.position[0], activeNode.position[1] + 60]; newNodeData.position = this.getNewNodePosition( - [lastSelectedNode.position[0] + 150, lastSelectedNode.position[1]], + [lastSelectedNode.position[0] + 200, lastSelectedNode.position[1]], [100, 0], ); } else { From 6167e629706178d5904afb0d8e4ac53cb17e9f7a Mon Sep 17 00:00:00 2001 From: Jan Oberhauser Date: Fri, 11 Sep 2020 18:03:37 +0200 Subject: [PATCH 012/284] :zap: Remove unnecessary GoogleSheetsOAuth scope --- .../nodes-base/credentials/GoogleSheetsOAuth2Api.credentials.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/packages/nodes-base/credentials/GoogleSheetsOAuth2Api.credentials.ts b/packages/nodes-base/credentials/GoogleSheetsOAuth2Api.credentials.ts index d13c55612..221f02c06 100644 --- a/packages/nodes-base/credentials/GoogleSheetsOAuth2Api.credentials.ts +++ b/packages/nodes-base/credentials/GoogleSheetsOAuth2Api.credentials.ts @@ -4,7 +4,6 @@ import { } from 'n8n-workflow'; const scopes = [ - 'https://www.googleapis.com/auth/drive', 'https://www.googleapis.com/auth/drive.file', 'https://www.googleapis.com/auth/spreadsheets', ]; From c8d009bced937d3015bb0bc273a0b132c9d288f1 Mon Sep 17 00:00:00 2001 From: Jan Oberhauser Date: Sat, 12 Sep 2020 12:16:07 +0200 Subject: [PATCH 013/284] :sparkles: Add expression support to credentials --- packages/cli/src/ActiveWorkflowRunner.ts | 4 +- packages/cli/src/CredentialsHelper.ts | 32 ++- packages/cli/src/WebhookHelpers.ts | 14 +- packages/core/src/NodeExecuteFunctions.ts | 10 +- packages/editor-ui/src/components/Node.vue | 2 +- .../src/components/mixins/workflowHelpers.ts | 2 +- packages/workflow/src/Expression.ts | 221 ++++++++++++++++++ packages/workflow/src/NodeHelpers.ts | 12 +- packages/workflow/src/Workflow.ts | 201 +--------------- packages/workflow/src/WorkflowDataProxy.ts | 4 +- packages/workflow/src/index.ts | 1 + packages/workflow/test/Workflow.test.ts | 4 +- 12 files changed, 283 insertions(+), 224 deletions(-) create mode 100644 packages/workflow/src/Expression.ts diff --git a/packages/cli/src/ActiveWorkflowRunner.ts b/packages/cli/src/ActiveWorkflowRunner.ts index 0d2e9bad3..a7a4a4cf2 100644 --- a/packages/cli/src/ActiveWorkflowRunner.ts +++ b/packages/cli/src/ActiveWorkflowRunner.ts @@ -234,7 +234,7 @@ export class ActiveWorkflowRunner { path = node.parameters.path as string; if (node.parameters.path === undefined) { - path = workflow.getSimpleParameterValue(node, webhookData.webhookDescription['path']) as string | undefined; + path = workflow.expression.getSimpleParameterValue(node, webhookData.webhookDescription['path']) as string | undefined; if (path === undefined) { // TODO: Use a proper logger @@ -243,7 +243,7 @@ export class ActiveWorkflowRunner { } } - const isFullPath: boolean = workflow.getSimpleParameterValue(node, webhookData.webhookDescription['isFullPath'], false) as boolean; + const isFullPath: boolean = workflow.expression.getSimpleParameterValue(node, webhookData.webhookDescription['isFullPath'], false) as boolean; const webhook = { workflowId: webhookData.workflowId, diff --git a/packages/cli/src/CredentialsHelper.ts b/packages/cli/src/CredentialsHelper.ts index 84e368adf..c0ca748a6 100644 --- a/packages/cli/src/CredentialsHelper.ts +++ b/packages/cli/src/CredentialsHelper.ts @@ -5,9 +5,14 @@ import { import { ICredentialDataDecryptedObject, ICredentialsHelper, + INode, INodeParameters, INodeProperties, + INodeType, + INodeTypes, + INodeTypeData, NodeHelpers, + Workflow, } from 'n8n-workflow'; import { @@ -18,6 +23,19 @@ import { } from './'; +const mockNodeTypes: INodeTypes = { + nodeTypes: {}, + init: async (nodeTypes?: INodeTypeData): Promise => { }, + getAll: (): INodeType[] => { + // Does not get used in Workflow so no need to return it + return []; + }, + getByName: (nodeType: string): INodeType | undefined => { + return undefined; + }, +}; + + export class CredentialsHelper extends ICredentialsHelper { /** @@ -107,7 +125,7 @@ export class CredentialsHelper extends ICredentialsHelper { const credentialsProperties = this.getCredentialsProperties(type); // Add the default credential values - const decryptedData = NodeHelpers.getNodeParameters(credentialsProperties, decryptedDataOriginal as INodeParameters, true, false) as ICredentialDataDecryptedObject; + let decryptedData = NodeHelpers.getNodeParameters(credentialsProperties, decryptedDataOriginal as INodeParameters, true, false) as ICredentialDataDecryptedObject; if (decryptedDataOriginal.oauthTokenData !== undefined) { // The OAuth data gets removed as it is not defined specifically as a parameter @@ -115,6 +133,18 @@ export class CredentialsHelper extends ICredentialsHelper { decryptedData.oauthTokenData = decryptedDataOriginal.oauthTokenData; } + const mockNode: INode = { + name: '', + typeVersion: 1, + type: 'mock', + position: [0, 0], + parameters: decryptedData as INodeParameters, + }; + + const workflow = new Workflow({ nodes: [mockNode], connections: {}, active: false, nodeTypes: mockNodeTypes}); + // Resolve expressions if any are set + decryptedData = workflow.expression.getComplexParameterValue(mockNode, decryptedData as INodeParameters, undefined) as ICredentialDataDecryptedObject; + // Load and apply the credentials overwrites if any exist const credentialsOverwrites = CredentialsOverwrites(); return credentialsOverwrites.applyOverwrite(type, decryptedData); diff --git a/packages/cli/src/WebhookHelpers.ts b/packages/cli/src/WebhookHelpers.ts index ffb544989..a1b77c797 100644 --- a/packages/cli/src/WebhookHelpers.ts +++ b/packages/cli/src/WebhookHelpers.ts @@ -114,8 +114,8 @@ export function getWorkflowWebhooksBasic(workflow: Workflow): IWebhookData[] { } // Get the responseMode - const responseMode = workflow.getSimpleParameterValue(workflowStartNode, webhookData.webhookDescription['responseMode'], 'onReceived'); - const responseCode = workflow.getSimpleParameterValue(workflowStartNode, webhookData.webhookDescription['responseCode'], 200) as number; + const responseMode = workflow.expression.getSimpleParameterValue(workflowStartNode, webhookData.webhookDescription['responseMode'], 'onReceived'); + const responseCode = workflow.expression.getSimpleParameterValue(workflowStartNode, webhookData.webhookDescription['responseCode'], 200) as number; if (!['onReceived', 'lastNode'].includes(responseMode as string)) { // If the mode is not known we error. Is probably best like that instead of using @@ -173,7 +173,7 @@ export function getWorkflowWebhooksBasic(workflow: Workflow): IWebhookData[] { await WorkflowHelpers.saveStaticData(workflow); if (webhookData.webhookDescription['responseHeaders'] !== undefined) { - const responseHeaders = workflow.getComplexParameterValue(workflowStartNode, webhookData.webhookDescription['responseHeaders'], undefined) as { + const responseHeaders = workflow.expression.getComplexParameterValue(workflowStartNode, webhookData.webhookDescription['responseHeaders'], undefined) as { entries?: Array<{ name: string; value: string; @@ -325,7 +325,7 @@ export function getWorkflowWebhooksBasic(workflow: Workflow): IWebhookData[] { return data; } - const responseData = workflow.getSimpleParameterValue(workflowStartNode, webhookData.webhookDescription['responseData'], 'firstEntryJson'); + const responseData = workflow.expression.getSimpleParameterValue(workflowStartNode, webhookData.webhookDescription['responseData'], 'firstEntryJson'); if (didSendResponse === false) { let data: IDataObject | IDataObject[]; @@ -340,13 +340,13 @@ export function getWorkflowWebhooksBasic(workflow: Workflow): IWebhookData[] { data = returnData.data!.main[0]![0].json; - const responsePropertyName = workflow.getSimpleParameterValue(workflowStartNode, webhookData.webhookDescription['responsePropertyName'], undefined); + const responsePropertyName = workflow.expression.getSimpleParameterValue(workflowStartNode, webhookData.webhookDescription['responsePropertyName'], undefined); if (responsePropertyName !== undefined) { data = get(data, responsePropertyName as string) as IDataObject; } - const responseContentType = workflow.getSimpleParameterValue(workflowStartNode, webhookData.webhookDescription['responseContentType'], undefined); + const responseContentType = workflow.expression.getSimpleParameterValue(workflowStartNode, webhookData.webhookDescription['responseContentType'], undefined); if (responseContentType !== undefined) { // Send the webhook response manually to be able to set the content-type @@ -379,7 +379,7 @@ export function getWorkflowWebhooksBasic(workflow: Workflow): IWebhookData[] { didSendResponse = true; } - const responseBinaryPropertyName = workflow.getSimpleParameterValue(workflowStartNode, webhookData.webhookDescription['responseBinaryPropertyName'], 'data'); + const responseBinaryPropertyName = workflow.expression.getSimpleParameterValue(workflowStartNode, webhookData.webhookDescription['responseBinaryPropertyName'], 'data'); if (responseBinaryPropertyName === undefined && didSendResponse === false) { responseCallback(new Error('No "responseBinaryPropertyName" is set.'), {}); diff --git a/packages/core/src/NodeExecuteFunctions.ts b/packages/core/src/NodeExecuteFunctions.ts index be0cf6497..5cf38f6a9 100644 --- a/packages/core/src/NodeExecuteFunctions.ts +++ b/packages/core/src/NodeExecuteFunctions.ts @@ -388,7 +388,7 @@ export function getNodeParameter(workflow: Workflow, runExecutionData: IRunExecu let returnData; try { - returnData = workflow.getParameterValue(value, runExecutionData, runIndex, itemIndex, node.name, connectionInputData); + returnData = workflow.expression.getParameterValue(value, runExecutionData, runIndex, itemIndex, node.name, connectionInputData); } catch (e) { e.message += ` [Error in parameter: "${parameterName}"]`; throw e; @@ -434,12 +434,12 @@ export function getNodeWebhookUrl(name: string, workflow: Workflow, node: INode, return undefined; } - const path = workflow.getSimpleParameterValue(node, webhookDescription['path']); + const path = workflow.expression.getSimpleParameterValue(node, webhookDescription['path']); if (path === undefined) { return undefined; } - const isFullPath: boolean = workflow.getSimpleParameterValue(node, webhookDescription['isFullPath'], false) as boolean; + const isFullPath: boolean = workflow.expression.getSimpleParameterValue(node, webhookDescription['isFullPath'], false) as boolean; return NodeHelpers.getNodeWebhookUrl(baseUrl, workflow.id!, node, path.toString(), isFullPath); } @@ -654,7 +654,7 @@ export function getExecuteFunctions(workflow: Workflow, runExecutionData: IRunEx return continueOnFail(node); }, evaluateExpression: (expression: string, itemIndex: number) => { - return workflow.resolveSimpleParameterValue('=' + expression, runExecutionData, runIndex, itemIndex, node.name, connectionInputData); + return workflow.expression.resolveSimpleParameterValue('=' + expression, runExecutionData, runIndex, itemIndex, node.name, connectionInputData); }, async executeWorkflow(workflowInfo: IExecuteWorkflowInfo, inputData?: INodeExecutionData[]): Promise { // tslint:disable-line:no-any return additionalData.executeWorkflow(workflowInfo, additionalData, inputData); @@ -752,7 +752,7 @@ export function getExecuteSingleFunctions(workflow: Workflow, runExecutionData: }, evaluateExpression: (expression: string, evaluateItemIndex: number | undefined) => { evaluateItemIndex = evaluateItemIndex === undefined ? itemIndex : evaluateItemIndex; - return workflow.resolveSimpleParameterValue('=' + expression, runExecutionData, runIndex, evaluateItemIndex, node.name, connectionInputData); + return workflow.expression.resolveSimpleParameterValue('=' + expression, runExecutionData, runIndex, evaluateItemIndex, node.name, connectionInputData); }, getContext(type: string): IContextObject { return NodeHelpers.getContext(runExecutionData, type, node); diff --git a/packages/editor-ui/src/components/Node.vue b/packages/editor-ui/src/components/Node.vue index 13c2fee62..06f9bf8ae 100644 --- a/packages/editor-ui/src/components/Node.vue +++ b/packages/editor-ui/src/components/Node.vue @@ -134,7 +134,7 @@ export default mixins(nodeBase, workflowHelpers).extend({ } if (this.nodeType !== null && this.nodeType.subtitle !== undefined) { - return this.workflow.getSimpleParameterValue(this.data as INode, this.nodeType.subtitle) as string | undefined; + return this.workflow.expression.getSimpleParameterValue(this.data as INode, this.nodeType.subtitle) as string | undefined; } if (this.data.parameters.operation !== undefined) { diff --git a/packages/editor-ui/src/components/mixins/workflowHelpers.ts b/packages/editor-ui/src/components/mixins/workflowHelpers.ts index 507d6d31e..a55ac3362 100644 --- a/packages/editor-ui/src/components/mixins/workflowHelpers.ts +++ b/packages/editor-ui/src/components/mixins/workflowHelpers.ts @@ -357,7 +357,7 @@ export const workflowHelpers = mixins( connectionInputData = []; } - return workflow.getParameterValue(expression, runExecutionData, runIndex, itemIndex, activeNode.name, connectionInputData, true); + return workflow.expression.getParameterValue(expression, runExecutionData, runIndex, itemIndex, activeNode.name, connectionInputData, true); }, // Saves the currently loaded workflow to the database. diff --git a/packages/workflow/src/Expression.ts b/packages/workflow/src/Expression.ts new file mode 100644 index 000000000..4df397c6d --- /dev/null +++ b/packages/workflow/src/Expression.ts @@ -0,0 +1,221 @@ + +import { + INode, + INodeExecutionData, + INodeParameters, + IRunExecutionData, + NodeParameterValue, + Workflow, + WorkflowDataProxy, +} from './'; + +// @ts-ignore +import * as tmpl from 'riot-tmpl'; + +// Set it to use double curly brackets instead of single ones +tmpl.brackets.set('{{ }}'); + +// Make sure that it does not always print an error when it could not resolve +// a variable +tmpl.tmpl.errorHandler = () => { }; + + +export class Expression { + + workflow: Workflow; + + constructor(workflow: Workflow) { + this.workflow = workflow; + } + + + /** + * Converts an object to a string in a way to make it clear that + * the value comes from an object + * + * @param {object} value + * @returns {string} + * @memberof Workflow + */ + convertObjectValueToString(value: object): string { + const typeName = Array.isArray(value) ? 'Array' : 'Object'; + return `[${typeName}: ${JSON.stringify(value)}]`; + } + + + + /** + * Resolves the paramter value. If it is an expression it will execute it and + * return the result. For everything simply the supplied value will be returned. + * + * @param {NodeParameterValue} parameterValue + * @param {(IRunExecutionData | null)} runExecutionData + * @param {number} runIndex + * @param {number} itemIndex + * @param {string} activeNodeName + * @param {INodeExecutionData[]} connectionInputData + * @param {boolean} [returnObjectAsString=false] + * @returns {(NodeParameterValue | INodeParameters | NodeParameterValue[] | INodeParameters[])} + * @memberof Workflow + */ + resolveSimpleParameterValue(parameterValue: NodeParameterValue, runExecutionData: IRunExecutionData | null, runIndex: number, itemIndex: number, activeNodeName: string, connectionInputData: INodeExecutionData[], returnObjectAsString = false): NodeParameterValue | INodeParameters | NodeParameterValue[] | INodeParameters[] { + // Check if it is an expression + if (typeof parameterValue !== 'string' || parameterValue.charAt(0) !== '=') { + // Is no expression so return value + return parameterValue; + } + + // Is an expression + + // Remove the equal sign + parameterValue = parameterValue.substr(1); + + // Generate a data proxy which allows to query workflow data + const dataProxy = new WorkflowDataProxy(this.workflow, runExecutionData, runIndex, itemIndex, activeNodeName, connectionInputData); + const data = dataProxy.getDataProxy(); + + // Execute the expression + try { + const returnValue = tmpl.tmpl(parameterValue, data); + if (typeof returnValue === 'function') { + throw new Error('Expression resolved to a function. Please add "()"'); + } else if (returnValue !== null && typeof returnValue === 'object') { + if (returnObjectAsString === true) { + return this.convertObjectValueToString(returnValue); + } + } + return returnValue; + } catch (e) { + throw new Error(`Expression is not valid: ${e.message}`); + } + } + + + + /** + * Resolves value of parameter. But does not work for workflow-data. + * + * @param {INode} node + * @param {(string | undefined)} parameterValue + * @param {string} [defaultValue] + * @returns {(string | undefined)} + * @memberof Workflow + */ + getSimpleParameterValue(node: INode, parameterValue: string | boolean | undefined, defaultValue?: boolean | number | string): boolean | number | string | undefined { + if (parameterValue === undefined) { + // Value is not set so return the default + return defaultValue; + } + + // Get the value of the node (can be an expression) + const runIndex = 0; + const itemIndex = 0; + const connectionInputData: INodeExecutionData[] = []; + const runData: IRunExecutionData = { + resultData: { + runData: {}, + } + }; + + return this.getParameterValue(parameterValue, runData, runIndex, itemIndex, node.name, connectionInputData) as boolean | number | string | undefined; + } + + + + /** + * Resolves value of complex parameter. But does not work for workflow-data. + * + * @param {INode} node + * @param {(NodeParameterValue | INodeParameters | NodeParameterValue[] | INodeParameters[])} parameterValue + * @param {(NodeParameterValue | INodeParameters | NodeParameterValue[] | INodeParameters[] | undefined)} [defaultValue] + * @returns {(NodeParameterValue | INodeParameters | NodeParameterValue[] | INodeParameters[] | undefined)} + * @memberof Workflow + */ + getComplexParameterValue(node: INode, parameterValue: NodeParameterValue | INodeParameters | NodeParameterValue[] | INodeParameters[], defaultValue: NodeParameterValue | INodeParameters | NodeParameterValue[] | INodeParameters[] | undefined = undefined): NodeParameterValue | INodeParameters | NodeParameterValue[] | INodeParameters[] | undefined { + if (parameterValue === undefined) { + // Value is not set so return the default + return defaultValue; + } + + // Get the value of the node (can be an expression) + const runIndex = 0; + const itemIndex = 0; + const connectionInputData: INodeExecutionData[] = []; + const runData: IRunExecutionData = { + resultData: { + runData: {}, + } + }; + + // Resolve the "outer" main values + const returnData = this.getParameterValue(parameterValue, runData, runIndex, itemIndex, node.name, connectionInputData); + + // Resolve the "inner" values + return this.getParameterValue(returnData, runData, runIndex, itemIndex, node.name, connectionInputData); + } + + + + /** + * Returns the resolved node parameter value. If it is an expression it will execute it and + * return the result. If the value to resolve is an array or object it will do the same + * for all of the items and values. + * + * @param {(NodeParameterValue | INodeParameters | NodeParameterValue[] | INodeParameters[])} parameterValue + * @param {(IRunExecutionData | null)} runExecutionData + * @param {number} runIndex + * @param {number} itemIndex + * @param {string} activeNodeName + * @param {INodeExecutionData[]} connectionInputData + * @param {boolean} [returnObjectAsString=false] + * @returns {(NodeParameterValue | INodeParameters | NodeParameterValue[] | INodeParameters[])} + * @memberof Workflow + */ + getParameterValue(parameterValue: NodeParameterValue | INodeParameters | NodeParameterValue[] | INodeParameters[], runExecutionData: IRunExecutionData | null, runIndex: number, itemIndex: number, activeNodeName: string, connectionInputData: INodeExecutionData[], returnObjectAsString = false): NodeParameterValue | INodeParameters | NodeParameterValue[] | INodeParameters[] { + // Helper function which returns true when the parameter is a complex one or array + const isComplexParameter = (value: NodeParameterValue | INodeParameters | NodeParameterValue[] | INodeParameters[]) => { + return typeof value === 'object'; + }; + + // Helper function which resolves a parameter value depending on if it is simply or not + const resolveParameterValue = (value: NodeParameterValue | INodeParameters | NodeParameterValue[] | INodeParameters[]) => { + if (isComplexParameter(value)) { + return this.getParameterValue(value, runExecutionData, runIndex, itemIndex, activeNodeName, connectionInputData, returnObjectAsString); + } else { + return this.resolveSimpleParameterValue(value as NodeParameterValue, runExecutionData, runIndex, itemIndex, activeNodeName, connectionInputData, returnObjectAsString); + } + }; + + // Check if it value is a simple one that we can get it resolved directly + if (!isComplexParameter(parameterValue)) { + return this.resolveSimpleParameterValue(parameterValue as NodeParameterValue, runExecutionData, runIndex, itemIndex, activeNodeName, connectionInputData, returnObjectAsString); + } + + // The parameter value is complex so resolve depending on type + + if (Array.isArray(parameterValue)) { + // Data is an array + const returnData = []; + for (const item of parameterValue) { + returnData.push(resolveParameterValue(item)); + } + + if (returnObjectAsString === true && typeof returnData === 'object') { + return this.convertObjectValueToString(returnData); + } + + return returnData as NodeParameterValue[] | INodeParameters[]; + } else { + // Data is an object + const returnData: INodeParameters = {}; + for (const key of Object.keys(parameterValue)) { + returnData[key] = resolveParameterValue((parameterValue as INodeParameters)[key]); + } + + if (returnObjectAsString === true && typeof returnData === 'object') { + return this.convertObjectValueToString(returnData); + } + return returnData; + } + } +} diff --git a/packages/workflow/src/NodeHelpers.ts b/packages/workflow/src/NodeHelpers.ts index 3e38931ae..f6a54414d 100644 --- a/packages/workflow/src/NodeHelpers.ts +++ b/packages/workflow/src/NodeHelpers.ts @@ -755,7 +755,7 @@ export function getNodeWebhooks(workflow: Workflow, node: INode, additionalData: const returnData: IWebhookData[] = []; for (const webhookDescription of nodeType.description.webhooks) { - let nodeWebhookPath = workflow.getSimpleParameterValue(node, webhookDescription['path']); + let nodeWebhookPath = workflow.expression.getSimpleParameterValue(node, webhookDescription['path']); if (nodeWebhookPath === undefined) { // TODO: Use a proper logger console.error(`No webhook path could be found for node "${node.name}" in workflow "${workflowId}".`); @@ -768,10 +768,10 @@ export function getNodeWebhooks(workflow: Workflow, node: INode, additionalData: nodeWebhookPath = nodeWebhookPath.slice(1); } - const isFullPath: boolean = workflow.getSimpleParameterValue(node, webhookDescription['isFullPath'], false) as boolean; + const isFullPath: boolean = workflow.expression.getSimpleParameterValue(node, webhookDescription['isFullPath'], false) as boolean; const path = getNodeWebhookPath(workflowId, node, nodeWebhookPath, isFullPath); - const httpMethod = workflow.getSimpleParameterValue(node, webhookDescription['httpMethod'], 'GET'); + const httpMethod = workflow.expression.getSimpleParameterValue(node, webhookDescription['httpMethod'], 'GET'); if (httpMethod === undefined) { // TODO: Use a proper logger @@ -809,7 +809,7 @@ export function getNodeWebhooksBasic(workflow: Workflow, node: INode): IWebhookD const returnData: IWebhookData[] = []; for (const webhookDescription of nodeType.description.webhooks) { - let nodeWebhookPath = workflow.getSimpleParameterValue(node, webhookDescription['path']); + let nodeWebhookPath = workflow.expression.getSimpleParameterValue(node, webhookDescription['path']); if (nodeWebhookPath === undefined) { // TODO: Use a proper logger console.error(`No webhook path could be found for node "${node.name}" in workflow "${workflowId}".`); @@ -822,11 +822,11 @@ export function getNodeWebhooksBasic(workflow: Workflow, node: INode): IWebhookD nodeWebhookPath = nodeWebhookPath.slice(1); } - const isFullPath: boolean = workflow.getSimpleParameterValue(node, webhookDescription['isFullPath'], false) as boolean; + const isFullPath: boolean = workflow.expression.getSimpleParameterValue(node, webhookDescription['isFullPath'], false) as boolean; const path = getNodeWebhookPath(workflowId, node, nodeWebhookPath, isFullPath); - const httpMethod = workflow.getSimpleParameterValue(node, webhookDescription['httpMethod']); + const httpMethod = workflow.expression.getSimpleParameterValue(node, webhookDescription['httpMethod']); if (httpMethod === undefined) { // TODO: Use a proper logger diff --git a/packages/workflow/src/Workflow.ts b/packages/workflow/src/Workflow.ts index 501e7f0de..c7208f6a8 100644 --- a/packages/workflow/src/Workflow.ts +++ b/packages/workflow/src/Workflow.ts @@ -1,5 +1,6 @@ import { + Expression, IConnections, IGetExecuteTriggerFunctions, INode, @@ -23,21 +24,11 @@ import { NodeParameterValue, ObservableObject, WebhookSetupMethodNames, - WorkflowDataProxy, WorkflowExecuteMode, } from './'; -// @ts-ignore -import * as tmpl from 'riot-tmpl'; import { IConnection, IDataObject, IObservableObject } from './Interfaces'; -// Set it to use double curly brackets instead of single ones -tmpl.brackets.set('{{ }}'); - -// Make sure that it does not always print an error when it could not resolve -// a variable -tmpl.tmpl.errorHandler = () => { }; - export class Workflow { id: string | undefined; @@ -46,6 +37,7 @@ export class Workflow { connectionsBySourceNode: IConnections; connectionsByDestinationNode: IConnections; nodeTypes: INodeTypes; + expression: Expression; active: boolean; settings: IWorkflowSettings; @@ -90,6 +82,8 @@ export class Workflow { this.staticData = ObservableObject.create(parameters.staticData || {}, undefined, { ignoreEmptyOnFirstChild: true }); this.settings = parameters.settings || {}; + + this.expression = new Expression(this); } @@ -147,21 +141,6 @@ export class Workflow { - /** - * Converts an object to a string in a way to make it clear that - * the value comes from an object - * - * @param {object} value - * @returns {string} - * @memberof Workflow - */ - convertObjectValueToString(value: object): string { - const typeName = Array.isArray(value) ? 'Array' : 'Object'; - return `[${typeName}: ${JSON.stringify(value)}]`; - } - - - /** * A workflow can only be activated if it has a node which has either triggers * or webhooks defined. @@ -706,65 +685,6 @@ export class Workflow { - /** - * Resolves value of parameter. But does not work for workflow-data. - * - * @param {INode} node - * @param {(string | undefined)} parameterValue - * @param {string} [defaultValue] - * @returns {(string | undefined)} - * @memberof Workflow - */ - getSimpleParameterValue(node: INode, parameterValue: string | boolean | undefined, defaultValue?: boolean | number | string): boolean | number | string | undefined { - if (parameterValue === undefined) { - // Value is not set so return the default - return defaultValue; - } - - // Get the value of the node (can be an expression) - const runIndex = 0; - const itemIndex = 0; - const connectionInputData: INodeExecutionData[] = []; - const runData: IRunExecutionData = { - resultData: { - runData: {}, - } - }; - - return this.getParameterValue(parameterValue, runData, runIndex, itemIndex, node.name, connectionInputData) as boolean | number | string | undefined; - } - - /** - * Resolves value of complex parameter. But does not work for workflow-data. - * - * @param {INode} node - * @param {(NodeParameterValue | INodeParameters | NodeParameterValue[] | INodeParameters[])} parameterValue - * @param {(NodeParameterValue | INodeParameters | NodeParameterValue[] | INodeParameters[] | undefined)} [defaultValue] - * @returns {(NodeParameterValue | INodeParameters | NodeParameterValue[] | INodeParameters[] | undefined)} - * @memberof Workflow - */ - getComplexParameterValue(node: INode, parameterValue: NodeParameterValue | INodeParameters | NodeParameterValue[] | INodeParameters[], defaultValue: NodeParameterValue | INodeParameters | NodeParameterValue[] | INodeParameters[] | undefined = undefined): NodeParameterValue | INodeParameters | NodeParameterValue[] | INodeParameters[] | undefined { - if (parameterValue === undefined) { - // Value is not set so return the default - return defaultValue; - } - - // Get the value of the node (can be an expression) - const runIndex = 0; - const itemIndex = 0; - const connectionInputData: INodeExecutionData[] = []; - const runData: IRunExecutionData = { - resultData: { - runData: {}, - } - }; - - // Resolve the "outer" main values - const returnData = this.getParameterValue(parameterValue, runData, runIndex, itemIndex, node.name, connectionInputData); - - // Resolve the "inner" values - return this.getParameterValue(returnData, runData, runIndex, itemIndex, node.name, connectionInputData); - } /** * Returns from which of the given nodes the workflow should get started from @@ -839,119 +759,6 @@ export class Workflow { - /** - * Returns the resolved node parameter value. If it is an expression it will execute it and - * return the result. If the value to resolve is an array or object it will do the same - * for all of the items and values. - * - * @param {(NodeParameterValue | INodeParameters | NodeParameterValue[] | INodeParameters[])} parameterValue - * @param {(IRunExecutionData | null)} runExecutionData - * @param {number} runIndex - * @param {number} itemIndex - * @param {string} activeNodeName - * @param {INodeExecutionData[]} connectionInputData - * @param {boolean} [returnObjectAsString=false] - * @returns {(NodeParameterValue | INodeParameters | NodeParameterValue[] | INodeParameters[])} - * @memberof Workflow - */ - getParameterValue(parameterValue: NodeParameterValue | INodeParameters | NodeParameterValue[] | INodeParameters[], runExecutionData: IRunExecutionData | null, runIndex: number, itemIndex: number, activeNodeName: string, connectionInputData: INodeExecutionData[], returnObjectAsString = false): NodeParameterValue | INodeParameters | NodeParameterValue[] | INodeParameters[] { - // Helper function which returns true when the parameter is a complex one or array - const isComplexParameter = (value: NodeParameterValue | INodeParameters | NodeParameterValue[] | INodeParameters[]) => { - return typeof value === 'object'; - }; - - // Helper function which resolves a parameter value depending on if it is simply or not - const resolveParameterValue = (value: NodeParameterValue | INodeParameters | NodeParameterValue[] | INodeParameters[]) => { - if (isComplexParameter(value)) { - return this.getParameterValue(value, runExecutionData, runIndex, itemIndex, activeNodeName, connectionInputData, returnObjectAsString); - } else { - return this.resolveSimpleParameterValue(value as NodeParameterValue, runExecutionData, runIndex, itemIndex, activeNodeName, connectionInputData, returnObjectAsString); - } - }; - - // Check if it value is a simple one that we can get it resolved directly - if (!isComplexParameter(parameterValue)) { - return this.resolveSimpleParameterValue(parameterValue as NodeParameterValue, runExecutionData, runIndex, itemIndex, activeNodeName, connectionInputData, returnObjectAsString); - } - - // The parameter value is complex so resolve depending on type - - if (Array.isArray(parameterValue)) { - // Data is an array - const returnData = []; - for (const item of parameterValue) { - returnData.push(resolveParameterValue(item)); - } - - if (returnObjectAsString === true && typeof returnData === 'object') { - return this.convertObjectValueToString(returnData); - } - - return returnData as NodeParameterValue[] | INodeParameters[]; - } else { - // Data is an object - const returnData: INodeParameters = {}; - for (const key of Object.keys(parameterValue)) { - returnData[key] = resolveParameterValue((parameterValue as INodeParameters)[key]); - } - - if (returnObjectAsString === true && typeof returnData === 'object') { - return this.convertObjectValueToString(returnData); - } - return returnData; - } - } - - - - /** - * Resolves the paramter value. If it is an expression it will execute it and - * return the result. For everything simply the supplied value will be returned. - * - * @param {NodeParameterValue} parameterValue - * @param {(IRunExecutionData | null)} runExecutionData - * @param {number} runIndex - * @param {number} itemIndex - * @param {string} activeNodeName - * @param {INodeExecutionData[]} connectionInputData - * @param {boolean} [returnObjectAsString=false] - * @returns {(NodeParameterValue | INodeParameters | NodeParameterValue[] | INodeParameters[])} - * @memberof Workflow - */ - resolveSimpleParameterValue(parameterValue: NodeParameterValue, runExecutionData: IRunExecutionData | null, runIndex: number, itemIndex: number, activeNodeName: string, connectionInputData: INodeExecutionData[], returnObjectAsString = false): NodeParameterValue | INodeParameters | NodeParameterValue[] | INodeParameters[] { - // Check if it is an expression - if (typeof parameterValue !== 'string' || parameterValue.charAt(0) !== '=') { - // Is no expression so return value - return parameterValue; - } - - // Is an expression - - // Remove the equal sign - parameterValue = parameterValue.substr(1); - - // Generate a data proxy which allows to query workflow data - const dataProxy = new WorkflowDataProxy(this, runExecutionData, runIndex, itemIndex, activeNodeName, connectionInputData); - const data = dataProxy.getDataProxy(); - - // Execute the expression - try { - const returnValue = tmpl.tmpl(parameterValue, data); - if (typeof returnValue === 'function') { - throw new Error('Expression resolved to a function. Please add "()"'); - } else if (returnValue !== null && typeof returnValue === 'object') { - if (returnObjectAsString === true) { - return this.convertObjectValueToString(returnValue); - } - } - return returnValue; - } catch (e) { - throw new Error(`Expression is not valid: ${e.message}`); - } - } - - - /** * Executes the Webhooks method of the node * diff --git a/packages/workflow/src/WorkflowDataProxy.ts b/packages/workflow/src/WorkflowDataProxy.ts index 8094991f3..852e28e0c 100644 --- a/packages/workflow/src/WorkflowDataProxy.ts +++ b/packages/workflow/src/WorkflowDataProxy.ts @@ -97,7 +97,7 @@ export class WorkflowDataProxy { if (typeof returnValue === 'string' && returnValue.charAt(0) === '=') { // The found value is an expression so resolve it - return that.workflow.getParameterValue(returnValue, that.runExecutionData, that.runIndex, that.itemIndex, that.activeNodeName, that.connectionInputData); + return that.workflow.expression.getParameterValue(returnValue, that.runExecutionData, that.runIndex, that.itemIndex, that.activeNodeName, that.connectionInputData); } return returnValue; @@ -337,7 +337,7 @@ export class WorkflowDataProxy { $env: this.envGetter(), $evaluateExpression: (expression: string, itemIndex?: number) => { itemIndex = itemIndex || that.itemIndex; - return that.workflow.getParameterValue('=' + expression, that.runExecutionData, that.runIndex, itemIndex, that.activeNodeName, that.connectionInputData); + return that.workflow.expression.getParameterValue('=' + expression, that.runExecutionData, that.runIndex, itemIndex, that.activeNodeName, that.connectionInputData); }, $item: (itemIndex: number, runIndex?: number) => { const defaultReturnRunIndex = runIndex === undefined ? -1 : runIndex; diff --git a/packages/workflow/src/index.ts b/packages/workflow/src/index.ts index 89d01313d..b2c24d55f 100644 --- a/packages/workflow/src/index.ts +++ b/packages/workflow/src/index.ts @@ -1,4 +1,5 @@ export * from './Interfaces'; +export * from './Expression'; export * from './Workflow'; export * from './WorkflowDataProxy'; export * from './WorkflowHooks'; diff --git a/packages/workflow/test/Workflow.test.ts b/packages/workflow/test/Workflow.test.ts index 51b092bef..61897734b 100644 --- a/packages/workflow/test/Workflow.test.ts +++ b/packages/workflow/test/Workflow.test.ts @@ -1097,7 +1097,7 @@ describe('Workflow', () => { for (const parameterName of Object.keys(testData.output)) { const parameterValue = nodes.find((node) => node.name === activeNodeName)!.parameters[parameterName]; - const result = workflow.getParameterValue(parameterValue, runExecutionData, runIndex, itemIndex, activeNodeName, connectionInputData); + const result = workflow.expression.getParameterValue(parameterValue, runExecutionData, runIndex, itemIndex, activeNodeName, connectionInputData); // @ts-ignore expect(result).toEqual(testData.output[parameterName]); } @@ -1247,7 +1247,7 @@ describe('Workflow', () => { const parameterName = 'values'; const parameterValue = nodes.find((node) => node.name === activeNodeName)!.parameters[parameterName]; - const result = workflow.getParameterValue(parameterValue, runExecutionData, runIndex, itemIndex, activeNodeName, connectionInputData); + const result = workflow.expression.getParameterValue(parameterValue, runExecutionData, runIndex, itemIndex, activeNodeName, connectionInputData); expect(result).toEqual({ string: [ From d48c6493294374c8a647de8f79cc98c704202398 Mon Sep 17 00:00:00 2001 From: Jan Oberhauser Date: Sat, 12 Sep 2020 14:02:33 +0200 Subject: [PATCH 014/284] :arrow_up: Upgrade some dependencies --- packages/cli/package.json | 6 +++--- packages/core/package.json | 6 +++--- packages/editor-ui/package.json | 6 +++--- packages/nodes-base/package.json | 6 +++--- packages/workflow/package.json | 6 +++--- 5 files changed, 15 insertions(+), 15 deletions(-) diff --git a/packages/cli/package.json b/packages/cli/package.json index 1caed60c0..a6f68fa20 100644 --- a/packages/cli/package.json +++ b/packages/cli/package.json @@ -60,7 +60,7 @@ "@types/convict": "^4.2.1", "@types/dotenv": "^8.2.0", "@types/express": "^4.17.6", - "@types/jest": "^25.2.1", + "@types/jest": "^26.0.13", "@types/localtunnel": "^1.9.0", "@types/lodash.get": "^4.4.6", "@types/node": "^14.0.27", @@ -68,11 +68,11 @@ "@types/parseurl": "^1.3.1", "@types/request-promise-native": "~1.0.15", "concurrently": "^5.1.0", - "jest": "^24.9.0", + "jest": "^26.4.2", "nodemon": "^2.0.2", "p-cancelable": "^2.0.0", "run-script-os": "^1.0.7", - "ts-jest": "^25.4.0", + "ts-jest": "^26.3.0", "ts-node": "^8.9.1", "tslint": "^6.1.2", "typescript": "~3.7.4" diff --git a/packages/core/package.json b/packages/core/package.json index 1c8a6b67d..247e7287e 100644 --- a/packages/core/package.json +++ b/packages/core/package.json @@ -28,14 +28,14 @@ "@types/cron": "^1.7.1", "@types/crypto-js": "^3.1.43", "@types/express": "^4.17.6", - "@types/jest": "^25.2.1", + "@types/jest": "^26.0.13", "@types/lodash.get": "^4.4.6", "@types/mime-types": "^2.1.0", "@types/node": "^14.0.27", "@types/request-promise-native": "~1.0.15", - "jest": "^24.9.0", + "jest": "^26.4.2", "source-map-support": "^0.5.9", - "ts-jest": "^25.4.0", + "ts-jest": "^26.3.0", "tslint": "^6.1.2", "typescript": "~3.7.4" }, diff --git a/packages/editor-ui/package.json b/packages/editor-ui/package.json index a325c78ab..bed677ebb 100644 --- a/packages/editor-ui/package.json +++ b/packages/editor-ui/package.json @@ -34,7 +34,7 @@ "@types/dateformat": "^3.0.0", "@types/express": "^4.17.6", "@types/file-saver": "^2.0.1", - "@types/jest": "^25.2.1", + "@types/jest": "^26.0.13", "@types/lodash.get": "^4.4.6", "@types/lodash.set": "^4.3.6", "@types/node": "^14.0.27", @@ -43,7 +43,7 @@ "@typescript-eslint/parser": "^2.13.0", "@vue/cli-plugin-babel": "^4.1.2", "@vue/cli-plugin-eslint": "^4.1.2", - "@vue/cli-plugin-typescript": "~4.1.2", + "@vue/cli-plugin-typescript": "~4.5.6", "@vue/cli-plugin-unit-jest": "^4.1.2", "@vue/cli-service": "^3.11.0", "@vue/eslint-config-standard": "^5.0.1", @@ -73,7 +73,7 @@ "quill-autoformat": "^0.1.1", "sass-loader": "^8.0.0", "string-template-parser": "^1.2.6", - "ts-jest": "^25.4.0", + "ts-jest": "^26.3.0", "tslint": "^6.1.2", "typescript": "~3.7.4", "vue": "^2.6.9", diff --git a/packages/nodes-base/package.json b/packages/nodes-base/package.json index b84b29fde..2582e3198 100644 --- a/packages/nodes-base/package.json +++ b/packages/nodes-base/package.json @@ -376,7 +376,7 @@ "@types/formidable": "^1.0.31", "@types/gm": "^1.18.2", "@types/imap-simple": "^4.2.0", - "@types/jest": "^25.2.1", + "@types/jest": "^26.0.13", "@types/lodash.set": "^4.3.6", "@types/mailparser": "^2.7.3", "@types/moment-timezone": "^0.5.12", @@ -391,9 +391,9 @@ "@types/uuid": "^3.4.6", "@types/xml2js": "^0.4.3", "gulp": "^4.0.0", - "jest": "^24.9.0", + "jest": "^26.4.2", "n8n-workflow": "~0.39.0", - "ts-jest": "^25.4.0", + "ts-jest": "^26.3.0", "tslint": "^6.1.2", "typescript": "~3.7.4" }, diff --git a/packages/workflow/package.json b/packages/workflow/package.json index 2a4cca848..06f90c2c7 100644 --- a/packages/workflow/package.json +++ b/packages/workflow/package.json @@ -26,11 +26,11 @@ ], "devDependencies": { "@types/express": "^4.17.6", - "@types/jest": "^25.2.1", + "@types/jest": "^26.0.13", "@types/lodash.get": "^4.4.6", "@types/node": "^14.0.27", - "jest": "^24.9.0", - "ts-jest": "^25.4.0", + "jest": "^26.4.2", + "ts-jest": "^26.3.0", "tslint": "^6.1.2", "typescript": "~3.7.4" }, From ac2e0040b0b016f56a8544e92bcf19083701af94 Mon Sep 17 00:00:00 2001 From: Jan Oberhauser Date: Sat, 12 Sep 2020 19:25:49 +0200 Subject: [PATCH 015/284] :zap: Set fixed version of @types/node to fix build --- packages/cli/package.json | 2 +- packages/core/package.json | 2 +- packages/editor-ui/package.json | 2 +- packages/node-dev/package.json | 2 +- packages/nodes-base/package.json | 2 +- packages/workflow/package.json | 2 +- 6 files changed, 6 insertions(+), 6 deletions(-) diff --git a/packages/cli/package.json b/packages/cli/package.json index a6f68fa20..d28954d33 100644 --- a/packages/cli/package.json +++ b/packages/cli/package.json @@ -63,7 +63,7 @@ "@types/jest": "^26.0.13", "@types/localtunnel": "^1.9.0", "@types/lodash.get": "^4.4.6", - "@types/node": "^14.0.27", + "@types/node": "14.0.27", "@types/open": "^6.1.0", "@types/parseurl": "^1.3.1", "@types/request-promise-native": "~1.0.15", diff --git a/packages/core/package.json b/packages/core/package.json index 247e7287e..7e9ec7715 100644 --- a/packages/core/package.json +++ b/packages/core/package.json @@ -31,7 +31,7 @@ "@types/jest": "^26.0.13", "@types/lodash.get": "^4.4.6", "@types/mime-types": "^2.1.0", - "@types/node": "^14.0.27", + "@types/node": "14.0.27", "@types/request-promise-native": "~1.0.15", "jest": "^26.4.2", "source-map-support": "^0.5.9", diff --git a/packages/editor-ui/package.json b/packages/editor-ui/package.json index bed677ebb..2f61b7765 100644 --- a/packages/editor-ui/package.json +++ b/packages/editor-ui/package.json @@ -37,7 +37,7 @@ "@types/jest": "^26.0.13", "@types/lodash.get": "^4.4.6", "@types/lodash.set": "^4.3.6", - "@types/node": "^14.0.27", + "@types/node": "14.0.27", "@types/quill": "^2.0.1", "@typescript-eslint/eslint-plugin": "^2.13.0", "@typescript-eslint/parser": "^2.13.0", diff --git a/packages/node-dev/package.json b/packages/node-dev/package.json index 51c25727c..6a07da4e1 100644 --- a/packages/node-dev/package.json +++ b/packages/node-dev/package.json @@ -54,7 +54,7 @@ "@oclif/command": "^1.5.18", "@oclif/errors": "^1.2.2", "@types/express": "^4.17.6", - "@types/node": "^14.0.27", + "@types/node": "14.0.27", "change-case": "^4.1.1", "copyfiles": "^2.1.1", "inquirer": "^7.0.1", diff --git a/packages/nodes-base/package.json b/packages/nodes-base/package.json index 2582e3198..72caf8ea7 100644 --- a/packages/nodes-base/package.json +++ b/packages/nodes-base/package.json @@ -383,7 +383,7 @@ "@types/mongodb": "^3.5.4", "@types/mqtt": "^2.5.0", "@types/mssql": "^6.0.2", - "@types/node": "^14.0.27", + "@types/node": "14.0.27", "@types/nodemailer": "^6.4.0", "@types/redis": "^2.8.11", "@types/request-promise-native": "~1.0.15", diff --git a/packages/workflow/package.json b/packages/workflow/package.json index 06f90c2c7..ae5b1ccd3 100644 --- a/packages/workflow/package.json +++ b/packages/workflow/package.json @@ -28,7 +28,7 @@ "@types/express": "^4.17.6", "@types/jest": "^26.0.13", "@types/lodash.get": "^4.4.6", - "@types/node": "^14.0.27", + "@types/node": "14.0.27", "jest": "^26.4.2", "ts-jest": "^26.3.0", "tslint": "^6.1.2", From 99f7eb2eca56cb4df99f690df421bb067f5f141b Mon Sep 17 00:00:00 2001 From: Jan Oberhauser Date: Sat, 12 Sep 2020 21:13:57 +0200 Subject: [PATCH 016/284] :zap: Apply also credential overwrites of parent and fix bug --- packages/cli/src/CredentialsOverwrites.ts | 23 ++++++++++++++++++++++- packages/cli/src/WorkflowRunner.ts | 12 ++++++++++-- packages/cli/src/WorkflowRunnerProcess.ts | 2 +- 3 files changed, 33 insertions(+), 4 deletions(-) diff --git a/packages/cli/src/CredentialsOverwrites.ts b/packages/cli/src/CredentialsOverwrites.ts index 40b6419da..bd962ae50 100644 --- a/packages/cli/src/CredentialsOverwrites.ts +++ b/packages/cli/src/CredentialsOverwrites.ts @@ -3,6 +3,7 @@ import { } from 'n8n-workflow'; import { + CredentialTypes, ICredentialsOverwrite, GenericHelpers, } from './'; @@ -49,7 +50,27 @@ class CredentialsOverwritesClass { } get(type: string): ICredentialDataDecryptedObject | undefined { - return this.overwriteData[type]; + const credentialTypes = CredentialTypes(); + const credentialTypeData = credentialTypes.getByName(type); + + if (credentialTypeData === undefined) { + throw new Error(`The credentials of type "${type}" are not known.`); + } + + if (credentialTypeData.extends === undefined) { + return this.overwriteData[type]; + } + + const overwrites: ICredentialDataDecryptedObject = {}; + for (const credentialsTypeName of credentialTypeData.extends) { + Object.assign(overwrites, this.get(credentialsTypeName)); + } + + if (this.overwriteData[type] !== undefined) { + Object.assign(overwrites, this.overwriteData[type]); + } + + return overwrites; } getAll(): ICredentialsOverwrite { diff --git a/packages/cli/src/WorkflowRunner.ts b/packages/cli/src/WorkflowRunner.ts index 0eb4f7a01..f0171322a 100644 --- a/packages/cli/src/WorkflowRunner.ts +++ b/packages/cli/src/WorkflowRunner.ts @@ -212,6 +212,7 @@ export class WorkflowRunner { let nodeTypeData: ITransferNodeTypes; let credentialTypeData: ICredentialsTypeData; + let credentialsOverwrites = this.credentialsOverwrites; if (loadAllNodeTypes === true) { // Supply all nodeTypes and credentialTypes @@ -219,15 +220,22 @@ export class WorkflowRunner { const credentialTypes = CredentialTypes(); credentialTypeData = credentialTypes.credentialTypes; } else { - // Supply only nodeTypes and credentialTypes which the workflow needs + // Supply only nodeTypes, credentialTypes and overwrites that the workflow needs nodeTypeData = WorkflowHelpers.getNodeTypeData(data.workflowData.nodes); credentialTypeData = WorkflowHelpers.getCredentialsData(data.credentials); + + credentialsOverwrites = {}; + for (const credentialName of Object.keys(credentialTypeData)) { + if (this.credentialsOverwrites[credentialName] !== undefined) { + credentialsOverwrites[credentialName] = this.credentialsOverwrites[credentialName]; + } + } } (data as unknown as IWorkflowExecutionDataProcessWithExecution).executionId = executionId; (data as unknown as IWorkflowExecutionDataProcessWithExecution).nodeTypeData = nodeTypeData; - (data as unknown as IWorkflowExecutionDataProcessWithExecution).credentialsOverwrite = this.credentialsOverwrites; + (data as unknown as IWorkflowExecutionDataProcessWithExecution).credentialsOverwrite = credentialsOverwrites; (data as unknown as IWorkflowExecutionDataProcessWithExecution).credentialsTypeData = credentialTypeData; // TODO: Still needs correct value const workflowHooks = WorkflowExecuteAdditionalData.getWorkflowHooksMain(data, executionId); diff --git a/packages/cli/src/WorkflowRunnerProcess.ts b/packages/cli/src/WorkflowRunnerProcess.ts index 41f467a8f..c46d5f3dd 100644 --- a/packages/cli/src/WorkflowRunnerProcess.ts +++ b/packages/cli/src/WorkflowRunnerProcess.ts @@ -66,7 +66,7 @@ export class WorkflowRunnerProcess { // Load the credentials overwrites if any exist const credentialsOverwrites = CredentialsOverwrites(); - await credentialsOverwrites.init(); + await credentialsOverwrites.init(inputData.credentialsOverwrite); this.workflow = new Workflow({ id: this.data.workflowData.id as string | undefined, name: this.data.workflowData.name, nodes: this.data.workflowData!.nodes, connections: this.data.workflowData!.connections, active: this.data.workflowData!.active, nodeTypes, staticData: this.data.workflowData!.staticData, settings: this.data.workflowData!.settings}); const additionalData = await WorkflowExecuteAdditionalData.getBase(this.data.credentials); From 542e772e0c1810173184627d02e36d97f65d899b Mon Sep 17 00:00:00 2001 From: Jan Oberhauser Date: Sat, 12 Sep 2020 21:40:37 +0200 Subject: [PATCH 017/284] :bug: Fix bug that nodes without input data did run if "alwaysOutputData" was activated #948 --- packages/core/src/WorkflowExecute.ts | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/packages/core/src/WorkflowExecute.ts b/packages/core/src/WorkflowExecute.ts index 072689acf..8bfe1a2d3 100644 --- a/packages/core/src/WorkflowExecute.ts +++ b/packages/core/src/WorkflowExecute.ts @@ -698,7 +698,10 @@ export class WorkflowExecute { return Promise.reject(new Error(`The node "${executionNode.name}" connects to not found node "${connectionData.node}"`)); } - this.addNodeToBeExecuted(workflow, connectionData, parseInt(outputIndex, 10), executionNode.name, nodeSuccessData!, runIndex); + if (nodeSuccessData![outputIndex] && nodeSuccessData![outputIndex].length !== 0) { + // Add the node only if there is data for it to process + this.addNodeToBeExecuted(workflow, connectionData, parseInt(outputIndex, 10), executionNode.name, nodeSuccessData!, runIndex); + } } } } From 38ddcbe703bfa0c42b3a1451d75f8fb0b7bfb2c3 Mon Sep 17 00:00:00 2001 From: Ricardo Espinoza Date: Sun, 13 Sep 2020 05:08:43 -0400 Subject: [PATCH 018/284] :sparkles: Add Microsoft Teams Node (#931) * :sparkles: Microsoft Team node * :zap: Small fix * :zap: Small improvements * :zap: Small improvements * :zap: Small fix * :zap: Small improvements --- .../MicrosoftOAuth2Api.credentials.ts | 6 +- .../MicrosoftTeamsOAuth2Api.credentials.ts | 21 + .../Microsoft/Teams/ChannelDescription.ts | 381 ++++++++++++++++++ .../Teams/ChannelMessageDescription.ts | 220 ++++++++++ .../nodes/Microsoft/Teams/GenericFunctions.ts | 79 ++++ .../Microsoft/Teams/MicrosoftTeams.node.ts | 217 ++++++++++ .../nodes/Microsoft/Teams/teams.png | Bin 0 -> 6376 bytes packages/nodes-base/package.json | 2 + 8 files changed, 924 insertions(+), 2 deletions(-) create mode 100644 packages/nodes-base/credentials/MicrosoftTeamsOAuth2Api.credentials.ts create mode 100644 packages/nodes-base/nodes/Microsoft/Teams/ChannelDescription.ts create mode 100644 packages/nodes-base/nodes/Microsoft/Teams/ChannelMessageDescription.ts create mode 100644 packages/nodes-base/nodes/Microsoft/Teams/GenericFunctions.ts create mode 100644 packages/nodes-base/nodes/Microsoft/Teams/MicrosoftTeams.node.ts create mode 100644 packages/nodes-base/nodes/Microsoft/Teams/teams.png diff --git a/packages/nodes-base/credentials/MicrosoftOAuth2Api.credentials.ts b/packages/nodes-base/credentials/MicrosoftOAuth2Api.credentials.ts index ad5770199..08990b2de 100644 --- a/packages/nodes-base/credentials/MicrosoftOAuth2Api.credentials.ts +++ b/packages/nodes-base/credentials/MicrosoftOAuth2Api.credentials.ts @@ -11,17 +11,19 @@ export class MicrosoftOAuth2Api implements ICredentialType { displayName = 'Microsoft OAuth2 API'; documentationUrl = 'microsoft'; properties = [ + //info about the tenantID + //https://docs.microsoft.com/en-us/azure/active-directory/develop/active-directory-v2-protocols#endpoints { displayName: 'Authorization URL', name: 'authUrl', type: 'string' as NodePropertyTypes, - default: 'https://login.microsoftonline.com/{yourtenantid}/oauth2/v2.0/authorize', + default: 'https://login.microsoftonline.com/common/oauth2/v2.0/authorize', }, { displayName: 'Access Token URL', name: 'accessTokenUrl', type: 'string' as NodePropertyTypes, - default: 'https://login.microsoftonline.com/{yourtenantid}/oauth2/v2.0/token', + default: 'https://login.microsoftonline.com/common/oauth2/v2.0/token', }, { displayName: 'Auth URI Query Parameters', diff --git a/packages/nodes-base/credentials/MicrosoftTeamsOAuth2Api.credentials.ts b/packages/nodes-base/credentials/MicrosoftTeamsOAuth2Api.credentials.ts new file mode 100644 index 000000000..7b1d9da3d --- /dev/null +++ b/packages/nodes-base/credentials/MicrosoftTeamsOAuth2Api.credentials.ts @@ -0,0 +1,21 @@ +import { + ICredentialType, + NodePropertyTypes, +} from 'n8n-workflow'; + +export class MicrosoftTeamsOAuth2Api implements ICredentialType { + name = 'microsoftTeamsOAuth2Api'; + extends = [ + 'microsoftOAuth2Api', + ]; + displayName = 'Microsoft OAuth2 API'; + properties = [ + //https://docs.microsoft.com/en-us/azure/active-directory/develop/v2-permissions-and-consent + { + displayName: 'Scope', + name: 'scope', + type: 'hidden' as NodePropertyTypes, + default: 'openid offline_access User.ReadWrite.All Group.ReadWrite.All', + }, + ]; +} diff --git a/packages/nodes-base/nodes/Microsoft/Teams/ChannelDescription.ts b/packages/nodes-base/nodes/Microsoft/Teams/ChannelDescription.ts new file mode 100644 index 000000000..5509c5e87 --- /dev/null +++ b/packages/nodes-base/nodes/Microsoft/Teams/ChannelDescription.ts @@ -0,0 +1,381 @@ +import { + INodeProperties, + } from 'n8n-workflow'; + +export const channelOperations = [ + { + displayName: 'Operation', + name: 'operation', + type: 'options', + displayOptions: { + show: { + resource: [ + 'channel', + ], + }, + }, + options: [ + { + name: 'Create', + value: 'create', + description: 'Create a channel', + }, + { + name: 'Delete', + value: 'delete', + description: 'Delete a channel', + }, + { + name: 'Get', + value: 'get', + description: 'Get a channel', + }, + { + name: 'Get All', + value: 'getAll', + description: 'Get all channels', + }, + { + name: 'Update', + value: 'update', + description: 'Update a channel', + }, + ], + default: 'create', + description: 'The operation to perform.', + }, +] as INodeProperties[]; + +export const channelFields = [ + +/* -------------------------------------------------------------------------- */ +/* channel:create */ +/* -------------------------------------------------------------------------- */ + { + displayName: 'Team ID', + name: 'teamId', + required: true, + type: 'options', + typeOptions: { + loadOptionsMethod: 'getTeams', + }, + displayOptions: { + show: { + operation: [ + 'create', + ], + resource: [ + 'channel', + ], + }, + }, + default: '', + description: 'Team ID', + }, + { + displayName: 'Name', + name: 'name', + required: true, + type: 'string', + displayOptions: { + show: { + operation: [ + 'create', + ], + resource: [ + 'channel', + ], + }, + }, + default: '', + description: 'Channel name as it will appear to the user in Microsoft Teams.', + }, + { + displayName: 'Options', + name: 'options', + type: 'collection', + displayOptions: { + show: { + operation: [ + 'create', + ], + resource: [ + 'channel', + ], + }, + }, + default: {}, + placeholder: 'Add Field', + options: [ + { + displayName: 'Description', + name: 'description', + type: 'string', + typeOptions: { + alwaysOpenEditWindow: true, + }, + default: '', + description: `channel's description`, + }, + { + displayName: 'Type', + name: 'type', + type: 'options', + options: [ + { + name: 'Private', + value: 'private', + }, + { + name: 'Standard', + value: 'standard', + }, + ], + default: 'standard', + description: 'The type of the channel', + }, + ], + }, +/* -------------------------------------------------------------------------- */ +/* channel:delete */ +/* -------------------------------------------------------------------------- */ + { + displayName: 'Team ID', + name: 'teamId', + required: true, + type: 'options', + typeOptions: { + loadOptionsMethod: 'getTeams', + }, + displayOptions: { + show: { + operation: [ + 'delete', + ], + resource: [ + 'channel', + ], + }, + }, + default: '', + description: 'Team ID', + }, + { + displayName: 'Channel ID', + name: 'channelId', + type: 'options', + typeOptions: { + loadOptionsMethod: 'getChannels', + loadOptionsDependsOn: [ + 'teamId', + ], + }, + displayOptions: { + show: { + operation: [ + 'delete', + ], + resource: [ + 'channel', + ], + }, + }, + default: '', + description: 'channel ID', + }, +/* -------------------------------------------------------------------------- */ +/* channel:get */ +/* -------------------------------------------------------------------------- */ + { + displayName: 'Team ID', + name: 'teamId', + required: true, + type: 'options', + typeOptions: { + loadOptionsMethod: 'getTeams', + }, + displayOptions: { + show: { + operation: [ + 'get', + ], + resource: [ + 'channel', + ], + }, + }, + default: '', + description: 'Team ID', + }, + { + displayName: 'Channel ID', + name: 'channelId', + type: 'options', + typeOptions: { + loadOptionsMethod: 'getChannels', + loadOptionsDependsOn: [ + 'teamId', + ], + }, + displayOptions: { + show: { + operation: [ + 'get', + ], + resource: [ + 'channel', + ], + }, + }, + default: '', + description: 'channel ID', + }, +/* -------------------------------------------------------------------------- */ +/* channel:getAll */ +/* -------------------------------------------------------------------------- */ + { + displayName: 'Team ID', + name: 'teamId', + required: true, + type: 'options', + typeOptions: { + loadOptionsMethod: 'getTeams', + }, + displayOptions: { + show: { + operation: [ + 'getAll', + ], + resource: [ + 'channel', + ], + }, + }, + default: '', + description: 'Team ID', + }, + { + displayName: 'Return All', + name: 'returnAll', + type: 'boolean', + displayOptions: { + show: { + operation: [ + 'getAll', + ], + resource: [ + 'channel', + ], + }, + }, + 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: [ + 'channel', + ], + returnAll: [ + false, + ], + }, + }, + typeOptions: { + minValue: 1, + maxValue: 500, + }, + default: 100, + description: 'How many results to return.', + }, +/* -------------------------------------------------------------------------- */ +/* channel:update */ +/* -------------------------------------------------------------------------- */ + { + displayName: 'Team ID', + name: 'teamId', + required: true, + type: 'options', + typeOptions: { + loadOptionsMethod: 'getTeams', + }, + displayOptions: { + show: { + operation: [ + 'update', + ], + resource: [ + 'channel', + ], + }, + }, + default: '', + description: 'Team ID', + }, + { + displayName: 'Channel ID', + name: 'channelId', + type: 'options', + typeOptions: { + loadOptionsMethod: 'getChannels', + loadOptionsDependsOn: [ + 'teamId', + ], + }, + displayOptions: { + show: { + operation: [ + 'update', + ], + resource: [ + 'channel', + ], + }, + }, + default: '', + description: 'Channel ID', + }, + { + displayName: 'Update Fields', + name: 'updateFields', + type: 'collection', + displayOptions: { + show: { + operation: [ + 'update', + ], + resource: [ + 'channel', + ], + }, + }, + default: {}, + placeholder: 'Add Field', + options: [ + { + displayName: 'Name', + name: 'name', + type: 'string', + default: '', + description: 'Channel name as it will appear to the user in Microsoft Teams.', + }, + { + displayName: 'Description', + name: 'description', + type: 'string', + typeOptions: { + alwaysOpenEditWindow: true, + }, + default: '', + description: `channel's description`, + }, + ], + }, +] as INodeProperties[]; diff --git a/packages/nodes-base/nodes/Microsoft/Teams/ChannelMessageDescription.ts b/packages/nodes-base/nodes/Microsoft/Teams/ChannelMessageDescription.ts new file mode 100644 index 000000000..dead663ee --- /dev/null +++ b/packages/nodes-base/nodes/Microsoft/Teams/ChannelMessageDescription.ts @@ -0,0 +1,220 @@ +import { + INodeProperties, + } from 'n8n-workflow'; + +export const channelMessageOperations = [ + { + displayName: 'Operation', + name: 'operation', + type: 'options', + displayOptions: { + show: { + resource: [ + 'channelMessage', + ], + }, + }, + options: [ + { + name: 'Create', + value: 'create', + description: 'Create a message', + }, + { + name: 'Get All', + value: 'getAll', + description: 'Get all messages', + }, + ], + default: 'create', + description: 'The operation to perform.', + }, +] as INodeProperties[]; + +export const channelMessageFields = [ + +/* -------------------------------------------------------------------------- */ +/* channelMessage:create */ +/* -------------------------------------------------------------------------- */ + { + displayName: 'Team ID', + name: 'teamId', + required: true, + type: 'options', + typeOptions: { + loadOptionsMethod: 'getTeams', + }, + displayOptions: { + show: { + operation: [ + 'create', + ], + resource: [ + 'channelMessage', + ], + }, + }, + default: '', + description: 'Team ID', + }, + { + displayName: 'Channel ID', + name: 'channelId', + type: 'options', + typeOptions: { + loadOptionsMethod: 'getChannels', + loadOptionsDependsOn: [ + 'teamId', + ], + }, + displayOptions: { + show: { + operation: [ + 'create', + ], + resource: [ + 'channelMessage', + ], + }, + }, + default: '', + description: 'Channel ID', + }, + { + displayName: 'Message Type', + name: 'messageType', + required: true, + type: 'options', + options: [ + { + name: 'Text', + value: 'text', + }, + { + name: 'HTML', + value: 'html', + }, + ], + displayOptions: { + show: { + operation: [ + 'create', + ], + resource: [ + 'channelMessage', + ], + }, + }, + default: '', + description: 'The type of the content', + }, + { + displayName: 'Message', + name: 'message', + required: true, + type: 'string', + typeOptions: { + alwaysOpenEditWindow: true, + }, + displayOptions: { + show: { + operation: [ + 'create', + ], + resource: [ + 'channelMessage', + ], + }, + }, + default: '', + description: 'The content of the item.', + }, +/* -------------------------------------------------------------------------- */ +/* channelMessage:getAll */ +/* -------------------------------------------------------------------------- */ + { + displayName: 'Team ID', + name: 'teamId', + required: true, + type: 'options', + typeOptions: { + loadOptionsMethod: 'getTeams', + }, + displayOptions: { + show: { + operation: [ + 'getAll', + ], + resource: [ + 'channelMessage', + ], + }, + }, + default: '', + description: 'Team ID', + }, + { + displayName: 'Channel ID', + name: 'channelId', + type: 'options', + typeOptions: { + loadOptionsMethod: 'getChannels', + loadOptionsDependsOn: [ + 'teamId', + ], + }, + displayOptions: { + show: { + operation: [ + 'getAll', + ], + resource: [ + 'channelMessage', + ], + }, + }, + default: '', + description: 'Channel ID', + }, + { + displayName: 'Return All', + name: 'returnAll', + type: 'boolean', + displayOptions: { + show: { + operation: [ + 'getAll', + ], + resource: [ + 'channelMessage', + ], + }, + }, + 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: [ + 'channelMessage', + ], + returnAll: [ + false, + ], + }, + }, + typeOptions: { + minValue: 1, + maxValue: 500, + }, + default: 100, + description: 'How many results to return.', + }, +] as INodeProperties[]; diff --git a/packages/nodes-base/nodes/Microsoft/Teams/GenericFunctions.ts b/packages/nodes-base/nodes/Microsoft/Teams/GenericFunctions.ts new file mode 100644 index 000000000..01ff3b964 --- /dev/null +++ b/packages/nodes-base/nodes/Microsoft/Teams/GenericFunctions.ts @@ -0,0 +1,79 @@ +import { + OptionsWithUri, + } from 'request'; + +import { + IExecuteFunctions, + IExecuteSingleFunctions, + ILoadOptionsFunctions, +} from 'n8n-core'; + +import { + IDataObject, +} from 'n8n-workflow'; + +export async function microsoftApiRequest(this: IExecuteFunctions | IExecuteSingleFunctions | ILoadOptionsFunctions, method: string, resource: string, body: any = {}, qs: IDataObject = {}, uri?: string, headers: IDataObject = {}): Promise { // tslint:disable-line:no-any + const options: OptionsWithUri = { + headers: { + 'Content-Type': 'application/json', + }, + method, + body, + qs, + uri: uri || `https://graph.microsoft.com${resource}`, + json: true + }; + try { + if (Object.keys(headers).length !== 0) { + options.headers = Object.assign({}, options.headers, headers); + } + //@ts-ignore + return await this.helpers.requestOAuth2.call(this, 'microsoftTeamsOAuth2Api', options); + } catch (error) { + if (error.response && error.response.body && error.response.body.error && error.response.body.error.message) { + // Try to return the error prettier + throw new Error(`Microsoft error response [${error.statusCode}]: ${error.response.body.error.message}`); + } + throw 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; + + do { + responseData = await microsoftApiRequest.call(this, method, endpoint, body, query, uri); + uri = responseData['@odata.nextLink']; + returnData.push.apply(returnData, responseData[propertyName]); + if (query.limit && query.limit <= returnData.length) { + return returnData; + } + } while ( + responseData['@odata.nextLink'] !== undefined + ); + + return returnData; +} + +export async function microsoftApiRequestAllItemsSkip(this: IExecuteFunctions | ILoadOptionsFunctions, propertyName: string ,method: string, endpoint: string, body: any = {}, query: IDataObject = {}): Promise { // tslint:disable-line:no-any + + const returnData: IDataObject[] = []; + + let responseData; + query['$top'] = 100; + query['$skip'] = 0; + + do { + responseData = await microsoftApiRequest.call(this, method, endpoint, body, query); + query['$skip'] += query['$top']; + returnData.push.apply(returnData, responseData[propertyName]); + } while ( + responseData['value'].length !== 0 + ); + + return returnData; +} diff --git a/packages/nodes-base/nodes/Microsoft/Teams/MicrosoftTeams.node.ts b/packages/nodes-base/nodes/Microsoft/Teams/MicrosoftTeams.node.ts new file mode 100644 index 000000000..b14142e48 --- /dev/null +++ b/packages/nodes-base/nodes/Microsoft/Teams/MicrosoftTeams.node.ts @@ -0,0 +1,217 @@ +import { + IExecuteFunctions, +} from 'n8n-core'; + +import { + IDataObject, + ILoadOptionsFunctions, + INodeExecutionData, + INodePropertyOptions, + INodeType, + INodeTypeDescription, +} from 'n8n-workflow'; + +import { + microsoftApiRequest, + microsoftApiRequestAllItems, +} from './GenericFunctions'; + +import { + channelFields, + channelOperations, +} from './ChannelDescription'; + +import { + channelMessageFields, + channelMessageOperations, +} from './ChannelMessageDescription'; + +export class MicrosoftTeams implements INodeType { + description: INodeTypeDescription = { + displayName: 'Microsoft Teams', + name: 'microsoftTeams', + icon: 'file:teams.png', + group: ['input'], + version: 1, + subtitle: '={{$parameter["operation"] + ": " + $parameter["resource"]}}', + description: 'Consume Microsoft Teams API', + defaults: { + name: 'Microsoft Teams', + color: '#555cc7', + }, + inputs: ['main'], + outputs: ['main'], + credentials: [ + { + name: 'microsoftTeamsOAuth2Api', + required: true, + }, + ], + properties: [ + { + displayName: 'Resource', + name: 'resource', + type: 'options', + options: [ + { + name: 'Channel', + value: 'channel', + }, + { + name: 'Channel Message (Beta)', + value: 'channelMessage', + }, + ], + default: 'channel', + description: 'The resource to operate on.', + }, + // CHANNEL + ...channelOperations, + ...channelFields, + /// MESSAGE + ...channelMessageOperations, + ...channelMessageFields, + ], + }; + + methods = { + loadOptions: { + // Get all the team's channels to display them to user so that he can + // select them easily + async getChannels(this: ILoadOptionsFunctions): Promise { + const returnData: INodePropertyOptions[] = []; + const teamId = this.getCurrentNodeParameter('teamId') as string; + const { value } = await microsoftApiRequest.call(this, 'GET', `/v1.0/teams/${teamId}/channels`); + for (const channel of value) { + const channelName = channel.displayName; + const channelId = channel.id; + returnData.push({ + name: channelName, + value: channelId, + }); + } + return returnData; + }, + // Get all the teams to display them to user so that he can + // select them easily + async getTeams(this: ILoadOptionsFunctions): Promise { + const returnData: INodePropertyOptions[] = []; + const { value } = await microsoftApiRequest.call(this, 'GET', '/v1.0/me/joinedTeams'); + for (const team of value) { + const teamName = team.displayName; + const teamId = team.id; + returnData.push({ + name: teamName, + value: teamId, + }); + } + 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 === 'channel') { + //https://docs.microsoft.com/en-us/graph/api/channel-post?view=graph-rest-beta&tabs=http + if (operation === 'create') { + const teamId = this.getNodeParameter('teamId', i) as string; + const name = this.getNodeParameter('name', i) as string; + const options = this.getNodeParameter('options', i) as IDataObject; + const body: IDataObject = { + displayName: name, + }; + if (options.description) { + body.description = options.description as string; + } + if (options.type) { + body.membershipType = options.type as string; + } + responseData = await microsoftApiRequest.call(this, 'POST', `/v1.0/teams/${teamId}/channels`, body); + } + //https://docs.microsoft.com/en-us/graph/api/channel-delete?view=graph-rest-beta&tabs=http + if (operation === 'delete') { + const teamId = this.getNodeParameter('teamId', i) as string; + const channelId = this.getNodeParameter('channelId', i) as string; + responseData = await microsoftApiRequest.call(this, 'DELETE', `/v1.0/teams/${teamId}/channels/${channelId}`); + responseData = { success: true }; + } + //https://docs.microsoft.com/en-us/graph/api/channel-get?view=graph-rest-beta&tabs=http + if (operation === 'get') { + const teamId = this.getNodeParameter('teamId', i) as string; + const channelId = this.getNodeParameter('channelId', i) as string; + responseData = await microsoftApiRequest.call(this, 'GET', `/v1.0/teams/${teamId}/channels/${channelId}`); + } + //https://docs.microsoft.com/en-us/graph/api/channel-list?view=graph-rest-beta&tabs=http + if (operation === 'getAll') { + const teamId = this.getNodeParameter('teamId', i) as string; + const returnAll = this.getNodeParameter('returnAll', 0) as boolean; + if (returnAll) { + responseData = await microsoftApiRequestAllItems.call(this, 'value', 'GET', `/v1.0/teams/${teamId}/channels`); + } else { + qs.limit = this.getNodeParameter('limit', 0) as number; + responseData = await microsoftApiRequestAllItems.call(this, 'value', 'GET', `/v1.0/teams/${teamId}/channels`, {}); + responseData = responseData.splice(0, qs.limit); + } + } + //https://docs.microsoft.com/en-us/graph/api/channel-patch?view=graph-rest-beta&tabs=http + if (operation === 'update') { + const teamId = this.getNodeParameter('teamId', i) as string; + const channelId = this.getNodeParameter('channelId', i) as string; + const updateFields = this.getNodeParameter('updateFields', i) as IDataObject; + const body: IDataObject = {}; + if (updateFields.name) { + body.displayName = updateFields.name as string; + } + if (updateFields.description) { + body.description = updateFields.description as string; + } + responseData = await microsoftApiRequest.call(this, 'PATCH', `/v1.0/teams/${teamId}/channels/${channelId}`, body); + responseData = { success: true }; + } + } + if (resource === 'channelMessage') { + //https://docs.microsoft.com/en-us/graph/api/channel-post-messages?view=graph-rest-beta&tabs=http + if (operation === 'create') { + const teamId = this.getNodeParameter('teamId', i) as string; + const channelId = this.getNodeParameter('channelId', i) as string; + const messageType = this.getNodeParameter('messageType', i) as string; + const message = this.getNodeParameter('message', i) as string; + const body: IDataObject = { + body: { + contentType: messageType, + content: message, + } + }; + responseData = await microsoftApiRequest.call(this, 'POST', `/beta/teams/${teamId}/channels/${channelId}/messages`, body); + } + //https://docs.microsoft.com/en-us/graph/api/channel-list-messages?view=graph-rest-beta&tabs=http + if (operation === 'getAll') { + const teamId = this.getNodeParameter('teamId', i) as string; + const channelId = this.getNodeParameter('channelId', i) as string; + const returnAll = this.getNodeParameter('returnAll', 0) as boolean; + if (returnAll) { + responseData = await microsoftApiRequestAllItems.call(this, 'value', 'GET', `/beta/teams/${teamId}/channels/${channelId}/messages`); + } else { + qs.limit = this.getNodeParameter('limit', 0) as number; + responseData = await microsoftApiRequestAllItems.call(this, 'value', 'GET', `/beta/teams/${teamId}/channels/${channelId}/messages`, {}); + responseData = responseData.splice(0, qs.limit); + } + } + } + if (Array.isArray(responseData)) { + returnData.push.apply(returnData, responseData as IDataObject[]); + } else { + returnData.push(responseData as IDataObject); + } + } + return [this.helpers.returnJsonArray(returnData)]; + } +} diff --git a/packages/nodes-base/nodes/Microsoft/Teams/teams.png b/packages/nodes-base/nodes/Microsoft/Teams/teams.png new file mode 100644 index 0000000000000000000000000000000000000000..89dcee67ffb78902c8479daeb317c871852aa53c GIT binary patch literal 6376 zcmZ`-1yogAx887Q1QjKu1w^{LOQ}OQ0s@CacXx*(2qK5}aOiH37U`CdmhMuz5BT8T z`|kbU9sgT#RhGrWA;kdz08d^{M*UXm-%d3wjN7-mLq+SY zK(mlkk_3Q?7+j=+=3okC^R#okV*w(bLbs|N)YXLA)6Ul3MaWZ><`0C>t$t@_r=k7>;%XyG zqpPG!E$!e8rRHVhVdJ0?!=a|87I8K+7gCpb`4|0mB}!xI>gp)O&hFvi!REou=HP6> z&M7D;$j-sV&c(%gi(qx}vUfG{WVLsp{WHk_#*u-#K%A`{U9BAKsqf;Nm^!$*iqg>B zCHi~(S*NR&`9GQLUH;nE?FQNJJnWoo9PIy%2KBW1e`t4}KWTsL>(6u|cfo{IoUNd@ z1>cn=#wqegg8yUtr@TM$>QEO4Temy7mc5m$7}sBvf0_S-b(dR6+QHVrS;Nr;0=-M} zZ_~fgs#cy*TU{9|JE*4Q!%}I=$e@F5f(+-%Z;1*iD>wk*0<# z#pkl+Xk{3x-l)QeQj4SI)C;bcCS3^mr{LIoAx1!spK&CN(ttfHU;* zNe>?`vC?HNR=&_zof4B0&)$TDw99882rxH0Iv#v&&U<2P?amvm*ho5Gl->VKNPcNV z>+Q>v{qphIFJBlff8O+<@bMfs-Piwic2s{MuzS!Z(7U!S?t9qFZYY(ah3#IWUrI3f z`x7wi#4X@ypaN##<;h-_$^ev@{=xEqCYvc{C;H15AiP7~5m`S-{QZ_v?=i zX0;BxI>JM*g<2Y2qUXZ|JKocc@RO~bnZ3z7ZDrt;C)g2qZ(>eE@q!Q*>STFbeLrA? zq($zgxf!IxQdW=Ld1Azf$Y@Oo(K1YA)!g9}K{|`QA3A*wpUI3O<#1}!u;J#o=->LO z_pzln3DJL48UGAOk|fvJe}`zQz;AcXbbF$pgygxYNJl(THD!^0T0*5iHdgFYv#7PI z$B20GEO!xU!79aKhZzY`9d|D*^GDuoJbsa z6A-$Anpxa9m1$s_`NyH$;c;F2&p3T&1Y!%yf|JqjAMie2f_#fN{+RPr<&dGq>j)kX zC(!P-Nd`qsLG@%j;nRy=tsu$VBm3j!1%QU6V;N6A%?#1(M3NB4!SYf~6DKP_$^%^u zFe(nTq&FMEuvSPOWxX$DLj`(eTql0kQj zRQVQd%VX|4alJdRA#_G5FuLc{AdPYu$v9X%IMjlh967ctmBCnK28khEE0#aV8^cnx zVhLV_3OrvR(0h!g^Gh%-wZZD+-o1n1>Ok<`8}Db^Npphx7amjr$Px97*rc~_)t*y_wFg@b zuO4}Lo1E*hr&A4k%$>vu`TP1>1;2i(qiCx`Xi@MI(u-g2&17}L+BQL(6BJ`sUd^N+ zN6q>viG-9}T-48*c-?DZb9c(KY!6!N<8Lo3+ub1EMt)TQE}nqE>Rd^8DsPu9;B$J?(^-$Amm zL!O{#;K#d_!`WF(rpPw0;bw(3=Vys~KaY_EiKmn#;upKz?aEqDjWfHt-JUcCSTD4D z*arMO89FgJ<#Ah;TcQ~5dm~F)AnVtP=WJ+)K$p;<$E{?bml^}BlW3IXd}FVP#U#kK zQqOsb#V4-RL&pox4o2-Fp?0(Fn+Y|_yrvGufEZD#(`IaTY4#CReiZu7vt;HggiWl%69i6ZTT)b|&P~+NeCZ^B^Bl?R#jNK0fYF6 zS5GY6fs&tnmatA?5NmGk_l1!q$$n8@VX{JxZv%?7o>o-VGuF)VE2dHMO(Z*tS=znHd7gD6T`prEYXS}8owB<^De8cN=exjvIM;n83@BCr)!cFn_l_B0%XH z+W$(n(SQxY3bL!;`|@e~^AA48d>rB*sHJ;e<>|^BhNWG#b{sj8FL0})-Eu()-FQG> zzz(^4zvjTdpF3Ik@mo_R&f>?vt2{5-#6{0$)6;_8y{oD=mVeFL5l;iUuQHyNEA`mv z+K^2NQTLkbU)DpnMd?1M#f6gvUP0gV7% z^0u4Wlr%2cn41?*@`PG6=>@{VFCOCsL}kYl9Ucz4gpv4gIYb1(A2)srg2D7MZBT}b z>mKzmq0}Tg8^O!+ou9=i_o2jsXln#P8m*)RRnya%DXGjw3Jk(NT<6U$-ul3x>HT46 z+d@{yaC3z*mmyK)Q`7srIS)|itJf*-X$MDGZY_kN{eDyOV}xNeRZ~5Vi#lS%OE9T? zcA7)vH~)>VXHeDqU{>#uW#af6tPFICNrk1ZrejG?JpJqLGv)l6W($s~j_uK$%9K{> zGarw+;$O`kiI(c)s)tTk#2ToeU{F6fB zdqRv(Im4;C)1Dv)vXXrE!ilm59#p~W`TLe;UsL&;FFYD(N5`Z0@@1K>0W7V#cZ_RN z^sRjiGL3w#6bLz?D&p`JeX()@&N z`E$fmW804eJZ==bN0Y;1JBZ~(6P5h><=7}W%&8l1T%xsfl+#^!NEt@rdQ3o3Ufp`9 zOhkH#W?w3YbDCn?VoN{NEgZ91TKZS{l#~8UBf5mJBo<#q zPmL;z@6%8T9^9>{Sjaxu*gN+-^ZWgiI^T>5hVVV!q>4A-6iJRKDhdlNyBI-5V83~x z?9Ci3h4S{RliI-e)1cMJA?qs|XGJkxwWcEL>DYlmA$1z-UV+5zuuQ?YAn4pDb5v3iRc+qs4hJc5gJRT~G!#t5BJp7bg11$6`5KzCE! z@}d;1uXWp1DN!&gjCLIVd;G!{G$~tyCaP?4IV2i<_X3L5&G| zmmmIdv2PEC*?>)JL)sxG6`2yXydphp-#5m>o|(gmm1ILWKx{K>i!nf+O<@b^$pp!p z)kLJRgxoWo{W7vm!2c?~>!~@TMdGepE1heBfdCJpWZm0;+I}r4H(~jg3FM*f#_2RA!4<1u0LR>G1gXZ_Z;}bdrnv+H%SWNJVOx zT&(3H5|5dAKyssY2-1YmIpP{=b=EyKW;ZWiqF%%Anvq}@;zfDX9qXM&tQb;x2}z*> zf4|F^-n~NsHr&HXOM)$Os$rNm616(656&IO{_h2 zKue>Pe}1mU-}7Be>W$b#_n zV*HY=1jF5IRW)IUZ^q%3+d5>T+JagxyG(OB91DZa?=n1qep9)U&~YFxA|k}lzO6@| zs0)fFD&C7`n77`i5pb297vR)@I8_6>*AzPaS(#R5pq=Kn77g*#B~Y#B>Z~i0|`00 zyOKSlqSt34%ZcbWg0XZ_5x5$rvM&Bj*mOJ3Nm<@Zn$Kt#$;V@@Hw3pg)Ahb|d8ETr zyX>eP@)EiK19HW_Y~MXwpZrU_^F>bwd-(Z-))+tWdlu%6kwd<}zZ{C5A@3(J?Y`>A zOh&!^P-x`rYGU8eyiy091`~V-`16DZ+M5nt{gw}`kqYEP~rXh2)r4X)tC~+x^ z!8vxyGN;P*Hl|s3QA0>bm@5S*YF~azS_j+Qs;2YiFh^quWZvL1uB`;Uff6@THn4C} zE-;inaboBNySicPlX^6{!xcNw8Z@p_D0OuVa^3{N#NajZVp;K?d}eu&n$!k17DIl| zlBHsm?EgSW#h5J}G(jt;pP$e%Uo&do8Q7JE6|ApUf-q$%7Sz?;wr+lqy8bbt+!hTi zLZ08&hQ3|tyx4I~Oc`&1FW$J9g-Pl8`9Wgd{!i05+-RM?s zn1UM0X0!UWrV`x;A_?9UKzL==`mVRHy8o{OePppZ|6~CUxrm#a(eTqz*YVXs-v>9} zJWh?izH7md4V3@-O2Yn46MhgOh}1@l+y+t=tZPN(F=MucF?DXm86!H~yhZL?(DiY3 zzosM@5ymz6^|A6+PQ+LnZx3`;8V#db#E30l8J^Z<@d&e`vPz%D%i$|zTB5P661*9~ z1X=g8z^i=$<$SYKj$|T=$Y>&ECUKH)gJE%kO$edSFW<hEn%ZXz~%>fzB}lluSB|Sgwj^KmOh^F zH)->!F_JEH3pbIn6(@?WXX>5vG$7Kj$n+VCb5CB8PhYXlt(bZELZuyJj*1JM!cuuCFizCi3fAaD-F*5NQ%gpJUY z-kjZEiwtN69-#{ggegT!fulzr?azhIHkDI_M~db7N!!m)bm+N#Aq1uHl937b{E$R0 zb;ZNo@y(5W*jYW?t9E74yg$^dE`NCnNpXmy1!yYbRD9wilx#G^JI`S6d zrVB%4hZ1e54MHDy{s6vy;(zA!kOfDPvQ)5Jz`1Y+novJ88;NSi%%4K4JXyhO55ae# zvd4dTPLw_3w9iFvpb-VWzwEQ`CBv`nNw#m-Zl07l2tT+ond66S?nWrJp3GQMBn;}R zLa4akHru+hesQZ$>gIQ-8?W`A!vV4Cr_mTV)+EFno~cldiex3F2$e>JYl;N#XxTpW z-RQouKRV}SiMa6i=v}LHmNdG1sgQXUe=ynJWc!tVXHZ_SV^U)p4}LHKQ%tn)ypCd2 z@j7QyEr4{tKd9L@DD>l(V!Nu1=eTY|CV-|mJKu$!sy|&TNQRql4dXTp3;88a*=iac z+e~%bcT4kv`XXZ%mY?_VdoxY4l#Q8lF5_iOUOf`^x~dbq$g+|?S4o?S7ANCD^BI_) zgSDzabC(5DVR72sI~dl-HK@kF`Fd7YJwpbN_GCMvd@?_3NKjB00RMOWHdPM)?Wz(H f{q;9H+RgowaAmUj-l5~W`+NBp$}*)=#=-vsDHfDG literal 0 HcmV?d00001 diff --git a/packages/nodes-base/package.json b/packages/nodes-base/package.json index 72caf8ea7..7023b26bb 100644 --- a/packages/nodes-base/package.json +++ b/packages/nodes-base/package.json @@ -113,6 +113,7 @@ "dist/credentials/MicrosoftOAuth2Api.credentials.js", "dist/credentials/MicrosoftOneDriveOAuth2Api.credentials.js", "dist/credentials/MicrosoftSql.credentials.js", + "dist/credentials/MicrosoftTeamsOAuth2Api.credentials.js", "dist/credentials/MoceanApi.credentials.js", "dist/credentials/MondayComApi.credentials.js", "dist/credentials/MongoDb.credentials.js", @@ -289,6 +290,7 @@ "dist/nodes/Microsoft/Excel/MicrosoftExcel.node.js", "dist/nodes/Microsoft/OneDrive/MicrosoftOneDrive.node.js", "dist/nodes/Microsoft/Sql/MicrosoftSql.node.js", + "dist/nodes/Microsoft/Teams/MicrosoftTeams.node.js", "dist/nodes/MoveBinaryData.node.js", "dist/nodes/Mocean/Mocean.node.js", "dist/nodes/MondayCom/MondayCom.node.js", From a2ee2773dbd869001a35e775ce09be5ef2257bfc Mon Sep 17 00:00:00 2001 From: Jan Oberhauser Date: Sun, 13 Sep 2020 11:09:17 +0200 Subject: [PATCH 019/284] :zap: Minor improvements to Microsoft Teams Node --- .../Microsoft/Teams/ChannelDescription.ts | 36 ++++++++++-------- .../Teams/ChannelMessageDescription.ts | 14 +++---- .../nodes/Microsoft/Teams/GenericFunctions.ts | 6 +-- .../Microsoft/Teams/MicrosoftTeams.node.ts | 8 ++-- .../nodes/Microsoft/Teams/teams.png | Bin 6376 -> 725 bytes 5 files changed, 34 insertions(+), 30 deletions(-) diff --git a/packages/nodes-base/nodes/Microsoft/Teams/ChannelDescription.ts b/packages/nodes-base/nodes/Microsoft/Teams/ChannelDescription.ts index 5509c5e87..c771fba35 100644 --- a/packages/nodes-base/nodes/Microsoft/Teams/ChannelDescription.ts +++ b/packages/nodes-base/nodes/Microsoft/Teams/ChannelDescription.ts @@ -1,6 +1,6 @@ import { INodeProperties, - } from 'n8n-workflow'; +} from 'n8n-workflow'; export const channelOperations = [ { @@ -48,9 +48,9 @@ export const channelOperations = [ export const channelFields = [ -/* -------------------------------------------------------------------------- */ -/* channel:create */ -/* -------------------------------------------------------------------------- */ + /* -------------------------------------------------------------------------- */ + /* channel:create */ + /* -------------------------------------------------------------------------- */ { displayName: 'Team ID', name: 'teamId', @@ -136,9 +136,10 @@ export const channelFields = [ }, ], }, -/* -------------------------------------------------------------------------- */ -/* channel:delete */ -/* -------------------------------------------------------------------------- */ + + /* -------------------------------------------------------------------------- */ + /* channel:delete */ + /* -------------------------------------------------------------------------- */ { displayName: 'Team ID', name: 'teamId', @@ -183,9 +184,10 @@ export const channelFields = [ default: '', description: 'channel ID', }, -/* -------------------------------------------------------------------------- */ -/* channel:get */ -/* -------------------------------------------------------------------------- */ + + /* -------------------------------------------------------------------------- */ + /* channel:get */ + /* -------------------------------------------------------------------------- */ { displayName: 'Team ID', name: 'teamId', @@ -230,9 +232,10 @@ export const channelFields = [ default: '', description: 'channel ID', }, -/* -------------------------------------------------------------------------- */ -/* channel:getAll */ -/* -------------------------------------------------------------------------- */ + + /* -------------------------------------------------------------------------- */ + /* channel:getAll */ + /* -------------------------------------------------------------------------- */ { displayName: 'Team ID', name: 'teamId', @@ -295,9 +298,10 @@ export const channelFields = [ default: 100, description: 'How many results to return.', }, -/* -------------------------------------------------------------------------- */ -/* channel:update */ -/* -------------------------------------------------------------------------- */ + + /* -------------------------------------------------------------------------- */ + /* channel:update */ + /* -------------------------------------------------------------------------- */ { displayName: 'Team ID', name: 'teamId', diff --git a/packages/nodes-base/nodes/Microsoft/Teams/ChannelMessageDescription.ts b/packages/nodes-base/nodes/Microsoft/Teams/ChannelMessageDescription.ts index dead663ee..61879542a 100644 --- a/packages/nodes-base/nodes/Microsoft/Teams/ChannelMessageDescription.ts +++ b/packages/nodes-base/nodes/Microsoft/Teams/ChannelMessageDescription.ts @@ -1,6 +1,6 @@ import { INodeProperties, - } from 'n8n-workflow'; +} from 'n8n-workflow'; export const channelMessageOperations = [ { @@ -33,9 +33,9 @@ export const channelMessageOperations = [ export const channelMessageFields = [ -/* -------------------------------------------------------------------------- */ -/* channelMessage:create */ -/* -------------------------------------------------------------------------- */ + /* -------------------------------------------------------------------------- */ + /* channelMessage:create */ + /* -------------------------------------------------------------------------- */ { displayName: 'Team ID', name: 'teamId', @@ -129,9 +129,9 @@ export const channelMessageFields = [ default: '', description: 'The content of the item.', }, -/* -------------------------------------------------------------------------- */ -/* channelMessage:getAll */ -/* -------------------------------------------------------------------------- */ + /* -------------------------------------------------------------------------- */ + /* channelMessage:getAll */ + /* -------------------------------------------------------------------------- */ { displayName: 'Team ID', name: 'teamId', diff --git a/packages/nodes-base/nodes/Microsoft/Teams/GenericFunctions.ts b/packages/nodes-base/nodes/Microsoft/Teams/GenericFunctions.ts index 01ff3b964..f807dba6e 100644 --- a/packages/nodes-base/nodes/Microsoft/Teams/GenericFunctions.ts +++ b/packages/nodes-base/nodes/Microsoft/Teams/GenericFunctions.ts @@ -1,6 +1,6 @@ import { OptionsWithUri, - } from 'request'; +} from 'request'; import { IExecuteFunctions, @@ -38,7 +38,7 @@ export async function microsoftApiRequest(this: IExecuteFunctions | IExecuteSing } } -export async function microsoftApiRequestAllItems(this: IExecuteFunctions | ILoadOptionsFunctions, propertyName: string ,method: string, endpoint: string, body: any = {}, query: IDataObject = {}): Promise { // tslint:disable-line:no-any +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[] = []; @@ -59,7 +59,7 @@ export async function microsoftApiRequestAllItems(this: IExecuteFunctions | ILoa return returnData; } -export async function microsoftApiRequestAllItemsSkip(this: IExecuteFunctions | ILoadOptionsFunctions, propertyName: string ,method: string, endpoint: string, body: any = {}, query: IDataObject = {}): Promise { // tslint:disable-line:no-any +export async function microsoftApiRequestAllItemsSkip(this: IExecuteFunctions | ILoadOptionsFunctions, propertyName: string, method: string, endpoint: string, body: any = {}, query: IDataObject = {}): Promise { // tslint:disable-line:no-any const returnData: IDataObject[] = []; diff --git a/packages/nodes-base/nodes/Microsoft/Teams/MicrosoftTeams.node.ts b/packages/nodes-base/nodes/Microsoft/Teams/MicrosoftTeams.node.ts index b14142e48..a8677b96e 100644 --- a/packages/nodes-base/nodes/Microsoft/Teams/MicrosoftTeams.node.ts +++ b/packages/nodes-base/nodes/Microsoft/Teams/MicrosoftTeams.node.ts @@ -152,11 +152,11 @@ export class MicrosoftTeams implements INodeType { //https://docs.microsoft.com/en-us/graph/api/channel-list?view=graph-rest-beta&tabs=http if (operation === 'getAll') { const teamId = this.getNodeParameter('teamId', i) as string; - const returnAll = this.getNodeParameter('returnAll', 0) as boolean; + const returnAll = this.getNodeParameter('returnAll', i) as boolean; if (returnAll) { responseData = await microsoftApiRequestAllItems.call(this, 'value', 'GET', `/v1.0/teams/${teamId}/channels`); } else { - qs.limit = this.getNodeParameter('limit', 0) as number; + qs.limit = this.getNodeParameter('limit', i) as number; responseData = await microsoftApiRequestAllItems.call(this, 'value', 'GET', `/v1.0/teams/${teamId}/channels`, {}); responseData = responseData.splice(0, qs.limit); } @@ -196,11 +196,11 @@ export class MicrosoftTeams implements INodeType { if (operation === 'getAll') { const teamId = this.getNodeParameter('teamId', i) as string; const channelId = this.getNodeParameter('channelId', i) as string; - const returnAll = this.getNodeParameter('returnAll', 0) as boolean; + const returnAll = this.getNodeParameter('returnAll', i) as boolean; if (returnAll) { responseData = await microsoftApiRequestAllItems.call(this, 'value', 'GET', `/beta/teams/${teamId}/channels/${channelId}/messages`); } else { - qs.limit = this.getNodeParameter('limit', 0) as number; + qs.limit = this.getNodeParameter('limit', i) as number; responseData = await microsoftApiRequestAllItems.call(this, 'value', 'GET', `/beta/teams/${teamId}/channels/${channelId}/messages`, {}); responseData = responseData.splice(0, qs.limit); } diff --git a/packages/nodes-base/nodes/Microsoft/Teams/teams.png b/packages/nodes-base/nodes/Microsoft/Teams/teams.png index 89dcee67ffb78902c8479daeb317c871852aa53c..6ccb7ac5dd7f5b4c53a48673be5145489134c0c2 100644 GIT binary patch literal 725 zcmeAS@N?(olHy`uVBq!ia0vp^HXzKw3=&b&bO2IM0(?STfpkpv0XT}w-VbC)W*>~r zIRs*40?C8XPz490GWSJi9fT+b3P$IEq~Z%s#$|vM15tDqn1o2>9Eiz17?T4+ky!_U ziX#dR0m-C-(+~sWvyKKQ9E-|17?*Pds5de9WLOG_kyLONNdEu-|IND}@817-{pNda z_3`3{llisBfB*S+<;J^1$Di-q|0K2KNPOPGFJFJ(d-&i%}?#hdfucAtO$zxG-}LG;Q5Imw(C@?4b@%83Z^2gEjy=SkUDq;+2eSfy?!`Bb)@e);>X3tbuo?4vC?{!=q9xEE5c6_f! z&Ea{!Umnic*YU^jit(vUO9kTm{v4gu*LHaI1@_A+^Vl5!GHHIf`ohw9PiBbwhIjKH zs;qB3AoNt#?BPYZM;{rvAMn>3-DE0PGR)?#*~xs(){UI5 z1$I=(Oeu~M=>7j8H=}r0)6qYDuSGU#oC%(7dikXg3*Y_3z!~bMTCTGTdQP7Xc~mCF cSNmGHpfIFny7v)FV7xJSy85}Sb4q9e0OFHh?f?J) literal 6376 zcmZ`-1yogAx887Q1QjKu1w^{LOQ}OQ0s@CacXx*(2qK5}aOiH37U`CdmhMuz5BT8T z`|kbU9sgT#RhGrWA;kdz08d^{M*UXm-%d3wjN7-mLq+SY zK(mlkk_3Q?7+j=+=3okC^R#okV*w(bLbs|N)YXLA)6Ul3MaWZ><`0C>t$t@_r=k7>;%XyG zqpPG!E$!e8rRHVhVdJ0?!=a|87I8K+7gCpb`4|0mB}!xI>gp)O&hFvi!REou=HP6> z&M7D;$j-sV&c(%gi(qx}vUfG{WVLsp{WHk_#*u-#K%A`{U9BAKsqf;Nm^!$*iqg>B zCHi~(S*NR&`9GQLUH;nE?FQNJJnWoo9PIy%2KBW1e`t4}KWTsL>(6u|cfo{IoUNd@ z1>cn=#wqegg8yUtr@TM$>QEO4Temy7mc5m$7}sBvf0_S-b(dR6+QHVrS;Nr;0=-M} zZ_~fgs#cy*TU{9|JE*4Q!%}I=$e@F5f(+-%Z;1*iD>wk*0<# z#pkl+Xk{3x-l)QeQj4SI)C;bcCS3^mr{LIoAx1!spK&CN(ttfHU;* zNe>?`vC?HNR=&_zof4B0&)$TDw99882rxH0Iv#v&&U<2P?amvm*ho5Gl->VKNPcNV z>+Q>v{qphIFJBlff8O+<@bMfs-Piwic2s{MuzS!Z(7U!S?t9qFZYY(ah3#IWUrI3f z`x7wi#4X@ypaN##<;h-_$^ev@{=xEqCYvc{C;H15AiP7~5m`S-{QZ_v?=i zX0;BxI>JM*g<2Y2qUXZ|JKocc@RO~bnZ3z7ZDrt;C)g2qZ(>eE@q!Q*>STFbeLrA? zq($zgxf!IxQdW=Ld1Azf$Y@Oo(K1YA)!g9}K{|`QA3A*wpUI3O<#1}!u;J#o=->LO z_pzln3DJL48UGAOk|fvJe}`zQz;AcXbbF$pgygxYNJl(THD!^0T0*5iHdgFYv#7PI z$B20GEO!xU!79aKhZzY`9d|D*^GDuoJbsa z6A-$Anpxa9m1$s_`NyH$;c;F2&p3T&1Y!%yf|JqjAMie2f_#fN{+RPr<&dGq>j)kX zC(!P-Nd`qsLG@%j;nRy=tsu$VBm3j!1%QU6V;N6A%?#1(M3NB4!SYf~6DKP_$^%^u zFe(nTq&FMEuvSPOWxX$DLj`(eTql0kQj zRQVQd%VX|4alJdRA#_G5FuLc{AdPYu$v9X%IMjlh967ctmBCnK28khEE0#aV8^cnx zVhLV_3OrvR(0h!g^Gh%-wZZD+-o1n1>Ok<`8}Db^Npphx7amjr$Px97*rc~_)t*y_wFg@b zuO4}Lo1E*hr&A4k%$>vu`TP1>1;2i(qiCx`Xi@MI(u-g2&17}L+BQL(6BJ`sUd^N+ zN6q>viG-9}T-48*c-?DZb9c(KY!6!N<8Lo3+ub1EMt)TQE}nqE>Rd^8DsPu9;B$J?(^-$Amm zL!O{#;K#d_!`WF(rpPw0;bw(3=Vys~KaY_EiKmn#;upKz?aEqDjWfHt-JUcCSTD4D z*arMO89FgJ<#Ah;TcQ~5dm~F)AnVtP=WJ+)K$p;<$E{?bml^}BlW3IXd}FVP#U#kK zQqOsb#V4-RL&pox4o2-Fp?0(Fn+Y|_yrvGufEZD#(`IaTY4#CReiZu7vt;HggiWl%69i6ZTT)b|&P~+NeCZ^B^Bl?R#jNK0fYF6 zS5GY6fs&tnmatA?5NmGk_l1!q$$n8@VX{JxZv%?7o>o-VGuF)VE2dHMO(Z*tS=znHd7gD6T`prEYXS}8owB<^De8cN=exjvIM;n83@BCr)!cFn_l_B0%XH z+W$(n(SQxY3bL!;`|@e~^AA48d>rB*sHJ;e<>|^BhNWG#b{sj8FL0})-Eu()-FQG> zzz(^4zvjTdpF3Ik@mo_R&f>?vt2{5-#6{0$)6;_8y{oD=mVeFL5l;iUuQHyNEA`mv z+K^2NQTLkbU)DpnMd?1M#f6gvUP0gV7% z^0u4Wlr%2cn41?*@`PG6=>@{VFCOCsL}kYl9Ucz4gpv4gIYb1(A2)srg2D7MZBT}b z>mKzmq0}Tg8^O!+ou9=i_o2jsXln#P8m*)RRnya%DXGjw3Jk(NT<6U$-ul3x>HT46 z+d@{yaC3z*mmyK)Q`7srIS)|itJf*-X$MDGZY_kN{eDyOV}xNeRZ~5Vi#lS%OE9T? zcA7)vH~)>VXHeDqU{>#uW#af6tPFICNrk1ZrejG?JpJqLGv)l6W($s~j_uK$%9K{> zGarw+;$O`kiI(c)s)tTk#2ToeU{F6fB zdqRv(Im4;C)1Dv)vXXrE!ilm59#p~W`TLe;UsL&;FFYD(N5`Z0@@1K>0W7V#cZ_RN z^sRjiGL3w#6bLz?D&p`JeX()@&N z`E$fmW804eJZ==bN0Y;1JBZ~(6P5h><=7}W%&8l1T%xsfl+#^!NEt@rdQ3o3Ufp`9 zOhkH#W?w3YbDCn?VoN{NEgZ91TKZS{l#~8UBf5mJBo<#q zPmL;z@6%8T9^9>{Sjaxu*gN+-^ZWgiI^T>5hVVV!q>4A-6iJRKDhdlNyBI-5V83~x z?9Ci3h4S{RliI-e)1cMJA?qs|XGJkxwWcEL>DYlmA$1z-UV+5zuuQ?YAn4pDb5v3iRc+qs4hJc5gJRT~G!#t5BJp7bg11$6`5KzCE! z@}d;1uXWp1DN!&gjCLIVd;G!{G$~tyCaP?4IV2i<_X3L5&G| zmmmIdv2PEC*?>)JL)sxG6`2yXydphp-#5m>o|(gmm1ILWKx{K>i!nf+O<@b^$pp!p z)kLJRgxoWo{W7vm!2c?~>!~@TMdGepE1heBfdCJpWZm0;+I}r4H(~jg3FM*f#_2RA!4<1u0LR>G1gXZ_Z;}bdrnv+H%SWNJVOx zT&(3H5|5dAKyssY2-1YmIpP{=b=EyKW;ZWiqF%%Anvq}@;zfDX9qXM&tQb;x2}z*> zf4|F^-n~NsHr&HXOM)$Os$rNm616(656&IO{_h2 zKue>Pe}1mU-}7Be>W$b#_n zV*HY=1jF5IRW)IUZ^q%3+d5>T+JagxyG(OB91DZa?=n1qep9)U&~YFxA|k}lzO6@| zs0)fFD&C7`n77`i5pb297vR)@I8_6>*AzPaS(#R5pq=Kn77g*#B~Y#B>Z~i0|`00 zyOKSlqSt34%ZcbWg0XZ_5x5$rvM&Bj*mOJ3Nm<@Zn$Kt#$;V@@Hw3pg)Ahb|d8ETr zyX>eP@)EiK19HW_Y~MXwpZrU_^F>bwd-(Z-))+tWdlu%6kwd<}zZ{C5A@3(J?Y`>A zOh&!^P-x`rYGU8eyiy091`~V-`16DZ+M5nt{gw}`kqYEP~rXh2)r4X)tC~+x^ z!8vxyGN;P*Hl|s3QA0>bm@5S*YF~azS_j+Qs;2YiFh^quWZvL1uB`;Uff6@THn4C} zE-;inaboBNySicPlX^6{!xcNw8Z@p_D0OuVa^3{N#NajZVp;K?d}eu&n$!k17DIl| zlBHsm?EgSW#h5J}G(jt;pP$e%Uo&do8Q7JE6|ApUf-q$%7Sz?;wr+lqy8bbt+!hTi zLZ08&hQ3|tyx4I~Oc`&1FW$J9g-Pl8`9Wgd{!i05+-RM?s zn1UM0X0!UWrV`x;A_?9UKzL==`mVRHy8o{OePppZ|6~CUxrm#a(eTqz*YVXs-v>9} zJWh?izH7md4V3@-O2Yn46MhgOh}1@l+y+t=tZPN(F=MucF?DXm86!H~yhZL?(DiY3 zzosM@5ymz6^|A6+PQ+LnZx3`;8V#db#E30l8J^Z<@d&e`vPz%D%i$|zTB5P661*9~ z1X=g8z^i=$<$SYKj$|T=$Y>&ECUKH)gJE%kO$edSFW<hEn%ZXz~%>fzB}lluSB|Sgwj^KmOh^F zH)->!F_JEH3pbIn6(@?WXX>5vG$7Kj$n+VCb5CB8PhYXlt(bZELZuyJj*1JM!cuuCFizCi3fAaD-F*5NQ%gpJUY z-kjZEiwtN69-#{ggegT!fulzr?azhIHkDI_M~db7N!!m)bm+N#Aq1uHl937b{E$R0 zb;ZNo@y(5W*jYW?t9E74yg$^dE`NCnNpXmy1!yYbRD9wilx#G^JI`S6d zrVB%4hZ1e54MHDy{s6vy;(zA!kOfDPvQ)5Jz`1Y+novJ88;NSi%%4K4JXyhO55ae# zvd4dTPLw_3w9iFvpb-VWzwEQ`CBv`nNw#m-Zl07l2tT+ond66S?nWrJp3GQMBn;}R zLa4akHru+hesQZ$>gIQ-8?W`A!vV4Cr_mTV)+EFno~cldiex3F2$e>JYl;N#XxTpW z-RQouKRV}SiMa6i=v}LHmNdG1sgQXUe=ynJWc!tVXHZ_SV^U)p4}LHKQ%tn)ypCd2 z@j7QyEr4{tKd9L@DD>l(V!Nu1=eTY|CV-|mJKu$!sy|&TNQRql4dXTp3;88a*=iac z+e~%bcT4kv`XXZ%mY?_VdoxY4l#Q8lF5_iOUOf`^x~dbq$g+|?S4o?S7ANCD^BI_) zgSDzabC(5DVR72sI~dl-HK@kF`Fd7YJwpbN_GCMvd@?_3NKjB00RMOWHdPM)?Wz(H f{q;9H+RgowaAmUj-l5~W`+NBp$}*)=#=-vsDHfDG From 6d8e9b1cd2f8f21e630641c336cf11015dc7207b Mon Sep 17 00:00:00 2001 From: Jan Oberhauser Date: Sun, 13 Sep 2020 12:35:13 +0200 Subject: [PATCH 020/284] :bug: Fix issue with overwrites --- packages/cli/src/CredentialsOverwrites.ts | 47 ++++++++++++++++++++--- 1 file changed, 41 insertions(+), 6 deletions(-) diff --git a/packages/cli/src/CredentialsOverwrites.ts b/packages/cli/src/CredentialsOverwrites.ts index bd962ae50..15bc3709a 100644 --- a/packages/cli/src/CredentialsOverwrites.ts +++ b/packages/cli/src/CredentialsOverwrites.ts @@ -11,25 +11,45 @@ import { class CredentialsOverwritesClass { + private credentialTypes = CredentialTypes(); private overwriteData: ICredentialsOverwrite = {}; + private resolvedTypes: string[] = []; + async init(overwriteData?: ICredentialsOverwrite) { if (overwriteData !== undefined) { // If data is already given it can directly be set instead of // loaded from environment - this.overwriteData = overwriteData; + this.__setData(JSON.parse(JSON.stringify(overwriteData))); return; } const data = await GenericHelpers.getConfigValue('credentials.overwrite.data') as string; try { - this.overwriteData = JSON.parse(data); + const overwriteData = JSON.parse(data); + this.__setData(overwriteData); } catch (error) { throw new Error(`The credentials-overwrite is not valid JSON.`); } } + + __setData(overwriteData: ICredentialsOverwrite) { + this.overwriteData = overwriteData; + + for (const credentialTypeData of this.credentialTypes.getAll()) { + const type = credentialTypeData.name; + + const overwrites = this.__getExtended(type); + + if (overwrites && Object.keys(overwrites).length) { + this.overwriteData[type] = overwrites; + } + } + } + + applyOverwrite(type: string, data: ICredentialDataDecryptedObject) { const overwrites = this.get(type); @@ -49,30 +69,45 @@ class CredentialsOverwritesClass { return returnData; } - get(type: string): ICredentialDataDecryptedObject | undefined { - const credentialTypes = CredentialTypes(); - const credentialTypeData = credentialTypes.getByName(type); + + __getExtended(type: string): ICredentialDataDecryptedObject | undefined { + + if (this.resolvedTypes.includes(type)) { + // Type got already resolved and can so returned directly + return this.overwriteData[type]; + } + + const credentialTypeData = this.credentialTypes.getByName(type); if (credentialTypeData === undefined) { throw new Error(`The credentials of type "${type}" are not known.`); } if (credentialTypeData.extends === undefined) { + this.resolvedTypes.push(type); return this.overwriteData[type]; } const overwrites: ICredentialDataDecryptedObject = {}; for (const credentialsTypeName of credentialTypeData.extends) { - Object.assign(overwrites, this.get(credentialsTypeName)); + Object.assign(overwrites, this.__getExtended(credentialsTypeName)); } if (this.overwriteData[type] !== undefined) { Object.assign(overwrites, this.overwriteData[type]); } + this.resolvedTypes.push(type); + return overwrites; } + + get(type: string): ICredentialDataDecryptedObject | undefined { + return this.overwriteData[type]; + } + + getAll(): ICredentialsOverwrite { return this.overwriteData; } From ae343b9f79bd0517df74ee1bcceca9eb9970f103 Mon Sep 17 00:00:00 2001 From: Ricardo Espinoza Date: Mon, 14 Sep 2020 02:11:14 -0400 Subject: [PATCH 021/284] :zap: Add appid to PhillipsHue credentials (#953) --- .../PhilipsHueOAuth2Api.credentials.ts | 20 ++++++++++++------- 1 file changed, 13 insertions(+), 7 deletions(-) diff --git a/packages/nodes-base/credentials/PhilipsHueOAuth2Api.credentials.ts b/packages/nodes-base/credentials/PhilipsHueOAuth2Api.credentials.ts index 9f8c6f90a..f2f31fe58 100644 --- a/packages/nodes-base/credentials/PhilipsHueOAuth2Api.credentials.ts +++ b/packages/nodes-base/credentials/PhilipsHueOAuth2Api.credentials.ts @@ -11,6 +11,12 @@ export class PhilipsHueOAuth2Api implements ICredentialType { displayName = 'PhilipHue OAuth2 API'; documentationUrl = 'philipsHue'; properties = [ + { + displayName: 'APP ID', + name: 'appId', + type: 'string' as NodePropertyTypes, + default: '', + }, { displayName: 'Authorization URL', name: 'authUrl', @@ -27,7 +33,7 @@ export class PhilipsHueOAuth2Api implements ICredentialType { displayName: 'Auth URI Query Parameters', name: 'authQueryParameters', type: 'hidden' as NodePropertyTypes, - default: '', + default: '={{"appid="+$parameter["appId"]}}', }, { displayName: 'Scope', @@ -36,11 +42,11 @@ export class PhilipsHueOAuth2Api implements ICredentialType { default: '', }, { - displayName: 'Authentication', - name: 'authentication', - type: 'hidden' as NodePropertyTypes, - default: 'header', - description: 'Method of authentication.', - }, + displayName: 'Authentication', + name: 'authentication', + type: 'hidden' as NodePropertyTypes, + default: 'header', + description: 'Method of authentication.', + }, ]; } From a27278a427442852eca2391b719525fcb121116f Mon Sep 17 00:00:00 2001 From: smamudhan <48641776+smamudhan@users.noreply.github.com> Date: Mon, 14 Sep 2020 13:28:01 +0530 Subject: [PATCH 022/284] Minor fixes for docs compatibility (#955) * :zap: Add documentationUrls for several nodes * :zap: Minor Updates - Updated Paddle documentationUrl - Updated CrateDB description - Updated Monday.com description - Fixed Nextcloud branding - Updated Redis description --- packages/nodes-base/credentials/PaddleApi.credentials.ts | 1 + packages/nodes-base/nodes/CrateDb/CrateDb.node.ts | 8 ++++---- .../nodes-base/nodes/MondayCom/BoardGroupDescription.ts | 2 +- .../nodes-base/nodes/MondayCom/BoardItemDescription.ts | 2 +- packages/nodes-base/nodes/NextCloud/NextCloud.node.ts | 8 ++++---- packages/nodes-base/nodes/Redis/Redis.node.ts | 8 ++++---- 6 files changed, 15 insertions(+), 14 deletions(-) diff --git a/packages/nodes-base/credentials/PaddleApi.credentials.ts b/packages/nodes-base/credentials/PaddleApi.credentials.ts index 143a24b3b..76c0d5bfb 100644 --- a/packages/nodes-base/credentials/PaddleApi.credentials.ts +++ b/packages/nodes-base/credentials/PaddleApi.credentials.ts @@ -6,6 +6,7 @@ import { export class PaddleApi implements ICredentialType { name = 'paddleApi'; displayName = 'Paddle API'; + documentationUrl = 'paddle'; properties = [ { displayName: 'Vendor Auth Code', diff --git a/packages/nodes-base/nodes/CrateDb/CrateDb.node.ts b/packages/nodes-base/nodes/CrateDb/CrateDb.node.ts index daddda129..a93c0c222 100644 --- a/packages/nodes-base/nodes/CrateDb/CrateDb.node.ts +++ b/packages/nodes-base/nodes/CrateDb/CrateDb.node.ts @@ -21,7 +21,7 @@ export class CrateDb implements INodeType { icon: 'file:cratedb.png', group: ['input'], version: 1, - description: 'Gets, add and update data in CrateDB.', + description: 'Add and update data in CrateDB.', defaults: { name: 'CrateDB', color: '#47889f', @@ -43,17 +43,17 @@ export class CrateDb implements INodeType { { name: 'Execute Query', value: 'executeQuery', - description: 'Executes a SQL query.', + description: 'Execute an SQL query', }, { name: 'Insert', value: 'insert', - description: 'Insert rows in database.', + description: 'Insert rows in database', }, { name: 'Update', value: 'update', - description: 'Updates rows in database.', + description: 'Update rows in database', }, ], default: 'insert', diff --git a/packages/nodes-base/nodes/MondayCom/BoardGroupDescription.ts b/packages/nodes-base/nodes/MondayCom/BoardGroupDescription.ts index d98ec6791..3650ff837 100644 --- a/packages/nodes-base/nodes/MondayCom/BoardGroupDescription.ts +++ b/packages/nodes-base/nodes/MondayCom/BoardGroupDescription.ts @@ -28,7 +28,7 @@ export const boardGroupOperations = [ { name: 'Get All', value: 'getAll', - description: `Get board's groups`, + description: `Get list of groups in a board`, }, ], default: 'create', diff --git a/packages/nodes-base/nodes/MondayCom/BoardItemDescription.ts b/packages/nodes-base/nodes/MondayCom/BoardItemDescription.ts index 9537c3252..c34203ec6 100644 --- a/packages/nodes-base/nodes/MondayCom/BoardItemDescription.ts +++ b/packages/nodes-base/nodes/MondayCom/BoardItemDescription.ts @@ -48,7 +48,7 @@ export const boardItemOperations = [ { name: 'Get All', value: 'getAll', - description: 'Get all item', + description: 'Get all items', }, { name: 'Get By Column Value', diff --git a/packages/nodes-base/nodes/NextCloud/NextCloud.node.ts b/packages/nodes-base/nodes/NextCloud/NextCloud.node.ts index a5b3109c6..47e8fe47a 100644 --- a/packages/nodes-base/nodes/NextCloud/NextCloud.node.ts +++ b/packages/nodes-base/nodes/NextCloud/NextCloud.node.ts @@ -20,15 +20,15 @@ import { export class NextCloud implements INodeType { description: INodeTypeDescription = { - displayName: 'NextCloud', + displayName: 'Nextcloud', name: 'nextCloud', icon: 'file:nextcloud.png', group: ['input'], version: 1, subtitle: '={{$parameter["operation"] + ": " + $parameter["resource"]}}', - description: 'Access data on NextCloud', + description: 'Access data on Nextcloud', defaults: { - name: 'NextCloud', + name: 'Nextcloud', color: '#1cafff', }, inputs: ['main'], @@ -170,7 +170,7 @@ export class NextCloud implements INodeType { { name: 'List', value: 'list', - description: 'Return the files and folders in a given folder', + description: 'Return the contents of a given folder', }, { name: 'Move', diff --git a/packages/nodes-base/nodes/Redis/Redis.node.ts b/packages/nodes-base/nodes/Redis/Redis.node.ts index 12263b1d6..53cdaf2bd 100644 --- a/packages/nodes-base/nodes/Redis/Redis.node.ts +++ b/packages/nodes-base/nodes/Redis/Redis.node.ts @@ -19,7 +19,7 @@ export class Redis implements INodeType { icon: 'file:redis.png', group: ['input'], version: 1, - description: 'Gets, sends data to Redis and receives generic information.', + description: 'Get, send and update data in Redis.', defaults: { name: 'Redis', color: '#0033AA', @@ -41,12 +41,12 @@ export class Redis implements INodeType { { name: 'Delete', value: 'delete', - description: 'Deletes a key from Redis.', + description: 'Delete a key from Redis.', }, { name: 'Get', value: 'get', - description: 'Returns the value of a key from Redis.', + description: 'Get the value of a key from Redis.', }, { name: 'Info', @@ -61,7 +61,7 @@ export class Redis implements INodeType { { name: 'Set', value: 'set', - description: 'Sets the value of a key in redis.', + description: 'Set the value of a key in redis.', }, ], default: 'info', From 397132688ea16d7e2960f9e47736212b0ffcb660 Mon Sep 17 00:00:00 2001 From: Jan Oberhauser Date: Mon, 14 Sep 2020 12:30:58 +0200 Subject: [PATCH 023/284] :zap: Wait for active workflow to finish before shutting down --- packages/cli/commands/start.ts | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/packages/cli/commands/start.ts b/packages/cli/commands/start.ts index ef9170a92..400b9043e 100644 --- a/packages/cli/commands/start.ts +++ b/packages/cli/commands/start.ts @@ -8,12 +8,14 @@ const open = require('open'); import * as config from '../config'; import { + ActiveExecutions, ActiveWorkflowRunner, CredentialTypes, CredentialsOverwrites, Db, ExternalHooks, GenericHelpers, + IExecutionsCurrentSummary, LoadNodesAndCredentials, NodeTypes, Server, @@ -85,6 +87,21 @@ export class Start extends Command { await Promise.all(removePromises); + // Wait for active workflow executions to finish + const activeExecutionsInstance = ActiveExecutions.getInstance(); + let executingWorkflows = activeExecutionsInstance.getActiveExecutions(); + + let count = 0; + while (executingWorkflows.length !== 0) { + if (count++ % 4 === 0) { + console.log(`Waiting for ${executingWorkflows.length} active executions to finish...`); + } + await new Promise((resolve) => { + setTimeout(resolve, 500); + }); + executingWorkflows = activeExecutionsInstance.getActiveExecutions(); + } + process.exit(processExistCode); } From 5cb1837ea9e9026e682795828d1db33da819ae99 Mon Sep 17 00:00:00 2001 From: Jan Oberhauser Date: Mon, 14 Sep 2020 12:59:52 +0200 Subject: [PATCH 024/284] :zap: Fix issue with Postmark Trigger node --- packages/nodes-base/nodes/Postmark/PostmarkTrigger.node.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/nodes-base/nodes/Postmark/PostmarkTrigger.node.ts b/packages/nodes-base/nodes/Postmark/PostmarkTrigger.node.ts index 4956d647b..e6c60bdda 100644 --- a/packages/nodes-base/nodes/Postmark/PostmarkTrigger.node.ts +++ b/packages/nodes-base/nodes/Postmark/PostmarkTrigger.node.ts @@ -127,7 +127,7 @@ export class PostmarkTrigger implements INodeType { if (this.getNodeParameter('includeContent') as boolean) { events.push('includeContent'); } - if (this.getNodeParameter('firstOpen') as boolean) { + if (events.includes('open') && this.getNodeParameter('firstOpen') as boolean) { events.push('firstOpen'); } From c123d44e1700fc116d33b1319b6a0c3e6849acbc Mon Sep 17 00:00:00 2001 From: Jan Oberhauser Date: Mon, 14 Sep 2020 13:05:21 +0200 Subject: [PATCH 025/284] :bug: Fix another issue with Postmark Trigger node --- .../nodes/Postmark/PostmarkTrigger.node.ts | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/packages/nodes-base/nodes/Postmark/PostmarkTrigger.node.ts b/packages/nodes-base/nodes/Postmark/PostmarkTrigger.node.ts index e6c60bdda..87ed571c2 100644 --- a/packages/nodes-base/nodes/Postmark/PostmarkTrigger.node.ts +++ b/packages/nodes-base/nodes/Postmark/PostmarkTrigger.node.ts @@ -124,11 +124,17 @@ export class PostmarkTrigger implements INodeType { const webhookData = this.getWorkflowStaticData('node'); const webhookUrl = this.getNodeWebhookUrl('default'); const events = this.getNodeParameter('events') as string[]; - if (this.getNodeParameter('includeContent') as boolean) { - events.push('includeContent'); + + if (events.includes('bounce') || events.includes('spamComplaint')) { + if (this.getNodeParameter('includeContent') as boolean) { + events.push('includeContent'); + } } - if (events.includes('open') && this.getNodeParameter('firstOpen') as boolean) { - events.push('firstOpen'); + + if (events.includes('open')) { + if (this.getNodeParameter('firstOpen') as boolean) { + events.push('firstOpen'); + } } // Get all webhooks From 4e7714848f2737a4e096beefe014e6ad88abe819 Mon Sep 17 00:00:00 2001 From: Tanay Pant <7481165+tanay1337@users.noreply.github.com> Date: Mon, 14 Sep 2020 13:06:28 +0200 Subject: [PATCH 026/284] =?UTF-8?q?=E2=9A=A1=20Add=20documentationUrl=20fo?= =?UTF-8?q?r=20MQTT=20Trigger?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- packages/nodes-base/credentials/Mqtt.credentials.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/nodes-base/credentials/Mqtt.credentials.ts b/packages/nodes-base/credentials/Mqtt.credentials.ts index bcc419a52..7bdd12ad4 100644 --- a/packages/nodes-base/credentials/Mqtt.credentials.ts +++ b/packages/nodes-base/credentials/Mqtt.credentials.ts @@ -7,6 +7,7 @@ import { export class Mqtt implements ICredentialType { name = 'mqtt'; displayName = 'MQTT'; + documentationUrl = 'mqtt'; properties = [ // The credentials to get from user and save encrypted. // Properties can be defined exactly in the same way From 653bbda78942a3786e6000d000f0955849dd8942 Mon Sep 17 00:00:00 2001 From: Jan Oberhauser Date: Mon, 14 Sep 2020 13:31:34 +0200 Subject: [PATCH 027/284] :bookmark: Release n8n-workflow@0.40.0 --- 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 ae5b1ccd3..d3645d888 100644 --- a/packages/workflow/package.json +++ b/packages/workflow/package.json @@ -1,6 +1,6 @@ { "name": "n8n-workflow", - "version": "0.39.0", + "version": "0.40.0", "description": "Workflow base code of n8n", "license": "SEE LICENSE IN LICENSE.md", "homepage": "https://n8n.io", From 1b1cfc58dc9ff00ff35b811ecdd7d92e9a46ec54 Mon Sep 17 00:00:00 2001 From: Jan Oberhauser Date: Mon, 14 Sep 2020 13:34:43 +0200 Subject: [PATCH 028/284] :arrow_up: Set n8n-workflow@0.40.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 7e9ec7715..fae3d71fc 100644 --- a/packages/core/package.json +++ b/packages/core/package.json @@ -46,7 +46,7 @@ "file-type": "^14.6.2", "lodash.get": "^4.4.2", "mime-types": "^2.1.27", - "n8n-workflow": "~0.39.0", + "n8n-workflow": "~0.40.0", "p-cancelable": "^2.0.0", "request": "^2.88.2", "request-promise-native": "^1.0.7" From 41e5ccedcea985d2094e0f030b62db829d50b594 Mon Sep 17 00:00:00 2001 From: Jan Oberhauser Date: Mon, 14 Sep 2020 13:35:15 +0200 Subject: [PATCH 029/284] :bookmark: Release n8n-core@0.45.0 --- 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 fae3d71fc..d44db6597 100644 --- a/packages/core/package.json +++ b/packages/core/package.json @@ -1,6 +1,6 @@ { "name": "n8n-core", - "version": "0.44.0", + "version": "0.45.0", "description": "Core functionality of n8n", "license": "SEE LICENSE IN LICENSE.md", "homepage": "https://n8n.io", From 0d52a8ca2414fb7aed12fef8cc86e623a35d2284 Mon Sep 17 00:00:00 2001 From: Jan Oberhauser Date: Mon, 14 Sep 2020 13:37:14 +0200 Subject: [PATCH 030/284] :arrow_up: Set n8n-core@0.45.0 and n8n-workflow@0.40.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 7023b26bb..b8bb7bd52 100644 --- a/packages/nodes-base/package.json +++ b/packages/nodes-base/package.json @@ -394,7 +394,7 @@ "@types/xml2js": "^0.4.3", "gulp": "^4.0.0", "jest": "^26.4.2", - "n8n-workflow": "~0.39.0", + "n8n-workflow": "~0.40.0", "ts-jest": "^26.3.0", "tslint": "^6.1.2", "typescript": "~3.7.4" @@ -423,7 +423,7 @@ "mqtt": "^4.2.0", "mssql": "^6.2.0", "mysql2": "^2.0.1", - "n8n-core": "~0.44.0", + "n8n-core": "~0.45.0", "nodemailer": "^6.4.6", "pdf-parse": "^1.1.1", "pg": "^8.3.0", From 439785db9d9b497130248748ece52449e0cd2ded Mon Sep 17 00:00:00 2001 From: Jan Oberhauser Date: Mon, 14 Sep 2020 13:37:39 +0200 Subject: [PATCH 031/284] :bookmark: Release n8n-nodes-base@0.77.0 --- 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 b8bb7bd52..28b45b122 100644 --- a/packages/nodes-base/package.json +++ b/packages/nodes-base/package.json @@ -1,6 +1,6 @@ { "name": "n8n-nodes-base", - "version": "0.76.0", + "version": "0.77.0", "description": "Base nodes of n8n", "license": "SEE LICENSE IN LICENSE.md", "homepage": "https://n8n.io", From 982b04c540f45d479afd7202d898b1994426fad5 Mon Sep 17 00:00:00 2001 From: Jan Oberhauser Date: Mon, 14 Sep 2020 13:38:52 +0200 Subject: [PATCH 032/284] :arrow_up: Set n8n-workflow@0.40.0 on n8n-editor-ui --- 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 2f61b7765..faa6da69f 100644 --- a/packages/editor-ui/package.json +++ b/packages/editor-ui/package.json @@ -66,7 +66,7 @@ "lodash.debounce": "^4.0.8", "lodash.get": "^4.4.2", "lodash.set": "^4.3.2", - "n8n-workflow": "~0.39.0", + "n8n-workflow": "~0.40.0", "node-sass": "^4.12.0", "prismjs": "^1.17.1", "quill": "^2.0.0-dev.3", From 1eb957dfe0050ffa00b3e5913e2ca9d18bd42d02 Mon Sep 17 00:00:00 2001 From: Jan Oberhauser Date: Mon, 14 Sep 2020 13:39:16 +0200 Subject: [PATCH 033/284] :bookmark: Release n8n-editor-ui@0.56.0 --- 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 faa6da69f..f373f3653 100644 --- a/packages/editor-ui/package.json +++ b/packages/editor-ui/package.json @@ -1,6 +1,6 @@ { "name": "n8n-editor-ui", - "version": "0.55.0", + "version": "0.56.0", "description": "Workflow Editor UI for n8n", "license": "SEE LICENSE IN LICENSE.md", "homepage": "https://n8n.io", From 46ec7e31285f80de20b84d2677d0a103a2394012 Mon Sep 17 00:00:00 2001 From: Jan Oberhauser Date: Mon, 14 Sep 2020 13:40:43 +0200 Subject: [PATCH 034/284] :arrow_up: Set n8n-core@0.45.0, n8n-editor-ui@0.56.0, n8n-nodes-base@0.77.0 and n8n-workflow@0.40.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 d28954d33..cd7b25298 100644 --- a/packages/cli/package.json +++ b/packages/cli/package.json @@ -102,10 +102,10 @@ "lodash.get": "^4.4.2", "mongodb": "^3.5.5", "mysql2": "^2.0.1", - "n8n-core": "~0.44.0", - "n8n-editor-ui": "~0.55.0", - "n8n-nodes-base": "~0.76.0", - "n8n-workflow": "~0.39.0", + "n8n-core": "~0.45.0", + "n8n-editor-ui": "~0.56.0", + "n8n-nodes-base": "~0.77.0", + "n8n-workflow": "~0.40.0", "oauth-1.0a": "^2.2.6", "open": "^7.0.0", "pg": "^8.3.0", From 2c043d9d9beebd0816481cc8afce7ef319f4f2f3 Mon Sep 17 00:00:00 2001 From: Jan Oberhauser Date: Mon, 14 Sep 2020 13:41:09 +0200 Subject: [PATCH 035/284] :bookmark: Release n8n@0.82.0 --- 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 cd7b25298..ed7c62ebf 100644 --- a/packages/cli/package.json +++ b/packages/cli/package.json @@ -1,6 +1,6 @@ { "name": "n8n", - "version": "0.81.0", + "version": "0.82.0", "description": "n8n Workflow Automation Tool", "license": "SEE LICENSE IN LICENSE.md", "homepage": "https://n8n.io", From 3d003fca728951115ef2a1bbbef2233a3279e0ba Mon Sep 17 00:00:00 2001 From: Jan Oberhauser Date: Mon, 14 Sep 2020 19:58:21 +0200 Subject: [PATCH 036/284] :zap: Improve speed of basic-auth with hashed password --- packages/cli/src/Server.ts | 29 +++++++++++++++++++++++------ 1 file changed, 23 insertions(+), 6 deletions(-) diff --git a/packages/cli/src/Server.ts b/packages/cli/src/Server.ts index d09855bcd..82682345a 100644 --- a/packages/cli/src/Server.ts +++ b/packages/cli/src/Server.ts @@ -20,7 +20,7 @@ import { RequestOptions } from 'oauth-1.0a'; import * as csrf from 'csrf'; import * as requestPromise from 'request-promise-native'; import { createHmac } from 'crypto'; -import { compareSync } from 'bcrypt'; +import { compare } from 'bcrypt'; import { ActiveExecutions, @@ -189,7 +189,9 @@ class App { const basicAuthHashEnabled = await GenericHelpers.getConfigValue('security.basicAuth.hash') as boolean; - this.app.use((req: express.Request, res: express.Response, next: express.NextFunction) => { + let validPassword: null | string = null; + + this.app.use(async (req: express.Request, res: express.Response, next: express.NextFunction) => { if (req.url.match(authIgnoreRegex)) { return next(); } @@ -201,12 +203,27 @@ class App { return ResponseHelper.basicAuthAuthorizationError(res, realm, 'Authorization is required!'); } - if (basicAuthData.name !== basicAuthUser || (!basicAuthHashEnabled && basicAuthData.pass !== basicAuthPassword) || (basicAuthHashEnabled && compareSync(basicAuthData.pass, basicAuthPassword) === false)) { - // Provided authentication data is wrong - return ResponseHelper.basicAuthAuthorizationError(res, realm, 'Authorization data is wrong!'); + if (basicAuthData.name === basicAuthUser) { + if (basicAuthHashEnabled === true) { + if (validPassword === null && await compare(basicAuthData.pass, basicAuthPassword)) { + // Password is valid so save for future requests + validPassword = basicAuthData.pass; + } + + if (validPassword === basicAuthData.pass && validPassword !== null) { + // Provided hash is correct + return next(); + } + } else { + if (basicAuthData.pass === basicAuthPassword) { + // Provided password is correct + return next(); + } + } } - next(); + // Provided authentication data is wrong + return ResponseHelper.basicAuthAuthorizationError(res, realm, 'Authorization data is wrong!'); }); } From 5ada586fa292da5e49c14b974068e30dbb17ce56 Mon Sep 17 00:00:00 2001 From: Jan Oberhauser Date: Mon, 14 Sep 2020 19:59:19 +0200 Subject: [PATCH 037/284] :bookmark: Release n8n@0.82.1 --- 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 ed7c62ebf..5f5c65a47 100644 --- a/packages/cli/package.json +++ b/packages/cli/package.json @@ -1,6 +1,6 @@ { "name": "n8n", - "version": "0.82.0", + "version": "0.82.1", "description": "n8n Workflow Automation Tool", "license": "SEE LICENSE IN LICENSE.md", "homepage": "https://n8n.io", From 181ba3c4e236279b65d102a8a33ae6896f160487 Mon Sep 17 00:00:00 2001 From: Jan Oberhauser Date: Tue, 15 Sep 2020 07:55:09 +0200 Subject: [PATCH 038/284] :books: License text fix --- LICENSE.md | 2 +- packages/cli/LICENSE.md | 2 +- packages/core/LICENSE.md | 2 +- packages/editor-ui/LICENSE.md | 2 +- packages/node-dev/LICENSE.md | 2 +- packages/nodes-base/LICENSE.md | 2 +- packages/workflow/LICENSE.md | 2 +- 7 files changed, 7 insertions(+), 7 deletions(-) diff --git a/LICENSE.md b/LICENSE.md index 24a7d38fc..3dd835dee 100644 --- a/LICENSE.md +++ b/LICENSE.md @@ -19,7 +19,7 @@ Condition notice. Software: n8n -License: Apache 2.0 +License: Apache 2.0 with Commons Clause Licensor: n8n GmbH diff --git a/packages/cli/LICENSE.md b/packages/cli/LICENSE.md index 24a7d38fc..3dd835dee 100644 --- a/packages/cli/LICENSE.md +++ b/packages/cli/LICENSE.md @@ -19,7 +19,7 @@ Condition notice. Software: n8n -License: Apache 2.0 +License: Apache 2.0 with Commons Clause Licensor: n8n GmbH diff --git a/packages/core/LICENSE.md b/packages/core/LICENSE.md index 24a7d38fc..3dd835dee 100644 --- a/packages/core/LICENSE.md +++ b/packages/core/LICENSE.md @@ -19,7 +19,7 @@ Condition notice. Software: n8n -License: Apache 2.0 +License: Apache 2.0 with Commons Clause Licensor: n8n GmbH diff --git a/packages/editor-ui/LICENSE.md b/packages/editor-ui/LICENSE.md index 24a7d38fc..3dd835dee 100644 --- a/packages/editor-ui/LICENSE.md +++ b/packages/editor-ui/LICENSE.md @@ -19,7 +19,7 @@ Condition notice. Software: n8n -License: Apache 2.0 +License: Apache 2.0 with Commons Clause Licensor: n8n GmbH diff --git a/packages/node-dev/LICENSE.md b/packages/node-dev/LICENSE.md index 24a7d38fc..3dd835dee 100644 --- a/packages/node-dev/LICENSE.md +++ b/packages/node-dev/LICENSE.md @@ -19,7 +19,7 @@ Condition notice. Software: n8n -License: Apache 2.0 +License: Apache 2.0 with Commons Clause Licensor: n8n GmbH diff --git a/packages/nodes-base/LICENSE.md b/packages/nodes-base/LICENSE.md index 24a7d38fc..3dd835dee 100644 --- a/packages/nodes-base/LICENSE.md +++ b/packages/nodes-base/LICENSE.md @@ -19,7 +19,7 @@ Condition notice. Software: n8n -License: Apache 2.0 +License: Apache 2.0 with Commons Clause Licensor: n8n GmbH diff --git a/packages/workflow/LICENSE.md b/packages/workflow/LICENSE.md index 24a7d38fc..3dd835dee 100644 --- a/packages/workflow/LICENSE.md +++ b/packages/workflow/LICENSE.md @@ -19,7 +19,7 @@ Condition notice. Software: n8n -License: Apache 2.0 +License: Apache 2.0 with Commons Clause Licensor: n8n GmbH From 615a44726d27b2d9b9152deffbd8fa47cfd6bc90 Mon Sep 17 00:00:00 2001 From: Ricardo Espinoza Date: Wed, 16 Sep 2020 02:33:59 -0400 Subject: [PATCH 039/284] :zap: Add author field to post resource on WordPress Node (#958) --- .../nodes/Wordpress/GenericFunctions.ts | 10 ++++++++-- .../nodes/Wordpress/PostDescription.ts | 20 +++++++++++++++++++ .../nodes/Wordpress/PostInterface.ts | 1 + .../nodes/Wordpress/Wordpress.node.ts | 9 ++++++++- 4 files changed, 37 insertions(+), 3 deletions(-) diff --git a/packages/nodes-base/nodes/Wordpress/GenericFunctions.ts b/packages/nodes-base/nodes/Wordpress/GenericFunctions.ts index e526017b5..a3bd42d74 100644 --- a/packages/nodes-base/nodes/Wordpress/GenericFunctions.ts +++ b/packages/nodes-base/nodes/Wordpress/GenericFunctions.ts @@ -1,10 +1,16 @@ -import { OptionsWithUri } from 'request'; +import { + OptionsWithUri, +} from 'request'; + import { IExecuteFunctions, IExecuteSingleFunctions, ILoadOptionsFunctions, } from 'n8n-core'; -import { IDataObject } from 'n8n-workflow'; + +import { + IDataObject, +} from 'n8n-workflow'; export async function wordpressApiRequest(this: IExecuteFunctions | IExecuteSingleFunctions | ILoadOptionsFunctions, method: string, resource: string, body: any = {}, qs: IDataObject = {}, uri?: string, option: IDataObject = {}): Promise { // tslint:disable-line:no-any const credentials = this.getCredentials('wordpressApi'); diff --git a/packages/nodes-base/nodes/Wordpress/PostDescription.ts b/packages/nodes-base/nodes/Wordpress/PostDescription.ts index d14f3b329..9c390591d 100644 --- a/packages/nodes-base/nodes/Wordpress/PostDescription.ts +++ b/packages/nodes-base/nodes/Wordpress/PostDescription.ts @@ -84,6 +84,16 @@ export const postFields = [ }, }, options: [ + { + displayName: 'Author ID', + name: 'authorId', + type: 'options', + typeOptions: { + loadOptionsMethod: 'getAuthors', + }, + default: '', + description: 'The ID for the author of the object.', + }, { displayName: 'Content', name: 'content', @@ -287,6 +297,16 @@ export const postFields = [ }, }, options: [ + { + displayName: 'Author ID', + name: 'authorId', + type: 'options', + typeOptions: { + loadOptionsMethod: 'getAuthors', + }, + default: '', + description: 'The ID for the author of the object.', + }, { displayName: 'Title', name: 'title', diff --git a/packages/nodes-base/nodes/Wordpress/PostInterface.ts b/packages/nodes-base/nodes/Wordpress/PostInterface.ts index ad4b378a1..e810c11cf 100644 --- a/packages/nodes-base/nodes/Wordpress/PostInterface.ts +++ b/packages/nodes-base/nodes/Wordpress/PostInterface.ts @@ -1,5 +1,6 @@ export interface IPost { + author?: number; id?: number; title?: string; content?: string; diff --git a/packages/nodes-base/nodes/Wordpress/Wordpress.node.ts b/packages/nodes-base/nodes/Wordpress/Wordpress.node.ts index 93d4df9de..6e8136959 100644 --- a/packages/nodes-base/nodes/Wordpress/Wordpress.node.ts +++ b/packages/nodes-base/nodes/Wordpress/Wordpress.node.ts @@ -47,7 +47,7 @@ export class Wordpress implements INodeType { { name: 'wordpressApi', required: true, - } + }, ], properties: [ { @@ -115,6 +115,7 @@ export class Wordpress implements INodeType { async getAuthors(this: ILoadOptionsFunctions): Promise { const returnData: INodePropertyOptions[] = []; const authors = await wordpressApiRequestAllItems.call(this, 'GET', '/users', {}, { who: 'authors' }); + console.log(authors); for (const author of authors) { const authorName = author.name; const authorId = author.id; @@ -147,6 +148,9 @@ export class Wordpress implements INodeType { const body: IPost = { title, }; + if (additionalFields.authorId) { + body.author = additionalFields.authorId as number; + } if (additionalFields.content) { body.content = additionalFields.content as string; } @@ -186,6 +190,9 @@ export class Wordpress implements INodeType { const body: IPost = { id: parseInt(postId, 10), }; + if (updateFields.authorId) { + body.author = updateFields.authorId as number; + } if (updateFields.title) { body.title = updateFields.title as string; } From 7c54cf6ac2569dd9dedb96d69ee187899bc681ba Mon Sep 17 00:00:00 2001 From: Jan Oberhauser Date: Wed, 16 Sep 2020 08:35:26 +0200 Subject: [PATCH 040/284] :zap: Minor improvements to Wordpress-Node --- packages/nodes-base/nodes/Wordpress/GenericFunctions.ts | 4 ++-- packages/nodes-base/nodes/Wordpress/Wordpress.node.ts | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/packages/nodes-base/nodes/Wordpress/GenericFunctions.ts b/packages/nodes-base/nodes/Wordpress/GenericFunctions.ts index a3bd42d74..8e29fa218 100644 --- a/packages/nodes-base/nodes/Wordpress/GenericFunctions.ts +++ b/packages/nodes-base/nodes/Wordpress/GenericFunctions.ts @@ -30,7 +30,7 @@ export async function wordpressApiRequest(this: IExecuteFunctions | IExecuteSing method, qs, body, - uri: uri ||`${credentials!.url}/wp-json/wp/v2${resource}`, + uri: uri || `${credentials!.url}/wp-json/wp/v2${resource}`, json: true }; options = Object.assign({}, options, option); @@ -41,7 +41,7 @@ export async function wordpressApiRequest(this: IExecuteFunctions | IExecuteSing return await this.helpers.request!(options); } catch (error) { let errorMessage = error.message; - if (error.response.body) { + if (error.response && error.response.body) { errorMessage = error.response.body.message || error.response.body.Message || error.message; } diff --git a/packages/nodes-base/nodes/Wordpress/Wordpress.node.ts b/packages/nodes-base/nodes/Wordpress/Wordpress.node.ts index 6e8136959..225422ddb 100644 --- a/packages/nodes-base/nodes/Wordpress/Wordpress.node.ts +++ b/packages/nodes-base/nodes/Wordpress/Wordpress.node.ts @@ -238,7 +238,7 @@ export class Wordpress implements INodeType { if (options.context) { qs.context = options.context as string; } - responseData = await wordpressApiRequest.call(this,'GET', `/posts/${postId}`, {}, qs); + responseData = await wordpressApiRequest.call(this, 'GET', `/posts/${postId}`, {}, qs); } //https://developer.wordpress.org/rest-api/reference/posts/#list-posts if (operation === 'getAll') { @@ -372,7 +372,7 @@ export class Wordpress implements INodeType { if (options.context) { qs.context = options.context as string; } - responseData = await wordpressApiRequest.call(this,'GET', `/users/${userId}`, {}, qs); + responseData = await wordpressApiRequest.call(this, 'GET', `/users/${userId}`, {}, qs); } //https://developer.wordpress.org/rest-api/reference/users/#list-users if (operation === 'getAll') { From 266112a8cb5bfe3009033c495c5e320fa0e6b0a2 Mon Sep 17 00:00:00 2001 From: Rupenieks <32895755+Rupenieks@users.noreply.github.com> Date: Wed, 16 Sep 2020 09:16:06 +0200 Subject: [PATCH 041/284] :zap: Clickup OAuth2 support (#649) * OAuth2 credentials, genericFunctions needed config, UI options for oauth2 support * :zap: Added options to decide when to to send the bearer * Fixed "undefined property split" issue * :zap: Small fix * :zap: Improvements to ClickUp-Node * :zap: Improvements Co-authored-by: Rupenieks Co-authored-by: ricardo Co-authored-by: Ricardo Espinoza --- packages/core/src/NodeExecuteFunctions.ts | 6 +++ .../ClickUpOAuth2Api.credentials.ts | 47 +++++++++++++++++++ .../nodes-base/nodes/ClickUp/ClickUp.node.ts | 34 ++++++++++++++ .../nodes/ClickUp/ClickUpTrigger.node.ts | 38 ++++++++++++++- .../nodes/ClickUp/GenericFunctions.ts | 35 ++++++++++---- packages/nodes-base/package.json | 1 + packages/workflow/src/Interfaces.ts | 1 + 7 files changed, 152 insertions(+), 10 deletions(-) create mode 100644 packages/nodes-base/credentials/ClickUpOAuth2Api.credentials.ts diff --git a/packages/core/src/NodeExecuteFunctions.ts b/packages/core/src/NodeExecuteFunctions.ts index 5cf38f6a9..03e8ffb94 100644 --- a/packages/core/src/NodeExecuteFunctions.ts +++ b/packages/core/src/NodeExecuteFunctions.ts @@ -152,6 +152,12 @@ export function requestOAuth2(this: IAllExecuteFunctions, credentialsType: strin // on the token-type used. const newRequestOptions = token.sign(requestOptions as clientOAuth2.RequestObject); + // If keep bearer is false remove the it from the authorization header + if (oAuth2Options?.keepBearer === false) { + //@ts-ignore + newRequestOptions?.headers?.Authorization = newRequestOptions?.headers?.Authorization.split(' ')[1]; + } + return this.helpers.request!(newRequestOptions) .catch(async (error: IResponseError) => { // TODO: Check if also other codes are possible diff --git a/packages/nodes-base/credentials/ClickUpOAuth2Api.credentials.ts b/packages/nodes-base/credentials/ClickUpOAuth2Api.credentials.ts new file mode 100644 index 000000000..3ce6d6c3f --- /dev/null +++ b/packages/nodes-base/credentials/ClickUpOAuth2Api.credentials.ts @@ -0,0 +1,47 @@ +import { + ICredentialType, + NodePropertyTypes, +} from 'n8n-workflow'; + +export class ClickUpOAuth2Api implements ICredentialType { + name = 'clickUpOAuth2Api'; + extends = [ + 'oAuth2Api', + ]; + displayName = 'ClickUp OAuth2 API'; + properties = [ + { + displayName: 'Authorization URL', + name: 'authUrl', + type: 'hidden' as NodePropertyTypes, + default: 'https://app.clickup.com/api', + required: true, + }, + { + displayName: 'Access Token URL', + name: 'accessTokenUrl', + type: 'hidden' as NodePropertyTypes, + default: 'https://api.clickup.com/api/v2/oauth/token', + required: true, + }, + { + displayName: 'Scope', + name: 'scope', + type: 'hidden' as NodePropertyTypes, + default: '', + }, + { + displayName: 'Auth URI Query Parameters', + name: 'authQueryParameters', + type: 'hidden' as NodePropertyTypes, + default: '', + }, + { + displayName: 'Authentication', + name: 'authentication', + type: 'hidden' as NodePropertyTypes, + default: 'body', + description: 'Resource to consume.', + }, + ]; +} diff --git a/packages/nodes-base/nodes/ClickUp/ClickUp.node.ts b/packages/nodes-base/nodes/ClickUp/ClickUp.node.ts index ad1fcd119..170b1c1e5 100644 --- a/packages/nodes-base/nodes/ClickUp/ClickUp.node.ts +++ b/packages/nodes-base/nodes/ClickUp/ClickUp.node.ts @@ -99,9 +99,43 @@ export class ClickUp implements INodeType { { name: 'clickUpApi', required: true, + displayOptions: { + show: { + authentication: [ + 'accessToken', + ], + }, + }, + }, + { + name: 'clickUpOAuth2Api', + required: true, + displayOptions: { + show: { + authentication: [ + 'oAuth2', + ], + }, + }, }, ], properties: [ + { + displayName: 'Authentication', + name: 'authentication', + type: 'options', + options: [ + { + name: 'Access Token', + value: 'accessToken', + }, + { + name: 'OAuth2', + value: 'oAuth2', + }, + ], + default: 'accessToken', + }, { displayName: 'Resource', name: 'resource', diff --git a/packages/nodes-base/nodes/ClickUp/ClickUpTrigger.node.ts b/packages/nodes-base/nodes/ClickUp/ClickUpTrigger.node.ts index 36d523313..7a844f707 100644 --- a/packages/nodes-base/nodes/ClickUp/ClickUpTrigger.node.ts +++ b/packages/nodes-base/nodes/ClickUp/ClickUpTrigger.node.ts @@ -34,9 +34,27 @@ export class ClickUpTrigger implements INodeType { outputs: ['main'], credentials: [ { - name: 'clickUpApi', + name: 'clickupApi', required: true, - } + displayOptions: { + show: { + authentication: [ + 'accessToken', + ], + }, + }, + }, + { + name: 'clickUpOAuth2Api', + required: true, + displayOptions: { + show: { + authentication: [ + 'oAuth2', + ], + }, + }, + }, ], webhooks: [ { @@ -47,6 +65,22 @@ export class ClickUpTrigger implements INodeType { }, ], properties: [ + { + displayName: 'Authentication', + name: 'authentication', + type: 'options', + options: [ + { + name: 'Access Token', + value: 'accessToken', + }, + { + name: 'OAuth2', + value: 'oAuth2', + }, + ], + default: 'accessToken', + }, { displayName: 'Team', name: 'team', diff --git a/packages/nodes-base/nodes/ClickUp/GenericFunctions.ts b/packages/nodes-base/nodes/ClickUp/GenericFunctions.ts index d836f7798..0adbe15c4 100644 --- a/packages/nodes-base/nodes/ClickUp/GenericFunctions.ts +++ b/packages/nodes-base/nodes/ClickUp/GenericFunctions.ts @@ -12,17 +12,12 @@ import { import { IDataObject, + IOAuth2Options, } from 'n8n-workflow'; export async function clickupApiRequest(this: IHookFunctions | IExecuteFunctions | IExecuteSingleFunctions | ILoadOptionsFunctions | IWebhookFunctions, method: string, resource: string, body: any = {}, qs: IDataObject = {}, uri?: string, option: IDataObject = {}): Promise { // tslint:disable-line:no-any - const credentials = this.getCredentials('clickUpApi'); - if (credentials === undefined) { - throw new Error('No credentials got returned!'); - } - const options: OptionsWithUri = { headers: { - Authorization: credentials.accessToken, 'Content-Type': 'application/json', }, method, @@ -31,15 +26,39 @@ export async function clickupApiRequest(this: IHookFunctions | IExecuteFunctions uri: uri ||`https://api.clickup.com/api/v2${resource}`, json: true }; + try { - return await this.helpers.request!(options); - } catch (error) { + const authenticationMethod = this.getNodeParameter('authentication', 0) as string; + + if (authenticationMethod === 'accessToken') { + + const credentials = this.getCredentials('clickUpApi'); + + if (credentials === undefined) { + throw new Error('No credentials got returned!'); + } + + options.headers!['Authorization'] = credentials.accessToken; + return await this.helpers.request!(options); + + } else { + + const oAuth2Options: IOAuth2Options = { + keepBearer: false, + tokenType: 'Bearer', + }; + // @ts-ignore + return await this.helpers.requestOAuth2!.call(this, 'clickUpOAuth2Api', options, oAuth2Options); + } + + } catch(error) { let errorMessage = error; if (error.err) { errorMessage = error.err; } throw new Error('ClickUp Error: ' + errorMessage); } + } export async function clickupApiRequestAllItems(this: IHookFunctions | IExecuteFunctions| ILoadOptionsFunctions, propertyName: string ,method: string, resource: string, body: any = {}, query: IDataObject = {}): Promise { // tslint:disable-line:no-any diff --git a/packages/nodes-base/package.json b/packages/nodes-base/package.json index 28b45b122..762c7e042 100644 --- a/packages/nodes-base/package.json +++ b/packages/nodes-base/package.json @@ -44,6 +44,7 @@ "dist/credentials/CircleCiApi.credentials.js", "dist/credentials/ClearbitApi.credentials.js", "dist/credentials/ClickUpApi.credentials.js", + "dist/credentials/ClickUpOAuth2Api.credentials.js", "dist/credentials/ClockifyApi.credentials.js", "dist/credentials/CockpitApi.credentials.js", "dist/credentials/CodaApi.credentials.js", diff --git a/packages/workflow/src/Interfaces.ts b/packages/workflow/src/Interfaces.ts index af62220d8..7f1deea37 100644 --- a/packages/workflow/src/Interfaces.ts +++ b/packages/workflow/src/Interfaces.ts @@ -16,6 +16,7 @@ export interface IOAuth2Options { includeCredentialsOnRefreshOnBody?: boolean; property?: string; tokenType?: string; + keepBearer?: boolean; } export interface IConnection { From 393bc8fd544e37af6c8d93cecffcbe6e183aa7d1 Mon Sep 17 00:00:00 2001 From: Jan Oberhauser Date: Wed, 16 Sep 2020 09:16:20 +0200 Subject: [PATCH 042/284] :zap: Remove old comment --- packages/core/src/NodeExecuteFunctions.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/packages/core/src/NodeExecuteFunctions.ts b/packages/core/src/NodeExecuteFunctions.ts index 03e8ffb94..b78518b90 100644 --- a/packages/core/src/NodeExecuteFunctions.ts +++ b/packages/core/src/NodeExecuteFunctions.ts @@ -162,7 +162,6 @@ export function requestOAuth2(this: IAllExecuteFunctions, credentialsType: strin .catch(async (error: IResponseError) => { // TODO: Check if also other codes are possible if (error.statusCode === 401) { - // TODO: Whole refresh process is not tested yet // Token is probably not valid anymore. So try refresh it. const tokenRefreshOptions: IDataObject = {}; From 1a411ebef7e46c268988c88298bdf374bfb80425 Mon Sep 17 00:00:00 2001 From: Rupenieks <32895755+Rupenieks@users.noreply.github.com> Date: Wed, 16 Sep 2020 18:20:27 +0200 Subject: [PATCH 043/284] :zap: Asana OAuth2 support (#669) * OAuth2 support * :zap: Improvements * :zap: Improvements * :zap: Improvements to Asana Trigger Node Co-authored-by: Rupenieks Co-authored-by: ricardo Co-authored-by: Jan Oberhauser --- .../credentials/AsanaApi.credentials.ts | 1 - .../credentials/AsanaOAuth2Api.credentials.ts | 47 ++++++ packages/nodes-base/nodes/Asana/Asana.node.ts | 69 ++++----- .../nodes/Asana/AsanaTrigger.node.ts | 135 +++++++++++------- .../nodes/Asana/GenericFunctions.ts | 57 ++++++-- packages/nodes-base/package.json | 1 + 6 files changed, 219 insertions(+), 91 deletions(-) create mode 100644 packages/nodes-base/credentials/AsanaOAuth2Api.credentials.ts diff --git a/packages/nodes-base/credentials/AsanaApi.credentials.ts b/packages/nodes-base/credentials/AsanaApi.credentials.ts index 56b42c8b7..c9096ed8a 100644 --- a/packages/nodes-base/credentials/AsanaApi.credentials.ts +++ b/packages/nodes-base/credentials/AsanaApi.credentials.ts @@ -3,7 +3,6 @@ import { NodePropertyTypes, } from 'n8n-workflow'; - export class AsanaApi implements ICredentialType { name = 'asanaApi'; displayName = 'Asana API'; diff --git a/packages/nodes-base/credentials/AsanaOAuth2Api.credentials.ts b/packages/nodes-base/credentials/AsanaOAuth2Api.credentials.ts new file mode 100644 index 000000000..7a4be0470 --- /dev/null +++ b/packages/nodes-base/credentials/AsanaOAuth2Api.credentials.ts @@ -0,0 +1,47 @@ +import { + ICredentialType, + NodePropertyTypes, +} from 'n8n-workflow'; + +export class AsanaOAuth2Api implements ICredentialType { + name = 'asanaOAuth2Api'; + extends = [ + 'oAuth2Api', + ]; + displayName = 'Asana OAuth2 API'; + properties = [ + { + displayName: 'Authorization URL', + name: 'authUrl', + type: 'hidden' as NodePropertyTypes, + default: 'https://app.asana.com/-/oauth_authorize', + required: true, + }, + { + displayName: 'Access Token URL', + name: 'accessTokenUrl', + type: 'hidden' as NodePropertyTypes, + default: 'https://app.asana.com/-/oauth_token', + required: true, + }, + { + displayName: 'Scope', + name: 'scope', + type: 'hidden' as NodePropertyTypes, + default: '', + }, + { + displayName: 'Auth URI Query Parameters', + name: 'authQueryParameters', + type: 'hidden' as NodePropertyTypes, + default: '', + }, + { + displayName: 'Authentication', + name: 'authentication', + type: 'hidden' as NodePropertyTypes, + default: 'body', + description: 'Resource to consume.', + }, + ]; +} diff --git a/packages/nodes-base/nodes/Asana/Asana.node.ts b/packages/nodes-base/nodes/Asana/Asana.node.ts index 8b26999c6..66fa27d1d 100644 --- a/packages/nodes-base/nodes/Asana/Asana.node.ts +++ b/packages/nodes-base/nodes/Asana/Asana.node.ts @@ -14,6 +14,7 @@ import { import { asanaApiRequest, asanaApiRequestAllItems, + getWorkspaces, } from './GenericFunctions'; export class Asana implements INodeType { @@ -35,9 +36,44 @@ export class Asana implements INodeType { { name: 'asanaApi', required: true, + displayOptions: { + show: { + authentication: [ + 'accessToken', + ], + }, + }, + }, + { + name: 'asanaOAuth2Api', + required: true, + displayOptions: { + show: { + authentication: [ + 'oAuth2', + ], + }, + }, }, ], properties: [ + { + displayName: 'Authentication', + name: 'authentication', + type: 'options', + options: [ + { + name: 'Access Token', + value: 'accessToken', + }, + { + name: 'OAuth2', + value: 'oAuth2', + }, + ], + default: 'accessToken', + description: 'The resource to operate on.', + }, { displayName: 'Resource', name: 'resource', @@ -1004,32 +1040,7 @@ export class Asana implements INodeType { loadOptions: { // Get all the available workspaces to display them to user so that he can // select them easily - async getWorkspaces(this: ILoadOptionsFunctions): Promise { - const endpoint = '/workspaces'; - const responseData = await asanaApiRequestAllItems.call(this, 'GET', endpoint, {}); - - const returnData: INodePropertyOptions[] = []; - for (const workspaceData of responseData) { - if (workspaceData.resource_type !== 'workspace') { - // Not sure if for some reason also ever other resources - // get returned but just in case filter them out - continue; - } - - returnData.push({ - name: workspaceData.name, - value: workspaceData.gid, - }); - } - - returnData.sort((a, b) => { - if (a.name < b.name) { return -1; } - if (a.name > b.name) { return 1; } - return 0; - }); - - return returnData; - }, + getWorkspaces, // Get all the available projects to display them to user so that they can be // selected easily @@ -1215,12 +1226,6 @@ export class Asana implements INodeType { const items = this.getInputData(); const returnData: IDataObject[] = []; - const credentials = this.getCredentials('asanaApi'); - - if (credentials === undefined) { - throw new Error('No credentials got returned!'); - } - const resource = this.getNodeParameter('resource', 0) as string; const operation = this.getNodeParameter('operation', 0) as string; diff --git a/packages/nodes-base/nodes/Asana/AsanaTrigger.node.ts b/packages/nodes-base/nodes/Asana/AsanaTrigger.node.ts index b4e481d16..2aad0cc9e 100644 --- a/packages/nodes-base/nodes/Asana/AsanaTrigger.node.ts +++ b/packages/nodes-base/nodes/Asana/AsanaTrigger.node.ts @@ -5,6 +5,8 @@ import { import { IDataObject, + ILoadOptionsFunctions, + INodePropertyOptions, INodeTypeDescription, INodeType, IWebhookResponseData, @@ -12,9 +14,12 @@ import { import { asanaApiRequest, + getWorkspaces, } from './GenericFunctions'; -import { createHmac } from 'crypto'; +import { + createHmac, +} from 'crypto'; export class AsanaTrigger implements INodeType { description: INodeTypeDescription = { @@ -26,7 +31,7 @@ export class AsanaTrigger implements INodeType { description: 'Starts the workflow when Asana events occure.', defaults: { name: 'Asana-Trigger', - color: '#559922', + color: '#FC636B', }, inputs: [], outputs: ['main'], @@ -34,7 +39,25 @@ export class AsanaTrigger implements INodeType { { name: 'asanaApi', required: true, - } + displayOptions: { + show: { + authentication: [ + 'accessToken', + ], + }, + }, + }, + { + name: 'asanaOAuth2Api', + required: true, + displayOptions: { + show: { + authentication: [ + 'oAuth2', + ], + }, + }, + }, ], webhooks: [ { @@ -45,6 +68,23 @@ export class AsanaTrigger implements INodeType { }, ], properties: [ + { + displayName: 'Authentication', + name: 'authentication', + type: 'options', + options: [ + { + name: 'Access Token', + value: 'accessToken', + }, + { + name: 'OAuth2', + value: 'oAuth2', + }, + ], + default: 'accessToken', + description: 'The resource to operate on.', + }, { displayName: 'Resource', name: 'resource', @@ -56,13 +96,31 @@ export class AsanaTrigger implements INodeType { { displayName: 'Workspace', name: 'workspace', - type: 'string', + type: 'options', + typeOptions: { + loadOptionsMethod: 'getWorkspaces', + }, + options: [], default: '', required: false, description: 'The workspace ID the resource is registered under. This is only required if you want to allow overriding existing webhooks.', }, ], + }; + methods = { + loadOptions: { + // Get all the available workspaces to display them to user so that he can + // select them easily + async getWorkspaces(this: ILoadOptionsFunctions): Promise { + const workspaces = await getWorkspaces.call(this); + workspaces.unshift({ + name: '', + value: '', + }); + return workspaces; + }, + }, }; // @ts-ignore (because of request) @@ -71,32 +129,29 @@ export class AsanaTrigger implements INodeType { async checkExists(this: IHookFunctions): Promise { const webhookData = this.getWorkflowStaticData('node'); - if (webhookData.webhookId === undefined) { - // No webhook id is set so no webhook can exist - return false; - } + const webhookUrl = this.getNodeWebhookUrl('default') as string; - // Webhook got created before so check if it still exists - const endpoint = `webhooks/${webhookData.webhookId}`; + const resource = this.getNodeParameter('resource') as string; - try { - await asanaApiRequest.call(this, 'GET', endpoint, {}); - } catch (e) { - if (e.statusCode === 404) { - // Webhook does not exist - delete webhookData.webhookId; + const workspace = this.getNodeParameter('workspace') as string; - return false; + const endpoint = '/webhooks'; + + const { data } = await asanaApiRequest.call(this, 'GET', endpoint, {}, { workspace }); + + for (const webhook of data) { + if (webhook.resource.gid === resource && webhook.target === webhookUrl) { + webhookData.webhookId = webhook.gid; + return true; } - - // Some error occured - throw e; } // If it did not error then the webhook exists - return true; + return false; }, async create(this: IHookFunctions): Promise { + const webhookData = this.getWorkflowStaticData('node'); + const webhookUrl = this.getNodeWebhookUrl('default') as string; if (webhookUrl.includes('%20')) { @@ -105,9 +160,7 @@ export class AsanaTrigger implements INodeType { const resource = this.getNodeParameter('resource') as string; - const workspace = this.getNodeParameter('workspace') as string; - - const endpoint = `webhooks`; + const endpoint = `/webhooks`; const body = { resource, @@ -115,29 +168,15 @@ export class AsanaTrigger implements INodeType { }; let responseData; - try { - responseData = await asanaApiRequest.call(this, 'POST', endpoint, body); - } catch(error) { - // delete webhook if it already exists - if (error.statusCode === 403) { - const webhookData = await asanaApiRequest.call(this, 'GET', endpoint, {}, { workspace }); - const webhook = webhookData.data.find((webhook: any) => { // tslint:disable-line:no-any - return webhook.target === webhookUrl && webhook.resource.gid === resource; - }); - await asanaApiRequest.call(this, 'DELETE', `${endpoint}/${webhook.gid}`, {}); - responseData = await asanaApiRequest.call(this, 'POST', endpoint, body); - } else { - throw error; - } - } - if (responseData.data === undefined || responseData.data.id === undefined) { + responseData = await asanaApiRequest.call(this, 'POST', endpoint, body); + + if (responseData.data === undefined || responseData.data.gid === undefined) { // Required data is missing so was not successful return false; } - const webhookData = this.getWorkflowStaticData('node'); - webhookData.webhookId = responseData.data.id as string; + webhookData.webhookId = responseData.data.gid as string; return true; }, @@ -145,7 +184,7 @@ export class AsanaTrigger implements INodeType { const webhookData = this.getWorkflowStaticData('node'); if (webhookData.webhookId !== undefined) { - const endpoint = `webhooks/${webhookData.webhookId}`; + const endpoint = `/webhooks/${webhookData.webhookId}`; const body = {}; try { @@ -165,15 +204,12 @@ export class AsanaTrigger implements INodeType { }, }; - - async webhook(this: IWebhookFunctions): Promise { const bodyData = this.getBodyData() as IDataObject; const headerData = this.getHeaderData() as IDataObject; const req = this.getRequestObject(); - const webhookData = this.getWorkflowStaticData('node') as IDataObject; - + const webhookData = this.getWorkflowStaticData('node'); if (headerData['x-hook-secret'] !== undefined) { // Is a create webhook confirmation request @@ -182,6 +218,7 @@ export class AsanaTrigger implements INodeType { const res = this.getResponseObject(); res.set('X-Hook-Secret', webhookData.hookSecret as string); res.status(200).end(); + return { noWebhookResponse: true, }; @@ -198,7 +235,7 @@ export class AsanaTrigger implements INodeType { // Check if the request is valid // (if the signature matches to data and hookSecret) - const computedSignature = createHmac("sha256", webhookData.hookSecret as string).update(JSON.stringify(req.body)).digest("hex"); + const computedSignature = createHmac('sha256', webhookData.hookSecret as string).update(JSON.stringify(req.body)).digest('hex'); if (headerData['x-hook-signature'] !== computedSignature) { // Signature is not valid so ignore call return {}; @@ -206,7 +243,7 @@ export class AsanaTrigger implements INodeType { return { workflowData: [ - this.helpers.returnJsonArray(req.body) + this.helpers.returnJsonArray(req.body.events) ], }; } diff --git a/packages/nodes-base/nodes/Asana/GenericFunctions.ts b/packages/nodes-base/nodes/Asana/GenericFunctions.ts index c8a0beb1b..44a04123e 100644 --- a/packages/nodes-base/nodes/Asana/GenericFunctions.ts +++ b/packages/nodes-base/nodes/Asana/GenericFunctions.ts @@ -10,6 +10,7 @@ import { import { IDataObject, + INodePropertyOptions, } from 'n8n-workflow'; import { @@ -26,16 +27,10 @@ import { * @returns {Promise} */ export async function asanaApiRequest(this: IHookFunctions | IExecuteFunctions | ILoadOptionsFunctions, method: string, endpoint: string, body: object, query?: object, uri?: string | undefined): Promise { // tslint:disable-line:no-any - const credentials = this.getCredentials('asanaApi'); - - if (credentials === undefined) { - throw new Error('No credentials got returned!'); - } + const authenticationMethod = this.getNodeParameter('authentication', 0); const options: OptionsWithUri = { - headers: { - Authorization: `Bearer ${credentials.accessToken}`, - }, + headers: {}, method, body: { data: body }, qs: query, @@ -44,13 +39,30 @@ export async function asanaApiRequest(this: IHookFunctions | IExecuteFunctions | }; try { - return await this.helpers.request!(options); + if (authenticationMethod === 'accessToken') { + const credentials = this.getCredentials('asanaApi'); + + if (credentials === undefined) { + throw new Error('No credentials got returned!'); + } + + options.headers!['Authorization'] = `Bearer ${credentials.accessToken}`; + + return await this.helpers.request!(options); + } else { + //@ts-ignore + return await this.helpers.requestOAuth2.call(this, 'asanaOAuth2Api', options); + } } catch (error) { if (error.statusCode === 401) { // Return a clear error throw new Error('The Asana credentials are not valid!'); } + if (error.statusCode === 403) { + throw error; + } + if (error.response && error.response.body && error.response.body.errors) { // Try to return the error prettier const errorMessages = error.response.body.errors.map((errorData: { message: string }) => { @@ -82,3 +94,30 @@ export async function asanaApiRequestAllItems(this: IExecuteFunctions | ILoadOpt return returnData; } + +export async function getWorkspaces(this: ILoadOptionsFunctions): Promise < INodePropertyOptions[] > { + const endpoint = '/workspaces'; + const responseData = await asanaApiRequestAllItems.call(this, 'GET', endpoint, {}); + + const returnData: INodePropertyOptions[] = []; + for(const workspaceData of responseData) { + if (workspaceData.resource_type !== 'workspace') { + // Not sure if for some reason also ever other resources + // get returned but just in case filter them out + continue; + } + + returnData.push({ + name: workspaceData.name, + value: workspaceData.gid, + }); + } + + returnData.sort((a, b) => { + if (a.name < b.name) { return -1; } + if (a.name > b.name) { return 1; } + return 0; + }); + + return returnData; +} diff --git a/packages/nodes-base/package.json b/packages/nodes-base/package.json index 762c7e042..500c3f994 100644 --- a/packages/nodes-base/package.json +++ b/packages/nodes-base/package.json @@ -33,6 +33,7 @@ "dist/credentials/AirtableApi.credentials.js", "dist/credentials/Amqp.credentials.js", "dist/credentials/AsanaApi.credentials.js", + "dist/credentials/AsanaOAuth2Api.credentials.js", "dist/credentials/Aws.credentials.js", "dist/credentials/AffinityApi.credentials.js", "dist/credentials/BannerbearApi.credentials.js", From 2fee4396f1c06e6137da937aed85b13ef02ef2e6 Mon Sep 17 00:00:00 2001 From: Jan Oberhauser Date: Wed, 16 Sep 2020 23:55:34 +0200 Subject: [PATCH 044/284] :bug: Fix Test-Webhook registration issues --- packages/cli/src/ActiveWorkflowRunner.ts | 2 ++ packages/cli/src/TestWebhooks.ts | 29 +++++++++--------------- 2 files changed, 13 insertions(+), 18 deletions(-) diff --git a/packages/cli/src/ActiveWorkflowRunner.ts b/packages/cli/src/ActiveWorkflowRunner.ts index a7a4a4cf2..ae462c0b3 100644 --- a/packages/cli/src/ActiveWorkflowRunner.ts +++ b/packages/cli/src/ActiveWorkflowRunner.ts @@ -317,6 +317,8 @@ export class ActiveWorkflowRunner { await workflow.runWebhookMethod('delete', webhookData, NodeExecuteFunctions, mode, false); } + await WorkflowHelpers.saveStaticData(workflow); + // if it's a mongo objectId convert it to string if (typeof workflowData.id === 'object') { workflowData.id = workflowData.id.toString(); diff --git a/packages/cli/src/TestWebhooks.ts b/packages/cli/src/TestWebhooks.ts index 129425854..2eb213ebf 100644 --- a/packages/cli/src/TestWebhooks.ts +++ b/packages/cli/src/TestWebhooks.ts @@ -3,11 +3,9 @@ import * as express from 'express'; import { IResponseCallbackData, IWorkflowDb, - NodeTypes, Push, ResponseHelper, WebhookHelpers, - WorkflowHelpers, } from './'; import { @@ -31,6 +29,7 @@ export class TestWebhooks { sessionId?: string; timeout: NodeJS.Timeout, workflowData: IWorkflowDb; + workflow: Workflow; }; } = {}; private activeWebhooks: ActiveWebhooks | null = null; @@ -64,10 +63,13 @@ export class TestWebhooks { const webhookKey = this.activeWebhooks!.getWebhookKey(webhookData.httpMethod, webhookData.path); - const workflowData = this.testWebhookData[webhookKey].workflowData; + // TODO: Clean that duplication up one day and improve code generally + if (this.testWebhookData[webhookKey] === undefined) { + // The requested webhook is not registered + throw new ResponseHelper.ResponseError(`The requested webhook "${httpMethod} ${path}" is not registered.`, 404, 404); + } - const nodeTypes = NodeTypes(); - const workflow = new Workflow({ id: webhookData.workflowId, name: workflowData.name, nodes: workflowData.nodes, connections: workflowData.connections, active: workflowData.active, nodeTypes, staticData: workflowData.staticData, settings: workflowData.settings}); + const workflow = this.testWebhookData[webhookKey].workflow; // Get the node which has the webhook defined to know where to start from and to // get additional data @@ -157,16 +159,14 @@ export class TestWebhooks { for (const webhookData of webhooks) { key = this.activeWebhooks!.getWebhookKey(webhookData.httpMethod, webhookData.path); - await this.activeWebhooks!.add(workflow, webhookData, mode); - this.testWebhookData[key] = { sessionId, timeout, + workflow, workflowData, }; - // Save static data! - this.testWebhookData[key].workflowData.staticData = workflow.staticData; + await this.activeWebhooks!.add(workflow, webhookData, mode); } return true; @@ -181,8 +181,6 @@ export class TestWebhooks { * @memberof TestWebhooks */ cancelTestWebhook(workflowId: string): boolean { - const nodeTypes = NodeTypes(); - let foundWebhook = false; for (const webhookKey of Object.keys(this.testWebhookData)) { const webhookData = this.testWebhookData[webhookKey]; @@ -205,8 +203,7 @@ export class TestWebhooks { } } - const workflowData = webhookData.workflowData; - const workflow = new Workflow({ id: workflowData.id.toString(), name: workflowData.name, nodes: workflowData.nodes, connections: workflowData.connections, active: workflowData.active, nodeTypes, staticData: workflowData.staticData, settings: workflowData.settings }); + const workflow = this.testWebhookData[webhookKey].workflow; // Remove the webhook delete this.testWebhookData[webhookKey]; @@ -225,14 +222,10 @@ export class TestWebhooks { return; } - const nodeTypes = NodeTypes(); - - let workflowData: IWorkflowDb; let workflow: Workflow; const workflows: Workflow[] = []; for (const webhookKey of Object.keys(this.testWebhookData)) { - workflowData = this.testWebhookData[webhookKey].workflowData; - workflow = new Workflow({ id: workflowData.id.toString(), name: workflowData.name, nodes: workflowData.nodes, connections: workflowData.connections, active: workflowData.active, nodeTypes, staticData: workflowData.staticData, settings: workflowData.settings }); + workflow = this.testWebhookData[webhookKey].workflow; workflows.push(workflow); } From b90f4fb3f64725d92420f01c05ff0968dc907c20 Mon Sep 17 00:00:00 2001 From: Jan Oberhauser Date: Wed, 16 Sep 2020 23:57:27 +0200 Subject: [PATCH 045/284] :zap: Deactivate Asana Trigger webhhook validation --- .../nodes/Asana/AsanaTrigger.node.ts | 19 ++++++++++++------- 1 file changed, 12 insertions(+), 7 deletions(-) diff --git a/packages/nodes-base/nodes/Asana/AsanaTrigger.node.ts b/packages/nodes-base/nodes/Asana/AsanaTrigger.node.ts index 2aad0cc9e..537831416 100644 --- a/packages/nodes-base/nodes/Asana/AsanaTrigger.node.ts +++ b/packages/nodes-base/nodes/Asana/AsanaTrigger.node.ts @@ -197,6 +197,7 @@ export class AsanaTrigger implements INodeType { // that no webhooks are registred anymore delete webhookData.webhookId; delete webhookData.webhookEvents; + delete webhookData.hookSecret; } return true; @@ -233,13 +234,17 @@ export class AsanaTrigger implements INodeType { return {}; } - // Check if the request is valid - // (if the signature matches to data and hookSecret) - const computedSignature = createHmac('sha256', webhookData.hookSecret as string).update(JSON.stringify(req.body)).digest('hex'); - if (headerData['x-hook-signature'] !== computedSignature) { - // Signature is not valid so ignore call - return {}; - } + // TODO: Had to be deactivated as it is currently not possible to get the secret + // in production mode as the static data overwrites each other because the + // two exist at the same time (create webhook [with webhookId] and receive + // webhook [with secret]) + // // Check if the request is valid + // // (if the signature matches to data and hookSecret) + // const computedSignature = createHmac('sha256', webhookData.hookSecret as string).update(JSON.stringify(req.body)).digest('hex'); + // if (headerData['x-hook-signature'] !== computedSignature) { + // // Signature is not valid so ignore call + // return {}; + // } return { workflowData: [ From 4056b6861c71fc12452a1924e1ec79fbd1d9ff42 Mon Sep 17 00:00:00 2001 From: Ahsan Virani Date: Thu, 17 Sep 2020 11:24:34 +0200 Subject: [PATCH 046/284] :zap: Ignore credentials endpoint in Auth (#966) --- packages/cli/src/Server.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/packages/cli/src/Server.ts b/packages/cli/src/Server.ts index 82682345a..55a3920d8 100644 --- a/packages/cli/src/Server.ts +++ b/packages/cli/src/Server.ts @@ -172,7 +172,8 @@ class App { async config(): Promise { this.versions = await GenericHelpers.getVersions(); - const authIgnoreRegex = new RegExp(`^\/(healthz|${this.endpointWebhook}|${this.endpointWebhookTest})\/?.*$`); + const ignoredEndpoints = _(['healthz', this.endpointWebhook, this.endpointWebhookTest, this.endpointPresetCredentials]).compact().join('|'); + const authIgnoreRegex = new RegExp(`^\/(${ignoredEndpoints})\/?.*$`); // Check for basic auth credentials if activated const basicAuthActive = config.get('security.basicAuth.active') as boolean; From ddf995fca132f729560dd4bddcc286a9981faf34 Mon Sep 17 00:00:00 2001 From: Harshil Agrawal Date: Thu, 17 Sep 2020 10:56:07 +0000 Subject: [PATCH 047/284] Add S3 documentation URL (#967) --- packages/nodes-base/credentials/S3.credentials.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/nodes-base/credentials/S3.credentials.ts b/packages/nodes-base/credentials/S3.credentials.ts index 9d5b08c19..36bd96bea 100644 --- a/packages/nodes-base/credentials/S3.credentials.ts +++ b/packages/nodes-base/credentials/S3.credentials.ts @@ -7,6 +7,7 @@ import { export class S3 implements ICredentialType { name = 's3'; displayName = 'S3'; + documentationUrl = 's3'; properties = [ { displayName: 'S3 endpoint', From cfe3f22204647cc7e2027844f1881ddb344a659f Mon Sep 17 00:00:00 2001 From: Rupenieks Date: Thu, 17 Sep 2020 16:22:24 +0200 Subject: [PATCH 048/284] :zap: Coupon->Update returnData format fix, coupon description fix --- packages/nodes-base/nodes/Paddle/CouponDescription.ts | 4 ++-- packages/nodes-base/nodes/Paddle/Paddle.node.ts | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/packages/nodes-base/nodes/Paddle/CouponDescription.ts b/packages/nodes-base/nodes/Paddle/CouponDescription.ts index 179ec825b..7f60c73c6 100644 --- a/packages/nodes-base/nodes/Paddle/CouponDescription.ts +++ b/packages/nodes-base/nodes/Paddle/CouponDescription.ts @@ -131,7 +131,7 @@ export const couponFields = [ displayName: 'Discount Amount Currency', name: 'discountAmount', type: 'number', - default: '', + default: 1, description: 'Discount amount in currency.', typeOptions: { minValue: 1 @@ -157,7 +157,7 @@ export const couponFields = [ displayName: 'Discount Amount %', name: 'discountAmount', type: 'number', - default: '', + default: 1, description: 'Discount amount in percentage.', typeOptions: { minValue: 1, diff --git a/packages/nodes-base/nodes/Paddle/Paddle.node.ts b/packages/nodes-base/nodes/Paddle/Paddle.node.ts index 94e1246f4..eda54da1d 100644 --- a/packages/nodes-base/nodes/Paddle/Paddle.node.ts +++ b/packages/nodes-base/nodes/Paddle/Paddle.node.ts @@ -210,7 +210,7 @@ export class Paddle implements INodeType { const discountAmount = this.getNodeParameter('discountAmount', i) as number; if (couponType === 'product') { - body.product_ids = (this.getNodeParameter('productIds', i) as string[]).join(); + body.product_ids = this.getNodeParameter('productIds', i) as string; } if (discountType === 'flat') { @@ -253,7 +253,7 @@ export class Paddle implements INodeType { const endpoint = '/2.1/product/create_coupon'; responseData = await paddleApiRequest.call(this, endpoint, 'POST', body); - responseData = responseData.response.coupon_codes; + responseData = responseData.response.coupon_codes.map((coupon : string) => ({coupon})); } } From 690e76e18789cc62095c61b6d16fbfb279af611b Mon Sep 17 00:00:00 2001 From: Jan Oberhauser Date: Thu, 17 Sep 2020 21:26:34 +0200 Subject: [PATCH 049/284] :zap: Add hook which gets called once n8n is ready --- packages/cli/src/Server.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/packages/cli/src/Server.ts b/packages/cli/src/Server.ts index 55a3920d8..d06527f87 100644 --- a/packages/cli/src/Server.ts +++ b/packages/cli/src/Server.ts @@ -1890,5 +1890,7 @@ export async function start(): Promise { const versions = await GenericHelpers.getVersions(); console.log(`n8n ready on ${ADDRESS}, port ${PORT}`); console.log(`Version: ${versions.cli}`); + + await app.externalHooks.run('n8n.ready', []); }); } From ce5a043fd417db1ecffa07ec6e3e77c5da1ee846 Mon Sep 17 00:00:00 2001 From: Ricardo Espinoza Date: Thu, 17 Sep 2020 17:02:23 -0400 Subject: [PATCH 050/284] :zap: Add retweet, reply and like operations (#964) --- .../nodes/Twitter/TweetDescription.ts | 113 +++++++++++++++++- .../nodes/Twitter/TweetInterface.ts | 1 + .../nodes-base/nodes/Twitter/Twitter.node.ts | 36 ++++++ 3 files changed, 149 insertions(+), 1 deletion(-) diff --git a/packages/nodes-base/nodes/Twitter/TweetDescription.ts b/packages/nodes-base/nodes/Twitter/TweetDescription.ts index 12cb87ebf..7e3204612 100644 --- a/packages/nodes-base/nodes/Twitter/TweetDescription.ts +++ b/packages/nodes-base/nodes/Twitter/TweetDescription.ts @@ -18,13 +18,23 @@ export const tweetOperations = [ { name: 'Create', value: 'create', - description: 'Create a new tweet', + description: 'Create or reply a tweet', }, { name: 'Search', value: 'search', description: 'Search tweets', }, + { + name: 'Like', + value: 'like', + description: 'Like a tweet', + }, + { + name: 'Retweet', + value: 'retweet', + description: 'Retweet a tweet', + }, ], default: 'create', description: 'The operation to perform.', @@ -87,6 +97,13 @@ export const tweetFields = [ default: false, description: 'Whether or not to put a pin on the exact coordinates a Tweet has been sent from.', }, + { + displayName: 'In Reply to Tweet', + name: 'inReplyToStatusId', + type: 'string', + default: '', + description: 'The ID of an existing status that the update is in reply to.', + }, { displayName: 'Location', name: 'locationFieldsUi', @@ -321,4 +338,98 @@ export const tweetFields = [ }, ], }, +/* -------------------------------------------------------------------------- */ +/* tweet:like */ +/* -------------------------------------------------------------------------- */ + { + displayName: 'Tweet ID', + name: 'tweetId', + type: 'string', + required: true, + default: '', + displayOptions: { + show: { + operation: [ + 'like', + ], + resource: [ + 'tweet', + ], + }, + }, + description: 'The ID of the tweet', + }, + { + displayName: 'Additional Fields', + name: 'additionalFields', + type: 'collection', + placeholder: 'Add Field', + default: {}, + displayOptions: { + show: { + operation: [ + 'like', + ], + resource: [ + 'tweet', + ], + }, + }, + options: [ + { + displayName: 'include Entities', + name: 'includeEntities', + type: 'boolean', + default: false, + description: 'The entities node will be omitted when set to false', + }, + ], + }, +/* -------------------------------------------------------------------------- */ +/* tweet:retweet */ +/* -------------------------------------------------------------------------- */ + { + displayName: 'Tweet ID', + name: 'tweetId', + type: 'string', + required: true, + default: '', + displayOptions: { + show: { + operation: [ + 'retweet', + ], + resource: [ + 'tweet', + ], + }, + }, + description: 'The ID of the tweet', + }, + { + displayName: 'Additional Fields', + name: 'additionalFields', + type: 'collection', + placeholder: 'Add Field', + default: {}, + displayOptions: { + show: { + operation: [ + 'retweet', + ], + resource: [ + 'tweet', + ], + }, + }, + options: [ + { + displayName: 'Trim User', + name: 'trimUser', + type: 'boolean', + default: false, + description: `When set to either true, each tweet returned in a timeline will include a user object including only the status authors numerical ID.`, + }, + ], + }, ] as INodeProperties[]; diff --git a/packages/nodes-base/nodes/Twitter/TweetInterface.ts b/packages/nodes-base/nodes/Twitter/TweetInterface.ts index 10b16d6a2..564e92cc1 100644 --- a/packages/nodes-base/nodes/Twitter/TweetInterface.ts +++ b/packages/nodes-base/nodes/Twitter/TweetInterface.ts @@ -5,4 +5,5 @@ export interface ITweet { media_ids?: string; possibly_sensitive?: boolean; status: string; + in_reply_to_status_id?: string; } diff --git a/packages/nodes-base/nodes/Twitter/Twitter.node.ts b/packages/nodes-base/nodes/Twitter/Twitter.node.ts index a62f9fd95..bab8fd657 100644 --- a/packages/nodes-base/nodes/Twitter/Twitter.node.ts +++ b/packages/nodes-base/nodes/Twitter/Twitter.node.ts @@ -28,6 +28,7 @@ import { import { ITweet, } from './TweetInterface'; +import { isDate } from 'util'; const ISO6391 = require('iso-639-1'); @@ -109,6 +110,10 @@ export class Twitter implements INodeType { status: text, }; + if (additionalFields.inReplyToStatusId) { + body.in_reply_to_status_id = additionalFields.inReplyToStatusId as string; + } + if (additionalFields.attachments) { const mediaIds = []; const attachments = additionalFields.attachments as string; @@ -225,6 +230,7 @@ export class Twitter implements INodeType { } } + console.log(body); responseData = await twitterApiRequest.call(this, 'POST', '/statuses/update.json', body); } // https://developer.twitter.com/en/docs/tweets/search/api-reference/get-search-tweets @@ -268,6 +274,36 @@ export class Twitter implements INodeType { responseData = responseData.statuses; } } + //https://developer.twitter.com/en/docs/twitter-api/v1/tweets/post-and-engage/api-reference/post-favorites-create + if (operation === 'like') { + const tweetId = this.getNodeParameter('tweetId', i) as string; + const additionalFields = this.getNodeParameter('additionalFields', i) as IDataObject; + + const qs: IDataObject = { + id: tweetId, + }; + + if (additionalFields.includeEntities) { + qs.include_entities = additionalFields.includeEntities as boolean; + } + + responseData = await twitterApiRequest.call(this, 'POST', '/favorites/create.json', {}, qs); + } + //https://developer.twitter.com/en/docs/twitter-api/v1/tweets/post-and-engage/api-reference/post-statuses-retweet-id + if (operation === 'retweet') { + const tweetId = this.getNodeParameter('tweetId', i) as string; + const additionalFields = this.getNodeParameter('additionalFields', i) as IDataObject; + + const qs: IDataObject = { + id: tweetId, + }; + + if (additionalFields.trimUser) { + qs.trim_user = additionalFields.trimUser as boolean; + } + + responseData = await twitterApiRequest.call(this, 'POST', `/statuses/retweet/${tweetId}.json`, {}, qs); + } } if (Array.isArray(responseData)) { returnData.push.apply(returnData, responseData as IDataObject[]); From 4192e7d3b4eda2a3e3011c162b7d752efe19f255 Mon Sep 17 00:00:00 2001 From: Jan Oberhauser Date: Thu, 17 Sep 2020 23:03:59 +0200 Subject: [PATCH 051/284] :zap: Minor improvements to Twitter-Node --- packages/nodes-base/nodes/Twitter/TweetDescription.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/nodes-base/nodes/Twitter/TweetDescription.ts b/packages/nodes-base/nodes/Twitter/TweetDescription.ts index 7e3204612..b7ddcf6c5 100644 --- a/packages/nodes-base/nodes/Twitter/TweetDescription.ts +++ b/packages/nodes-base/nodes/Twitter/TweetDescription.ts @@ -377,11 +377,11 @@ export const tweetFields = [ }, options: [ { - displayName: 'include Entities', + displayName: 'Include Entities', name: 'includeEntities', type: 'boolean', default: false, - description: 'The entities node will be omitted when set to false', + description: 'The entities will be omitted when set to false', }, ], }, From a5655d035284801969a30ab8ab8bb08687e9a795 Mon Sep 17 00:00:00 2001 From: Ricardo Espinoza Date: Thu, 17 Sep 2020 17:06:58 -0400 Subject: [PATCH 052/284] :zap: Add self hosted support to Sentry.io Node (#965) --- .../SentryIoServerApi.credentials.ts | 23 ++++++ .../nodes/SentryIo/GenericFunctions.ts | 17 ++++- .../nodes/SentryIo/SentryIo.node.ts | 70 ++++++++++++++++++- packages/nodes-base/package.json | 1 + 4 files changed, 109 insertions(+), 2 deletions(-) create mode 100644 packages/nodes-base/credentials/SentryIoServerApi.credentials.ts diff --git a/packages/nodes-base/credentials/SentryIoServerApi.credentials.ts b/packages/nodes-base/credentials/SentryIoServerApi.credentials.ts new file mode 100644 index 000000000..0f2a50510 --- /dev/null +++ b/packages/nodes-base/credentials/SentryIoServerApi.credentials.ts @@ -0,0 +1,23 @@ +import { + ICredentialType, + NodePropertyTypes, +} from 'n8n-workflow'; + +export class SentryIoServerApi implements ICredentialType { + name = 'sentryIoServerApi'; + displayName = 'Sentry.io API'; + properties = [ + { + displayName: 'Token', + name: 'token', + type: 'string' as NodePropertyTypes, + default: '', + }, { + displayName: 'URL', + name: 'url', + type: 'string' as NodePropertyTypes, + default: '', + placeholder: 'https://example.com', + }, + ]; +} diff --git a/packages/nodes-base/nodes/SentryIo/GenericFunctions.ts b/packages/nodes-base/nodes/SentryIo/GenericFunctions.ts index 82105de7d..a5bcfd64a 100644 --- a/packages/nodes-base/nodes/SentryIo/GenericFunctions.ts +++ b/packages/nodes-base/nodes/SentryIo/GenericFunctions.ts @@ -17,6 +17,8 @@ import { export async function sentryIoApiRequest(this: IHookFunctions | IExecuteFunctions | IExecuteSingleFunctions | ILoadOptionsFunctions | IWebhookFunctions, method: string, resource: string, body: any = {}, qs: IDataObject = {}, uri?: string, option: IDataObject = {}): Promise { // tslint:disable-line:no-any const authentication = this.getNodeParameter('authentication', 0); + const version = this.getNodeParameter('sentryVersion', 0); + const options: OptionsWithUri = { headers: {}, method, @@ -37,10 +39,22 @@ export async function sentryIoApiRequest(this: IHookFunctions | IExecuteFunction delete options.qs.limit; } + let credentialName; + try { if (authentication === 'accessToken') { - const credentials = this.getCredentials('sentryIoApi'); + if (version === 'cloud') { + credentialName = 'sentryIoApi'; + } else { + credentialName = 'sentryIoServerApi'; + } + + const credentials = this.getCredentials(credentialName); + + if (credentials?.url) { + options.uri = `${credentials?.url}${resource}`; + } options.headers = { Authorization: `Bearer ${credentials?.token}`, @@ -50,6 +64,7 @@ export async function sentryIoApiRequest(this: IHookFunctions | IExecuteFunction return this.helpers.request(options); } else { + return await this.helpers.requestOAuth2!.call(this, 'sentryIoOAuth2Api', options); } diff --git a/packages/nodes-base/nodes/SentryIo/SentryIo.node.ts b/packages/nodes-base/nodes/SentryIo/SentryIo.node.ts index 74213e543..3996ad294 100644 --- a/packages/nodes-base/nodes/SentryIo/SentryIo.node.ts +++ b/packages/nodes-base/nodes/SentryIo/SentryIo.node.ts @@ -45,7 +45,12 @@ import { sentryIoApiRequest, sentryApiRequestAllItems, } from './GenericFunctions'; -import { ICommit, IPatchSet, IRef } from './Interface'; + +import { + ICommit, + IPatchSet, + IRef, +} from './Interface'; export class SentryIo implements INodeType { description: INodeTypeDescription = { @@ -71,6 +76,9 @@ export class SentryIo implements INodeType { authentication: [ 'oAuth2', ], + sentryVersion: [ + 'cloud', + ], }, }, }, @@ -82,15 +90,55 @@ export class SentryIo implements INodeType { authentication: [ 'accessToken', ], + sentryVersion: [ + 'cloud', + ], + }, + }, + }, + { + name: 'sentryIoServerApi', + required: true, + displayOptions: { + show: { + authentication: [ + 'accessToken', + ], + sentryVersion: [ + 'server', + ], }, }, }, ], properties: [ + { + displayName: 'Sentry Version', + name: 'sentryVersion', + type: 'options', + options: [ + { + name: 'Cloud', + value: 'cloud', + }, + { + name: 'Server (Self Hosted)', + value: 'server', + }, + ], + default: 'cloud', + }, { displayName: 'Authentication', name: 'authentication', type: 'options', + displayOptions: { + show: { + sentryVersion: [ + 'cloud', + ], + }, + }, options: [ { name: 'Access Token', @@ -104,6 +152,26 @@ export class SentryIo implements INodeType { default: 'accessToken', description: 'The resource to operate on.', }, + { + displayName: 'Authentication', + name: 'authentication', + type: 'options', + displayOptions: { + show: { + sentryVersion: [ + 'server', + ], + }, + }, + options: [ + { + name: 'Access Token', + value: 'accessToken', + }, + ], + default: 'accessToken', + description: 'The resource to operate on.', + }, { displayName: 'Resource', name: 'resource', diff --git a/packages/nodes-base/package.json b/packages/nodes-base/package.json index 500c3f994..ba6cb1a53 100644 --- a/packages/nodes-base/package.json +++ b/packages/nodes-base/package.json @@ -143,6 +143,7 @@ "dist/credentials/ShopifyApi.credentials.js", "dist/credentials/SalesforceOAuth2Api.credentials.js", "dist/credentials/SentryIoApi.credentials.js", + "dist/credentials/SentryIoServerApi.credentials.js", "dist/credentials/SentryIoOAuth2Api.credentials.js", "dist/credentials/SlackApi.credentials.js", "dist/credentials/SlackOAuth2Api.credentials.js", From 1461e084785d2c73ad36f07b90ac632dcb2d7abb Mon Sep 17 00:00:00 2001 From: Ricardo Espinoza Date: Thu, 17 Sep 2020 17:10:13 -0400 Subject: [PATCH 053/284] :zap: Small improvement (#957) --- packages/nodes-base/credentials/GoogleOAuth2Api.credentials.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/nodes-base/credentials/GoogleOAuth2Api.credentials.ts b/packages/nodes-base/credentials/GoogleOAuth2Api.credentials.ts index 55d9bc66e..0dc677ea8 100644 --- a/packages/nodes-base/credentials/GoogleOAuth2Api.credentials.ts +++ b/packages/nodes-base/credentials/GoogleOAuth2Api.credentials.ts @@ -27,7 +27,7 @@ export class GoogleOAuth2Api implements ICredentialType { displayName: 'Auth URI Query Parameters', name: 'authQueryParameters', type: 'hidden' as NodePropertyTypes, - default: 'access_type=offline', + default: 'access_type=offline&prompt=consent', }, { displayName: 'Authentication', From bbfe59c2111d849ecccef0757d68fcaa1667abb0 Mon Sep 17 00:00:00 2001 From: Ricardo Espinoza Date: Thu, 17 Sep 2020 17:20:12 -0400 Subject: [PATCH 054/284] :sparkles: Add Taiga Integration (#939) * Add Taiga integration * :zap: Improvements to Taiga-Node * :zap: Improvements * :zap: Small improvements * :zap: Improvements * :zap: Change project slug for project id * :zap: Small improvement Co-authored-by: renanfilipe --- .../credentials/TaigaCloudApi.credentials.ts | 23 ++ .../credentials/TaigaServerApi.credentials.ts | 30 ++ .../nodes/Taiga/GenericFunctions.ts | 129 +++++++ .../nodes-base/nodes/Taiga/IssueOperations.ts | 40 ++ packages/nodes-base/nodes/Taiga/Taiga.node.ts | 329 ++++++++++++++++ .../nodes/Taiga/TaigaTrigger.node.ts | 225 +++++++++++ .../nodes/Taiga/issueOperationFields.ts | 357 ++++++++++++++++++ packages/nodes-base/nodes/Taiga/taiga.png | Bin 0 -> 4820 bytes packages/nodes-base/package.json | 4 + 9 files changed, 1137 insertions(+) create mode 100644 packages/nodes-base/credentials/TaigaCloudApi.credentials.ts create mode 100644 packages/nodes-base/credentials/TaigaServerApi.credentials.ts create mode 100644 packages/nodes-base/nodes/Taiga/GenericFunctions.ts create mode 100644 packages/nodes-base/nodes/Taiga/IssueOperations.ts create mode 100644 packages/nodes-base/nodes/Taiga/Taiga.node.ts create mode 100644 packages/nodes-base/nodes/Taiga/TaigaTrigger.node.ts create mode 100644 packages/nodes-base/nodes/Taiga/issueOperationFields.ts create mode 100644 packages/nodes-base/nodes/Taiga/taiga.png diff --git a/packages/nodes-base/credentials/TaigaCloudApi.credentials.ts b/packages/nodes-base/credentials/TaigaCloudApi.credentials.ts new file mode 100644 index 000000000..8dc8d771d --- /dev/null +++ b/packages/nodes-base/credentials/TaigaCloudApi.credentials.ts @@ -0,0 +1,23 @@ +import { + ICredentialType, + NodePropertyTypes, +} from 'n8n-workflow'; + +export class TaigaCloudApi implements ICredentialType { + name = 'taigaCloudApi'; + displayName = 'Taiga Cloud API'; + properties = [ + { + displayName: 'Username', + name: 'username', + type: 'string' as NodePropertyTypes, + default: '', + }, + { + displayName: 'Password', + name: 'password', + type: 'string' as NodePropertyTypes, + default: '', + }, + ]; +} diff --git a/packages/nodes-base/credentials/TaigaServerApi.credentials.ts b/packages/nodes-base/credentials/TaigaServerApi.credentials.ts new file mode 100644 index 000000000..6e57b3b29 --- /dev/null +++ b/packages/nodes-base/credentials/TaigaServerApi.credentials.ts @@ -0,0 +1,30 @@ +import { + ICredentialType, + NodePropertyTypes, +} from 'n8n-workflow'; + +export class TaigaServerApi implements ICredentialType { + name = 'taigaServerApi'; + displayName = 'Taiga Server API'; + properties = [ + { + displayName: 'Username', + name: 'username', + type: 'string' as NodePropertyTypes, + default: '', + }, + { + displayName: 'Password', + name: 'password', + type: 'string' as NodePropertyTypes, + default: '', + }, + { + displayName: 'URL', + name: 'url', + type: 'string' as NodePropertyTypes, + default: '', + placeholder: 'https://taiga.yourdomain.com', + }, + ]; +} diff --git a/packages/nodes-base/nodes/Taiga/GenericFunctions.ts b/packages/nodes-base/nodes/Taiga/GenericFunctions.ts new file mode 100644 index 000000000..a73ee457b --- /dev/null +++ b/packages/nodes-base/nodes/Taiga/GenericFunctions.ts @@ -0,0 +1,129 @@ +import { + OptionsWithUri, + } from 'request'; + +import { + IExecuteFunctions, + IExecuteSingleFunctions, + IHookFunctions, + ILoadOptionsFunctions, + IWebhookFunctions, +} from 'n8n-core'; + +import { + ICredentialDataDecryptedObject, + IDataObject, + } from 'n8n-workflow'; + + import { + createHash, +} from 'crypto'; + +export async function getAuthorization( + this: IHookFunctions | IExecuteFunctions | IExecuteSingleFunctions | ILoadOptionsFunctions | IWebhookFunctions, + credentials?: ICredentialDataDecryptedObject, +): Promise { + if (credentials === undefined) { + throw new Error('No credentials got returned!'); + } + + const { password, username } = credentials; + const options: OptionsWithUri = { + headers: { 'Content-Type': 'application/json' }, + method: 'POST', + body: { + type: 'normal', + password, + username, + }, + uri: (credentials.url) ? `${credentials.url}/api/v1/auth` : 'https://api.taiga.io/api/v1/auth', + json: true, + }; + + try { + const response = await this.helpers.request!(options); + + return response.auth_token; + } catch (error) { + throw new Error('Taiga Error: ' + error.err || error); + } +} + +export async function taigaApiRequest( + this: IHookFunctions | IExecuteFunctions | IExecuteSingleFunctions | ILoadOptionsFunctions | IWebhookFunctions, + method: string, + resource: string, + body = {}, + query = {}, + uri?: string | undefined, + option = {}, +): Promise { // tslint:disable-line:no-any + + const version = this.getNodeParameter('version', 0, 'cloud') as string; + + let credentials; + + if (version === 'server') { + credentials = this.getCredentials('taigaServerApi') as ICredentialDataDecryptedObject; + } else { + credentials = this.getCredentials('taigaCloudApi') as ICredentialDataDecryptedObject; + } + + const authToken = await getAuthorization.call(this, credentials); + + const options: OptionsWithUri = { + headers: { + 'Content-Type': 'application/json', + }, + auth: { + bearer: authToken, + }, + qs: query, + method, + body, + uri: uri || (credentials.url) ? `${credentials.url}/api/v1${resource}` : `https://api.taiga.io/api/v1${resource}`, + json: true + }; + + if (Object.keys(option).length !== 0) { + Object.assign(options, option); + } + + try { + return await this.helpers.request!(options); + } catch (error) { + let errorMessage = error; + if (error.response.body && error.response.body._error_message) { + errorMessage = error.response.body._error_message; + } + + throw new Error(`Taigan error response [${error.statusCode}]: ${errorMessage}`); + } +} + +export async function taigaApiRequestAllItems(this: IHookFunctions | IExecuteFunctions| ILoadOptionsFunctions, method: string, resource: string, body: any = {}, query: IDataObject = {}): Promise { // tslint:disable-line:no-any + + const returnData: IDataObject[] = []; + + let responseData; + + let uri: string | undefined; + + do { + responseData = await taigaApiRequest.call(this, method, resource, body, query, uri, { resolveWithFullResponse: true }); + returnData.push.apply(returnData, responseData.body); + uri = responseData.headers['x-pagination-next']; + if (query.limit && returnData.length >= query.limit) { + return returnData; + } + } while ( + responseData.headers['x-pagination-next'] !== undefined && + responseData.headers['x-pagination-next'] !== '' + ); + return returnData; +} + +export function getAutomaticSecret(credentials: ICredentialDataDecryptedObject) { + const data = `${credentials.username},${credentials.password}`; + return createHash('md5').update(data).digest('hex'); +} diff --git a/packages/nodes-base/nodes/Taiga/IssueOperations.ts b/packages/nodes-base/nodes/Taiga/IssueOperations.ts new file mode 100644 index 000000000..b20d1e439 --- /dev/null +++ b/packages/nodes-base/nodes/Taiga/IssueOperations.ts @@ -0,0 +1,40 @@ +import { + INodeProperties, +} from 'n8n-workflow'; + +export const issueOperations = [ + { + displayName: 'Operation', + name: 'operation', + type: 'options', + options: [ + { + name: 'Create', + value: 'create', + description: 'Create an issue', + }, + { + name: 'Delete', + value: 'delete', + description: 'Delete an issue', + }, + { + name: 'Get', + value: 'get', + description: 'Get an issue', + }, + { + name: 'Get All', + value: 'getAll', + description: 'Get all issues', + }, + { + name: 'Update', + value: 'update', + description: 'Update an issue', + }, + ], + default: 'create', + description: 'The operation to perform.', + }, +] as INodeProperties[]; diff --git a/packages/nodes-base/nodes/Taiga/Taiga.node.ts b/packages/nodes-base/nodes/Taiga/Taiga.node.ts new file mode 100644 index 000000000..be16ed357 --- /dev/null +++ b/packages/nodes-base/nodes/Taiga/Taiga.node.ts @@ -0,0 +1,329 @@ +import { + IExecuteFunctions, +} from 'n8n-core'; + +import { + IDataObject, + ILoadOptionsFunctions, + INodeExecutionData, + INodePropertyOptions, + INodeType, + INodeTypeDescription, +} from 'n8n-workflow'; + +import { + taigaApiRequest, + taigaApiRequestAllItems, +} from './GenericFunctions'; + +import { + issueOperations, +} from './IssueOperations'; + +import { + issueOperationFields, +} from './issueOperationFields'; + +export class Taiga implements INodeType { + description: INodeTypeDescription = { + displayName: 'Taiga', + name: 'taiga', + icon: 'file:taiga.png', + group: ['transform'], + version: 1, + subtitle: '={{$parameter["operation"] + ": " + $parameter["resource"]}}', + description: 'Consume Taiga API', + defaults: { + name: 'Taiga', + color: '#772244', + }, + inputs: ['main'], + outputs: ['main'], + credentials: [ + { + name: 'taigaCloudApi', + displayOptions: { + show: { + version: [ + 'cloud', + ], + }, + }, + required: true, + }, + { + name: 'taigaServerApi', + displayOptions: { + show: { + version: [ + 'server', + ], + }, + }, + required: true, + }, + ], + properties: [ + { + displayName: 'Taiga Version', + name: 'version', + type: 'options', + options: [ + { + name: 'Cloud', + value: 'cloud', + }, + { + name: 'Server (Self Hosted)', + value: 'server', + }, + ], + default: 'cloud', + }, + { + displayName: 'Resource', + name: 'resource', + type: 'options', + options: [ + { + name: 'Issue', + value: 'issue', + }, + ], + default: 'issue', + description: 'Resource to consume.', + }, + ...issueOperations, + ...issueOperationFields, + ], + }; + + methods = { + loadOptions: { + // Get all the available tags to display them to user so that he can + // select them easily + async getTypes(this: ILoadOptionsFunctions): Promise { + const projectId = this.getCurrentNodeParameter('projectId') as string; + + const returnData: INodePropertyOptions[] = []; + + const types = await taigaApiRequest.call(this, 'GET', `/issue-types?project=${projectId}`); + for (const type of types) { + const typeName = type.name; + const typeId = type.id; + returnData.push({ + name: typeName, + value: typeId, + }); + } + return returnData; + }, + + // Get all the available statuses to display them to user so that he can + // select them easily + async getStatuses(this: ILoadOptionsFunctions): Promise { + const returnData: INodePropertyOptions[] = []; + + const projectId = this.getCurrentNodeParameter('projectId') as string; + + const statuses = await taigaApiRequest.call(this,'GET', '/issue-statuses', {}, { project: projectId }); + for (const status of statuses) { + const statusName = status.name; + const statusId = status.id; + returnData.push({ + name: statusName, + value: statusId, + }); + } + return returnData; + }, + + // Get all the available users to display them to user so that he can + // select them easily + async getProjectUsers(this: ILoadOptionsFunctions): Promise { + const returnData: INodePropertyOptions[] = []; + + const projectId = this.getCurrentNodeParameter('projectId') as string; + + const users = await taigaApiRequest.call(this,'GET', '/users', {}, { project: projectId }); + for (const user of users) { + const userName = user.username; + const userId = user.id; + returnData.push({ + name: userName, + value: userId, + }); + } + return returnData; + }, + + // Get all the available priorities to display them to user so that he can + // select them easily + async getProjectPriorities(this: ILoadOptionsFunctions): Promise { + const returnData: INodePropertyOptions[] = []; + + const projectId = this.getCurrentNodeParameter('projectId') as string; + + const priorities = await taigaApiRequest.call(this,'GET', '/priorities', {}, { project: projectId }); + for (const priority of priorities) { + const priorityName = priority.name; + const priorityId = priority.id; + returnData.push({ + name: priorityName, + value: priorityId, + }); + } + return returnData; + }, + + // Get all the available severities to display them to user so that he can + // select them easily + async getProjectSeverities(this: ILoadOptionsFunctions): Promise { + const returnData: INodePropertyOptions[] = []; + + const projectId = this.getCurrentNodeParameter('projectId') as string; + + const severities = await taigaApiRequest.call(this,'GET', '/severities', {}, { project: projectId }); + for (const severity of severities) { + const severityName = severity.name; + const severityId = severity.id; + returnData.push({ + name: severityName, + value: severityId, + }); + } + return returnData; + }, + + // Get all the available milestones to display them to user so that he can + // select them easily + async getProjectMilestones(this: ILoadOptionsFunctions): Promise { + const returnData: INodePropertyOptions[] = []; + + const projectId = this.getCurrentNodeParameter('projectId') as string; + + const milestones = await taigaApiRequest.call(this,'GET', '/milestones', {}, { project: projectId }); + for (const milestone of milestones) { + const milestoneName = milestone.name; + const milestoneId = milestone.id; + returnData.push({ + name: milestoneName, + value: milestoneId, + }); + } + return returnData; + }, + + // Get all the available projects to display them to user so that he can + // select them easily + async getUserProjects(this: ILoadOptionsFunctions): Promise { + const returnData: INodePropertyOptions[] = []; + + const { id } = await taigaApiRequest.call(this, 'GET', '/users/me'); + + const projects = await taigaApiRequest.call(this,'GET', '/projects', {}, { member: id }); + for (const project of projects) { + const projectName = project.name; + const projectId = project.id; + returnData.push({ + name: projectName, + value: projectId, + }); + } + return returnData; + }, + }, + }; + + async execute(this: IExecuteFunctions): Promise { + const items = this.getInputData(); + const returnData: IDataObject[] = []; + let responseData; + + const resource = this.getNodeParameter('resource', 0) as string; + const operation = this.getNodeParameter('operation', 0) as string; + + const qs: IDataObject = {}; + + for (let i = 0; i < items.length; i++) { + if (resource === 'issue') { + if (operation === 'create') { + const projectId = this.getNodeParameter('projectId', i) as number; + + const subject = this.getNodeParameter('subject', i) as string; + + const additionalFields = this.getNodeParameter('additionalFields', i) as IDataObject; + + const body: IDataObject = { + project: projectId, + subject, + }; + + Object.assign(body, additionalFields); + + if (body.tags) { + body.tags = (body.tags as string).split(',') as string[]; + } + + responseData = await taigaApiRequest.call(this, 'POST', '/issues', body); + } + + if (operation === 'update') { + + const issueId = this.getNodeParameter('issueId', i) as string; + + const updateFields = this.getNodeParameter('updateFields', i) as IDataObject; + + const body: IDataObject = {}; + + Object.assign(body, updateFields); + + if (body.tags) { + body.tags = (body.tags as string).split(',') as string[]; + } + + const { version } = await taigaApiRequest.call(this, 'GET', `/issues/${issueId}`); + + body.version = version; + + responseData = await taigaApiRequest.call(this, 'PATCH', `/issues/${issueId}`, body); + } + + if (operation === 'delete') { + const issueId = this.getNodeParameter('issueId', i) as string; + responseData = await taigaApiRequest.call(this, 'DELETE', `/issues/${issueId}`); + responseData = { success: true }; + } + + if (operation === 'get') { + const issueId = this.getNodeParameter('issueId', i) as string; + responseData = await taigaApiRequest.call(this, 'GET', `/issues/${issueId}`); + } + + if (operation === 'getAll') { + + const projectId = this.getNodeParameter('projectId', i) as number; + const returnAll = this.getNodeParameter('returnAll', i) as boolean; + + qs.project = projectId; + + if (returnAll === true) { + responseData = await taigaApiRequestAllItems.call(this, 'GET', '/issues', {}, qs); + + } else { + qs.limit = this.getNodeParameter('limit', i) as number; + responseData = await taigaApiRequestAllItems.call(this, 'GET', '/issues', {}, qs); + responseData = responseData.splice(0, qs.limit); + } + } + } + if (Array.isArray(responseData)) { + returnData.push.apply(returnData, responseData as IDataObject[]); + } else { + returnData.push(responseData as IDataObject); + } + } + + return [this.helpers.returnJsonArray(returnData)]; + } +} diff --git a/packages/nodes-base/nodes/Taiga/TaigaTrigger.node.ts b/packages/nodes-base/nodes/Taiga/TaigaTrigger.node.ts new file mode 100644 index 000000000..952ca721a --- /dev/null +++ b/packages/nodes-base/nodes/Taiga/TaigaTrigger.node.ts @@ -0,0 +1,225 @@ +import { + ICredentialDataDecryptedObject, + IDataObject, + ILoadOptionsFunctions, + INodePropertyOptions, + INodeType, + INodeTypeDescription, + IWebhookFunctions, + IWebhookResponseData, +} from 'n8n-workflow'; + +import { + IHookFunctions, +} from 'n8n-core'; + +import { + taigaApiRequest, + getAutomaticSecret, +} from './GenericFunctions'; + +// import { +// createHmac, +// } from 'crypto'; + +export class TaigaTrigger implements INodeType { + description: INodeTypeDescription = { + displayName: 'Taiga Trigger', + name: 'taigaTrigger', + icon: 'file:taiga.png', + group: ['trigger'], + version: 1, + subtitle: '={{"project:" + $parameter["projectSlug"]}}', + description: 'Handle Taiga events via webhook', + defaults: { + name: 'Taiga Trigger', + color: '#772244', + }, + inputs: [], + outputs: ['main'], + credentials: [ + { + name: 'taigaCloudApi', + displayOptions: { + show: { + version: [ + 'cloud', + ], + }, + }, + required: true, + }, + { + name: 'taigaServerApi', + displayOptions: { + show: { + version: [ + 'server', + ], + }, + }, + required: true, + }, + ], + webhooks: [ + { + name: 'default', + httpMethod: 'POST', + responseMode: 'onReceived', + path: 'webhook', + }, + ], + properties: [ + { + displayName: 'Taiga Version', + name: 'version', + type: 'options', + options: [ + { + name: 'Cloud', + value: 'cloud', + }, + { + name: 'Server (Self Hosted)', + value: 'server', + }, + ], + default: 'cloud', + }, + { + displayName: 'Project ID', + name: 'projectId', + type: 'options', + typeOptions: { + loadOptionsMethod: 'getUserProjects', + }, + default: '', + description: 'Project ID', + required: true, + }, + ], + }; + + methods = { + loadOptions: { + // Get all the available projects to display them to user so that he can + // select them easily + async getUserProjects(this: ILoadOptionsFunctions): Promise { + const returnData: INodePropertyOptions[] = []; + + const { id } = await taigaApiRequest.call(this, 'GET', '/users/me'); + + const projects = await taigaApiRequest.call(this,'GET', '/projects', {}, { member: id }); + for (const project of projects) { + const projectName = project.name; + const projectId = project.id; + returnData.push({ + name: projectName, + value: projectId, + }); + } + return returnData; + }, + }, + }; + + // @ts-ignore + webhookMethods = { + default: { + async checkExists(this: IHookFunctions): Promise { + const webhookUrl = this.getNodeWebhookUrl('default') as string; + + const webhookData = this.getWorkflowStaticData('node'); + + const endpoint = `/webhooks`; + + const webhooks = await taigaApiRequest.call(this, 'GET', endpoint); + + for (const webhook of webhooks) { + if (webhook.url === webhookUrl) { + webhookData.webhookId = webhook.id; + webhookData.key = webhook.key; + return true; + } + } + + return false; + }, + async create(this: IHookFunctions): Promise { + const version = this.getNodeParameter('version') as string; + + let credentials; + + if (version === 'server') { + credentials = this.getCredentials('taigaServerApi') as ICredentialDataDecryptedObject; + } else { + credentials = this.getCredentials('taigaCloudApi') as ICredentialDataDecryptedObject; + } + + const webhookUrl = this.getNodeWebhookUrl('default') as string; + + const webhookData = this.getWorkflowStaticData('node'); + + const projectId = this.getNodeParameter('projectId') as string; + + const key = getAutomaticSecret(credentials); + + const body: IDataObject = { + name: `n8n-webhook:${webhookUrl}`, + url: webhookUrl, + key, //can't validate the secret, see: https://github.com/taigaio/taiga-back/issues/1031 + project: projectId, + }; + const { id } = await taigaApiRequest.call(this, 'POST', '/webhooks', body); + + webhookData.webhookId = id; + webhookData.key = key; + + return true; + }, + async delete(this: IHookFunctions): Promise { + const webhookData = this.getWorkflowStaticData('node'); + try { + await taigaApiRequest.call(this, 'DELETE', `/webhooks/${webhookData.webhookId}`); + } catch(error) { + return false; + } + delete webhookData.webhookId; + delete webhookData.key; + return true; + }, + }, + }; + + async webhook(this: IWebhookFunctions): Promise { + //const webhookData = this.getWorkflowStaticData('node'); + const req = this.getRequestObject(); + const bodyData = req.body; + //const headerData = this.getHeaderData(); + + + // TODO + // Validate signature + // https://github.com/taigaio/taiga-back/issues/1031 + + // //@ts-ignore + // const requestSignature: string = headerData['x-taiga-webhook-signature']; + + // if (requestSignature === undefined) { + // return {}; + // } + + // //@ts-ignore + // const computedSignature = createHmac('sha1', webhookData.key as string).update(JSON.stringify(bodyData)).digest('hex'); + + // if (requestSignature !== computedSignature) { + // return {}; + // } + + return { + workflowData: [ + this.helpers.returnJsonArray(bodyData) + ], + }; + } +} diff --git a/packages/nodes-base/nodes/Taiga/issueOperationFields.ts b/packages/nodes-base/nodes/Taiga/issueOperationFields.ts new file mode 100644 index 000000000..1989d59d0 --- /dev/null +++ b/packages/nodes-base/nodes/Taiga/issueOperationFields.ts @@ -0,0 +1,357 @@ +import { + INodeProperties, +} from 'n8n-workflow'; + +export const issueOperationFields = [ + { + displayName: 'Project ID', + name: 'projectId', + type: 'options', + typeOptions: { + loadOptionsMethod: 'getUserProjects', + }, + displayOptions: { + show: { + resource: [ + 'issue', + ], + operation: [ + 'create', + 'getAll', + 'update', + ], + }, + }, + default: '', + description: 'The project ID.', + required: true, + }, + { + displayName: 'Subject', + name: 'subject', + type: 'string', + displayOptions: { + show: { + resource: [ + 'issue', + ], + operation: [ + 'create', + ], + }, + }, + default: '', + required: true, + }, + { + displayName: 'Additional Fields', + name: 'additionalFields', + type: 'collection', + placeholder: 'Add Field', + default: {}, + displayOptions: { + show: { + resource: [ + 'issue', + ], + operation: [ + 'create', + ], + }, + }, + options: [ + { + displayName: 'Assigned To', + name: 'assigned_to', + type: 'options', + typeOptions: { + loadOptionsDependsOn: [ + 'projectId', + ], + loadOptionsMethod: 'getProjectUsers', + }, + default: '', + description: 'User id to you want assign the issue to', + }, + { + displayName: 'Blocked Note', + name: 'blocked_note', + type: 'string', + default: '', + description: 'Reason why the issue is blocked', + }, + { + displayName: 'Description', + name: 'description', + type: 'string', + default: '', + }, + { + displayName: 'Is Blocked', + name: 'is_blocked', + type: 'boolean', + default: false, + }, + { + displayName: 'Is Closed', + name: 'is_closed', + type: 'boolean', + default: false, + }, + { + displayName: 'Milestone ID', + name: 'milestone', + type: 'options', + typeOptions: { + loadOptionsDependsOn: [ + 'projectSlug', + ], + loadOptionsMethod: 'getProjectMilestones', + }, + default: '', + }, + { + displayName: 'Priority ID', + name: 'priority', + type: 'options', + typeOptions: { + loadOptionsDependsOn: [ + 'projectSlug', + ], + loadOptionsMethod: 'getProjectPriorities', + }, + default: '', + }, + { + displayName: 'Severity ID', + name: 'severity', + type: 'options', + typeOptions: { + loadOptionsDependsOn: [ + 'projectSlug', + ], + loadOptionsMethod: 'getProjectSeverities', + }, + default: '', + }, + { + displayName: 'Status ID', + name: 'status', + type: 'options', + typeOptions: { + loadOptionsMethod: 'getStatuses', + }, + default: '', + }, + { + displayName: 'Tags', + name: 'tags', + type: 'string', + description: 'Tags separated by comma.', + default: '', + placeholder: 'product, sales', + }, + { + displayName: 'Type ID', + name: 'type', + type: 'options', + typeOptions: { + loadOptionsDependsOn: [ + 'projectSlug', + ], + loadOptionsMethod: 'getTypes' + }, + default: '', + }, + ], + }, + { + displayName: 'Issue ID', + name: 'issueId', + type: 'string', + displayOptions: { + show: { + resource: [ + 'issue', + ], + operation: [ + 'update', + 'delete', + 'get', + ], + }, + }, + default: '', + required: true, + }, + { + displayName: 'Update Fields', + name: 'updateFields', + type: 'collection', + placeholder: 'Add Field', + default: {}, + displayOptions: { + show: { + resource: [ + 'issue', + ], + operation: [ + 'update', + ], + }, + }, + options: [ + { + displayName: 'Assigned To', + name: 'assigned_to', + type: 'options', + typeOptions: { + loadOptionsDependsOn: [ + 'projectId', + ], + loadOptionsMethod: 'getProjectUsers', + }, + default: '', + description: 'User id to you want assign the issue to', + }, + { + displayName: 'Blocked Note', + name: 'blocked_note', + type: 'string', + default: '', + description: 'Reason why the issue is blocked', + }, + { + displayName: 'Description', + name: 'description', + type: 'string', + default: '', + }, + { + displayName: 'Is Blocked', + name: 'is_blocked', + type: 'boolean', + default: false, + }, + { + displayName: 'Is Closed', + name: 'is_closed', + type: 'boolean', + default: false, + }, + { + displayName: 'Milestone ID', + name: 'milestone', + type: 'options', + typeOptions: { + loadOptionsDependsOn: [ + 'projectSlug', + ], + loadOptionsMethod: 'getProjectMilestones', + }, + default: '', + }, + { + displayName: 'Priority ID', + name: 'priority', + type: 'options', + typeOptions: { + loadOptionsDependsOn: [ + 'projectSlug', + ], + loadOptionsMethod: 'getProjectPriorities', + }, + default: '', + }, + { + displayName: 'Severity ID', + name: 'severity', + type: 'options', + typeOptions: { + loadOptionsDependsOn: [ + 'projectSlug', + ], + loadOptionsMethod: 'getProjectSeverities', + }, + default: '', + }, + { + displayName: 'Status ID', + name: 'status', + type: 'options', + typeOptions: { + loadOptionsMethod: 'getStatuses', + }, + default: '', + }, + { + displayName: 'Subject', + name: 'subject', + type: 'string', + default: '', + }, + { + displayName: 'Tags', + name: 'tags', + type: 'string', + description: 'Tags separated by comma.', + default: '', + placeholder: 'product, sales', + }, + { + displayName: 'Type ID', + name: 'type', + type: 'options', + typeOptions: { + loadOptionsDependsOn: [ + 'projectSlug', + ], + loadOptionsMethod: 'getTypes' + }, + default: '', + }, + ], + }, + { + displayName: 'Return All', + name: 'returnAll', + type: 'boolean', + displayOptions: { + show: { + operation: [ + 'getAll', + ], + resource: [ + 'issue', + ], + }, + }, + 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: [ + 'issue', + ], + returnAll: [ + false, + ], + }, + }, + typeOptions: { + minValue: 1, + maxValue: 500, + }, + default: 100, + description: 'How many results to return.', + }, + +] as INodeProperties[]; diff --git a/packages/nodes-base/nodes/Taiga/taiga.png b/packages/nodes-base/nodes/Taiga/taiga.png new file mode 100644 index 0000000000000000000000000000000000000000..8976bae6efd011318ae3067112dec40b75db9535 GIT binary patch literal 4820 zcmV;_5-aVAP)004R>004l5008;`004mK004C`008P>0026e000+ooVrmw00006 zVoOIv0RI600RN!9r;`8x00(qQO+^Rf2ptR^9^ZSPtN;KJ*hxe|RA}DaTX}GuS9SlL zd%xvv+9gYhWyf|BCr$!M2_^{*VVVpvp>(D|*J+2gY?(rULMer$0}arb042jf0|^i) znQ2R>4HOs(W5N;)O&n*zONi?wN^DC{PkNHx`fYbP{o_4JPqOr$FqLQ8GxJ88_uYH$ z`JL_FbH4is=aD}0(@$X6uV0DM^xVrBputT;`w#E?IX-;jU!OM_obw5I^R6u@P0wxi z(*ErLs92bu7`uGyne!=wIA06hH~ApKYM60T-djn~l_cm2FYUiItOS|+&V2R%7vO&w ze*oi8ouq6}_H{yvR|8-GEQHvT>C3);{O8ZnXHS0Vya1LPy};k?zXzKy+k)=Fo@=Ei z-$O*XCSZUdNzkReqkYHvFX%sW)t+ncqX+hGs5~2P;o%)4s1_?@j_16Chz1wIHi(I6 z!1bJWlnbS?(ThhnTAYmq{Q1-Wh_Di5yk^`2u&WHSB*Yb7+Iw4A3p1bJ;CS9Jz@Pr_ z`*GxlhbY&dd%bYP9xS))N@C&2TXOwfuOENH!#pYMCnu}b@BF{sW^$N1k&%m^DS%6Yr79yW5LW_sa)#L z%Wk|PCxpl+QIgk*&VT5~ADi|wUa^!fl|iV@JU^|z^3E?>BEm(xcVc$BIOe6@w>RU~ zqBAiO4LGjz_POHR>8&riaB+YB;9q=H zk&DLOAYJ(?A~Nd)3@`*3(nj{-;GysvvL*)|*BNEzQO9wIE!%!6WG}!OBH^PEk_13n zYoD$MwcDAwdiw|d?A$C|pRYm1DjL{2G}Q!a78FH-(g4!Zm4g6wdT9>;Sh~`ryRuy% zj96nCfC51gNRyx{5k<=uG4PR~8x}4sTreDA#hbI2WM7M;R3V~883hD46i#R=eo>J` zDC7$hL86kjI&BNMXQUSwU%QiM#tV7rIxAz4SNsJ4c1MjJn@9+`LLuA`irp1c2)w9tUN*;v#4g^1E_fZ&9bhfne5G2!;b(t-?tmY@e=p)7 zhS8<~MvOKwPedZ!l^c?-lWN1#UA88JVBxMhk^%G}h0EYC4LPUjHP{n7z^vQDv z+ynvuGav*V#*vW1ZKD)su*1x)fCVfZti5FsQg9*LIyEskQc4x9?VKAs0*-1CbgU|D zxzq*>thFYr)@Q7>YE5PgiPrNwL$aS}wepa@=bWzhuKh}VX7SC_$nae|7Nrl3R8B4(a1mFFsRrMX`naJoAU z6|01q3$5R?Wxx^6Pd6v514wO-UptrqhH#!{S{RT35f zC?UkNRD@n0+ff*2=VnS{rP=bRQrcTof;Cfr^1HvM$2)9Ez&h&TL*GA$m;b>H0Aq{< z-JokG7Z;Kd6PO%p_Dx_i09d4COtc8vT4R-)@^VDfs#F4?gb-B#1ce?LWR^XRpmCU{ zFi-J!Z=%=tBxeG%3+m+$qbRi zDpRWjQE__inLGac?y@x&M~*%9%LBgsp7&zYmSM8S?)H55gCxWaB&i3q_8ivin!sd1 zureiM0M;lSJD!^sQo2M06)JJyfhV$O4oq~9UOM7At|OXp8{m+bLIT6ga?yp2@!J`N znGIX+mqNby^yGL!TYcsgyI;kRJ^je~0KfPZSL020-3DuIx_@NwcO2JwFA1>&ja{>g zh`e?Jz(Sh9>Co2>_dNTpqaGUu7$}}B zN);*$i;;qpcPmj8=gM={kvLXez5|nKU(@`MS?ApL&b!>3>-;ZQZ z>^<4dhG}6oTgJ@E84*oIsw>m$2Gwwr*6m%m#j=eW(Q=KMYc0bBPGsH(#W7?${MO!ch!1%&fpr%&Zu!z|agfVAyrYxFDyt z);_GYy3hAhryXYdQa|P2PJ&+4V#SW$x}fo<5Zs)#U|C^|aZc+#05CJg zCr-M?n(EGNyUqv_8fP~d))Ut&<&;8*TV2=N7RS+NMYDo&&Vkul3jhpe3ueQ-j^ce8 zS+=~Q_V^_^oJGyU3dam62f+R$jwf8tyG=-OOB4Kh77@%Wz>v&v!0a%?1+&YI!-;d@ z?yU|OYVw))ZQq{cql&N;C6EQkm3oL1H@*ZCc5}YVNn%4uO#d!$Tv>3$zOdh9=<@!b+4R z^*FoASnliXpB@|8GMn+6zSZltOsN|EIhc7%n zIei*m_}ZtI)@z-8VDDqd^>$%kbg&ZE!~Kry)QIR(BFcb4)^K0bvTcbO9G2s9X`)_8 zLLJ4mC{xML4=?ny7C=iWm6VQFTKS&qMMBCT(Q#dn5RPzCw7kk%6)=H}5Tb&{scYI; zb6*hFzv8-H>HQynD-N|gS^~f?_8-AdzxfERxp}vmpPoCM$z_iVA-01s!rJzsLxp9v z)z#@>rcwxsR$Fb9c1vewM|G@HLH`B0>^Qgh=`d*q7pxo&IBY#B`OXqtahZE z5+vHLM<6Dm8i1#@(RZJkJo&KWc={7xxO3IUvb0s)g0tT-W{a=e}}xM=a~=cqDZV%REpi1Qiu(?jFeuxhW?F0$F8M zwHVIUiqSk<=7rO5T#eGTneyPut%#Y`0>I|~tcG6R9Fiu)3J^N5QenT>w(g_r}gq%UWM(KUVmIE{{wWNd)E1Q5KQHhsMr|VM6#j`-e#dRo!<0_p5 zm9Sh;M#TUmvpunW>!tfW&&P@JXMZ&dZ`rXOFC0H9diwhs7cdCWkxAxHSMx+fMp@Gu z7|cwSLTNAw;>;@SxSfR;Jt~RQbMv!dXV?ufi# zWNW*IJ%DwzX;q@&`Mw8Wc*#zHHEfMu4i*6F#cEGDU+-Om9XCtM1@sDQ91-h5y{B9$ zoyEtpHLYNqG@K-{AA~h@qK<$a$ANTY4nX^B3k1ZqaMieN0ce@opm`c%=GBB>7)#}H zX`mj|vmHca0R6t7?nd%g$9aeIiUw;f=sjI2zRhD>7G9ZU>(S+TBxwci^V zYux$y*&(fUyU=4I>L#F9iq6;0Is$IidqV)y?Z%NHmad~fZQcYjbgM<17T0L=#!)(s zYK`MHL`)NjO;`uD1$^n%SHX2%2q`zAabRE5!X$+49_cN*X)jz;va@5B*GgOf;JRKk z&^IvC0Wbh~L^SOCDco@F&F2pI@~f{#I-3DoJBoE|VNS|b-6MT7!jY|3Yi+o#vRN(y zZtY)@5W@8L_0M>o-~Q4@gCu~_l%K{$+b(M_W4nhzy@sO)57XH8ZKGJ-?Upy%7)W)e z>)FAssp?c|n3=ih#+sRxHMX*nfiboQXhB3SKn@W(L?pVqdZx3PTy2G-x#hE55Tene z#}5f>OZ(~tTW=;qQEpwTGIm=uC^At1Zr7x#-qF>8~_cWVs zs|pDK%r?^6Mhl8-0{)TX$gdlt2SGGSz$gi^35)?DsD~Np<_m!ZnIyo&y*&eIfa~or zwFTVO)6=+gKolBn9v6ZNTI;+JGOtuJB}mL9NnDDfs0JYZ&wu$>(>pkT%eVa+nEAL6 za)^ko!uslGMx$^)VvW7$^qEsHJb7pz;v~Xv-}*;V2;upDy36ytUbc3~b-iI@&8X|R zqt@CHA!LwFrF{VB4p?gi5~51LM~hR1D5%w<{Has+*Z16lf4+D1`^}}|EarQQ7#`Z3 zPgHy_k^C5dO;}&+cb+YO&UgJ6s`WDBq|v$gkMI7I0T4Dn(*TaW?Tzol&mR9FMusjB z*=$#8XmB*8R9x<))&Gr7<*Ky%sv9v;c!9QU+4=i|#Jd4_Xm4r5%%88-%MYgfwB7&o zk2gfF4Z9#dS$F|g?7RlXm=i+CO+?f#eF|p&zBcN9&-LTSp8C;7iM6pe;?LyAapBm- zn%N#FqAQ7L6lYsmF!-po_HH4>%%jbBKpRCHej~nCufTP^JTu>mM*OY30x{3le$Mw( zr=lo0w?%#l+VI~z6p9lV9vZ{x$>%0}yZaRp?Z#5FG|cvadQkf|v&9pK_Wj=n3;+{T z&*O@nFM%=UWOJ+9g+|=&)kfd%xqh;L1LO9*0|pl2`I1IF3%ZhklC}2ZLdfEyPd05UK#FfA}REiyS&F)}(bIXW{iEig1XFffATQ1Ji&03~!qSaf7zbY(hiZ)9m^ uc>ppnGB7PLI4v?cR53C-GC4XkFfA}NIxsNvx#&#*0000 Date: Thu, 17 Sep 2020 23:20:27 +0200 Subject: [PATCH 055/284] :zap: Minor improvements to Taiga-Node --- packages/nodes-base/nodes/Taiga/GenericFunctions.ts | 8 ++++---- packages/nodes-base/nodes/Taiga/Taiga.node.ts | 12 ++++++------ packages/nodes-base/nodes/Taiga/TaigaTrigger.node.ts | 4 ++-- 3 files changed, 12 insertions(+), 12 deletions(-) diff --git a/packages/nodes-base/nodes/Taiga/GenericFunctions.ts b/packages/nodes-base/nodes/Taiga/GenericFunctions.ts index a73ee457b..56f635793 100644 --- a/packages/nodes-base/nodes/Taiga/GenericFunctions.ts +++ b/packages/nodes-base/nodes/Taiga/GenericFunctions.ts @@ -1,6 +1,6 @@ import { OptionsWithUri, - } from 'request'; +} from 'request'; import { IExecuteFunctions, @@ -13,9 +13,9 @@ import { import { ICredentialDataDecryptedObject, IDataObject, - } from 'n8n-workflow'; +} from 'n8n-workflow'; - import { +import { createHash, } from 'crypto'; @@ -101,7 +101,7 @@ export async function taigaApiRequest( } } -export async function taigaApiRequestAllItems(this: IHookFunctions | IExecuteFunctions| ILoadOptionsFunctions, method: string, resource: string, body: any = {}, query: IDataObject = {}): Promise { // tslint:disable-line:no-any +export async function taigaApiRequestAllItems(this: IHookFunctions | IExecuteFunctions | ILoadOptionsFunctions, method: string, resource: string, body: any = {}, query: IDataObject = {}): Promise { // tslint:disable-line:no-any const returnData: IDataObject[] = []; diff --git a/packages/nodes-base/nodes/Taiga/Taiga.node.ts b/packages/nodes-base/nodes/Taiga/Taiga.node.ts index be16ed357..dd888afc6 100644 --- a/packages/nodes-base/nodes/Taiga/Taiga.node.ts +++ b/packages/nodes-base/nodes/Taiga/Taiga.node.ts @@ -126,7 +126,7 @@ export class Taiga implements INodeType { const projectId = this.getCurrentNodeParameter('projectId') as string; - const statuses = await taigaApiRequest.call(this,'GET', '/issue-statuses', {}, { project: projectId }); + const statuses = await taigaApiRequest.call(this, 'GET', '/issue-statuses', {}, { project: projectId }); for (const status of statuses) { const statusName = status.name; const statusId = status.id; @@ -145,7 +145,7 @@ export class Taiga implements INodeType { const projectId = this.getCurrentNodeParameter('projectId') as string; - const users = await taigaApiRequest.call(this,'GET', '/users', {}, { project: projectId }); + const users = await taigaApiRequest.call(this, 'GET', '/users', {}, { project: projectId }); for (const user of users) { const userName = user.username; const userId = user.id; @@ -164,7 +164,7 @@ export class Taiga implements INodeType { const projectId = this.getCurrentNodeParameter('projectId') as string; - const priorities = await taigaApiRequest.call(this,'GET', '/priorities', {}, { project: projectId }); + const priorities = await taigaApiRequest.call(this, 'GET', '/priorities', {}, { project: projectId }); for (const priority of priorities) { const priorityName = priority.name; const priorityId = priority.id; @@ -183,7 +183,7 @@ export class Taiga implements INodeType { const projectId = this.getCurrentNodeParameter('projectId') as string; - const severities = await taigaApiRequest.call(this,'GET', '/severities', {}, { project: projectId }); + const severities = await taigaApiRequest.call(this, 'GET', '/severities', {}, { project: projectId }); for (const severity of severities) { const severityName = severity.name; const severityId = severity.id; @@ -202,7 +202,7 @@ export class Taiga implements INodeType { const projectId = this.getCurrentNodeParameter('projectId') as string; - const milestones = await taigaApiRequest.call(this,'GET', '/milestones', {}, { project: projectId }); + const milestones = await taigaApiRequest.call(this, 'GET', '/milestones', {}, { project: projectId }); for (const milestone of milestones) { const milestoneName = milestone.name; const milestoneId = milestone.id; @@ -221,7 +221,7 @@ export class Taiga implements INodeType { const { id } = await taigaApiRequest.call(this, 'GET', '/users/me'); - const projects = await taigaApiRequest.call(this,'GET', '/projects', {}, { member: id }); + const projects = await taigaApiRequest.call(this, 'GET', '/projects', {}, { member: id }); for (const project of projects) { const projectName = project.name; const projectId = project.id; diff --git a/packages/nodes-base/nodes/Taiga/TaigaTrigger.node.ts b/packages/nodes-base/nodes/Taiga/TaigaTrigger.node.ts index 952ca721a..8dbdd6e2f 100644 --- a/packages/nodes-base/nodes/Taiga/TaigaTrigger.node.ts +++ b/packages/nodes-base/nodes/Taiga/TaigaTrigger.node.ts @@ -109,7 +109,7 @@ export class TaigaTrigger implements INodeType { const { id } = await taigaApiRequest.call(this, 'GET', '/users/me'); - const projects = await taigaApiRequest.call(this,'GET', '/projects', {}, { member: id }); + const projects = await taigaApiRequest.call(this, 'GET', '/projects', {}, { member: id }); for (const project of projects) { const projectName = project.name; const projectId = project.id; @@ -181,7 +181,7 @@ export class TaigaTrigger implements INodeType { const webhookData = this.getWorkflowStaticData('node'); try { await taigaApiRequest.call(this, 'DELETE', `/webhooks/${webhookData.webhookId}`); - } catch(error) { + } catch (error) { return false; } delete webhookData.webhookId; From c522ba75ca6d551b094943332cfaef385411da43 Mon Sep 17 00:00:00 2001 From: Rupenieks <32895755+Rupenieks@users.noreply.github.com> Date: Thu, 17 Sep 2020 23:32:12 +0200 Subject: [PATCH 056/284] :sparkles: Add LinkedIn Integration (#942) * :construction: node logic, request logic, authentication, logo, descriptions * :construction: Posting image, article and text finished. * :construction: Posting image, article and text in post finished * :zap: Post creation (image, articles, text) * :zap: Added post creation by organization, fixed up descriptions, credentials, indentation * :zap: Fix issues on LinkedIn-Node Co-authored-by: Jan Oberhauser Co-authored-by: Jan --- .../LinkedInOAuth2Api.credentials.ts | 55 ++++ .../nodes/LinkedIn/GenericFunctions.ts | 52 ++++ .../nodes/LinkedIn/LinkedIn.node.ts | 249 +++++++++++++++++ .../nodes/LinkedIn/PostDescription.ts | 250 ++++++++++++++++++ .../nodes-base/nodes/LinkedIn/linkedin.png | Bin 0 -> 1760 bytes packages/nodes-base/package.json | 2 + 6 files changed, 608 insertions(+) create mode 100644 packages/nodes-base/credentials/LinkedInOAuth2Api.credentials.ts create mode 100644 packages/nodes-base/nodes/LinkedIn/GenericFunctions.ts create mode 100644 packages/nodes-base/nodes/LinkedIn/LinkedIn.node.ts create mode 100644 packages/nodes-base/nodes/LinkedIn/PostDescription.ts create mode 100644 packages/nodes-base/nodes/LinkedIn/linkedin.png diff --git a/packages/nodes-base/credentials/LinkedInOAuth2Api.credentials.ts b/packages/nodes-base/credentials/LinkedInOAuth2Api.credentials.ts new file mode 100644 index 000000000..f0c9ba525 --- /dev/null +++ b/packages/nodes-base/credentials/LinkedInOAuth2Api.credentials.ts @@ -0,0 +1,55 @@ +import { + ICredentialType, + NodePropertyTypes, +} from 'n8n-workflow'; + + +export class LinkedInOAuth2Api implements ICredentialType { + name = 'linkedInOAuth2Api'; + extends = [ + 'oAuth2Api', + ]; + displayName = 'LinkedIn OAuth2 API'; + properties = [ + { + displayName: 'Organization Support', + name: 'organizationSupport', + type: 'boolean' as NodePropertyTypes, + default: true, + description: 'Request permissions to post as an orgaization.', + }, + { + displayName: 'Authorization URL', + name: 'authUrl', + type: 'hidden' as NodePropertyTypes, + default: 'https://www.linkedin.com/oauth/v2/authorization', + required: true, + }, + { + displayName: 'Access Token URL', + name: 'accessTokenUrl', + type: 'hidden' as NodePropertyTypes, + default: 'https://www.linkedin.com/oauth/v2/accessToken', + required: true, + }, + { + displayName: 'Scope', + name: 'scope', + type: 'hidden' as NodePropertyTypes, + default: '=r_liteprofile,r_emailaddress,w_member_social{{$parameter["organizationSupport"] === true ? ",w_organization_social":""}}', + description: 'Standard scopes for posting on behalf of a user or organization. See this resource .' + }, + { + displayName: 'Auth URI Query Parameters', + name: 'authQueryParameters', + type: 'hidden' as NodePropertyTypes, + default: '', + }, + { + displayName: 'Authentication', + name: 'authentication', + type: 'hidden' as NodePropertyTypes, + default: 'body', + }, + ]; +} diff --git a/packages/nodes-base/nodes/LinkedIn/GenericFunctions.ts b/packages/nodes-base/nodes/LinkedIn/GenericFunctions.ts new file mode 100644 index 000000000..23045edd8 --- /dev/null +++ b/packages/nodes-base/nodes/LinkedIn/GenericFunctions.ts @@ -0,0 +1,52 @@ +import { + OptionsWithUrl, +} from 'request'; + +import { + IExecuteFunctions, + IHookFunctions, + ILoadOptionsFunctions, +} from 'n8n-core'; + +export async function linkedInApiRequest(this: IHookFunctions | IExecuteFunctions | ILoadOptionsFunctions, method: string, endpoint: string, body: any = {}, binary?: boolean, headers?: object): Promise { // tslint:disable-line:no-any + const options: OptionsWithUrl = { + headers: { + 'Accept': 'application/json', + 'X-Restli-Protocol-Version': '2.0.0' + }, + method, + body, + url: binary ? endpoint : `https://api.linkedin.com/v2${endpoint}`, + json: true, + }; + + // If uploading binary data + if (binary) { + delete options.json; + options.encoding = null; + } + + if (Object.keys(body).length === 0) { + delete options.body; + } + + try { + return await this.helpers.requestOAuth2!.call(this, 'linkedInOAuth2Api', options, { tokenType: 'Bearer' }); + } catch (error) { + if (error.respose && error.response.body && error.response.body.detail) { + throw new Error(`Linkedin Error response [${error.statusCode}]: ${error.response.body.detail}`); + } + throw error; + } +} + + +export function validateJSON(json: string | undefined): any { // tslint:disable-line:no-any + let result; + try { + result = JSON.parse(json!); + } catch (exception) { + result = ''; + } + return result; +} diff --git a/packages/nodes-base/nodes/LinkedIn/LinkedIn.node.ts b/packages/nodes-base/nodes/LinkedIn/LinkedIn.node.ts new file mode 100644 index 000000000..6220b6771 --- /dev/null +++ b/packages/nodes-base/nodes/LinkedIn/LinkedIn.node.ts @@ -0,0 +1,249 @@ +import { IExecuteFunctions, BINARY_ENCODING } from 'n8n-core'; +import { + IDataObject, + INodeTypeDescription, + INodeExecutionData, + INodeType, + ILoadOptionsFunctions, + INodePropertyOptions, +} from 'n8n-workflow'; +import { linkedInApiRequest } from './GenericFunctions'; +import { postOperations, postFields } from './PostDescription'; + +export class LinkedIn implements INodeType { + description: INodeTypeDescription = { + displayName: 'LinkedIn', + name: 'linkedIn', + icon: 'file:linkedin.png', + group: ['input'], + version: 1, + description: 'Consume LinkedIn Api', + defaults: { + name: 'LinkedIn', + color: '#0075b4', + }, + inputs: ['main'], + outputs: ['main'], + credentials: [ + { + name: 'linkedInOAuth2Api', + required: true, + }, + ], + properties: [ + { + displayName: 'Resource', + name: 'resource', + type: 'options', + options: [ + { + name: 'Post', + value: 'post', + }, + ], + default: 'post', + description: 'The resource to consume', + }, + //POST + ...postOperations, + ...postFields, + ], + + + }; + + methods = { + loadOptions: { + // Get Person URN which has to be used with other LinkedIn API Requests + // https://docs.microsoft.com/en-us/linkedin/consumer/integrations/self-serve/sign-in-with-linkedin + async getPersonUrn(this: ILoadOptionsFunctions): Promise { + const returnData: INodePropertyOptions[] = []; + const person = await linkedInApiRequest.call(this, 'GET', '/me', {}); + returnData.push({ name: `${person.localizedFirstName} ${person.localizedLastName}`, value: person.id }); + return returnData; + }, + } + }; + + async execute(this: IExecuteFunctions): Promise { + const items = this.getInputData(); + const returnData: IDataObject[] = []; + let responseData; + const resource = this.getNodeParameter('resource', 0) as string; + const operation = this.getNodeParameter('operation', 0) as string; + let body = {}; + + for (let i = 0; i < items.length; i++) { + if (resource === 'post') { + if (operation === 'create') { + const text = this.getNodeParameter('text', i) as string; + const shareMediaCategory = this.getNodeParameter('shareMediaCategory', i) as string; + const postAs = this.getNodeParameter('postAs', i) as string; + const additionalFields = this.getNodeParameter('additionalFields', i) as IDataObject; + + let authorUrn = ''; + let visibility = 'PUBLIC'; + + if (postAs === 'person') { + const personUrn = this.getNodeParameter('person', i) as string; + // Only if posting as a person can user decide if post visible by public or connections + visibility = additionalFields.visibility as string || 'PUBLIC'; + authorUrn = `urn:li:person:${personUrn}`; + } else { + const organizationUrn = this.getNodeParameter('organization', i) as string; + authorUrn = `urn:li:organization:${organizationUrn}`; + } + + let description = ''; + let title = ''; + let originalUrl = ''; + + if (shareMediaCategory === 'IMAGE') { + + if (additionalFields.description) { + description = additionalFields.description as string; + } + if (additionalFields.title) { + title = additionalFields.title as string; + } + // Send a REQUEST to prepare a register of a media image file + const registerRequest = { + registerUploadRequest: { + recipes: [ + 'urn:li:digitalmediaRecipe:feedshare-image', + ], + owner: authorUrn, + serviceRelationships: [ + { + relationshipType: 'OWNER', + identifier: 'urn:li:userGeneratedContent', + }, + ], + }, + }; + + const registerObject = await linkedInApiRequest.call(this, 'POST', '/assets?action=registerUpload', registerRequest); + + // Response provides a specific upload URL that is used to upload the binary image file + const uploadUrl = registerObject.value.uploadMechanism['com.linkedin.digitalmedia.uploading.MediaUploadHttpRequest'].uploadUrl as string; + const asset = registerObject.value.asset as string; + + // Prepare binary file upload + const item = items[i]; + + if (item.binary === undefined) { + throw new Error('No binary data exists on item!'); + } + + const propertyNameUpload = this.getNodeParameter('binaryPropertyName', i) as string; + + if (item.binary[propertyNameUpload] === undefined) { + throw new Error(`No binary data property "${propertyNameUpload}" does not exists on item!`); + } + + // Buffer binary data + const buffer = Buffer.from(item.binary[propertyNameUpload].data, BINARY_ENCODING) as Buffer; + // Upload image + await linkedInApiRequest.call(this, 'POST', uploadUrl, buffer, true); + + body = { + author: authorUrn, + lifecycleState: 'PUBLISHED', + specificContent: { + 'com.linkedin.ugc.ShareContent': { + shareCommentary: { + text, + }, + shareMediaCategory: 'IMAGE', + media: [ + { + status: 'READY', + description: { + text: description, + }, + media: asset, + title: { + text: title, + }, + }, + ], + }, + }, + visibility: { + 'com.linkedin.ugc.MemberNetworkVisibility': visibility, + }, + }; + + } else if (shareMediaCategory === 'ARTICLE') { + const additionalFields = this.getNodeParameter('additionalFields', i) as IDataObject; + + if (additionalFields.description) { + description = additionalFields.description as string; + } + if (additionalFields.title) { + title = additionalFields.title as string; + } + if (additionalFields.originalUrl) { + originalUrl = additionalFields.originalUrl as string; + } + + body = { + author: `${authorUrn}`, + lifecycleState: 'PUBLISHED', + specificContent: { + 'com.linkedin.ugc.ShareContent': { + shareCommentary: { + text, + }, + shareMediaCategory, + media: [ + { + status: 'READY', + description: { + text: description + }, + originalUrl, + title: { + text: title + } + } + ] + } + }, + visibility: { + 'com.linkedin.ugc.MemberNetworkVisibility': visibility + } + }; + } else { + body = { + author: authorUrn, + lifecycleState: 'PUBLISHED', + specificContent: { + 'com.linkedin.ugc.ShareContent': { + shareCommentary: { + text, + }, + shareMediaCategory, + }, + }, + visibility: { + 'com.linkedin.ugc.MemberNetworkVisibility': visibility + } + }; + } + + const endpoint = '/ugcPosts'; + responseData = await linkedInApiRequest.call(this, 'POST', endpoint, body); + } + } + + if (Array.isArray(responseData)) { + returnData.push.apply(returnData, responseData as IDataObject[]); + } else { + returnData.push(responseData as IDataObject); + } + } + + return [this.helpers.returnJsonArray(returnData)]; + } +} diff --git a/packages/nodes-base/nodes/LinkedIn/PostDescription.ts b/packages/nodes-base/nodes/LinkedIn/PostDescription.ts new file mode 100644 index 000000000..7bfb57907 --- /dev/null +++ b/packages/nodes-base/nodes/LinkedIn/PostDescription.ts @@ -0,0 +1,250 @@ +import { INodeProperties } from "n8n-workflow"; + +export const postOperations = [ + { + displayName: 'Operation', + name: 'operation', + type: 'options', + displayOptions: { + show: { + resource: [ + 'post', + ], + }, + }, + options: [ + { + name: 'Create', + value: 'create', + description: 'Create a new post', + }, + ], + default: 'create', + description: 'The operation to perform.', + }, +] as INodeProperties[]; + +export const postFields = [ +/* -------------------------------------------------------------------------- */ +/* post:create */ +/* -------------------------------------------------------------------------- */ + { + displayName: 'Post As', + name: 'postAs', + type: 'options', + default: '', + description: 'If to post on behalf of a user or an organization.', + options: [ + { + name: 'Person', + value: 'person', + }, + { + name: 'Organization', + value: 'organization', + }, + ], + }, + { + displayName: 'Person', + name: 'person', + type: 'options', + typeOptions: { + loadOptionsMethod: 'getPersonUrn', + }, + default: '', + required: true, + description: 'Person as which the post should be posted as.', + displayOptions: { + show: { + operation: [ + 'create', + ], + postAs: [ + 'person', + ], + resource: [ + 'post', + ], + } + } + }, + { + displayName: 'Organization', + name: 'organization', + type: 'string', + default: '', + description: 'URN of Organization as which the post should be posted as', + displayOptions: { + show: { + operation: [ + 'create', + ], + postAs: [ + 'organization', + ], + resource: [ + 'post', + ], + }, + }, + }, + { + displayName: 'Text', + name: 'text', + type: 'string', + default: '', + description: 'The primary content of the post.', + displayOptions: { + show: { + operation: [ + 'create', + ], + resource: [ + 'post', + ], + }, + }, + }, + { + displayName: 'Media Category', + name: 'shareMediaCategory', + type: 'options', + default: 'NONE', + options: [ + { + name: 'None', + value: 'NONE', + description: 'The post does not contain any media, and will only consist of text', + }, + { + name: 'Article', + value: 'ARTICLE', + description: 'The post contains an article URL', + }, + { + name: 'Image', + value: 'IMAGE', + description: 'The post contains an image', + } + ], + displayOptions: { + show: { + operation: [ + 'create', + ], + resource: [ + 'post', + ], + }, + }, + }, + { + displayName: 'Binary Property', + displayOptions: { + show: { + operation: [ + 'create', + ], + resource: [ + 'post', + ], + shareMediaCategory: [ + 'IMAGE', + ], + }, + }, + name: 'binaryPropertyName', + type: 'string', + default: 'data', + description: 'Object property name which holds binary data', + required: true, + }, + { + displayName: 'Additional Fields', + name: 'additionalFields', + type: 'collection', + placeholder: 'Add Field', + default: {}, + displayOptions: { + show: { + operation: [ + 'create', + ], + resource: [ + 'post', + ], + }, + }, + options: [ + { + displayName: 'Description', + name: 'description', + type: 'string', + default: '', + description: 'Provide a short description for your image or article.', + displayOptions: { + show: { + '/shareMediaCategory': [ + 'ARTICLE', + 'IMAGE', + ], + }, + }, + }, + { + displayName: 'Original URL', + name: 'originalUrl', + type: 'string', + default: '', + description: 'Provide the URL of the article you would like to share here.', + displayOptions: { + show: { + '/shareMediaCategory': [ + 'ARTICLE', + ], + }, + }, + }, + { + displayName: 'Title', + name: 'title', + type: 'string', + default: '', + description: 'Customize the title of your image or article.', + displayOptions: { + show: { + '/shareMediaCategory': [ + 'ARTICLE', + 'IMAGE', + ], + }, + }, + }, + { + displayName: 'Visibility', + name: 'visibility', + type: 'options', + default: 'PUBLIC', + description: 'Dictate if post will be seen by the public or only connections.', + displayOptions: { + show: { + '/postAs': [ + 'person', + ], + }, + }, + options: [ + { + name: 'Connections', + value: 'CONNECTIONS', + }, + { + name: 'Public', + value: 'PUBLIC', + }, + ], + }, + ], + }, +] as INodeProperties[]; diff --git a/packages/nodes-base/nodes/LinkedIn/linkedin.png b/packages/nodes-base/nodes/LinkedIn/linkedin.png new file mode 100644 index 0000000000000000000000000000000000000000..dd53ef11d313ea62b09992a0e78599df78fad0e0 GIT binary patch literal 1760 zcmZ{kc{tnY7RP^@gorBbSSwVki`3FsVu-D_O61zLmJ(}JCPhV1N~NZap-ro#T3dyR zt(Hbf?P4e%4NbY72vSRJQ;gkQ)pllVM?t< zs;pJY(;vkq!r0;4T}9EITPag{M;rE#7bRZF-h}uZhHl!ymVjLesXIp_5ATAe<20u^ z_ePn1(*{A1^@oxxywI_$Fd!Q6K2xr(=)a!-X1t8{wPY7OMUHs`cs#B<8mrj!JPAHAJWmf+V zoUm#utbKW2de7ob*DWLW{>2LO#DB)le03Z(lZceAp&lykb9|3#NEzx4!mIGdI_i{I z%=rPckx+f>0)lEIqhxH-ZTS96C1>;{=-3CZp%?dYNV2+$M1k5MG|@7*$0-eO^6w|D zL*zBC2BK1X-P|vt+alG40ng>OmNcv4(0)q`n8EfxueH-p7>1y$k+z!`QlM zr{#dMg>`npXZV3X>nfYAUsbL^OU9m(W>4mVk%c@yc@FM`!27ghahm~?Y~HJ`ru&xt z`j2@kW^6ebVRAF5l6}04QOd1nS9KQBcf!j*symZso$eM-e8tW|b@_inRelVy?jd7| zb_h4Kr?>>Pz6N-~G6m=H8*zfBQ0^@{x&wjsTHeam3Oc9i#nGu{*di9UqUdsa7G4Vf zLC7XCPEgj`e-X<(}NX`?!U5dREr5%DOTJFL6)yrViP$gS@PHL_b{_5;0PoEjf)T^#OQQ9^5M zmR;&DQ!Vr974Ol8mgjw$iM}F^=@P42`=V#uQS(2%wnQxGntB_CB;co}#V51@pe8=0@ud8TpipfXJ@pMHrNQCyJ(%*Pz#K(Nr@l5U`uR0hfClGLaeu)H%Uv5-D?U;ks)S;246~ z)#&1N8wJ>hASfV|G0j|1lm)W$a8g1@NEJdv=Uc?%ug{*D7OC~qaKh2zW z%EM-O@Xb^fMK6?1lh}gzqQO$YroNveHG^Kyt2=q55f1{->I);0Pi~E8!pShkSn7EWOATPms9tySOxbMqrM1cnhw$&ER_CWpG-jPv#GIQjjyu zSc+Bsl1mK88?x4s#(a@vGvWfBVRv#yCNBpCAZArX@r5O?=$+xEsCV^9C$qta~c<# ziu94oA2GV95}Y4=+he+*{e2l#{PnQDakrC9iTc(wo=l53Y z83LKFaRBC>jBlhUL^)5knZs{EEaAB^m}Yo(Uqcz-AT zr5uL|vPOR4oH1g$2X*x(la`G%|Kd1|R+_FOUb(7iO@aIY4hT1pu^6I`m00B}KdYam z{{WHhAClCf8>fqxv?9g9hhj%21XGC7QJP-FNCIGb!3cT5z%U Date: Thu, 17 Sep 2020 23:35:07 +0200 Subject: [PATCH 057/284] :zap: Minor improvements to LinkedIn-Node --- .../nodes/LinkedIn/PostDescription.ts | 2 +- .../nodes-base/nodes/LinkedIn/linkedin.png | Bin 1760 -> 543 bytes 2 files changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/nodes-base/nodes/LinkedIn/PostDescription.ts b/packages/nodes-base/nodes/LinkedIn/PostDescription.ts index 7bfb57907..f23e8229a 100644 --- a/packages/nodes-base/nodes/LinkedIn/PostDescription.ts +++ b/packages/nodes-base/nodes/LinkedIn/PostDescription.ts @@ -70,7 +70,7 @@ export const postFields = [ } }, { - displayName: 'Organization', + displayName: 'Organization URN', name: 'organization', type: 'string', default: '', diff --git a/packages/nodes-base/nodes/LinkedIn/linkedin.png b/packages/nodes-base/nodes/LinkedIn/linkedin.png index dd53ef11d313ea62b09992a0e78599df78fad0e0..be815e9ed97ccd9d48da926dcced020a6f0ca362 100644 GIT binary patch literal 543 zcmeAS@N?(olHy`uVBq!ia0vp^HXzKw3=&b&bO2Hn0X`wFK$@X^JN=Op_r9q>tJO<_ z{DK+wz1^|9=7*nb?^lI0oGXQ*jIJE*O7eRD;_-tmlPfGWR3zu*xH&u%3t&9Kz`&^Q z>EamTas2I+(|OGX09)AwB+Fr8zyoa zhKhaCQTy^rJ>6i+5vR6qOSay%H20phz4NCfv$%&?k&nhhR@IO4ElU*+?)VpY`l9;c zJpoqhbk^GWcGc|Oq#Jbd`~T#Bk1}t?-kHMJ&fK$oQsc>pdEG%r zb}yW>`0gd{WMj+4JU_m@Oin9tgTe~DWM4f_yX)j literal 1760 zcmZ{kc{tnY7RP^@gorBbSSwVki`3FsVu-D_O61zLmJ(}JCPhV1N~NZap-ro#T3dyR zt(Hbf?P4e%4NbY72vSRJQ;gkQ)pllVM?t< zs;pJY(;vkq!r0;4T}9EITPag{M;rE#7bRZF-h}uZhHl!ymVjLesXIp_5ATAe<20u^ z_ePn1(*{A1^@oxxywI_$Fd!Q6K2xr(=)a!-X1t8{wPY7OMUHs`cs#B<8mrj!JPAHAJWmf+V zoUm#utbKW2de7ob*DWLW{>2LO#DB)le03Z(lZceAp&lykb9|3#NEzx4!mIGdI_i{I z%=rPckx+f>0)lEIqhxH-ZTS96C1>;{=-3CZp%?dYNV2+$M1k5MG|@7*$0-eO^6w|D zL*zBC2BK1X-P|vt+alG40ng>OmNcv4(0)q`n8EfxueH-p7>1y$k+z!`QlM zr{#dMg>`npXZV3X>nfYAUsbL^OU9m(W>4mVk%c@yc@FM`!27ghahm~?Y~HJ`ru&xt z`j2@kW^6ebVRAF5l6}04QOd1nS9KQBcf!j*symZso$eM-e8tW|b@_inRelVy?jd7| zb_h4Kr?>>Pz6N-~G6m=H8*zfBQ0^@{x&wjsTHeam3Oc9i#nGu{*di9UqUdsa7G4Vf zLC7XCPEgj`e-X<(}NX`?!U5dREr5%DOTJFL6)yrViP$gS@PHL_b{_5;0PoEjf)T^#OQQ9^5M zmR;&DQ!Vr974Ol8mgjw$iM}F^=@P42`=V#uQS(2%wnQxGntB_CB;co}#V51@pe8=0@ud8TpipfXJ@pMHrNQCyJ(%*Pz#K(Nr@l5U`uR0hfClGLaeu)H%Uv5-D?U;ks)S;246~ z)#&1N8wJ>hASfV|G0j|1lm)W$a8g1@NEJdv=Uc?%ug{*D7OC~qaKh2zW z%EM-O@Xb^fMK6?1lh}gzqQO$YroNveHG^Kyt2=q55f1{->I);0Pi~E8!pShkSn7EWOATPms9tySOxbMqrM1cnhw$&ER_CWpG-jPv#GIQjjyu zSc+Bsl1mK88?x4s#(a@vGvWfBVRv#yCNBpCAZArX@r5O?=$+xEsCV^9C$qta~c<# ziu94oA2GV95}Y4=+he+*{e2l#{PnQDakrC9iTc(wo=l53Y z83LKFaRBC>jBlhUL^)5knZs{EEaAB^m}Yo(Uqcz-AT zr5uL|vPOR4oH1g$2X*x(la`G%|Kd1|R+_FOUb(7iO@aIY4hT1pu^6I`m00B}KdYam z{{WHhAClCf8>fqxv?9g9hhj%21XGC7QJP-FNCIGb!3cT5z%U Date: Fri, 18 Sep 2020 06:58:49 +0100 Subject: [PATCH 058/284] :zap: Add 'typecast' functionality to Airtable node (#922) * Added 'typecast' functionality for Airtable node as offered by the official API. Minor grammatical corrections for some descriptions in the same node. * Fixed minor issue with erroneous tabs/spaces added whilst editing Co-authored-by: stewart-is <40069421+stewart-is@users.noreply.github.com> --- .../nodes/Airtable/Airtable.node.ts | 43 ++++++++++++++++--- 1 file changed, 38 insertions(+), 5 deletions(-) diff --git a/packages/nodes-base/nodes/Airtable/Airtable.node.ts b/packages/nodes-base/nodes/Airtable/Airtable.node.ts index acdee495e..42a5c2900 100644 --- a/packages/nodes-base/nodes/Airtable/Airtable.node.ts +++ b/packages/nodes-base/nodes/Airtable/Airtable.node.ts @@ -107,7 +107,7 @@ export class Airtable implements INodeType { }, }, default: true, - description: 'If all fields should be send to Airtable or only specific ones.', + description: 'If all fields should be sent to Airtable or only specific ones.', }, { displayName: 'Fields', @@ -130,7 +130,7 @@ export class Airtable implements INodeType { default: [], placeholder: 'Name', required: true, - description: 'The name of fields of which the data should be send to Airtable.', + description: 'The name of fields for which data should be sent to Airtable.', }, // ---------------------------------- @@ -188,7 +188,7 @@ export class Airtable implements INodeType { maxValue: 100, }, default: 100, - description: 'How many results to return.', + description: 'Number of results to return.', }, { @@ -331,7 +331,7 @@ export class Airtable implements INodeType { }, }, default: true, - description: 'If all fields should be send to Airtable or only specific ones.', + description: 'If all fields should be sent to Airtable or only specific ones.', }, { displayName: 'Fields', @@ -354,8 +354,29 @@ export class Airtable implements INodeType { default: [], placeholder: 'Name', required: true, - description: 'The name of fields of which the data should be send to Airtable.', + description: 'The name of fields for which data should be sent to Airtable.', }, + + // ---------------------------------- + // append + update + // ---------------------------------- + + { + displayName: 'Typecast', + name: 'typecast', + type: 'boolean', + displayOptions: { + show: { + operation: [ + 'append', + 'update', + ], + }, + }, + default: false, + description: 'If the Airtable API should attempt mapping of string values for linked records & select options.', + }, + ], }; @@ -386,6 +407,7 @@ export class Airtable implements INodeType { let addAllFields: boolean; let fields: string[]; + let typecast: boolean; for (let i = 0; i < items.length; i++) { addAllFields = this.getNodeParameter('addAllFields', i) as boolean; @@ -403,6 +425,11 @@ export class Airtable implements INodeType { body.fields[fieldName] = items[i].json[fieldName]; } } + + typecast = this.getNodeParameter('typecast', i) as boolean; + if (typecast === true) { + body['typecast'] = true; + } responseData = await apiRequest.call(this, requestMethod, endpoint, body, qs); @@ -494,6 +521,7 @@ export class Airtable implements INodeType { let id: string; let updateAllFields: boolean; let fields: string[]; + let typecast: boolean; for (let i = 0; i < items.length; i++) { updateAllFields = this.getNodeParameter('updateAllFields', i) as boolean; @@ -512,6 +540,11 @@ export class Airtable implements INodeType { } } + typecast = this.getNodeParameter('typecast', i) as boolean; + if (typecast === true) { + body['typecast'] = true; + } + id = this.getNodeParameter('id', i) as string; endpoint = `${application}/${table}/${id}`; From 30cef2a408558fc3f5cd1d000fe03f0f3386678a Mon Sep 17 00:00:00 2001 From: Jan Oberhauser Date: Fri, 18 Sep 2020 07:59:10 +0200 Subject: [PATCH 059/284] :zap: Minor improvements to Airtable-Node --- .../nodes/Airtable/Airtable.node.ts | 37 ++++++++++++------- 1 file changed, 23 insertions(+), 14 deletions(-) diff --git a/packages/nodes-base/nodes/Airtable/Airtable.node.ts b/packages/nodes-base/nodes/Airtable/Airtable.node.ts index 42a5c2900..9f22b3d2b 100644 --- a/packages/nodes-base/nodes/Airtable/Airtable.node.ts +++ b/packages/nodes-base/nodes/Airtable/Airtable.node.ts @@ -360,11 +360,11 @@ export class Airtable implements INodeType { // ---------------------------------- // append + update // ---------------------------------- - { - displayName: 'Typecast', - name: 'typecast', - type: 'boolean', + displayName: 'Options', + name: 'options', + type: 'collection', + placeholder: 'Add Option', displayOptions: { show: { operation: [ @@ -373,10 +373,17 @@ export class Airtable implements INodeType { ], }, }, - default: false, - description: 'If the Airtable API should attempt mapping of string values for linked records & select options.', + default: {}, + options: [ + { + displayName: 'Typecast', + name: 'typecast', + type: 'boolean', + default: false, + description: 'If the Airtable API should attempt mapping of string values for linked records & select options.', + }, + ], }, - ], }; @@ -407,9 +414,11 @@ export class Airtable implements INodeType { let addAllFields: boolean; let fields: string[]; - let typecast: boolean; + let options: IDataObject; + for (let i = 0; i < items.length; i++) { addAllFields = this.getNodeParameter('addAllFields', i) as boolean; + options = this.getNodeParameter('options', i, {}) as IDataObject; if (addAllFields === true) { // Add all the fields the item has @@ -425,9 +434,8 @@ export class Airtable implements INodeType { body.fields[fieldName] = items[i].json[fieldName]; } } - - typecast = this.getNodeParameter('typecast', i) as boolean; - if (typecast === true) { + + if (options.typecast === true) { body['typecast'] = true; } @@ -521,9 +529,11 @@ export class Airtable implements INodeType { let id: string; let updateAllFields: boolean; let fields: string[]; - let typecast: boolean; + let options: IDataObject; + for (let i = 0; i < items.length; i++) { updateAllFields = this.getNodeParameter('updateAllFields', i) as boolean; + options = this.getNodeParameter('options', i, {}) as IDataObject; if (updateAllFields === true) { // Update all the fields the item has @@ -540,8 +550,7 @@ export class Airtable implements INodeType { } } - typecast = this.getNodeParameter('typecast', i) as boolean; - if (typecast === true) { + if (options.typecast === true) { body['typecast'] = true; } From 534b852fc248de10bd961385e25f4afd3af0607d Mon Sep 17 00:00:00 2001 From: remoremorali <69923370+remoremorali@users.noreply.github.com> Date: Fri, 18 Sep 2020 08:27:44 +0200 Subject: [PATCH 060/284] :zap: Add support for custom rules when checking emails. (#899) * Add support for custom rules when checking emails. * Add support for custom rules when checking emails. --- .../nodes-base/nodes/EmailReadImap.node.ts | 76 ++++++++++++++++--- 1 file changed, 66 insertions(+), 10 deletions(-) diff --git a/packages/nodes-base/nodes/EmailReadImap.node.ts b/packages/nodes-base/nodes/EmailReadImap.node.ts index 920c0d41f..1c204690b 100644 --- a/packages/nodes-base/nodes/EmailReadImap.node.ts +++ b/packages/nodes-base/nodes/EmailReadImap.node.ts @@ -30,7 +30,8 @@ export class EmailReadImap implements INodeType { color: '#44AA22', }, inputs: [], - outputs: ['main'], + outputs: ['main', 'main'], + outputNames: ['data', 'error'], credentials: [ { name: 'imap', @@ -130,6 +131,27 @@ export class EmailReadImap implements INodeType { }, description: 'Prefix for name of the binary property to which to
write the attachments. An index starting with 0 will be added.
So if name is "attachment_" the first attachment is saved to "attachment_0"', }, + { + displayName: 'Use custom email config', + name: 'useCustomEmailConfig', + type: 'boolean', + default: false, + description: 'If custom email rules should be used.', + }, + { + displayName: 'Custom email rules', + name: 'customEmailConfig', + type: 'string', + default: "['UNSEEN']", + displayOptions: { + show: { + useCustomEmailConfig: [ + true + ], + }, + }, + description: 'Custom email fetching rules. See node-imap\'s search function for more details' + }, { displayName: 'Options', name: 'options', @@ -177,7 +199,11 @@ export class EmailReadImap implements INodeType { return ''; } - return await connection.getPartData(message, textParts[0]); + try{ + return await connection.getPartData(message, textParts[0]); + } catch { + return ''; + } }; @@ -211,10 +237,20 @@ export class EmailReadImap implements INodeType { // Returns all the new unseen messages const getNewEmails = async (connection: ImapSimple): Promise => { const format = this.getNodeParameter('format', 0) as string; - const searchCriteria = [ + + let searchCriteria = [ 'UNSEEN' ]; - + const useCustomEmailConfig = this.getNodeParameter('useCustomEmailConfig') as boolean; + if (useCustomEmailConfig) { + const customEmailConfig = this.getNodeParameter('customEmailConfig') as string; + try { + searchCriteria = eval(customEmailConfig); + } catch (err) { + throw new Error(`Parsing of ${customEmailConfig}\nfailed with error ${err}`); + } + } + let fetchOptions = {}; if (format === 'simple' || format === 'raw') { @@ -334,6 +370,19 @@ export class EmailReadImap implements INodeType { let connection: ImapSimple; + let empty: INodeExecutionData[] = []; + let errToJson = (err: Error) => { + return { + json: + { + message: err.message, + stack: err.stack + } + }; + } + let emitError = (err: Error) => { + this.emit([empty, [errToJson(err)]]); + } const config: ImapSimpleOptions = { imap: { @@ -345,10 +394,14 @@ export class EmailReadImap implements INodeType { authTimeout: 3000 }, onmail: async () => { - const returnData = await getNewEmails(connection); + try{ + const returnData = await getNewEmails(connection); - if (returnData.length) { - this.emit([returnData]); + if (returnData.length) { + this.emit([returnData, empty]); + } + }catch(e) { + emitError(e); } }, }; @@ -361,9 +414,12 @@ export class EmailReadImap implements INodeType { // Connect to the IMAP server and open the mailbox // that we get informed whenever a new email arrives - connection = await imapConnect(config); - await connection.openBox(mailbox); - + try { + connection = await imapConnect(config); + await connection.openBox(mailbox); + } catch (e) { + emitError(e); + } // When workflow and so node gets set to inactive close the connectoin async function closeFunction() { From 0ea3411f5d30e66a2417b24af75dc5688c84258f Mon Sep 17 00:00:00 2001 From: Jan Oberhauser Date: Fri, 18 Sep 2020 08:28:32 +0200 Subject: [PATCH 061/284] :zap: Improve changes to IMAP Node --- .../nodes-base/nodes/EmailReadImap.node.ts | 91 +++++-------------- 1 file changed, 24 insertions(+), 67 deletions(-) diff --git a/packages/nodes-base/nodes/EmailReadImap.node.ts b/packages/nodes-base/nodes/EmailReadImap.node.ts index 1c204690b..0181e6d76 100644 --- a/packages/nodes-base/nodes/EmailReadImap.node.ts +++ b/packages/nodes-base/nodes/EmailReadImap.node.ts @@ -30,8 +30,7 @@ export class EmailReadImap implements INodeType { color: '#44AA22', }, inputs: [], - outputs: ['main', 'main'], - outputNames: ['data', 'error'], + outputs: ['main'], credentials: [ { name: 'imap', @@ -131,27 +130,6 @@ export class EmailReadImap implements INodeType { }, description: 'Prefix for name of the binary property to which to
write the attachments. An index starting with 0 will be added.
So if name is "attachment_" the first attachment is saved to "attachment_0"', }, - { - displayName: 'Use custom email config', - name: 'useCustomEmailConfig', - type: 'boolean', - default: false, - description: 'If custom email rules should be used.', - }, - { - displayName: 'Custom email rules', - name: 'customEmailConfig', - type: 'string', - default: "['UNSEEN']", - displayOptions: { - show: { - useCustomEmailConfig: [ - true - ], - }, - }, - description: 'Custom email fetching rules. See node-imap\'s search function for more details' - }, { displayName: 'Options', name: 'options', @@ -159,6 +137,13 @@ export class EmailReadImap implements INodeType { placeholder: 'Add Option', default: {}, options: [ + { + displayName: 'Custom email rules', + name: 'customEmailConfig', + type: 'string', + default: '["UNSEEN"]', + description: 'Custom email fetching rules. See node-imap\'s search function for more details' + }, { displayName: 'Ignore SSL Issues', name: 'allowUnauthorizedCerts', @@ -184,6 +169,16 @@ export class EmailReadImap implements INodeType { const postProcessAction = this.getNodeParameter('postProcessAction') as string; const options = this.getNodeParameter('options', {}) as IDataObject; + let searchCriteria = [ + 'UNSEEN' + ]; + if (options.customEmailConfig !== undefined) { + try { + searchCriteria = JSON.parse(options.customEmailConfig as string); + } catch (err) { + throw new Error(`Custom email config is not valid JSON.`); + } + } // Returns the email text const getText = async (parts: any[], message: Message, subtype: string) => { // tslint:disable-line:no-any @@ -235,22 +230,9 @@ export class EmailReadImap implements INodeType { // Returns all the new unseen messages - const getNewEmails = async (connection: ImapSimple): Promise => { + const getNewEmails = async (connection: ImapSimple, searchCriteria: string[]): Promise => { const format = this.getNodeParameter('format', 0) as string; - let searchCriteria = [ - 'UNSEEN' - ]; - const useCustomEmailConfig = this.getNodeParameter('useCustomEmailConfig') as boolean; - if (useCustomEmailConfig) { - const customEmailConfig = this.getNodeParameter('customEmailConfig') as string; - try { - searchCriteria = eval(customEmailConfig); - } catch (err) { - throw new Error(`Parsing of ${customEmailConfig}\nfailed with error ${err}`); - } - } - let fetchOptions = {}; if (format === 'simple' || format === 'raw') { @@ -274,8 +256,6 @@ export class EmailReadImap implements INodeType { let attachments: IBinaryData[]; let propertyName: string; - - // All properties get by default moved to metadata except the ones // which are defined here which get set on the top level. const topLevelProperties = [ @@ -367,22 +347,7 @@ export class EmailReadImap implements INodeType { return newEmails; }; - - let connection: ImapSimple; - let empty: INodeExecutionData[] = []; - let errToJson = (err: Error) => { - return { - json: - { - message: err.message, - stack: err.stack - } - }; - } - let emitError = (err: Error) => { - this.emit([empty, [errToJson(err)]]); - } const config: ImapSimpleOptions = { imap: { @@ -394,14 +359,10 @@ export class EmailReadImap implements INodeType { authTimeout: 3000 }, onmail: async () => { - try{ - const returnData = await getNewEmails(connection); + const returnData = await getNewEmails(connection, searchCriteria); - if (returnData.length) { - this.emit([returnData, empty]); - } - }catch(e) { - emitError(e); + if (returnData.length) { + this.emit([returnData]); } }, }; @@ -414,12 +375,8 @@ export class EmailReadImap implements INodeType { // Connect to the IMAP server and open the mailbox // that we get informed whenever a new email arrives - try { - connection = await imapConnect(config); - await connection.openBox(mailbox); - } catch (e) { - emitError(e); - } + connection = await imapConnect(config); + await connection.openBox(mailbox); // When workflow and so node gets set to inactive close the connectoin async function closeFunction() { From e18d7934ad07e8352cb1ccd815a30f4c18c0f39a Mon Sep 17 00:00:00 2001 From: Jan Oberhauser Date: Fri, 18 Sep 2020 09:18:43 +0200 Subject: [PATCH 062/284] :zap: Fix build --- packages/cli/package.json | 2 +- packages/nodes-base/package.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/cli/package.json b/packages/cli/package.json index 5f5c65a47..e6c9f47ac 100644 --- a/packages/cli/package.json +++ b/packages/cli/package.json @@ -101,7 +101,7 @@ "localtunnel": "^2.0.0", "lodash.get": "^4.4.2", "mongodb": "^3.5.5", - "mysql2": "^2.0.1", + "mysql2": "~2.1.0", "n8n-core": "~0.45.0", "n8n-editor-ui": "~0.56.0", "n8n-nodes-base": "~0.77.0", diff --git a/packages/nodes-base/package.json b/packages/nodes-base/package.json index 090087c58..f553fa3bc 100644 --- a/packages/nodes-base/package.json +++ b/packages/nodes-base/package.json @@ -431,7 +431,7 @@ "mongodb": "^3.5.5", "mqtt": "^4.2.0", "mssql": "^6.2.0", - "mysql2": "^2.0.1", + "mysql2": "~2.1.0", "n8n-core": "~0.45.0", "nodemailer": "^6.4.6", "pdf-parse": "^1.1.1", From a552febab494f8ecc022391f046752f1f9f5a4cc Mon Sep 17 00:00:00 2001 From: Ricardo Espinoza Date: Fri, 18 Sep 2020 03:42:01 -0400 Subject: [PATCH 063/284] :zap: Feature/extended active campaign (#928) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * feat: 🎸 node/activecampaign account, tag, account contact * feat: 🎸 node/ActiveCampaign Update an accountContact assoc * feat: 🎸 node/activecampaign Update an account * feat: 🎸 node/activecampaign Get an account * feat: 🎸 node/activecampaign GetAll & Delete an account * feat: 🎸 node/activecampaign change ID's type to number * refactor: 💡 node/activecampaign merge getAll properties * :zap: Improvements to #923 * :zap: Improvements * :zap: Improvements * :zap: Add breaking change message Co-authored-by: Ronald Diquélou --- packages/cli/BREAKING-CHANGES.md | 22 + .../AccountContactDescription.ts | 175 ++++++++ .../ActiveCampaign/AccountDescription.ts | 302 ++++++++++++++ .../ActiveCampaign/ActiveCampaign.node.ts | 393 +++++++++++++++++- .../ActiveCampaign/ConnectionDescription.ts | 51 +-- .../ActiveCampaign/ContactDescription.ts | 352 +++++++++++----- .../ActiveCampaign/ContactTagDescription.ts | 95 +++++ .../nodes/ActiveCampaign/DealDescription.ts | 54 +-- .../ActiveCampaign/EcomCustomerDescription.ts | 50 +-- .../ActiveCampaign/EcomOrderDescription.ts | 55 +-- .../EcomOrderProductsDescription.ts | 52 +-- .../nodes/ActiveCampaign/GenericFunctions.ts | 67 ++- .../nodes/ActiveCampaign/TagDescription.ts | 232 +++++++++++ 13 files changed, 1584 insertions(+), 316 deletions(-) create mode 100644 packages/nodes-base/nodes/ActiveCampaign/AccountContactDescription.ts create mode 100644 packages/nodes-base/nodes/ActiveCampaign/AccountDescription.ts create mode 100644 packages/nodes-base/nodes/ActiveCampaign/ContactTagDescription.ts create mode 100644 packages/nodes-base/nodes/ActiveCampaign/TagDescription.ts diff --git a/packages/cli/BREAKING-CHANGES.md b/packages/cli/BREAKING-CHANGES.md index 9269427ae..c0c008a5f 100644 --- a/packages/cli/BREAKING-CHANGES.md +++ b/packages/cli/BREAKING-CHANGES.md @@ -2,6 +2,28 @@ This list shows all the versions which include breaking changes and how to upgrade. +## 0.83.0 + +### What changed? + +In the Active Campaign Node, we have changed how the operation `getAll` works on various resources to keep consistency across them. To achive this a new parameter called 'Simple' was added. + +### When is action necessary? + +When one of the following resources/operations is used: + +| Resource | Operation | +|--|--| +| Deal | Get All | +| Connector | Get All | +| E-commerce Order | Get All | +| E-commerce Customer | Get All | +| E-commerce Order Products | Get All | + +### How to upgrade: + +Open the affected resource/operation and set the parameter `Simple` to false. + ## 0.79.0 ### What changed? diff --git a/packages/nodes-base/nodes/ActiveCampaign/AccountContactDescription.ts b/packages/nodes-base/nodes/ActiveCampaign/AccountContactDescription.ts new file mode 100644 index 000000000..5a88c8a4e --- /dev/null +++ b/packages/nodes-base/nodes/ActiveCampaign/AccountContactDescription.ts @@ -0,0 +1,175 @@ +import { + INodeProperties, +} from 'n8n-workflow'; + +export const accountContactOperations = [ + { + displayName: 'Operation', + name: 'operation', + type: 'options', + displayOptions: { + show: { + resource: [ + 'accountContact', + ], + }, + }, + options: [ + { + name: 'Create', + value: 'create', + description: 'Create an association', + }, + { + name: 'Delete', + value: 'delete', + description: 'Delete an association', + }, + { + name: 'Update', + value: 'update', + description: 'Update an association', + }, + ], + default: 'create', + description: 'The operation to perform.', + }, +] as INodeProperties[]; + +export const accountContactFields = [ + // ---------------------------------- + // accountContact:create + // ---------------------------------- + { + displayName: 'Account ID', + name: 'account', + type: 'number', + default: '', + required: true, + displayOptions: { + show: { + operation: [ + 'create', + ], + resource: [ + 'accountContact', + ], + }, + }, + description: 'Account ID', + }, + { + displayName: 'Contact ID', + name: 'contact', + type: 'number', + default: '', + required: true, + displayOptions: { + show: { + operation: [ + 'create', + ], + resource: [ + 'accountContact', + ], + }, + }, + description: 'Contact ID', + }, + { + displayName: 'Additional Fields', + name: 'additionalFields', + type: 'collection', + placeholder: 'Add Field', + displayOptions: { + show: { + operation: [ + 'create', + ], + resource: [ + 'accountContact', + ], + }, + }, + default: {}, + options: [ + { + displayName: 'Job title', + name: 'jobTitle', + type: 'string', + default: '', + description: 'Job Title of the contact at the account', + }, + ], + }, + // ---------------------------------- + // accountContact:delete + // ---------------------------------- + { + displayName: 'Account Contact ID', + name: 'accountContactId', + type: 'number', + displayOptions: { + show: { + operation: [ + 'delete', + ], + resource: [ + 'accountContact', + ], + }, + }, + default: 0, + required: true, + description: 'ID of the account contact to delete.', + }, + // ---------------------------------- + // accountContact:update + // ---------------------------------- + { + displayName: 'Account Contact ID', + name: 'accountContactId', + type: 'number', + default: '', + required: true, + displayOptions: { + show: { + operation: [ + 'update', + ], + resource: [ + 'accountContact', + ], + }, + }, + description: 'Account ID', + }, + + { + displayName: 'Update Fields', + name: 'updateFields', + type: 'collection', + description: 'The fields to update.', + placeholder: 'Add Field', + displayOptions: { + show: { + operation: [ + 'update', + ], + resource: [ + 'accountContact', + ], + }, + }, + default: {}, + options: [ + { + displayName: 'Job title', + name: 'jobTitle', + type: 'string', + default: '', + description: 'Job Title of the contact at the account', + }, + ], + }, +] as INodeProperties[]; diff --git a/packages/nodes-base/nodes/ActiveCampaign/AccountDescription.ts b/packages/nodes-base/nodes/ActiveCampaign/AccountDescription.ts new file mode 100644 index 000000000..bc91bcef0 --- /dev/null +++ b/packages/nodes-base/nodes/ActiveCampaign/AccountDescription.ts @@ -0,0 +1,302 @@ +import { + INodeProperties, +} from 'n8n-workflow'; + +import { + activeCampaignDefaultGetAllProperties, +} from './GenericFunctions'; + +export const accountOperations = [ + { + displayName: 'Operation', + name: 'operation', + type: 'options', + displayOptions: { + show: { + resource: [ + 'account', + ], + }, + }, + options: [ + { + name: 'Create', + value: 'create', + description: 'Create an account', + }, + { + name: 'Delete', + value: 'delete', + description: 'Delete an account', + }, + { + name: 'Get', + value: 'get', + description: 'Get data of an account', + }, + { + name: 'Get All', + value: 'getAll', + description: 'Get data of all accounts', + }, + { + name: 'Update', + value: 'update', + description: 'Update an account', + }, + ], + default: 'create', + description: 'The operation to perform.', + }, +] as INodeProperties[]; + +export const accountFields = [ + // ---------------------------------- + // contact:create + // ---------------------------------- + { + displayName: 'Name', + name: 'name', + type: 'string', + default: '', + required: true, + displayOptions: { + show: { + operation: [ + 'create', + ], + resource: [ + 'account', + ], + }, + }, + description: 'Account\'s name.', + }, + { + displayName: 'Additional Fields', + name: 'additionalFields', + type: 'collection', + placeholder: 'Add Field', + displayOptions: { + show: { + operation: [ + 'create', + ], + resource: [ + 'account', + ], + }, + }, + default: {}, + options: [ + { + displayName: 'Account URL', + name: 'accountUrl', + type: 'string', + default: '', + description: `Account's website`, + }, + { + displayName: 'Fields', + name: 'fields', + placeholder: 'Add Custom Fields', + description: 'Adds a custom fields to set also values which have not been predefined.', + type: 'fixedCollection', + typeOptions: { + multipleValues: true, + }, + default: {}, + options: [ + { + name: 'property', + displayName: 'Field', + values: [ + { + displayName: 'Field ID', + name: 'customFieldId', + type: 'options', + typeOptions: { + loadOptionsMethod: 'getAccountCustomFields', + }, + default: '', + description: 'ID of the field to set.', + }, + { + displayName: 'Field Value', + name: 'fieldValue', + type: 'string', + default: '', + description: 'Value of the field to set.', + }, + ], + }, + ], + }, + ], + }, + + // ---------------------------------- + // contact:update + // ---------------------------------- + { + displayName: 'Account ID', + name: 'accountId', + type: 'number', + displayOptions: { + show: { + operation: [ + 'update', + ], + resource: [ + 'account', + ], + }, + }, + default: 0, + required: true, + description: 'ID of the account to update.', + }, + { + displayName: 'Update Fields', + name: 'updateFields', + type: 'collection', + description: 'The fields to update.', + placeholder: 'Add Field', + displayOptions: { + show: { + operation: [ + 'update', + ], + resource: [ + 'account', + ], + }, + }, + default: {}, + options: [ + { + displayName: 'Name', + name: 'name', + type: 'string', + default: '', + description: 'Account\'s name.', + }, + { + displayName: 'Account URL', + name: 'accountUrl', + type: 'string', + default: '', + description: 'Account\'s website', + }, + { + displayName: 'Fields', + name: 'fields', + placeholder: 'Add Fields', + description: 'Adds a custom fields to set also values which have not been predefined.', + type: 'fixedCollection', + typeOptions: { + multipleValues: true, + }, + default: {}, + options: [ + { + name: 'property', + displayName: 'Field', + values: [ + { + displayName: 'Field ID', + name: 'customFieldId', + type: 'options', + typeOptions: { + loadOptionsMethod: 'getAccountCustomFields', + }, + default: '', + description: 'ID of the field to set.', + }, + { + displayName: 'Field Value', + name: 'fieldValue', + type: 'string', + default: '', + description: 'Value of the field to set.', + }, + ], + }, + ], + }, + ], + }, + // ---------------------------------- + // account:delete + // ---------------------------------- + { + displayName: 'Account ID', + name: 'accountId', + type: 'number', + displayOptions: { + show: { + operation: [ + 'delete', + ], + resource: [ + 'account', + ], + }, + }, + default: 0, + required: true, + description: 'ID of the account to delete.', + }, + // ---------------------------------- + // account:get + // ---------------------------------- + { + displayName: 'Account ID', + name: 'accountId', + type: 'number', + displayOptions: { + show: { + operation: [ + 'get', + ], + resource: [ + 'account', + ], + }, + }, + default: 0, + required: true, + description: 'ID of the account to get.', + }, + // ---------------------------------- + // account:getAll + // ---------------------------------- + ...activeCampaignDefaultGetAllProperties('account', 'getAll'), + { + displayName: 'Filters', + name: 'filters', + type: 'collection', + placeholder: 'Add Filter', + displayOptions: { + show: { + operation: [ + 'getAll', + ], + resource: [ + 'account', + ], + }, + }, + default: {}, + options: [ + { + displayName: 'Search', + name: 'search', + type: 'string', + default: '', + description: 'Search by name', + }, + ], + }, + +] as INodeProperties[]; diff --git a/packages/nodes-base/nodes/ActiveCampaign/ActiveCampaign.node.ts b/packages/nodes-base/nodes/ActiveCampaign/ActiveCampaign.node.ts index a7b47f6e2..1683ff5b0 100644 --- a/packages/nodes-base/nodes/ActiveCampaign/ActiveCampaign.node.ts +++ b/packages/nodes-base/nodes/ActiveCampaign/ActiveCampaign.node.ts @@ -7,6 +7,8 @@ import { INodeTypeDescription, INodeExecutionData, INodeType, + ILoadOptionsFunctions, + INodePropertyOptions, } from 'n8n-workflow'; import { @@ -45,12 +47,31 @@ import { connectionFields } from './ConnectionDescription'; +import { + accountFields, + accountOperations +} from "./AccountDescription"; + +import { + tagFields, + tagOperations +} from "./TagDescription"; + +import { + accountContactFields, + accountContactOperations +} from "./AccountContactDescription"; + +import { + contactTagFields, + contactTagOperations, +} from "./ContactTagDescription"; + interface CustomProperty { name: string; value: string; } - /** * Add the additional fields to the body * @@ -63,6 +84,10 @@ function addAdditionalFields(body: IDataObject, additionalFields: IDataObject) { for (const customProperty of (additionalFields.customProperties as IDataObject)!.property! as CustomProperty[]) { body[customProperty.name] = customProperty.value; } + } else if (key === 'fieldValues' && (additionalFields.fieldValues as IDataObject).property !== undefined) { + body.fieldValues = (additionalFields.fieldValues as IDataObject).property; + } else if (key === 'fields' && (additionalFields.fields as IDataObject).property !== undefined) { + body.fields = (additionalFields.fields as IDataObject).property; } else { body[key] = additionalFields[key]; } @@ -88,7 +113,7 @@ export class ActiveCampaign implements INodeType { { name: 'activeCampaignApi', required: true, - } + }, ], properties: [ // ---------------------------------- @@ -99,18 +124,30 @@ export class ActiveCampaign implements INodeType { name: 'resource', type: 'options', options: [ + { + name: 'Account', + value: 'account', + }, + { + name: 'Account Contact', + value: 'accountContact', + }, { name: 'Contact', value: 'contact', }, { - name: 'Deal', - value: 'deal', + name: 'Contact Tag', + value: 'contactTag', }, { name: 'Connection', value: 'connection' }, + { + name: 'Deal', + value: 'deal', + }, { name: 'E-commerce Order', value: 'ecommerceOrder', @@ -122,7 +159,11 @@ export class ActiveCampaign implements INodeType { { name: 'E-commerce Order Products', value: 'ecommerceOrderProducts' - } + }, + { + name: 'Tag', + value: 'tag', + }, ], default: 'contact', description: 'The resource to operate on.', @@ -131,7 +172,11 @@ export class ActiveCampaign implements INodeType { // ---------------------------------- // operations // ---------------------------------- + ...accountOperations, ...contactOperations, + ...accountContactOperations, + ...contactTagOperations, + ...tagOperations, ...dealOperations, ...connectionOperations, ...ecomOrderOperations, @@ -141,6 +186,26 @@ export class ActiveCampaign implements INodeType { // ---------------------------------- // fields // ---------------------------------- + // ---------------------------------- + // tag + // ---------------------------------- + ...tagFields, + + // ---------------------------------- + // tag + // ---------------------------------- + ...contactTagFields, + + // ---------------------------------- + // account + // ---------------------------------- + ...accountFields, + + // ---------------------------------- + // account + // ---------------------------------- + ...accountContactFields, + // ---------------------------------- // contact // ---------------------------------- @@ -174,6 +239,40 @@ export class ActiveCampaign implements INodeType { ], }; + methods = { + loadOptions: { + // Get all the available custom fields to display them to user so that he can + // select them easily + async getContactCustomFields(this: ILoadOptionsFunctions): Promise { + const returnData: INodePropertyOptions[] = []; + const { fields } = await activeCampaignApiRequest.call(this, 'GET', '/api/3/fields', {}); + for (const field of fields) { + const fieldName = field.title; + const fieldId = field.id; + returnData.push({ + name: fieldName, + value: fieldId, + }); + } + return returnData; + }, + // Get all the available custom fields to display them to user so that he can + // select them easily + async getAccountCustomFields(this: ILoadOptionsFunctions): Promise { + const returnData: INodePropertyOptions[] = []; + const { accountCustomFieldMeta: fields } = await activeCampaignApiRequest.call(this, 'GET', '/api/3/accountCustomFieldMeta', {}); + for (const field of fields) { + const fieldName = field.fieldLabel; + const fieldId = field.id; + returnData.push({ + name: fieldName, + value: fieldId, + }); + } + return returnData; + }, + }, + }; async execute(this: IExecuteFunctions): Promise { const items = this.getInputData(); @@ -254,11 +353,24 @@ export class ActiveCampaign implements INodeType { requestMethod = 'GET'; returnAll = this.getNodeParameter('returnAll', i) as boolean; + const simple = this.getNodeParameter('simple', i, true) as boolean; + const additionalFields = this.getNodeParameter('additionalFields', i) as IDataObject; + if (returnAll === false) { qs.limit = this.getNodeParameter('limit', i) as number; } - dataKey = 'contacts'; + Object.assign(qs, additionalFields); + + if (qs.orderBy) { + qs[qs.orderBy as string] = true; + delete qs.orderBy; + } + + if (simple === true) { + dataKey = 'contacts'; + } + endpoint = `/api/3/contacts`; } else if (operation === 'update') { @@ -278,6 +390,246 @@ export class ActiveCampaign implements INodeType { const updateFields = this.getNodeParameter('updateFields', i) as IDataObject; addAdditionalFields(body.contact as IDataObject, updateFields); + } else { + throw new Error(`The operation "${operation}" is not known`); + } + } else if (resource === 'account') { + if (operation === 'create') { + // ---------------------------------- + // account:create + // ---------------------------------- + + requestMethod = 'POST'; + + endpoint = '/api/3/accounts'; + + dataKey = 'account'; + + body.account = { + name: this.getNodeParameter('name', i) as string, + } as IDataObject; + + const additionalFields = this.getNodeParameter('additionalFields', i) as IDataObject; + addAdditionalFields(body.account as IDataObject, additionalFields); + + } else if (operation === 'delete') { + // ---------------------------------- + // account:delete + // ---------------------------------- + + requestMethod = 'DELETE'; + + const accountId = this.getNodeParameter('accountId', i) as number; + endpoint = `/api/3/accounts/${accountId}`; + + } else if (operation === 'get') { + // ---------------------------------- + // account:get + // ---------------------------------- + + requestMethod = 'GET'; + + const accountId = this.getNodeParameter('accountId', i) as number; + endpoint = `/api/3/accounts/${accountId}`; + + } else if (operation === 'getAll') { + // ---------------------------------- + // account:getAll + // ---------------------------------- + + requestMethod = 'GET'; + + const simple = this.getNodeParameter('simple', i, true) as boolean; + returnAll = this.getNodeParameter('returnAll', i) as boolean; + if (returnAll === false) { + qs.limit = this.getNodeParameter('limit', i) as number; + } + + if (simple === true) { + dataKey = 'accounts'; + } + + endpoint = `/api/3/accounts`; + + const filters = this.getNodeParameter('filters', i) as IDataObject; + Object.assign(qs, filters); + + } else if (operation === 'update') { + // ---------------------------------- + // account:update + // ---------------------------------- + + requestMethod = 'PUT'; + + const accountId = this.getNodeParameter('accountId', i) as number; + endpoint = `/api/3/accounts/${accountId}`; + + dataKey = 'account'; + + body.account = {} as IDataObject; + + const updateFields = this.getNodeParameter('updateFields', i) as IDataObject; + addAdditionalFields(body.account as IDataObject, updateFields); + + } else { + throw new Error(`The operation "${operation}" is not known`); + } + } else if (resource === 'accountContact') { + if (operation === 'create') { + // ---------------------------------- + // account:create + // ---------------------------------- + + requestMethod = 'POST'; + + endpoint = '/api/3/accountContacts'; + + dataKey = 'accountContact'; + + body.accountContact = { + contact: this.getNodeParameter('contact', i) as string, + account: this.getNodeParameter('account', i) as string, + } as IDataObject; + + const additionalFields = this.getNodeParameter('additionalFields', i) as IDataObject; + addAdditionalFields(body.account as IDataObject, additionalFields); + + } else if (operation === 'update') { + // ---------------------------------- + // accountContact:update + // ---------------------------------- + + requestMethod = 'PUT'; + + const accountContactId = this.getNodeParameter('accountContactId', i) as number; + endpoint = `/api/3/accountContacts/${accountContactId}`; + + dataKey = 'accountContact'; + + body.accountContact = {} as IDataObject; + + const updateFields = this.getNodeParameter('updateFields', i) as IDataObject; + addAdditionalFields(body.accountContact as IDataObject, updateFields); + + } else if (operation === 'delete') { + // ---------------------------------- + // accountContact:delete + // ---------------------------------- + + requestMethod = 'DELETE'; + + const accountContactId = this.getNodeParameter('accountContactId', i) as number; + endpoint = `/api/3/accountContacts/${accountContactId}`; + + } else { + throw new Error(`The operation "${operation}" is not known`); + } + } else if (resource === 'contactTag') { + if (operation === 'add') { + // ---------------------------------- + // contactTag:add + // ---------------------------------- + + requestMethod = 'POST'; + + endpoint = '/api/3/contactTags'; + + dataKey = 'contactTag'; + + body.contactTag = { + contact: this.getNodeParameter('contactId', i) as string, + tag: this.getNodeParameter('tagId', i) as string, + } as IDataObject; + + } else if (operation === 'remove') { + // ---------------------------------- + // contactTag:remove + // ---------------------------------- + + requestMethod = 'DELETE'; + + const contactTagId = this.getNodeParameter('contactTagId', i) as number; + endpoint = `/api/3/contactTags/${contactTagId}`; + + } else { + throw new Error(`The operation "${operation}" is not known`); + } + } else if (resource === 'tag') { + if (operation === 'create') { + // ---------------------------------- + // tag:create + // ---------------------------------- + + requestMethod = 'POST'; + + endpoint = '/api/3/tags'; + + dataKey = 'tag'; + + body.tag = { + tag: this.getNodeParameter('name', i) as string, + tagType: this.getNodeParameter('tagType', i) as string, + } as IDataObject; + + const additionalFields = this.getNodeParameter('additionalFields', i) as IDataObject; + addAdditionalFields(body.tag as IDataObject, additionalFields); + + } else if (operation === 'delete') { + // ---------------------------------- + // tag:delete + // ---------------------------------- + + requestMethod = 'DELETE'; + + const tagId = this.getNodeParameter('tagId', i) as number; + endpoint = `/api/3/tags/${tagId}`; + + } else if (operation === 'get') { + // ---------------------------------- + // tag:get + // ---------------------------------- + + requestMethod = 'GET'; + + const tagId = this.getNodeParameter('tagId', i) as number; + endpoint = `/api/3/tags/${tagId}`; + + } else if (operation === 'getAll') { + // ---------------------------------- + // tags:getAll + // ---------------------------------- + + requestMethod = 'GET'; + + const simple = this.getNodeParameter('simple', i, true) as boolean; + returnAll = this.getNodeParameter('returnAll', i) as boolean; + if (returnAll === false) { + qs.limit = this.getNodeParameter('limit', i) as number; + } + + if (simple === true) { + dataKey = 'tags'; + } + + endpoint = `/api/3/tags`; + + } else if (operation === 'update') { + // ---------------------------------- + // tags:update + // ---------------------------------- + + requestMethod = 'PUT'; + + const tagId = this.getNodeParameter('tagId', i) as number; + endpoint = `/api/3/tags/${tagId}`; + + dataKey = 'tag'; + + body.tag = {} as IDataObject; + + const updateFields = this.getNodeParameter('updateFields', i) as IDataObject; + addAdditionalFields(body.tag as IDataObject, updateFields); + } else { throw new Error(`The operation "${operation}" is not known`); } @@ -358,11 +710,16 @@ export class ActiveCampaign implements INodeType { requestMethod = 'GET'; + const simple = this.getNodeParameter('simple', i, true) as boolean; returnAll = this.getNodeParameter('returnAll', i) as boolean; if (returnAll === false) { qs.limit = this.getNodeParameter('limit', i) as number; } + if (simple === true) { + dataKey = 'deals'; + } + endpoint = `/api/3/deals`; } else if (operation === 'createNote') { @@ -455,11 +812,16 @@ export class ActiveCampaign implements INodeType { requestMethod = 'GET'; + const simple = this.getNodeParameter('simple', i, true) as boolean; returnAll = this.getNodeParameter('returnAll', i) as boolean; if (returnAll === false) { qs.limit = this.getNodeParameter('limit', i) as number; } + if (simple === true) { + dataKey = 'connections'; + } + endpoint = `/api/3/connections`; } else { @@ -548,11 +910,16 @@ export class ActiveCampaign implements INodeType { requestMethod = 'GET'; + const simple = this.getNodeParameter('simple', i, true) as boolean; returnAll = this.getNodeParameter('returnAll', i) as boolean; if (returnAll === false) { qs.limit = this.getNodeParameter('limit', i) as number; } + if (simple === true) { + dataKey = 'ecomOrders'; + } + endpoint = `/api/3/ecomOrders`; } else { @@ -633,11 +1000,16 @@ export class ActiveCampaign implements INodeType { requestMethod = 'GET'; + const simple = this.getNodeParameter('simple', i, true) as boolean; returnAll = this.getNodeParameter('returnAll', i) as boolean; if (returnAll === false) { qs.limit = this.getNodeParameter('limit', i) as number; } + if (simple === true) { + dataKey = 'ecomCustomers'; + } + endpoint = `/api/3/ecomCustomers`; } else { @@ -662,6 +1034,8 @@ export class ActiveCampaign implements INodeType { requestMethod = 'GET'; + //dataKey = 'ecomOrderProducts'; + const orderId = this.getNodeParameter('orderId', i) as number; endpoint = `/api/3/ecomOrders/${orderId}/orderProducts`; @@ -672,11 +1046,16 @@ export class ActiveCampaign implements INodeType { requestMethod = 'GET'; + const simple = this.getNodeParameter('simple', i, true) as boolean; returnAll = this.getNodeParameter('returnAll', i) as boolean; if (returnAll === false) { qs.limit = this.getNodeParameter('limit', i) as number; } + if (simple === true) { + dataKey = 'ecomOrderProducts'; + } + endpoint = `/api/3/ecomOrderProducts`; } else { @@ -703,4 +1082,4 @@ export class ActiveCampaign implements INodeType { return [this.helpers.returnJsonArray(returnData)]; } -} \ No newline at end of file +} diff --git a/packages/nodes-base/nodes/ActiveCampaign/ConnectionDescription.ts b/packages/nodes-base/nodes/ActiveCampaign/ConnectionDescription.ts index e962e4041..20700a374 100644 --- a/packages/nodes-base/nodes/ActiveCampaign/ConnectionDescription.ts +++ b/packages/nodes-base/nodes/ActiveCampaign/ConnectionDescription.ts @@ -1,4 +1,10 @@ -import { INodeProperties } from "n8n-workflow"; +import { + INodeProperties, +} from "n8n-workflow"; + +import { + activeCampaignDefaultGetAllProperties, +} from "./GenericFunctions"; export const connectionOperations = [ { @@ -278,45 +284,6 @@ export const connectionFields = [ // ---------------------------------- // connection:getAll // ---------------------------------- - { - displayName: 'Return All', - name: 'returnAll', - type: 'boolean', - displayOptions: { - show: { - operation: [ - 'getAll', - ], - resource: [ - 'connection', - ], - }, - }, - 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: [ - 'connection', - ], - returnAll: [ - false, - ], - }, - }, - typeOptions: { - minValue: 1, - maxValue: 500, - }, - default: 100, - description: 'How many results to return.', - }, + ...activeCampaignDefaultGetAllProperties('connection', 'getAll'), + ] as INodeProperties[]; diff --git a/packages/nodes-base/nodes/ActiveCampaign/ContactDescription.ts b/packages/nodes-base/nodes/ActiveCampaign/ContactDescription.ts index 25b42b367..ce492cd8d 100644 --- a/packages/nodes-base/nodes/ActiveCampaign/ContactDescription.ts +++ b/packages/nodes-base/nodes/ActiveCampaign/ContactDescription.ts @@ -1,4 +1,10 @@ -import { INodeProperties } from "n8n-workflow"; +import { + INodeProperties, +} from 'n8n-workflow'; + +import { + activeCampaignDefaultGetAllProperties, +} from './GenericFunctions'; export const contactOperations = [ { @@ -100,6 +106,42 @@ export const contactFields = [ }, default: {}, options: [ + { + displayName: 'Custom Fields', + name: 'fieldValues', + placeholder: 'Add Custom Fields', + description: 'Adds a custom fields to set also values which have not been predefined.', + type: 'fixedCollection', + typeOptions: { + multipleValues: true, + }, + default: {}, + options: [ + { + name: 'property', + displayName: 'Custom Field', + values: [ + { + displayName: 'Field ID', + name: 'field', + type: 'options', + typeOptions: { + loadOptionsMethod: 'getContactCustomFields', + }, + default: '', + description: 'ID of the field to set.', + }, + { + displayName: 'Field Value', + name: 'value', + type: 'string', + default: '', + description: 'Value of the field to set.', + }, + ] + }, + ], + }, { displayName: 'First Name', name: 'firstName', @@ -121,39 +163,6 @@ export const contactFields = [ default: '', description: 'Phone number of the contact.', }, - { - displayName: 'Custom Properties', - name: 'customProperties', - placeholder: 'Add Custom Property', - description: 'Adds a custom property to set also values which have not been predefined.', - type: 'fixedCollection', - typeOptions: { - multipleValues: true, - }, - default: {}, - options: [ - { - name: 'property', - displayName: 'Property', - values: [ - { - displayName: 'Property Name', - name: 'name', - type: 'string', - default: '', - description: 'Name of the property to set.', - }, - { - displayName: 'Property Value', - name: 'value', - type: 'string', - default: '', - description: 'Value of the property to set.', - }, - ] - }, - ], - }, ], }, @@ -196,6 +205,42 @@ export const contactFields = [ }, default: {}, options: [ + { + displayName: 'Custom Fields', + name: 'fieldValues', + placeholder: 'Add Custom Fields', + description: 'Adds a custom fields to set also values which have not been predefined.', + type: 'fixedCollection', + typeOptions: { + multipleValues: true, + }, + default: {}, + options: [ + { + name: 'property', + displayName: 'Custom Field', + values: [ + { + displayName: 'Field ID', + name: 'field', + type: 'options', + typeOptions: { + loadOptionsMethod: 'getContactCustomFields', + }, + default: '', + description: 'ID of the field to set.', + }, + { + displayName: 'Field Value', + name: 'value', + type: 'string', + default: '', + description: 'Value of the field to set.', + }, + ] + }, + ], + }, { displayName: 'Email', name: 'email', @@ -224,39 +269,6 @@ export const contactFields = [ default: '', description: 'Phone number of the contact.', }, - { - displayName: 'Custom Properties', - name: 'customProperties', - placeholder: 'Add Custom Property', - description: 'Adds a custom property to set also values which have not been predefined.', - type: 'fixedCollection', - typeOptions: { - multipleValues: true, - }, - default: {}, - options: [ - { - name: 'property', - displayName: 'Property', - values: [ - { - displayName: 'Property Name', - name: 'name', - type: 'string', - default: '', - description: 'Name of the property to set.', - }, - { - displayName: 'Property Value', - name: 'value', - type: 'string', - default: '', - description: 'Value of the property to set.', - }, - ] - }, - ], - }, ], }, @@ -307,10 +319,12 @@ export const contactFields = [ // ---------------------------------- // contact:getAll // ---------------------------------- + ...activeCampaignDefaultGetAllProperties('contact', 'getAll'), { - displayName: 'Return All', - name: 'returnAll', - type: 'boolean', + displayName: 'Additional Fields', + name: 'additionalFields', + type: 'collection', + placeholder: 'Add Field', displayOptions: { show: { operation: [ @@ -321,31 +335,179 @@ export const contactFields = [ ], }, }, - 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, - ], + default: {}, + options: [ + { + displayName: 'Datetime', + name: 'datetime', + type: 'dateTime', + default: '', + description: 'Contacts created on the specified date', }, - }, - typeOptions: { - minValue: 1, - maxValue: 500, - }, - default: 100, - description: 'How many results to return.', + { + displayName: 'Email', + name: 'email', + type: 'string', + default: '', + description: 'Email address of the contact you want to get', + }, + { + displayName: 'Email Like', + name: 'email_like', + type: 'string', + default: '', + description: 'Filter contacts that contain the given value in the email address', + }, + { + displayName: 'Exclude', + name: 'exclude', + type: 'string', + default: '', + description: 'Exclude from the response the contact with the given ID', + }, + { + displayName: 'Form ID', + name: 'formid', + type: 'string', + default: '', + description: 'Filter contacts associated with the given form', + }, + { + displayName: 'List ID', + name: 'listid', + type: 'string', + default: '', + description: 'Filter contacts associated with the given list', + }, + { + displayName: 'Search', + name: 'search', + type: 'string', + default: '', + description: 'Filter contacts that match the given value in the contact names, organization, phone or email', + }, + { + displayName: 'Segment ID', + name: 'segmentid', + type: 'string', + default: '', + description: 'Return only contacts that match a list segment', + }, + { + displayName: 'Series ID', + name: 'seriesid', + type: 'string', + default: '', + description: 'Filter contacts associated with the given automation', + }, + { + displayName: 'Status', + name: 'status', + type: 'options', + options: [ + { + name: 'Any', + value: -1, + }, + { + name: 'Unconfirmed', + value: 0, + }, + { + name: 'Active', + value: 1, + }, + { + name: 'Unsubscribed', + value: 2, + }, + { + name: 'Bounced', + value: 3, + }, + ], + default: '', + }, + { + displayName: 'Tag ID', + name: 'tagid', + type: 'string', + default: '', + description: 'Filter contacts associated with the given tag', + }, + { + displayName: 'Created Before', + name: 'filters[created_before]', + type: 'dateTime', + default: '', + description: 'Filter contacts that were created prior to this date', + }, + { + displayName: 'Created After', + name: 'filters[created_after]', + type: 'dateTime', + default: '', + description: 'Filter contacts that were created after this date', + }, + { + displayName: 'Updated Before', + name: 'filters[updated_before]', + type: 'dateTime', + default: '', + description: 'Filter contacts that were updated before this date', + }, + { + displayName: 'Updated After', + name: 'filters[updated_after]', + type: 'dateTime', + default: '', + description: 'Filter contacts that were updated after this date', + }, + { + displayName: 'Wait ID', + name: 'waitid', + type: 'string', + default: '', + description: 'Filter by contacts in the wait queue of an automation block', + }, + { + displayName: 'Order By', + name: 'orderBy', + type: 'options', + options: [ + { + name: 'Creation Date', + value: 'orders[cdate]', + description: 'Order contacts by creation date', + }, + { + name: 'Email', + value: 'orders[email]', + description: 'Order contacts by email', + }, + { + name: 'First Name', + value: 'orders[first_name]', + description: 'Order contacts by first name', + }, + { + name: 'Last Name', + value: 'orders[last_name]', + description: 'Order contacts by last name', + }, + { + name: 'Name', + value: 'orders[name]', + description: 'Order contacts by full name', + }, + { + name: 'Score', + value: 'orders[score]', + description: 'Order contacts by score', + }, + ], + default: '', + }, + ], }, ] as INodeProperties[]; diff --git a/packages/nodes-base/nodes/ActiveCampaign/ContactTagDescription.ts b/packages/nodes-base/nodes/ActiveCampaign/ContactTagDescription.ts new file mode 100644 index 000000000..59e823d9a --- /dev/null +++ b/packages/nodes-base/nodes/ActiveCampaign/ContactTagDescription.ts @@ -0,0 +1,95 @@ +import { + INodeProperties, +} from 'n8n-workflow'; + +export const contactTagOperations = [ + { + displayName: 'Operation', + name: 'operation', + type: 'options', + displayOptions: { + show: { + resource: [ + 'contactTag', + ], + }, + }, + options: [ + { + name: 'Add', + value: 'add', + description: 'Add a tag to a contact', + }, + { + name: 'Remove', + value: 'remove', + description: 'Remove a tag from a contact', + }, + ], + default: 'add', + description: 'The operation to perform.', + }, +] as INodeProperties[]; + +export const contactTagFields = [ + // ---------------------------------- + // contactTag:add + // ---------------------------------- + { + displayName: 'Tag ID', + name: 'tagId', + type: 'number', + default: '', + required: true, + displayOptions: { + show: { + operation: [ + 'add', + ], + resource: [ + 'contactTag', + ], + }, + }, + description: 'Tag ID', + }, + { + displayName: 'Contact ID', + name: 'contactId', + type: 'number', + default: '', + required: true, + displayOptions: { + show: { + operation: [ + 'add', + ], + resource: [ + 'contactTag', + ], + }, + }, + description: 'Contact ID', + }, + // ---------------------------------- + // contactTag:delete + // ---------------------------------- + { + displayName: 'Contact Tag ID', + name: 'contactTagId', + type: 'number', + displayOptions: { + show: { + operation: [ + 'remove', + ], + resource: [ + 'contactTag', + ], + }, + }, + default: 0, + required: true, + description: 'ID of the contact tag to delete.', + }, +] as INodeProperties[]; diff --git a/packages/nodes-base/nodes/ActiveCampaign/DealDescription.ts b/packages/nodes-base/nodes/ActiveCampaign/DealDescription.ts index 9d61c4339..95bd83bf8 100644 --- a/packages/nodes-base/nodes/ActiveCampaign/DealDescription.ts +++ b/packages/nodes-base/nodes/ActiveCampaign/DealDescription.ts @@ -1,6 +1,14 @@ -import { INodeProperties } from "n8n-workflow"; +import { + INodeProperties, +} from "n8n-workflow"; -import { allCurrencies } from './currencies'; +import { + allCurrencies, +} from './currencies'; + +import { + activeCampaignDefaultGetAllProperties, +} from "./GenericFunctions"; export const dealOperations = [ { @@ -387,47 +395,7 @@ export const dealFields = [ // ---------------------------------- // deal:getAll // ---------------------------------- - { - displayName: 'Return All', - name: 'returnAll', - type: 'boolean', - displayOptions: { - show: { - operation: [ - 'getAll', - ], - resource: [ - 'deal', - ], - }, - }, - 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: [ - 'deal', - ], - returnAll: [ - false, - ], - }, - }, - typeOptions: { - minValue: 1, - maxValue: 500, - }, - default: 100, - description: 'How many results to return.', - }, + ...activeCampaignDefaultGetAllProperties('deal', 'getAll'), // ---------------------------------- // dealNote:create diff --git a/packages/nodes-base/nodes/ActiveCampaign/EcomCustomerDescription.ts b/packages/nodes-base/nodes/ActiveCampaign/EcomCustomerDescription.ts index 2b8b0d33c..009307441 100644 --- a/packages/nodes-base/nodes/ActiveCampaign/EcomCustomerDescription.ts +++ b/packages/nodes-base/nodes/ActiveCampaign/EcomCustomerDescription.ts @@ -1,4 +1,10 @@ -import { INodeProperties } from "n8n-workflow"; +import { + INodeProperties, +} from 'n8n-workflow'; + +import { + activeCampaignDefaultGetAllProperties, +} from './GenericFunctions'; export const ecomCustomerOperations = [ { @@ -246,45 +252,5 @@ export const ecomCustomerFields = [ // ---------------------------------- // ecommerceCustomer:getAll // ---------------------------------- - { - displayName: 'Return All', - name: 'returnAll', - type: 'boolean', - displayOptions: { - show: { - operation: [ - 'getAll', - ], - resource: [ - 'ecommerceCustomer', - ], - }, - }, - 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: [ - 'ecommerceCustomer', - ], - returnAll: [ - false, - ], - }, - }, - typeOptions: { - minValue: 1, - maxValue: 500, - }, - default: 100, - description: 'How many results to return.', - }, + ...activeCampaignDefaultGetAllProperties('ecommerceCustomer', 'getAll'), ] as INodeProperties[]; diff --git a/packages/nodes-base/nodes/ActiveCampaign/EcomOrderDescription.ts b/packages/nodes-base/nodes/ActiveCampaign/EcomOrderDescription.ts index 752b551ad..b57d9b966 100644 --- a/packages/nodes-base/nodes/ActiveCampaign/EcomOrderDescription.ts +++ b/packages/nodes-base/nodes/ActiveCampaign/EcomOrderDescription.ts @@ -1,6 +1,14 @@ -import { INodeProperties } from "n8n-workflow"; +import { + INodeProperties, +} from "n8n-workflow"; -import { allCurrencies } from './currencies'; +import { + allCurrencies, +} from './currencies'; + +import { + activeCampaignDefaultGetAllProperties, +} from './GenericFunctions'; export const ecomOrderOperations = [ { @@ -672,45 +680,6 @@ export const ecomOrderFields = [ // ---------------------------------- // ecommerceOrder:getAll // ---------------------------------- - { - displayName: 'Return All', - name: 'returnAll', - type: 'boolean', - displayOptions: { - show: { - operation: [ - 'getAll', - ], - resource: [ - 'ecommerceOrder', - ], - }, - }, - 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: [ - 'ecommerceOrder', - ], - returnAll: [ - false, - ], - }, - }, - typeOptions: { - minValue: 1, - maxValue: 500, - }, - default: 100, - description: 'How many results to return.', - }, + ...activeCampaignDefaultGetAllProperties('ecommerceOrder', 'getAll'), + ] as INodeProperties[]; diff --git a/packages/nodes-base/nodes/ActiveCampaign/EcomOrderProductsDescription.ts b/packages/nodes-base/nodes/ActiveCampaign/EcomOrderProductsDescription.ts index 1f53c47f0..5613bff04 100644 --- a/packages/nodes-base/nodes/ActiveCampaign/EcomOrderProductsDescription.ts +++ b/packages/nodes-base/nodes/ActiveCampaign/EcomOrderProductsDescription.ts @@ -1,4 +1,10 @@ -import { INodeProperties } from "n8n-workflow"; +import { + INodeProperties, +} from "n8n-workflow"; + +import { + activeCampaignDefaultGetAllProperties, +} from './GenericFunctions'; export const ecomOrderProductsOperations = [ { @@ -80,45 +86,5 @@ export const ecomOrderProductsFields = [ // ---------------------------------- // ecommerceOrderProducts:getAll // ---------------------------------- - { - displayName: 'Return All', - name: 'returnAll', - type: 'boolean', - displayOptions: { - show: { - operation: [ - 'getAll', - ], - resource: [ - 'ecommerceOrderProducts', - ], - }, - }, - 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: [ - 'ecommerceOrderProducts', - ], - returnAll: [ - false, - ], - }, - }, - typeOptions: { - minValue: 1, - maxValue: 500, - }, - default: 100, - description: 'How many results to return.', - }, -] as INodeProperties[]; \ No newline at end of file + ...activeCampaignDefaultGetAllProperties('ecommerceOrderProducts', 'getAll'), +] as INodeProperties[]; diff --git a/packages/nodes-base/nodes/ActiveCampaign/GenericFunctions.ts b/packages/nodes-base/nodes/ActiveCampaign/GenericFunctions.ts index 833e334b9..99ebaed62 100644 --- a/packages/nodes-base/nodes/ActiveCampaign/GenericFunctions.ts +++ b/packages/nodes-base/nodes/ActiveCampaign/GenericFunctions.ts @@ -4,7 +4,7 @@ import { } from 'n8n-core'; import { - IDataObject, ILoadOptionsFunctions, + IDataObject, ILoadOptionsFunctions, INodeProperties, } from 'n8n-workflow'; import { OptionsWithUri } from 'request'; @@ -49,6 +49,8 @@ export async function activeCampaignApiRequest(this: IHookFunctions | IExecuteFu options.body = body; } + console.log(options); + try { const responseData = await this.helpers.request!(options); @@ -124,3 +126,66 @@ export async function activeCampaignApiRequestAllItems(this: IHookFunctions | IE return returnData; } + +export function activeCampaignDefaultGetAllProperties (resource: string, operation: string): INodeProperties [] { + return [ + { + displayName: 'Return All', + name: 'returnAll', + type: 'boolean', + displayOptions: { + show: { + operation: [ + operation, + ], + resource: [ + resource, + ], + }, + }, + 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: [ + operation, + ], + resource: [ + resource, + ], + returnAll: [ + false, + ], + }, + }, + typeOptions: { + minValue: 1, + maxValue: 500, + }, + default: 100, + description: 'How many results to return.', + }, + { + displayName: 'Simple', + name: 'simple', + type: 'boolean', + displayOptions: { + show: { + operation: [ + operation, + ], + resource: [ + resource, + ], + }, + }, + default: true, + description: 'When set to true a simplify version of the response will be used else the raw data will be used', + }, + ]; +} diff --git a/packages/nodes-base/nodes/ActiveCampaign/TagDescription.ts b/packages/nodes-base/nodes/ActiveCampaign/TagDescription.ts new file mode 100644 index 000000000..87f307fcd --- /dev/null +++ b/packages/nodes-base/nodes/ActiveCampaign/TagDescription.ts @@ -0,0 +1,232 @@ +import { + INodeProperties, +} from 'n8n-workflow'; + +import { + activeCampaignDefaultGetAllProperties, +} from './GenericFunctions'; + +export const tagOperations = [ + { + displayName: 'Operation', + name: 'operation', + type: 'options', + displayOptions: { + show: { + resource: [ + 'tag', + ], + }, + }, + options: [ + { + name: 'Create', + value: 'create', + description: 'Create a tag', + }, + { + name: 'Delete', + value: 'delete', + description: 'Delete a tag', + }, + { + name: 'Get', + value: 'get', + description: 'Get data of a tag', + }, + { + name: 'Get All', + value: 'getAll', + description: 'Get data of all tags', + }, + { + name: 'Update', + value: 'update', + description: 'Update a tag', + }, + ], + default: 'create', + description: 'The operation to perform.', + }, +] as INodeProperties[]; + +export const tagFields = [ + // ---------------------------------- + // contact:create + // ---------------------------------- + { + displayName: 'Type', + name: 'tagType', + type: 'options', + default: 'contact', + required: true, + displayOptions: { + show: { + operation: [ + 'create', + ], + resource: [ + 'tag', + ], + }, + }, + options: [ + { + name: 'Contact', + value: 'contact', + description: 'Tag contact', + }, + { + name: 'Template', + value: 'template', + description: 'Tag template', + }, + ], + description: 'Tag-type of the new tag', + }, + { + displayName: 'Name', + name: 'name', + type: 'string', + default: '', + required: true, + displayOptions: { + show: { + operation: [ + 'create', + ], + resource: [ + 'tag', + ], + }, + }, + description: 'Name of the new tag', + }, + { + displayName: 'Additional Fields', + name: 'additionalFields', + type: 'collection', + placeholder: 'Add Field', + displayOptions: { + show: { + operation: [ + 'create', + ], + resource: [ + 'tag', + ], + }, + }, + default: {}, + options: [ + { + displayName: 'Description', + name: 'description', + type: 'string', + default: '', + description: 'Description of the new tag', + }, + ], + }, + // ---------------------------------- + // tag:update + // ---------------------------------- + { + displayName: 'Tag ID', + name: 'tagId', + type: 'number', + displayOptions: { + show: { + operation: [ + 'update', + ], + resource: [ + 'tag', + ], + }, + }, + default: 0, + required: true, + description: 'ID of the tag to update.', + }, + { + displayName: 'Update Fields', + name: 'updateFields', + type: 'collection', + description: 'The fields to update.', + placeholder: 'Add Field', + displayOptions: { + show: { + operation: [ + 'update', + ], + resource: [ + 'tag', + ], + }, + }, + default: {}, + options: [ + { + displayName: 'Tag', + name: 'tag', + type: 'string', + default: '', + description: 'Name of the contact.', + }, + { + displayName: 'Description', + name: 'description', + type: 'string', + default: '', + description: 'Description of the tag being updated', + }, + ], + }, + // ---------------------------------- + // tag:delete + // ---------------------------------- + { + displayName: 'Tag ID', + name: 'tagId', + type: 'number', + displayOptions: { + show: { + operation: [ + 'delete', + ], + resource: [ + 'tag', + ], + }, + }, + default: 0, + required: true, + description: 'ID of the tag to delete.', + }, + // ---------------------------------- + // contact:get + // ---------------------------------- + { + displayName: 'Tag ID', + name: 'tagId', + type: 'number', + displayOptions: { + show: { + operation: [ + 'get', + ], + resource: [ + 'tag', + ], + }, + }, + default: 0, + required: true, + description: 'ID of the tag to get.', + }, + // ---------------------------------- + // tag:getAll + // ---------------------------------- + ...activeCampaignDefaultGetAllProperties('tag', 'getAll') +] as INodeProperties[]; From ed0f701c6aa026820b8d2a0279913a4dd826ed77 Mon Sep 17 00:00:00 2001 From: Jan Oberhauser Date: Fri, 18 Sep 2020 09:42:46 +0200 Subject: [PATCH 064/284] :zap: Minor improvements to ActiveCampaign-Node --- .../ActiveCampaign/ActiveCampaign.node.ts | 2 +- .../ActiveCampaignTrigger.node.ts | 2 +- .../nodes/ActiveCampaign/GenericFunctions.ts | 42 +++++++++---------- 3 files changed, 22 insertions(+), 24 deletions(-) diff --git a/packages/nodes-base/nodes/ActiveCampaign/ActiveCampaign.node.ts b/packages/nodes-base/nodes/ActiveCampaign/ActiveCampaign.node.ts index 1683ff5b0..632c78995 100644 --- a/packages/nodes-base/nodes/ActiveCampaign/ActiveCampaign.node.ts +++ b/packages/nodes-base/nodes/ActiveCampaign/ActiveCampaign.node.ts @@ -474,7 +474,7 @@ export class ActiveCampaign implements INodeType { } else { throw new Error(`The operation "${operation}" is not known`); } - } else if (resource === 'accountContact') { + } else if (resource === 'accountContact') { if (operation === 'create') { // ---------------------------------- // account:create diff --git a/packages/nodes-base/nodes/ActiveCampaign/ActiveCampaignTrigger.node.ts b/packages/nodes-base/nodes/ActiveCampaign/ActiveCampaignTrigger.node.ts index b76ed9fd3..d27835979 100644 --- a/packages/nodes-base/nodes/ActiveCampaign/ActiveCampaignTrigger.node.ts +++ b/packages/nodes-base/nodes/ActiveCampaign/ActiveCampaignTrigger.node.ts @@ -142,7 +142,7 @@ export class ActiveCampaignTrigger implements INodeType { const webhookData = this.getWorkflowStaticData('node'); try { await activeCampaignApiRequest.call(this, 'DELETE', `/api/3/webhooks/${webhookData.webhookId}`, {}); - } catch(error) { + } catch (error) { return false; } delete webhookData.webhookId; diff --git a/packages/nodes-base/nodes/ActiveCampaign/GenericFunctions.ts b/packages/nodes-base/nodes/ActiveCampaign/GenericFunctions.ts index 99ebaed62..97f174065 100644 --- a/packages/nodes-base/nodes/ActiveCampaign/GenericFunctions.ts +++ b/packages/nodes-base/nodes/ActiveCampaign/GenericFunctions.ts @@ -49,8 +49,6 @@ export async function activeCampaignApiRequest(this: IHookFunctions | IExecuteFu options.body = body; } - console.log(options); - try { const responseData = await this.helpers.request!(options); @@ -127,11 +125,11 @@ export async function activeCampaignApiRequestAllItems(this: IHookFunctions | IE return returnData; } -export function activeCampaignDefaultGetAllProperties (resource: string, operation: string): INodeProperties [] { +export function activeCampaignDefaultGetAllProperties(resource: string, operation: string): INodeProperties[] { return [ { displayName: 'Return All', - name: 'returnAll', + name: 'returnAll', type: 'boolean', displayOptions: { show: { @@ -148,26 +146,26 @@ export function activeCampaignDefaultGetAllProperties (resource: string, operati }, { displayName: 'Limit', - name: 'limit', + name: 'limit', type: 'number', displayOptions: { - show: { - operation: [ - operation, - ], - resource: [ - resource, - ], - returnAll: [ - false, - ], + show: { + operation: [ + operation, + ], + resource: [ + resource, + ], + returnAll: [ + false, + ], + }, }, - }, - typeOptions: { - minValue: 1, - maxValue: 500, - }, - default: 100, + typeOptions: { + minValue: 1, + maxValue: 500, + }, + default: 100, description: 'How many results to return.', }, { @@ -185,7 +183,7 @@ export function activeCampaignDefaultGetAllProperties (resource: string, operati }, }, default: true, - description: 'When set to true a simplify version of the response will be used else the raw data will be used', + description: 'When set to true a simplify version of the response will be used else the raw data.', }, ]; } From 283b40c9f565db6e70b8193ff2e750ca06f88e76 Mon Sep 17 00:00:00 2001 From: Ricardo Espinoza Date: Fri, 18 Sep 2020 03:50:54 -0400 Subject: [PATCH 065/284] :zap: Add share operation to Google Drive (#968) --- .../nodes/Google/Drive/GoogleDrive.node.ts | 449 +++++++++++++++++- 1 file changed, 448 insertions(+), 1 deletion(-) diff --git a/packages/nodes-base/nodes/Google/Drive/GoogleDrive.node.ts b/packages/nodes-base/nodes/Google/Drive/GoogleDrive.node.ts index aa1ee291f..8cc38249a 100644 --- a/packages/nodes-base/nodes/Google/Drive/GoogleDrive.node.ts +++ b/packages/nodes-base/nodes/Google/Drive/GoogleDrive.node.ts @@ -124,6 +124,11 @@ export class GoogleDrive implements INodeType { value: 'list', description: 'List files and folders', }, + { + name: 'Share', + value: 'share', + description: 'Share a file', + }, { name: 'Upload', value: 'upload', @@ -156,6 +161,11 @@ export class GoogleDrive implements INodeType { value: 'delete', description: 'Delete a folder', }, + { + name: 'Share', + value: 'share', + description: 'Share a folder', + }, ], default: 'create', description: 'The operation to perform.', @@ -478,6 +488,241 @@ export class GoogleDrive implements INodeType { }, + // ---------------------------------- + // file:share + // ---------------------------------- + { + displayName: 'File ID', + name: 'fileId', + type: 'string', + default: '', + required: true, + displayOptions: { + show: { + operation: [ + 'share' + ], + resource: [ + 'file', + 'folder', + ], + }, + }, + description: 'The ID of the file or shared drive.', + }, + { + displayName: 'Permissions', + name: 'permissionsUi', + placeholder: 'Add Permission', + type: 'fixedCollection', + default: '', + typeOptions: { + multipleValues: false, + }, + displayOptions: { + show: { + resource: [ + 'file', + ], + operation: [ + 'share', + 'folder', + ], + }, + }, + options: [ + { + displayName: 'Permission', + name: 'permissionsValues', + values: [ + { + displayName: 'Role', + name: 'role', + type: 'options', + options: [ + { + name: 'Owner', + value: 'owner', + }, + { + name: 'Organizer', + value: 'organizer', + }, + { + name: 'File Organizer', + value: 'fileOrganizer', + }, + { + name: 'Writer', + value: 'writer', + }, + { + name: 'Commenter', + value: 'commenter', + }, + { + name: 'Reader', + value: 'reader', + }, + ], + default: '', + }, + { + displayName: 'Type', + name: 'type', + type: 'options', + options: [ + { + name: 'User', + value: 'user', + }, + { + name: 'Group', + value: 'group', + }, + { + name: 'Domain', + value: 'domain', + }, + { + name: 'anyone', + value: 'anyone', + }, + ], + default: '', + }, + { + displayName: 'Email Address', + name: 'emailAddress', + type: 'string', + displayOptions: { + show: { + type: [ + 'user', + 'group', + ], + }, + }, + default: '', + description: 'The email address of the user or group to which this permission refers', + }, + { + displayName: 'Domain', + name: 'domain', + type: 'string', + displayOptions: { + show: { + type: [ + 'domain', + ], + }, + }, + default: '', + description: 'The domain to which this permission refers', + }, + { + displayName: 'Allow File Discovery', + name: 'allowFileDiscovery', + type: 'boolean', + displayOptions: { + show: { + type: [ + 'domain', + 'anyone', + ], + }, + }, + default: false, + description: 'Whether the permission allows the file to be discovered through search', + }, + ], + }, + ], + }, + + { + displayName: 'Parents', + name: 'parents', + type: 'string', + typeOptions: { + multipleValues: true, + }, + default: [], + displayOptions: { + show: { + operation: [ + 'upload', + ], + resource: [ + 'file', + ], + }, + }, + description: 'The IDs of the parent folders which contain the file.', + }, + { + displayName: 'Binary Data', + name: 'binaryData', + type: 'boolean', + default: false, + displayOptions: { + show: { + operation: [ + 'upload' + ], + resource: [ + 'file', + ], + }, + }, + description: 'If the data to upload should be taken from binary field.', + }, + { + displayName: 'File Content', + name: 'fileContent', + type: 'string', + default: '', + displayOptions: { + show: { + operation: [ + 'upload' + ], + resource: [ + 'file', + ], + binaryData: [ + false + ], + }, + + }, + placeholder: '', + description: 'The text content of the file to upload.', + }, + { + displayName: 'Binary Property', + name: 'binaryPropertyName', + type: 'string', + default: 'data', + required: true, + displayOptions: { + show: { + operation: [ + 'upload' + ], + resource: [ + 'file', + ], + binaryData: [ + true + ], + }, + + }, + placeholder: '', + description: 'Name of the binary property which contains
the data for the file to be uploaded.', + }, + // ---------------------------------- // file:upload // ---------------------------------- @@ -617,10 +862,95 @@ export class GoogleDrive implements INodeType { placeholder: 'Add Option', default: {}, options: [ + { + displayName: 'Email Message', + name: 'emailMessage', + type: 'string', + displayOptions: { + show: { + '/operation': [ + 'share' + ], + '/resource': [ + 'file', + 'folder', + ], + }, + }, + default: '', + description: 'A plain text custom message to include in the notification email.', + }, + { + displayName: 'Enforce Single Parent', + name: 'enforceSingleParent', + type: 'boolean', + displayOptions: { + show: { + '/operation': [ + 'share' + ], + '/resource': [ + 'file', + 'folder', + ], + }, + }, + default: false, + description: `Set to true to opt in to API behavior that aims for all items to have exactly one parent
+ This parameter only takes effect if the item is not in a shared drive`, + }, { displayName: 'Fields', name: 'fields', type: 'multiOptions', + displayOptions: { + show: { + '/operation': [ + 'share', + ], + '/resource': [ + 'file', + 'folder', + ], + }, + }, + options: [ + { + name: '*', + value: '*', + description: 'All fields.', + }, + { + name: 'Email Address', + value: 'emailAddress', + }, + { + name: 'Display Name', + value: 'displayName', + }, + { + name: 'Deleted', + value: 'deleted', + }, + ], + default: [], + description: 'The fields to return.', + }, + { + displayName: 'Fields', + name: 'fields', + type: 'multiOptions', + displayOptions: { + hide: { + '/operation': [ + 'share' + ], + '/resource': [ + 'file', + 'folder', + ], + }, + }, options: [ { name: '*', @@ -696,6 +1026,98 @@ export class GoogleDrive implements INodeType { default: [], description: 'The fields to return.', }, + { + displayName: 'Move To New Owners Root', + name: 'moveToNewOwnersRoot', + type: 'boolean', + displayOptions: { + show: { + '/operation': [ + 'share' + ], + '/resource': [ + 'file', + 'folder', + ], + }, + }, + default: '', + description: `This parameter only takes effect if the item is not in a shared drive and the request is attempting to transfer the ownership of the item.
+ When set to true, the item is moved to the new owner's My Drive root folder and all prior parents removed`, + }, + { + displayName: 'Send Notification Email', + name: 'sendNotificationEmail', + type: 'boolean', + displayOptions: { + show: { + '/operation': [ + 'share' + ], + '/resource': [ + 'file', + 'folder', + ], + }, + }, + default: '', + description: 'Whether to send a notification email when sharing to users or groups', + }, + { + displayName: 'Supports All Drives', + name: 'supportsAllDrives', + type: 'boolean', + displayOptions: { + show: { + '/operation': [ + 'share' + ], + '/resource': [ + 'file', + 'folder', + ], + }, + }, + default: false, + description: 'Whether the requesting application supports both My Drives and shared drives', + }, + { + displayName: 'Transfer Ownership', + name: 'transferOwnership', + type: 'boolean', + displayOptions: { + show: { + '/operation': [ + 'share' + ], + '/resource': [ + 'file', + 'folder', + ], + }, + }, + default: false, + description: 'Whether to transfer ownership to the specified user and downgrade the current owner to a writer.', + }, + { + displayName: 'Use Domain Admin Access', + name: 'useDomainAdminAccess', + type: 'boolean', + displayOptions: { + show: { + '/operation': [ + 'share' + ], + '/resource': [ + 'file', + 'folder', + ], + }, + }, + default: false, + description: `Issue the request as a domain administrator; if set to true, then the requester will be granted access if the file ID parameter
+ refers to a shared drive and the requester is an administrator of the domain to which the shared drive belongs.`, + }, { displayName: 'File Name', @@ -837,7 +1259,6 @@ export class GoogleDrive implements INodeType { }, ], }, - ], }; @@ -1122,6 +1543,32 @@ export class GoogleDrive implements INodeType { success: true, }); } + if (operation === 'share') { + + const fileId = this.getNodeParameter('fileId', i) as string; + + const permissions = this.getNodeParameter('permissionsUi', i) as IDataObject; + + const options = this.getNodeParameter('options', i) as IDataObject; + + const body: IDataObject = {}; + + const qs: IDataObject = {}; + + if (permissions.permissionsValues) { + Object.assign(body, permissions.permissionsValues); + } + + Object.assign(qs, options); + + if (qs.fields) { + qs.fields = (qs.fields as string[]).join(','); + } + + const response = await googleApiRequest.call(this, 'POST', `/drive/v3/files/${fileId}/permissions`, body, qs); + + returnData.push(response as IDataObject); + } } else { throw new Error(`The resource "${resource}" is not known!`); } From 43396b7b9cd3f63cba8512f5531f0cd46ffabc25 Mon Sep 17 00:00:00 2001 From: Jan Oberhauser Date: Fri, 18 Sep 2020 09:57:05 +0200 Subject: [PATCH 066/284] :zap: Minor improvements to GoogleDrive-Node --- packages/nodes-base/nodes/Google/Drive/GoogleDrive.node.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/packages/nodes-base/nodes/Google/Drive/GoogleDrive.node.ts b/packages/nodes-base/nodes/Google/Drive/GoogleDrive.node.ts index 8cc38249a..479388b09 100644 --- a/packages/nodes-base/nodes/Google/Drive/GoogleDrive.node.ts +++ b/packages/nodes-base/nodes/Google/Drive/GoogleDrive.node.ts @@ -585,11 +585,12 @@ export class GoogleDrive implements INodeType { value: 'domain', }, { - name: 'anyone', + name: 'Anyone', value: 'anyone', }, ], default: '', + description: 'Information about the different types can be found here.', }, { displayName: 'Email Address', From 28d5f22f61fba5a1cdc9b0f7fa97f118be0f4a4f Mon Sep 17 00:00:00 2001 From: Jan Oberhauser Date: Fri, 18 Sep 2020 09:57:38 +0200 Subject: [PATCH 067/284] :zap: Remove debug code --- packages/nodes-base/nodes/Box/BoxTrigger.node.ts | 2 -- packages/nodes-base/nodes/Mandrill/GenericFunctions.ts | 2 -- packages/nodes-base/nodes/Twitter/Twitter.node.ts | 1 - packages/nodes-base/nodes/Wordpress/Wordpress.node.ts | 1 - 4 files changed, 6 deletions(-) diff --git a/packages/nodes-base/nodes/Box/BoxTrigger.node.ts b/packages/nodes-base/nodes/Box/BoxTrigger.node.ts index 7bcb3d507..7071b57ed 100644 --- a/packages/nodes-base/nodes/Box/BoxTrigger.node.ts +++ b/packages/nodes-base/nodes/Box/BoxTrigger.node.ts @@ -275,8 +275,6 @@ export class BoxTrigger implements INodeType { const endpoint = '/webhooks'; const webhooks = await boxApiRequestAllItems.call(this, 'entries', 'GET', endpoint, {}); - console.log(webhooks); - for (const webhook of webhooks) { if (webhook.address === webhookUrl && webhook.target.id === targetId && diff --git a/packages/nodes-base/nodes/Mandrill/GenericFunctions.ts b/packages/nodes-base/nodes/Mandrill/GenericFunctions.ts index 538579339..727cfc2b4 100644 --- a/packages/nodes-base/nodes/Mandrill/GenericFunctions.ts +++ b/packages/nodes-base/nodes/Mandrill/GenericFunctions.ts @@ -33,8 +33,6 @@ export async function mandrillApiRequest(this: IExecuteFunctions | IHookFunction try { return await this.helpers.request!(options); } catch (error) { - console.error(error); - const errorMessage = error.response.body.message || error.response.body.Message; if (error.name === 'Invalid_Key') { throw new Error('The provided API key is not a valid Mandrill API key'); diff --git a/packages/nodes-base/nodes/Twitter/Twitter.node.ts b/packages/nodes-base/nodes/Twitter/Twitter.node.ts index bab8fd657..3fa5f21ce 100644 --- a/packages/nodes-base/nodes/Twitter/Twitter.node.ts +++ b/packages/nodes-base/nodes/Twitter/Twitter.node.ts @@ -230,7 +230,6 @@ export class Twitter implements INodeType { } } - console.log(body); responseData = await twitterApiRequest.call(this, 'POST', '/statuses/update.json', body); } // https://developer.twitter.com/en/docs/tweets/search/api-reference/get-search-tweets diff --git a/packages/nodes-base/nodes/Wordpress/Wordpress.node.ts b/packages/nodes-base/nodes/Wordpress/Wordpress.node.ts index 225422ddb..d7f930aee 100644 --- a/packages/nodes-base/nodes/Wordpress/Wordpress.node.ts +++ b/packages/nodes-base/nodes/Wordpress/Wordpress.node.ts @@ -115,7 +115,6 @@ export class Wordpress implements INodeType { async getAuthors(this: ILoadOptionsFunctions): Promise { const returnData: INodePropertyOptions[] = []; const authors = await wordpressApiRequestAllItems.call(this, 'GET', '/users', {}, { who: 'authors' }); - console.log(authors); for (const author of authors) { const authorName = author.name; const authorId = author.id; From c7f1bb51144d0c44fe9d01acd830ab7fd34d57e4 Mon Sep 17 00:00:00 2001 From: Jan Oberhauser Date: Fri, 18 Sep 2020 09:59:15 +0200 Subject: [PATCH 068/284] :bookmark: Release n8n-workflow@0.41.0 --- 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 d3645d888..6ce40446f 100644 --- a/packages/workflow/package.json +++ b/packages/workflow/package.json @@ -1,6 +1,6 @@ { "name": "n8n-workflow", - "version": "0.40.0", + "version": "0.41.0", "description": "Workflow base code of n8n", "license": "SEE LICENSE IN LICENSE.md", "homepage": "https://n8n.io", From cfcfb17b06c996215bfff2e3de9fa0d1f1c7388a Mon Sep 17 00:00:00 2001 From: Jan Oberhauser Date: Fri, 18 Sep 2020 09:59:59 +0200 Subject: [PATCH 069/284] :arrow_up: Set n8n-workflow@0.41.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 d44db6597..7f2b9d7eb 100644 --- a/packages/core/package.json +++ b/packages/core/package.json @@ -46,7 +46,7 @@ "file-type": "^14.6.2", "lodash.get": "^4.4.2", "mime-types": "^2.1.27", - "n8n-workflow": "~0.40.0", + "n8n-workflow": "~0.41.0", "p-cancelable": "^2.0.0", "request": "^2.88.2", "request-promise-native": "^1.0.7" From 65320fabd5d04bf673ea0c489f0f57c81732f34c Mon Sep 17 00:00:00 2001 From: Jan Oberhauser Date: Fri, 18 Sep 2020 10:00:23 +0200 Subject: [PATCH 070/284] :bookmark: Release n8n-core@0.46.0 --- 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 7f2b9d7eb..1409fca92 100644 --- a/packages/core/package.json +++ b/packages/core/package.json @@ -1,6 +1,6 @@ { "name": "n8n-core", - "version": "0.45.0", + "version": "0.46.0", "description": "Core functionality of n8n", "license": "SEE LICENSE IN LICENSE.md", "homepage": "https://n8n.io", From e1298d8031b34502989ad5e59c808e32882d9872 Mon Sep 17 00:00:00 2001 From: Jan Oberhauser Date: Fri, 18 Sep 2020 10:01:37 +0200 Subject: [PATCH 071/284] :arrow_up: Set n8n-core@0.46.0 and n8n-workflow@0.41.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 f553fa3bc..0d69a86df 100644 --- a/packages/nodes-base/package.json +++ b/packages/nodes-base/package.json @@ -403,7 +403,7 @@ "@types/xml2js": "^0.4.3", "gulp": "^4.0.0", "jest": "^26.4.2", - "n8n-workflow": "~0.40.0", + "n8n-workflow": "~0.41.0", "ts-jest": "^26.3.0", "tslint": "^6.1.2", "typescript": "~3.7.4" @@ -432,7 +432,7 @@ "mqtt": "^4.2.0", "mssql": "^6.2.0", "mysql2": "~2.1.0", - "n8n-core": "~0.45.0", + "n8n-core": "~0.46.0", "nodemailer": "^6.4.6", "pdf-parse": "^1.1.1", "pg": "^8.3.0", From ce35dd9ad5854572714cd3d5cc414d949cde869b Mon Sep 17 00:00:00 2001 From: Jan Oberhauser Date: Fri, 18 Sep 2020 10:01:59 +0200 Subject: [PATCH 072/284] :bookmark: Release n8n-nodes-base@0.78.0 --- 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 0d69a86df..00369e939 100644 --- a/packages/nodes-base/package.json +++ b/packages/nodes-base/package.json @@ -1,6 +1,6 @@ { "name": "n8n-nodes-base", - "version": "0.77.0", + "version": "0.78.0", "description": "Base nodes of n8n", "license": "SEE LICENSE IN LICENSE.md", "homepage": "https://n8n.io", From e2e1dbee809ed7dd5883aad408089a49935c86ad Mon Sep 17 00:00:00 2001 From: Jan Oberhauser Date: Fri, 18 Sep 2020 10:03:39 +0200 Subject: [PATCH 073/284] :arrow_up: Set n8n-core@0.46.0, n8n-nodes-base@0.78.0 and n8n-workflow@0.41.0 on n8n --- packages/cli/package.json | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/cli/package.json b/packages/cli/package.json index e6c9f47ac..aa07c3014 100644 --- a/packages/cli/package.json +++ b/packages/cli/package.json @@ -102,10 +102,10 @@ "lodash.get": "^4.4.2", "mongodb": "^3.5.5", "mysql2": "~2.1.0", - "n8n-core": "~0.45.0", + "n8n-core": "~0.46.0", "n8n-editor-ui": "~0.56.0", - "n8n-nodes-base": "~0.77.0", - "n8n-workflow": "~0.40.0", + "n8n-nodes-base": "~0.78.0", + "n8n-workflow": "~0.41.0", "oauth-1.0a": "^2.2.6", "open": "^7.0.0", "pg": "^8.3.0", From 8f4cf16fb2fd6e97e41190a9ac921be172eae220 Mon Sep 17 00:00:00 2001 From: Jan Oberhauser Date: Fri, 18 Sep 2020 10:04:06 +0200 Subject: [PATCH 074/284] :bookmark: Release n8n@0.83.0 --- 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 aa07c3014..497e47b3e 100644 --- a/packages/cli/package.json +++ b/packages/cli/package.json @@ -1,6 +1,6 @@ { "name": "n8n", - "version": "0.82.1", + "version": "0.83.0", "description": "n8n Workflow Automation Tool", "license": "SEE LICENSE IN LICENSE.md", "homepage": "https://n8n.io", From a27e764de0595874febc15a55c0d04001c1529f0 Mon Sep 17 00:00:00 2001 From: Tanay Pant <7481165+tanay1337@users.noreply.github.com> Date: Fri, 18 Sep 2020 10:09:59 +0200 Subject: [PATCH 075/284] :zap: Copy edits to breaking changes for 0.83.0 --- packages/cli/BREAKING-CHANGES.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/cli/BREAKING-CHANGES.md b/packages/cli/BREAKING-CHANGES.md index c0c008a5f..f7e5e7a76 100644 --- a/packages/cli/BREAKING-CHANGES.md +++ b/packages/cli/BREAKING-CHANGES.md @@ -6,7 +6,7 @@ This list shows all the versions which include breaking changes and how to upgra ### What changed? -In the Active Campaign Node, we have changed how the operation `getAll` works on various resources to keep consistency across them. To achive this a new parameter called 'Simple' was added. +In the Active Campaign Node, we have changed how the `getAll` operation works with various resources for the sake of consistency. To achieve this, a new parameter called 'Simple' has been added. ### When is action necessary? From 2faa862ada08020f2c31ed169f3a3110aab51001 Mon Sep 17 00:00:00 2001 From: Jan Oberhauser Date: Sat, 19 Sep 2020 10:05:17 +0200 Subject: [PATCH 076/284] :zap: Auto mention users in reply tweets --- packages/nodes-base/nodes/Twitter/TweetInterface.ts | 1 + packages/nodes-base/nodes/Twitter/Twitter.node.ts | 1 + 2 files changed, 2 insertions(+) diff --git a/packages/nodes-base/nodes/Twitter/TweetInterface.ts b/packages/nodes-base/nodes/Twitter/TweetInterface.ts index 564e92cc1..fee604601 100644 --- a/packages/nodes-base/nodes/Twitter/TweetInterface.ts +++ b/packages/nodes-base/nodes/Twitter/TweetInterface.ts @@ -1,4 +1,5 @@ export interface ITweet { + auto_populate_reply_metadata?: boolean; display_coordinates?: boolean; lat?: number; long?: number; diff --git a/packages/nodes-base/nodes/Twitter/Twitter.node.ts b/packages/nodes-base/nodes/Twitter/Twitter.node.ts index 3fa5f21ce..88a4770e3 100644 --- a/packages/nodes-base/nodes/Twitter/Twitter.node.ts +++ b/packages/nodes-base/nodes/Twitter/Twitter.node.ts @@ -112,6 +112,7 @@ export class Twitter implements INodeType { if (additionalFields.inReplyToStatusId) { body.in_reply_to_status_id = additionalFields.inReplyToStatusId as string; + body.auto_populate_reply_metadata = true; } if (additionalFields.attachments) { From 6064aa1a5a0d1b6ec84113ccc9c0d1290413bc5b Mon Sep 17 00:00:00 2001 From: Jan Oberhauser Date: Sun, 20 Sep 2020 13:22:17 +0200 Subject: [PATCH 077/284] :zap: Remove docker-compose exmple as support will be removed #962 --- docker/compose/withMongo/.env | 9 ------ docker/compose/withMongo/README.md | 26 ---------------- docker/compose/withMongo/docker-compose.yml | 34 --------------------- docker/compose/withMongo/init-data.sh | 17 ----------- 4 files changed, 86 deletions(-) delete mode 100644 docker/compose/withMongo/.env delete mode 100644 docker/compose/withMongo/README.md delete mode 100644 docker/compose/withMongo/docker-compose.yml delete mode 100755 docker/compose/withMongo/init-data.sh diff --git a/docker/compose/withMongo/.env b/docker/compose/withMongo/.env deleted file mode 100644 index a1141f23f..000000000 --- a/docker/compose/withMongo/.env +++ /dev/null @@ -1,9 +0,0 @@ -MONGO_INITDB_ROOT_USERNAME=changeUser -MONGO_INITDB_ROOT_PASSWORD=changePassword -MONGO_INITDB_DATABASE=n8n - -MONGO_NON_ROOT_USERNAME=changeUser -MONGO_NON_ROOT_PASSWORD=changePassword - -N8N_BASIC_AUTH_USER=changeUser -N8N_BASIC_AUTH_PASSWORD=changePassword diff --git a/docker/compose/withMongo/README.md b/docker/compose/withMongo/README.md deleted file mode 100644 index bfb4b0f64..000000000 --- a/docker/compose/withMongo/README.md +++ /dev/null @@ -1,26 +0,0 @@ -# n8n with MongoDB - -Starts n8n with MongoDB as database. - - -## Start - -To start n8n with MongoDB simply start docker-compose by executing the following -command in the current folder. - - -**IMPORTANT:** But before you do that change the default users and passwords in the `.env` file! - -``` -docker-compose up -d -``` - -To stop it execute: - -``` -docker-compose stop -``` - -## Configuration - -The default name of the database, user and password for MongoDB can be changed in the `.env` file in the current directory. diff --git a/docker/compose/withMongo/docker-compose.yml b/docker/compose/withMongo/docker-compose.yml deleted file mode 100644 index 67cfcf001..000000000 --- a/docker/compose/withMongo/docker-compose.yml +++ /dev/null @@ -1,34 +0,0 @@ -version: '3.1' - -services: - - mongo: - image: mongo:4.0 - restart: always - environment: - - MONGO_INITDB_ROOT_USERNAME - - MONGO_INITDB_ROOT_PASSWORD - - MONGO_INITDB_DATABASE - - MONGO_NON_ROOT_USERNAME - - MONGO_NON_ROOT_PASSWORD - volumes: - - ./init-data.sh:/docker-entrypoint-initdb.d/init-data.sh - - n8n: - image: n8nio/n8n - restart: always - environment: - - DB_TYPE=mongodb - - DB_MONGODB_CONNECTION_URL=mongodb://${MONGO_NON_ROOT_USERNAME}:${MONGO_NON_ROOT_PASSWORD}@mongo:27017/${MONGO_INITDB_DATABASE} - - N8N_BASIC_AUTH_ACTIVE=true - - N8N_BASIC_AUTH_USER - - N8N_BASIC_AUTH_PASSWORD - ports: - - 5678:5678 - links: - - mongo - volumes: - - ~/.n8n:/root/.n8n - # Wait 5 seconds to start n8n to make sure that MongoDB is ready - # when n8n tries to connect to it - command: /bin/sh -c "sleep 5; n8n start" diff --git a/docker/compose/withMongo/init-data.sh b/docker/compose/withMongo/init-data.sh deleted file mode 100755 index bf5c10c84..000000000 --- a/docker/compose/withMongo/init-data.sh +++ /dev/null @@ -1,17 +0,0 @@ -#!/bin/bash -set -e; - -# Create a default non-root role -MONGO_NON_ROOT_ROLE="${MONGO_NON_ROOT_ROLE:-readWrite}" - -if [ -n "${MONGO_NON_ROOT_USERNAME:-}" ] && [ -n "${MONGO_NON_ROOT_PASSWORD:-}" ]; then - "${mongo[@]}" "$MONGO_INITDB_DATABASE" <<-EOJS - db.createUser({ - user: $(_js_escape "$MONGO_NON_ROOT_USERNAME"), - pwd: $(_js_escape "$MONGO_NON_ROOT_PASSWORD"), - roles: [ { role: $(_js_escape "$MONGO_NON_ROOT_ROLE"), db: $(_js_escape "$MONGO_INITDB_DATABASE") } ] - }) - EOJS -else - echo "SETUP INFO: No Environment variables given!" -fi From 9e97722c6365a5dc8a607582833b06a9b78ba44d Mon Sep 17 00:00:00 2001 From: Jan Oberhauser Date: Sun, 20 Sep 2020 13:37:29 +0200 Subject: [PATCH 078/284] :bug: Fix issue with Google Sheets with non-latin Sheet names #961 --- packages/nodes-base/nodes/Google/Sheet/GoogleSheets.node.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/nodes-base/nodes/Google/Sheet/GoogleSheets.node.ts b/packages/nodes-base/nodes/Google/Sheet/GoogleSheets.node.ts index 001a617fb..65f074669 100644 --- a/packages/nodes-base/nodes/Google/Sheet/GoogleSheets.node.ts +++ b/packages/nodes-base/nodes/Google/Sheet/GoogleSheets.node.ts @@ -611,6 +611,7 @@ export class GoogleSheets implements INodeType { let range = ''; if (operation !== 'delete') { range = this.getNodeParameter('range', 0) as string; + range = encodeURIComponent(range); } const options = this.getNodeParameter('options', 0, {}) as IDataObject; From f3b136abeec2c51f077e75635155132a139e3839 Mon Sep 17 00:00:00 2001 From: Tanay Pant Date: Mon, 21 Sep 2020 11:11:02 +0200 Subject: [PATCH 079/284] :hammer: Add missing documentationUrl variables --- packages/nodes-base/credentials/AsanaOAuth2Api.credentials.ts | 1 + packages/nodes-base/credentials/ClickUpOAuth2Api.credentials.ts | 1 + .../nodes-base/credentials/LinkedInOAuth2Api.credentials.ts | 1 + .../credentials/MicrosoftTeamsOAuth2Api.credentials.ts | 1 + .../nodes-base/credentials/SentryIoServerApi.credentials.ts | 1 + packages/nodes-base/credentials/Sftp.credentials.ts | 2 +- packages/nodes-base/credentials/TaigaCloudApi.credentials.ts | 1 + packages/nodes-base/credentials/TaigaServerApi.credentials.ts | 1 + packages/nodes-base/credentials/TodoistOAuth2Api.credentials.ts | 1 + 9 files changed, 9 insertions(+), 1 deletion(-) diff --git a/packages/nodes-base/credentials/AsanaOAuth2Api.credentials.ts b/packages/nodes-base/credentials/AsanaOAuth2Api.credentials.ts index 7a4be0470..b40949bb6 100644 --- a/packages/nodes-base/credentials/AsanaOAuth2Api.credentials.ts +++ b/packages/nodes-base/credentials/AsanaOAuth2Api.credentials.ts @@ -9,6 +9,7 @@ export class AsanaOAuth2Api implements ICredentialType { 'oAuth2Api', ]; displayName = 'Asana OAuth2 API'; + documentationUrl = 'asana'; properties = [ { displayName: 'Authorization URL', diff --git a/packages/nodes-base/credentials/ClickUpOAuth2Api.credentials.ts b/packages/nodes-base/credentials/ClickUpOAuth2Api.credentials.ts index 3ce6d6c3f..b8e703c19 100644 --- a/packages/nodes-base/credentials/ClickUpOAuth2Api.credentials.ts +++ b/packages/nodes-base/credentials/ClickUpOAuth2Api.credentials.ts @@ -9,6 +9,7 @@ export class ClickUpOAuth2Api implements ICredentialType { 'oAuth2Api', ]; displayName = 'ClickUp OAuth2 API'; + documentationUrl = 'clickUp'; properties = [ { displayName: 'Authorization URL', diff --git a/packages/nodes-base/credentials/LinkedInOAuth2Api.credentials.ts b/packages/nodes-base/credentials/LinkedInOAuth2Api.credentials.ts index f0c9ba525..f53835af3 100644 --- a/packages/nodes-base/credentials/LinkedInOAuth2Api.credentials.ts +++ b/packages/nodes-base/credentials/LinkedInOAuth2Api.credentials.ts @@ -10,6 +10,7 @@ export class LinkedInOAuth2Api implements ICredentialType { 'oAuth2Api', ]; displayName = 'LinkedIn OAuth2 API'; + documentationUrl = 'linkedIn'; properties = [ { displayName: 'Organization Support', diff --git a/packages/nodes-base/credentials/MicrosoftTeamsOAuth2Api.credentials.ts b/packages/nodes-base/credentials/MicrosoftTeamsOAuth2Api.credentials.ts index 7b1d9da3d..16d365fad 100644 --- a/packages/nodes-base/credentials/MicrosoftTeamsOAuth2Api.credentials.ts +++ b/packages/nodes-base/credentials/MicrosoftTeamsOAuth2Api.credentials.ts @@ -9,6 +9,7 @@ export class MicrosoftTeamsOAuth2Api implements ICredentialType { 'microsoftOAuth2Api', ]; displayName = 'Microsoft OAuth2 API'; + documentationUrl = 'microsoft'; properties = [ //https://docs.microsoft.com/en-us/azure/active-directory/develop/v2-permissions-and-consent { diff --git a/packages/nodes-base/credentials/SentryIoServerApi.credentials.ts b/packages/nodes-base/credentials/SentryIoServerApi.credentials.ts index 0f2a50510..02c06ee09 100644 --- a/packages/nodes-base/credentials/SentryIoServerApi.credentials.ts +++ b/packages/nodes-base/credentials/SentryIoServerApi.credentials.ts @@ -6,6 +6,7 @@ import { export class SentryIoServerApi implements ICredentialType { name = 'sentryIoServerApi'; displayName = 'Sentry.io API'; + documentationUrl = 'sentryIo'; properties = [ { displayName: 'Token', diff --git a/packages/nodes-base/credentials/Sftp.credentials.ts b/packages/nodes-base/credentials/Sftp.credentials.ts index bf2ce34f0..5c7dd2122 100644 --- a/packages/nodes-base/credentials/Sftp.credentials.ts +++ b/packages/nodes-base/credentials/Sftp.credentials.ts @@ -6,7 +6,7 @@ import { export class Sftp implements ICredentialType { name = 'sftp'; displayName = 'SFTP'; - documentationUrl = 'sftp'; + documentationUrl = 'ftp'; properties = [ { displayName: 'Host', diff --git a/packages/nodes-base/credentials/TaigaCloudApi.credentials.ts b/packages/nodes-base/credentials/TaigaCloudApi.credentials.ts index 8dc8d771d..753028a4e 100644 --- a/packages/nodes-base/credentials/TaigaCloudApi.credentials.ts +++ b/packages/nodes-base/credentials/TaigaCloudApi.credentials.ts @@ -6,6 +6,7 @@ import { export class TaigaCloudApi implements ICredentialType { name = 'taigaCloudApi'; displayName = 'Taiga Cloud API'; + documentationUrl = 'taiga'; properties = [ { displayName: 'Username', diff --git a/packages/nodes-base/credentials/TaigaServerApi.credentials.ts b/packages/nodes-base/credentials/TaigaServerApi.credentials.ts index 6e57b3b29..a67e7913a 100644 --- a/packages/nodes-base/credentials/TaigaServerApi.credentials.ts +++ b/packages/nodes-base/credentials/TaigaServerApi.credentials.ts @@ -6,6 +6,7 @@ import { export class TaigaServerApi implements ICredentialType { name = 'taigaServerApi'; displayName = 'Taiga Server API'; + documentationUrl = 'taiga'; properties = [ { displayName: 'Username', diff --git a/packages/nodes-base/credentials/TodoistOAuth2Api.credentials.ts b/packages/nodes-base/credentials/TodoistOAuth2Api.credentials.ts index 9341e69c6..b7baa37c2 100644 --- a/packages/nodes-base/credentials/TodoistOAuth2Api.credentials.ts +++ b/packages/nodes-base/credentials/TodoistOAuth2Api.credentials.ts @@ -9,6 +9,7 @@ export class TodoistOAuth2Api implements ICredentialType { 'oAuth2Api', ]; displayName = 'Todoist OAuth2 API'; + documentationUrl = 'todoist'; properties = [ { displayName: 'Authorization URL', From 24c95c4f095070b5ea9f495fa6173fd288652dac Mon Sep 17 00:00:00 2001 From: Jan Oberhauser Date: Tue, 22 Sep 2020 11:31:48 +0200 Subject: [PATCH 080/284] :zap: Use short variables for direct input data --- .../src/components/VariableSelector.vue | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/packages/editor-ui/src/components/VariableSelector.vue b/packages/editor-ui/src/components/VariableSelector.vue index 1975f332a..1bf23af23 100644 --- a/packages/editor-ui/src/components/VariableSelector.vue +++ b/packages/editor-ui/src/components/VariableSelector.vue @@ -250,7 +250,7 @@ export default mixins( * @returns * @memberof Workflow */ - getNodeOutputData (runData: IRunData, nodeName: string, filterText: string, itemIndex = 0, runIndex = 0, inputName = 'main', outputIndex = 0): IVariableSelectorOption[] | null { + getNodeOutputData (runData: IRunData, nodeName: string, filterText: string, itemIndex = 0, runIndex = 0, inputName = 'main', outputIndex = 0, useShort = false): IVariableSelectorOption[] | null { if (!runData.hasOwnProperty(nodeName)) { // No data found for node return null; @@ -291,9 +291,12 @@ export default mixins( // Get json data if (outputData.hasOwnProperty('json')) { + + const jsonPropertyPrefix = useShort === true ? '$json' : `$node["${nodeName}"].json`; + const jsonDataOptions: IVariableSelectorOption[] = []; for (const propertyName of Object.keys(outputData.json)) { - jsonDataOptions.push.apply(jsonDataOptions, this.jsonDataToFilterOption(outputData.json[propertyName], `$node["${nodeName}"].json`, propertyName, filterText)); + jsonDataOptions.push.apply(jsonDataOptions, this.jsonDataToFilterOption(outputData.json[propertyName], jsonPropertyPrefix, propertyName, filterText)); } if (jsonDataOptions.length) { @@ -308,6 +311,9 @@ export default mixins( // Get binary data if (outputData.hasOwnProperty('binary')) { + + const binaryPropertyPrefix = useShort === true ? '$binary' : `$node["${nodeName}"].binary`; + const binaryData = []; let binaryPropertyData = []; @@ -326,7 +332,7 @@ export default mixins( binaryPropertyData.push( { name: propertyName, - key: `$node["${nodeName}"].binary.${dataPropertyName}.${propertyName}`, + key: `${binaryPropertyPrefix}.${dataPropertyName}.${propertyName}`, value: outputData.binary![dataPropertyName][propertyName], }, ); @@ -336,7 +342,7 @@ export default mixins( binaryData.push( { name: dataPropertyName, - key: `$node["${nodeName}"].binary.${dataPropertyName}`, + key: `${binaryPropertyPrefix}.${dataPropertyName}`, options: this.sortOptions(binaryPropertyData), allowParentSelect: true, }, @@ -347,7 +353,7 @@ export default mixins( returnData.push( { name: 'Binary', - key: `$node["${nodeName}"].binary`, + key: binaryPropertyPrefix, options: this.sortOptions(binaryData), allowParentSelect: true, }, @@ -474,7 +480,7 @@ export default mixins( // (example "IF" node. If node is connected to "true" or to "false" output) const outputIndex = this.workflow.getNodeConnectionOutputIndex(activeNode.name, parentNode[0], 'main'); - tempOutputData = this.getNodeOutputData(runData, parentNode[0], filterText, itemIndex, 0, 'main', outputIndex) as IVariableSelectorOption[]; + tempOutputData = this.getNodeOutputData(runData, parentNode[0], filterText, itemIndex, 0, 'main', outputIndex, true) as IVariableSelectorOption[]; if (tempOutputData) { if (JSON.stringify(tempOutputData).length < 102400) { From ea353bbd58b53cd0093db3d7d48362a3be46afe1 Mon Sep 17 00:00:00 2001 From: Jan Oberhauser Date: Tue, 22 Sep 2020 16:34:21 +0200 Subject: [PATCH 081/284] :bug: Fix bug with JSON attachments on Mandrill-Node --- packages/nodes-base/nodes/Mandrill/Mandrill.node.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/nodes-base/nodes/Mandrill/Mandrill.node.ts b/packages/nodes-base/nodes/Mandrill/Mandrill.node.ts index 642faa1c3..bd73be095 100644 --- a/packages/nodes-base/nodes/Mandrill/Mandrill.node.ts +++ b/packages/nodes-base/nodes/Mandrill/Mandrill.node.ts @@ -551,7 +551,7 @@ export class Mandrill implements INodeType { }, { displayName: 'Attachments', - name: 'attachments', + name: 'attachmentsJson', type: 'json', typeOptions: { alwaysOpenEditWindow: true, From 899ca1a0a8dc639144618500df529b6ba3f00656 Mon Sep 17 00:00:00 2001 From: Jan Oberhauser Date: Wed, 23 Sep 2020 08:14:24 +0200 Subject: [PATCH 082/284] :zap: Fix naming of Hubspot Developer Credentials --- .../nodes-base/credentials/HubspotDeveloperApi.credentials.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/nodes-base/credentials/HubspotDeveloperApi.credentials.ts b/packages/nodes-base/credentials/HubspotDeveloperApi.credentials.ts index 2364e3dd5..5e21f5077 100644 --- a/packages/nodes-base/credentials/HubspotDeveloperApi.credentials.ts +++ b/packages/nodes-base/credentials/HubspotDeveloperApi.credentials.ts @@ -5,7 +5,7 @@ import { export class HubspotDeveloperApi implements ICredentialType { name = 'hubspotDeveloperApi'; - displayName = 'Hubspot API'; + displayName = 'Hubspot Developer API'; documentationUrl = 'hubspot'; properties = [ { From fc6b196b12a0d3db00c20c5379d10174c02d2dcf Mon Sep 17 00:00:00 2001 From: Jan Oberhauser Date: Wed, 23 Sep 2020 08:16:40 +0200 Subject: [PATCH 083/284] :zap: Fix naming of Microsoft Credentials --- .../credentials/MicrosoftExcelOAuth2Api.credentials.ts | 2 +- .../credentials/MicrosoftOneDriveOAuth2Api.credentials.ts | 2 +- .../credentials/MicrosoftTeamsOAuth2Api.credentials.ts | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/nodes-base/credentials/MicrosoftExcelOAuth2Api.credentials.ts b/packages/nodes-base/credentials/MicrosoftExcelOAuth2Api.credentials.ts index 5d469ade4..c13e148a8 100644 --- a/packages/nodes-base/credentials/MicrosoftExcelOAuth2Api.credentials.ts +++ b/packages/nodes-base/credentials/MicrosoftExcelOAuth2Api.credentials.ts @@ -8,7 +8,7 @@ export class MicrosoftExcelOAuth2Api implements ICredentialType { extends = [ 'microsoftOAuth2Api', ]; - displayName = 'Microsoft OAuth2 API'; + displayName = 'Microsoft Excel OAuth2 API'; documentationUrl = 'microsoft'; properties = [ //https://docs.microsoft.com/en-us/azure/active-directory/develop/v2-permissions-and-consent diff --git a/packages/nodes-base/credentials/MicrosoftOneDriveOAuth2Api.credentials.ts b/packages/nodes-base/credentials/MicrosoftOneDriveOAuth2Api.credentials.ts index eb1578dc0..9bfc92cb8 100644 --- a/packages/nodes-base/credentials/MicrosoftOneDriveOAuth2Api.credentials.ts +++ b/packages/nodes-base/credentials/MicrosoftOneDriveOAuth2Api.credentials.ts @@ -8,7 +8,7 @@ export class MicrosoftOneDriveOAuth2Api implements ICredentialType { extends = [ 'microsoftOAuth2Api', ]; - displayName = 'Microsoft OAuth2 API'; + displayName = 'Microsoft Drive OAuth2 API'; documentationUrl = 'microsoft'; properties = [ //https://docs.microsoft.com/en-us/azure/active-directory/develop/v2-permissions-and-consent diff --git a/packages/nodes-base/credentials/MicrosoftTeamsOAuth2Api.credentials.ts b/packages/nodes-base/credentials/MicrosoftTeamsOAuth2Api.credentials.ts index 16d365fad..1afbae247 100644 --- a/packages/nodes-base/credentials/MicrosoftTeamsOAuth2Api.credentials.ts +++ b/packages/nodes-base/credentials/MicrosoftTeamsOAuth2Api.credentials.ts @@ -8,7 +8,7 @@ export class MicrosoftTeamsOAuth2Api implements ICredentialType { extends = [ 'microsoftOAuth2Api', ]; - displayName = 'Microsoft OAuth2 API'; + displayName = 'Microsoft Teams OAuth2 API'; documentationUrl = 'microsoft'; properties = [ //https://docs.microsoft.com/en-us/azure/active-directory/develop/v2-permissions-and-consent From 1401cebdc398ec712fee8973c7478f47754521c3 Mon Sep 17 00:00:00 2001 From: Jan Oberhauser Date: Wed, 23 Sep 2020 09:28:02 +0200 Subject: [PATCH 084/284] :shirt: Fix lint issue --- packages/cli/src/Server.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/cli/src/Server.ts b/packages/cli/src/Server.ts index d06527f87..eb1c56d7d 100644 --- a/packages/cli/src/Server.ts +++ b/packages/cli/src/Server.ts @@ -1296,7 +1296,7 @@ class App { const oAuth2Parameters = { clientId: _.get(oauthCredentials, 'clientId') as string, - clientSecret: _.get(oauthCredentials, 'clientSecret', '') as string, + clientSecret: _.get(oauthCredentials, 'clientSecret', '') as string | undefined, accessTokenUri: _.get(oauthCredentials, 'accessTokenUrl', '') as string, authorizationUri: _.get(oauthCredentials, 'authUrl', '') as string, redirectUri: `${WebhookHelpers.getWebhookBaseUrl()}${this.restEndpoint}/oauth2-credential/callback`, From 88ccc5cb6f8e7a72afb6324095b581129f8b4f9b Mon Sep 17 00:00:00 2001 From: Jan Oberhauser Date: Wed, 23 Sep 2020 09:42:39 +0200 Subject: [PATCH 085/284] :bug: Fix credential rename issue for active workflows #935 --- packages/cli/src/ActiveWorkflowRunner.ts | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/packages/cli/src/ActiveWorkflowRunner.ts b/packages/cli/src/ActiveWorkflowRunner.ts index ae462c0b3..34e07d292 100644 --- a/packages/cli/src/ActiveWorkflowRunner.ts +++ b/packages/cli/src/ActiveWorkflowRunner.ts @@ -497,7 +497,11 @@ export class ActiveWorkflowRunner { if (this.activeWorkflows !== null) { // Remove all the webhooks of the workflow - await this.removeWorkflowWebhooks(workflowId); + try { + await this.removeWorkflowWebhooks(workflowId); + } catch (error) { + console.error(`Could not remove webhooks of workflow "${workflowId}" because of error: "${error.message}"`); + } if (this.activationErrors[workflowId] !== undefined) { // If there were any activation errors delete them From 2db6ae0f76fe12c1a8ac71128a1cdadc1c51c71b Mon Sep 17 00:00:00 2001 From: Jan Oberhauser Date: Wed, 23 Sep 2020 11:10:23 +0200 Subject: [PATCH 086/284] :zap: Simplify documentation help code and display only if URL is set --- .../src/components/CredentialsEdit.vue | 29 +++++++------------ 1 file changed, 10 insertions(+), 19 deletions(-) diff --git a/packages/editor-ui/src/components/CredentialsEdit.vue b/packages/editor-ui/src/components/CredentialsEdit.vue index 9ea199ef3..75c800727 100644 --- a/packages/editor-ui/src/components/CredentialsEdit.vue +++ b/packages/editor-ui/src/components/CredentialsEdit.vue @@ -4,7 +4,7 @@
{{title}}
-
+
- Need help? Open credential docs + Need help? Open credential docs
@@ -109,26 +109,17 @@ export default mixins( } } }, - documentationUrl (): string { + documentationUrl (): string | undefined { + let credentialTypeName = ''; if (this.editCredentials) { - const credentialType = this.$store.getters.credentialType(this.editCredentials.type); - if (credentialType.documentationUrl === undefined) { - return credentialType.name; - } else { - return `${credentialType.documentationUrl}`; - } + credentialTypeName = this.editCredentials.type; } else { - if (this.credentialType) { - const credentialType = this.$store.getters.credentialType(this.credentialType); + credentialTypeName = this.credentialType; + } - if (credentialType.documentationUrl === undefined) { - return credentialType.name; - } else { - return `${credentialType.documentationUrl}`; - } - } else { - return ''; - } + const credentialType = this.$store.getters.credentialType(credentialTypeName); + if (credentialType.documentationUrl !== undefined) { + return `${credentialType.documentationUrl}`; } }, node (): INodeUi { From 62bc1a55cb0ad1a1d9566c235f592611c4ac7ee4 Mon Sep 17 00:00:00 2001 From: Jan Oberhauser Date: Wed, 23 Sep 2020 13:20:50 +0200 Subject: [PATCH 087/284] :bug: Fix credentials resolve issue --- packages/workflow/src/Expression.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/packages/workflow/src/Expression.ts b/packages/workflow/src/Expression.ts index 4df397c6d..e25b0b104 100644 --- a/packages/workflow/src/Expression.ts +++ b/packages/workflow/src/Expression.ts @@ -205,6 +205,8 @@ export class Expression { } return returnData as NodeParameterValue[] | INodeParameters[]; + } else if (parameterValue === null) { + return parameterValue; } else { // Data is an object const returnData: INodeParameters = {}; From 029581df0889b6cea64ac173c848b899811d15a9 Mon Sep 17 00:00:00 2001 From: Jan Oberhauser Date: Wed, 23 Sep 2020 13:22:53 +0200 Subject: [PATCH 088/284] :bookmark: Release n8n-workflow@0.42.0 --- 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 6ce40446f..a8944484c 100644 --- a/packages/workflow/package.json +++ b/packages/workflow/package.json @@ -1,6 +1,6 @@ { "name": "n8n-workflow", - "version": "0.41.0", + "version": "0.42.0", "description": "Workflow base code of n8n", "license": "SEE LICENSE IN LICENSE.md", "homepage": "https://n8n.io", From cf9fcf8736909e636108926c2507663106cfeb37 Mon Sep 17 00:00:00 2001 From: Jan Oberhauser Date: Wed, 23 Sep 2020 13:23:47 +0200 Subject: [PATCH 089/284] :arrow_up: Set n8n-workflow@0.42.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 1409fca92..f01cfc53f 100644 --- a/packages/core/package.json +++ b/packages/core/package.json @@ -46,7 +46,7 @@ "file-type": "^14.6.2", "lodash.get": "^4.4.2", "mime-types": "^2.1.27", - "n8n-workflow": "~0.41.0", + "n8n-workflow": "~0.42.0", "p-cancelable": "^2.0.0", "request": "^2.88.2", "request-promise-native": "^1.0.7" From 55f4fea17e6b8ec1fd43eef4fc841fda6e6fc681 Mon Sep 17 00:00:00 2001 From: Jan Oberhauser Date: Wed, 23 Sep 2020 13:24:14 +0200 Subject: [PATCH 090/284] :bookmark: Release n8n-core@0.47.0 --- 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 f01cfc53f..0abdc5033 100644 --- a/packages/core/package.json +++ b/packages/core/package.json @@ -1,6 +1,6 @@ { "name": "n8n-core", - "version": "0.46.0", + "version": "0.47.0", "description": "Core functionality of n8n", "license": "SEE LICENSE IN LICENSE.md", "homepage": "https://n8n.io", From 8ccc2e4b8348dde8acfd922082e363fb4c24074f Mon Sep 17 00:00:00 2001 From: Jan Oberhauser Date: Wed, 23 Sep 2020 13:25:03 +0200 Subject: [PATCH 091/284] :arrow_up: Set n8n-core@0.47.0 and n8n-workflow@0.42.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 00369e939..58bf85c8b 100644 --- a/packages/nodes-base/package.json +++ b/packages/nodes-base/package.json @@ -403,7 +403,7 @@ "@types/xml2js": "^0.4.3", "gulp": "^4.0.0", "jest": "^26.4.2", - "n8n-workflow": "~0.41.0", + "n8n-workflow": "~0.42.0", "ts-jest": "^26.3.0", "tslint": "^6.1.2", "typescript": "~3.7.4" @@ -432,7 +432,7 @@ "mqtt": "^4.2.0", "mssql": "^6.2.0", "mysql2": "~2.1.0", - "n8n-core": "~0.46.0", + "n8n-core": "~0.47.0", "nodemailer": "^6.4.6", "pdf-parse": "^1.1.1", "pg": "^8.3.0", From c0bb416c8ca65d3fb4268b547eb1e80e3ccf6655 Mon Sep 17 00:00:00 2001 From: Jan Oberhauser Date: Wed, 23 Sep 2020 13:25:36 +0200 Subject: [PATCH 092/284] :bookmark: Release n8n-nodes-base@0.79.0 --- 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 58bf85c8b..f69fa6aed 100644 --- a/packages/nodes-base/package.json +++ b/packages/nodes-base/package.json @@ -1,6 +1,6 @@ { "name": "n8n-nodes-base", - "version": "0.78.0", + "version": "0.79.0", "description": "Base nodes of n8n", "license": "SEE LICENSE IN LICENSE.md", "homepage": "https://n8n.io", From 3c3e1725dbf6dee014e5029f7aab95023aa70c87 Mon Sep 17 00:00:00 2001 From: Jan Oberhauser Date: Wed, 23 Sep 2020 13:26:45 +0200 Subject: [PATCH 093/284] :arrow_up: Set n8n-workflow@0.42.0 on n8n-editor-ui --- 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 f373f3653..fea9e1242 100644 --- a/packages/editor-ui/package.json +++ b/packages/editor-ui/package.json @@ -66,7 +66,7 @@ "lodash.debounce": "^4.0.8", "lodash.get": "^4.4.2", "lodash.set": "^4.3.2", - "n8n-workflow": "~0.40.0", + "n8n-workflow": "~0.42.0", "node-sass": "^4.12.0", "prismjs": "^1.17.1", "quill": "^2.0.0-dev.3", From b16447023924a1e30cbfc9b4398938e44940f117 Mon Sep 17 00:00:00 2001 From: Jan Oberhauser Date: Wed, 23 Sep 2020 13:27:17 +0200 Subject: [PATCH 094/284] :bookmark: Release n8n-editor-ui@0.57.0 --- 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 fea9e1242..6f1c341fc 100644 --- a/packages/editor-ui/package.json +++ b/packages/editor-ui/package.json @@ -1,6 +1,6 @@ { "name": "n8n-editor-ui", - "version": "0.56.0", + "version": "0.57.0", "description": "Workflow Editor UI for n8n", "license": "SEE LICENSE IN LICENSE.md", "homepage": "https://n8n.io", From 5cb5020cf688917c2b83d6e81eb41bf4e5eb8719 Mon Sep 17 00:00:00 2001 From: Jan Oberhauser Date: Wed, 23 Sep 2020 13:31:03 +0200 Subject: [PATCH 095/284] :shirt: Fix lint issue --- packages/editor-ui/src/components/CredentialsEdit.vue | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/packages/editor-ui/src/components/CredentialsEdit.vue b/packages/editor-ui/src/components/CredentialsEdit.vue index 75c800727..49e3a12ac 100644 --- a/packages/editor-ui/src/components/CredentialsEdit.vue +++ b/packages/editor-ui/src/components/CredentialsEdit.vue @@ -112,15 +112,16 @@ export default mixins( documentationUrl (): string | undefined { let credentialTypeName = ''; if (this.editCredentials) { - credentialTypeName = this.editCredentials.type; + credentialTypeName = this.editCredentials.type as string; } else { - credentialTypeName = this.credentialType; + credentialTypeName = this.credentialType as string; } const credentialType = this.$store.getters.credentialType(credentialTypeName); if (credentialType.documentationUrl !== undefined) { return `${credentialType.documentationUrl}`; } + return undefined; }, node (): INodeUi { return this.$store.getters.activeNode; From 86ed2f3096f8fe48b4a1d8a03048885220a243b0 Mon Sep 17 00:00:00 2001 From: Jan Oberhauser Date: Wed, 23 Sep 2020 13:31:38 +0200 Subject: [PATCH 096/284] :arrow_up: Set n8n-core@0.47.0, n8n-editor-ui@0.57.0, n8n-nodes-base@0.79.0 and n8n-workflow@0.42.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 497e47b3e..e9e99ee18 100644 --- a/packages/cli/package.json +++ b/packages/cli/package.json @@ -102,10 +102,10 @@ "lodash.get": "^4.4.2", "mongodb": "^3.5.5", "mysql2": "~2.1.0", - "n8n-core": "~0.46.0", - "n8n-editor-ui": "~0.56.0", - "n8n-nodes-base": "~0.78.0", - "n8n-workflow": "~0.41.0", + "n8n-core": "~0.47.0", + "n8n-editor-ui": "~0.57.0", + "n8n-nodes-base": "~0.79.0", + "n8n-workflow": "~0.42.0", "oauth-1.0a": "^2.2.6", "open": "^7.0.0", "pg": "^8.3.0", From 395fe69bd39b0c52f3d2b40adef1384d750d983f Mon Sep 17 00:00:00 2001 From: Jan Oberhauser Date: Wed, 23 Sep 2020 13:32:05 +0200 Subject: [PATCH 097/284] :bookmark: Release n8n@0.84.0 --- 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 e9e99ee18..0f7834e20 100644 --- a/packages/cli/package.json +++ b/packages/cli/package.json @@ -1,6 +1,6 @@ { "name": "n8n", - "version": "0.83.0", + "version": "0.84.0", "description": "n8n Workflow Automation Tool", "license": "SEE LICENSE IN LICENSE.md", "homepage": "https://n8n.io", From 3374fda7c66e58bd8a36e3d565b420f376a465ed Mon Sep 17 00:00:00 2001 From: Jan Oberhauser Date: Wed, 23 Sep 2020 14:14:31 +0200 Subject: [PATCH 098/284] :zap: Make it possible to open endpoints --- packages/cli/config/index.ts | 6 ++++++ packages/cli/src/Server.ts | 9 +++++++-- 2 files changed, 13 insertions(+), 2 deletions(-) diff --git a/packages/cli/config/index.ts b/packages/cli/config/index.ts index 5d66a1b3b..f6d82afaf 100644 --- a/packages/cli/config/index.ts +++ b/packages/cli/config/index.ts @@ -300,6 +300,12 @@ const config = convict({ }, security: { + excludeEndpoints: { + doc: 'Additional endpoints to exclude auth checks. Multiple endpoints can be separated by colon (":")', + format: String, + default: '', + env: 'N8N_AUTH_EXCLUDE_ENDPOINTS' + }, basicAuth: { active: { format: 'Boolean', diff --git a/packages/cli/src/Server.ts b/packages/cli/src/Server.ts index eb1c56d7d..711a15d61 100644 --- a/packages/cli/src/Server.ts +++ b/packages/cli/src/Server.ts @@ -172,8 +172,13 @@ class App { async config(): Promise { this.versions = await GenericHelpers.getVersions(); - const ignoredEndpoints = _(['healthz', this.endpointWebhook, this.endpointWebhookTest, this.endpointPresetCredentials]).compact().join('|'); - const authIgnoreRegex = new RegExp(`^\/(${ignoredEndpoints})\/?.*$`); + + const excludeEndpoints = config.get('security.excludeEndpoints') as string; + + const ignoredEndpoints = ['healthz', this.endpointWebhook, this.endpointWebhookTest, this.endpointPresetCredentials]; + ignoredEndpoints.push.apply(ignoredEndpoints, excludeEndpoints.split(':')); + + const authIgnoreRegex = new RegExp(`^\/(${_(ignoredEndpoints).compact().join('|')})\/?.*$`); // Check for basic auth credentials if activated const basicAuthActive = config.get('security.basicAuth.active') as boolean; From acae926a36441cc4a0d5f30f412235d081af7ebc Mon Sep 17 00:00:00 2001 From: Jan Oberhauser Date: Wed, 23 Sep 2020 14:15:01 +0200 Subject: [PATCH 099/284] :bookmark: Release n8n@0.84.1 --- 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 0f7834e20..e92f760d1 100644 --- a/packages/cli/package.json +++ b/packages/cli/package.json @@ -1,6 +1,6 @@ { "name": "n8n", - "version": "0.84.0", + "version": "0.84.1", "description": "n8n Workflow Automation Tool", "license": "SEE LICENSE IN LICENSE.md", "homepage": "https://n8n.io", From 9cb106c9b47203836c787fbbf868c37afafc13e4 Mon Sep 17 00:00:00 2001 From: Jan Oberhauser Date: Wed, 23 Sep 2020 17:31:00 +0200 Subject: [PATCH 100/284] :bug: Fix issue that OAuth window did not close --- packages/editor-ui/src/components/CredentialsInput.vue | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/packages/editor-ui/src/components/CredentialsInput.vue b/packages/editor-ui/src/components/CredentialsInput.vue index 27a459733..385f0ff9b 100644 --- a/packages/editor-ui/src/components/CredentialsInput.vue +++ b/packages/editor-ui/src/components/CredentialsInput.vue @@ -404,10 +404,11 @@ export default mixins( message: 'Connected successfully!', type: 'success', }); + + // Make sure that the event gets removed again + window.removeEventListener('message', receiveMessage, false); } - // Make sure that the event gets removed again - window.removeEventListener('message', receiveMessage, false); }; window.addEventListener('message', receiveMessage, false); From 92374095235e65e5c2e7b0743f1b9452cf0dfc25 Mon Sep 17 00:00:00 2001 From: Jan Oberhauser Date: Wed, 23 Sep 2020 17:31:59 +0200 Subject: [PATCH 101/284] :bookmark: Release n8n-editor-ui@0.57.1 --- 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 6f1c341fc..d5a27b099 100644 --- a/packages/editor-ui/package.json +++ b/packages/editor-ui/package.json @@ -1,6 +1,6 @@ { "name": "n8n-editor-ui", - "version": "0.57.0", + "version": "0.57.1", "description": "Workflow Editor UI for n8n", "license": "SEE LICENSE IN LICENSE.md", "homepage": "https://n8n.io", From 8bca883764f0a497ed6771543accd95c75235197 Mon Sep 17 00:00:00 2001 From: Jan Oberhauser Date: Wed, 23 Sep 2020 17:33:41 +0200 Subject: [PATCH 102/284] :arrow_up: Set n8n-editor-ui@0.57.1 on n8n --- 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 e92f760d1..05b9ff108 100644 --- a/packages/cli/package.json +++ b/packages/cli/package.json @@ -103,7 +103,7 @@ "mongodb": "^3.5.5", "mysql2": "~2.1.0", "n8n-core": "~0.47.0", - "n8n-editor-ui": "~0.57.0", + "n8n-editor-ui": "~0.57.1", "n8n-nodes-base": "~0.79.0", "n8n-workflow": "~0.42.0", "oauth-1.0a": "^2.2.6", From c3a7685529f8e42a5b9abe53095024c159262f46 Mon Sep 17 00:00:00 2001 From: Jan Oberhauser Date: Wed, 23 Sep 2020 17:35:05 +0200 Subject: [PATCH 103/284] :bookmark: Release n8n@0.84.2 --- 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 05b9ff108..e0708db7f 100644 --- a/packages/cli/package.json +++ b/packages/cli/package.json @@ -1,6 +1,6 @@ { "name": "n8n", - "version": "0.84.1", + "version": "0.84.2", "description": "n8n Workflow Automation Tool", "license": "SEE LICENSE IN LICENSE.md", "homepage": "https://n8n.io", From 521425894c3e20b4508f6f1558f372a886109b2f Mon Sep 17 00:00:00 2001 From: Jan Oberhauser Date: Wed, 23 Sep 2020 18:09:36 +0200 Subject: [PATCH 104/284] :bug: Set old version of jwks-rsa to fix build --- 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 e0708db7f..97628c70a 100644 --- a/packages/cli/package.json +++ b/packages/cli/package.json @@ -97,7 +97,7 @@ "google-timezones-json": "^1.0.2", "inquirer": "^7.0.1", "jsonwebtoken": "^8.5.1", - "jwks-rsa": "^1.6.0", + "jwks-rsa": "~1.9.0", "localtunnel": "^2.0.0", "lodash.get": "^4.4.2", "mongodb": "^3.5.5", From bb33333eba34d45012acceb6742539e26dd83065 Mon Sep 17 00:00:00 2001 From: Jan Oberhauser Date: Wed, 23 Sep 2020 18:10:29 +0200 Subject: [PATCH 105/284] :bookmark: Release n8n@0.84.3 --- 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 97628c70a..8dfdd3ccf 100644 --- a/packages/cli/package.json +++ b/packages/cli/package.json @@ -1,6 +1,6 @@ { "name": "n8n", - "version": "0.84.2", + "version": "0.84.3", "description": "n8n Workflow Automation Tool", "license": "SEE LICENSE IN LICENSE.md", "homepage": "https://n8n.io", From 3ed3ee779bff8c3a5f33b0866131cc1410a98587 Mon Sep 17 00:00:00 2001 From: Ricardo Espinoza Date: Wed, 23 Sep 2020 16:20:44 -0400 Subject: [PATCH 106/284] :zap: Fix issue #984 (#985) --- packages/nodes-base/nodes/Google/Sheet/GoogleSheets.node.ts | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/packages/nodes-base/nodes/Google/Sheet/GoogleSheets.node.ts b/packages/nodes-base/nodes/Google/Sheet/GoogleSheets.node.ts index 65f074669..ff12b218e 100644 --- a/packages/nodes-base/nodes/Google/Sheet/GoogleSheets.node.ts +++ b/packages/nodes-base/nodes/Google/Sheet/GoogleSheets.node.ts @@ -611,7 +611,10 @@ export class GoogleSheets implements INodeType { let range = ''; if (operation !== 'delete') { range = this.getNodeParameter('range', 0) as string; - range = encodeURIComponent(range); + if (range.includes('!')) { + const [sheet, ranges] = range.split('!'); + range = `${encodeURIComponent(sheet)}!${ranges}`; + } } const options = this.getNodeParameter('options', 0, {}) as IDataObject; From 30121a02eaaa3667e996e37b5e8c6fe4d13de4a4 Mon Sep 17 00:00:00 2001 From: Jan Oberhauser Date: Wed, 23 Sep 2020 22:22:57 +0200 Subject: [PATCH 107/284] :bookmark: Release n8n-nodes-base@0.79.1 --- 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 f69fa6aed..375d8a66d 100644 --- a/packages/nodes-base/package.json +++ b/packages/nodes-base/package.json @@ -1,6 +1,6 @@ { "name": "n8n-nodes-base", - "version": "0.79.0", + "version": "0.79.1", "description": "Base nodes of n8n", "license": "SEE LICENSE IN LICENSE.md", "homepage": "https://n8n.io", From 6d7c5069774c73abbbf5cee4adcde2dc5028d8c9 Mon Sep 17 00:00:00 2001 From: Jan Oberhauser Date: Wed, 23 Sep 2020 22:23:58 +0200 Subject: [PATCH 108/284] :arrow_up: n8n-nodes-base@0.79.1 on n8n --- 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 8dfdd3ccf..22b213cd2 100644 --- a/packages/cli/package.json +++ b/packages/cli/package.json @@ -104,7 +104,7 @@ "mysql2": "~2.1.0", "n8n-core": "~0.47.0", "n8n-editor-ui": "~0.57.1", - "n8n-nodes-base": "~0.79.0", + "n8n-nodes-base": "~0.79.1", "n8n-workflow": "~0.42.0", "oauth-1.0a": "^2.2.6", "open": "^7.0.0", From 93963da1a11b79d5ba8fc57fda5a4f52c8e2ba56 Mon Sep 17 00:00:00 2001 From: Jan Oberhauser Date: Wed, 23 Sep 2020 22:24:42 +0200 Subject: [PATCH 109/284] :bookmark: Release n8n@0.84.4 --- 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 22b213cd2..15ccd63b3 100644 --- a/packages/cli/package.json +++ b/packages/cli/package.json @@ -1,6 +1,6 @@ { "name": "n8n", - "version": "0.84.3", + "version": "0.84.4", "description": "n8n Workflow Automation Tool", "license": "SEE LICENSE IN LICENSE.md", "homepage": "https://n8n.io", From fb595772f3dfcf8147e40ffa8db07bebe6d53f69 Mon Sep 17 00:00:00 2001 From: Jan Oberhauser Date: Thu, 24 Sep 2020 09:38:12 +0200 Subject: [PATCH 110/284] :zap: Remove necessary console logs --- packages/core/src/ActiveWorkflows.ts | 4 ---- 1 file changed, 4 deletions(-) diff --git a/packages/core/src/ActiveWorkflows.ts b/packages/core/src/ActiveWorkflows.ts index 5576aeed3..2e3b6d7c1 100644 --- a/packages/core/src/ActiveWorkflows.ts +++ b/packages/core/src/ActiveWorkflows.ts @@ -67,8 +67,6 @@ export class ActiveWorkflows { * @memberof ActiveWorkflows */ async add(id: string, workflow: Workflow, additionalData: IWorkflowExecuteAdditionalData, getTriggerFunctions: IGetExecuteTriggerFunctions, getPollFunctions: IGetExecutePollFunctions): Promise { - console.log('ADD ID (active): ' + id); - this.workflowData[id] = {}; const triggerNodes = workflow.getTriggerNodes(); @@ -204,8 +202,6 @@ export class ActiveWorkflows { * @memberof ActiveWorkflows */ async remove(id: string): Promise { - console.log('REMOVE ID (active): ' + id); - if (!this.isActive(id)) { // Workflow is currently not registered throw new Error(`The workflow with the id "${id}" is currently not active and can so not be removed`); From c5e14e4c2ffa435559dc85c8c6595bca8584e50f Mon Sep 17 00:00:00 2001 From: Jan Oberhauser Date: Thu, 24 Sep 2020 10:02:05 +0200 Subject: [PATCH 111/284] :shirt: Fix lint issue --- packages/nodes-base/nodes/HtmlExtract/HtmlExtract.node.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/packages/nodes-base/nodes/HtmlExtract/HtmlExtract.node.ts b/packages/nodes-base/nodes/HtmlExtract/HtmlExtract.node.ts index ddedbc082..f482829c2 100644 --- a/packages/nodes-base/nodes/HtmlExtract/HtmlExtract.node.ts +++ b/packages/nodes-base/nodes/HtmlExtract/HtmlExtract.node.ts @@ -7,6 +7,8 @@ import { IDataObject, } from 'n8n-workflow'; +type Cheerio = ReturnType; + interface IValueData { attribute?: string; cssSelector: string; From 2072f53316d9ade76b634e9979b84b511732e3c0 Mon Sep 17 00:00:00 2001 From: Jan Oberhauser Date: Mon, 28 Sep 2020 12:11:52 +0200 Subject: [PATCH 112/284] :zap: Change bcrypt library to make it work better in windows --- packages/cli/package.json | 4 ++-- packages/cli/src/Server.ts | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/cli/package.json b/packages/cli/package.json index 15ccd63b3..3e8f853f9 100644 --- a/packages/cli/package.json +++ b/packages/cli/package.json @@ -54,7 +54,7 @@ "devDependencies": { "@oclif/dev-cli": "^1.22.2", "@types/basic-auth": "^1.1.2", - "@types/bcrypt": "^3.0.0", + "@types/bcryptjs": "^2.4.1", "@types/compression": "1.0.1", "@types/connect-history-api-fallback": "^1.3.1", "@types/convict": "^4.2.1", @@ -82,7 +82,7 @@ "@oclif/errors": "^1.2.2", "@types/jsonwebtoken": "^8.3.4", "basic-auth": "^2.0.1", - "bcrypt": "^5.0.0", + "bcryptjs": "^2.4.3", "body-parser": "^1.18.3", "body-parser-xml": "^1.1.0", "client-oauth2": "^4.2.5", diff --git a/packages/cli/src/Server.ts b/packages/cli/src/Server.ts index 711a15d61..67e3f8974 100644 --- a/packages/cli/src/Server.ts +++ b/packages/cli/src/Server.ts @@ -20,7 +20,7 @@ import { RequestOptions } from 'oauth-1.0a'; import * as csrf from 'csrf'; import * as requestPromise from 'request-promise-native'; import { createHmac } from 'crypto'; -import { compare } from 'bcrypt'; +import { compare } from 'bcryptjs'; import { ActiveExecutions, From 83f6f0c8449349422ba7959f7d082202d9859b23 Mon Sep 17 00:00:00 2001 From: Jan Oberhauser Date: Mon, 28 Sep 2020 14:36:32 +0200 Subject: [PATCH 113/284] :zap: Fix issue with some issue types on Jira --- packages/nodes-base/nodes/Jira/Jira.node.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/nodes-base/nodes/Jira/Jira.node.ts b/packages/nodes-base/nodes/Jira/Jira.node.ts index 07c29bca9..e1e21ec01 100644 --- a/packages/nodes-base/nodes/Jira/Jira.node.ts +++ b/packages/nodes-base/nodes/Jira/Jira.node.ts @@ -153,7 +153,7 @@ export class Jira implements INodeType { } } else { for (const issueType of issueTypes) { - if (issueType.scope.project.id === projectId) { + if (issueType.scope === undefined || issueType.scope.project.id === projectId) { const issueTypeName = issueType.name; const issueTypeId = issueType.id; From 3350723bf3b06a85c08998ee84b8d2dd8a44e02f Mon Sep 17 00:00:00 2001 From: Rytis Ilciukas Date: Tue, 29 Sep 2020 10:39:51 +0200 Subject: [PATCH 114/284] :zap: fix malformed html (#999) --- packages/editor-ui/src/components/NodeSettings.vue | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/editor-ui/src/components/NodeSettings.vue b/packages/editor-ui/src/components/NodeSettings.vue index 6808b8256..07f8ac7b2 100644 --- a/packages/editor-ui/src/components/NodeSettings.vue +++ b/packages/editor-ui/src/components/NodeSettings.vue @@ -248,7 +248,7 @@ export default mixins( type: 'boolean', default: false, noDataExpression: true, - description: 'If active, the workflow continues even if this node\'s
previous nodes - so your workflow should account for unexpected output data.', + description: 'If active, the workflow continues even if this node\'s
execution fails. When this occurs, the node passes along input data from
previous nodes - so your workflow should account for unexpected output data.', }, ] as INodeProperties[], From 6f576b3da9d2fa613560b02cb21ba40dd59499f4 Mon Sep 17 00:00:00 2001 From: Jan Oberhauser Date: Tue, 29 Sep 2020 21:57:22 +0200 Subject: [PATCH 115/284] :zap: Add Schema to Postgres-Update #996 --- .../nodes/Postgres/Postgres.node.functions.ts | 5 ++++- packages/nodes-base/nodes/Postgres/Postgres.node.ts | 13 +++++++++++++ 2 files changed, 17 insertions(+), 1 deletion(-) diff --git a/packages/nodes-base/nodes/Postgres/Postgres.node.functions.ts b/packages/nodes-base/nodes/Postgres/Postgres.node.functions.ts index 0e7f73827..eaeebe313 100644 --- a/packages/nodes-base/nodes/Postgres/Postgres.node.functions.ts +++ b/packages/nodes-base/nodes/Postgres/Postgres.node.functions.ts @@ -105,11 +105,14 @@ export async function pgUpdate( items: INodeExecutionData[], ): Promise { const table = getNodeParam('table', 0) as string; + const schema = getNodeParam('schema', 0) as string; const updateKey = getNodeParam('updateKey', 0) as string; const columnString = getNodeParam('columns', 0) as string; const columns = columnString.split(',').map(column => column.trim()); + const te = new pgp.helpers.TableName({ table, schema }); + // Make sure that the updateKey does also get queried if (!columns.includes(updateKey)) { columns.unshift(updateKey); @@ -120,7 +123,7 @@ export async function pgUpdate( // Generate the multi-row update query const query = - pgp.helpers.update(updateItems, columns, table) + ' WHERE v.' + updateKey + ' = t.' + updateKey; + pgp.helpers.update(updateItems, columns, te) + ' WHERE v.' + updateKey + ' = t.' + updateKey; // Executing the query to update the data await db.none(query); diff --git a/packages/nodes-base/nodes/Postgres/Postgres.node.ts b/packages/nodes-base/nodes/Postgres/Postgres.node.ts index 7c706a0fc..162851084 100644 --- a/packages/nodes-base/nodes/Postgres/Postgres.node.ts +++ b/packages/nodes-base/nodes/Postgres/Postgres.node.ts @@ -136,6 +136,19 @@ export class Postgres implements INodeType { // ---------------------------------- // update // ---------------------------------- + { + displayName: 'Schema', + name: 'schema', + type: 'string', + displayOptions: { + show: { + operation: ['update'], + }, + }, + default: 'public', + required: true, + description: 'Name of the schema the table belongs to', + }, { displayName: 'Table', name: 'table', From f1680386ca7237a29f4bc90c61e14ec3166b4981 Mon Sep 17 00:00:00 2001 From: Jan Oberhauser Date: Wed, 30 Sep 2020 15:50:43 +0200 Subject: [PATCH 116/284] :zap: Get OAuth-Callback URLs from backend --- packages/cli/src/Interfaces.ts | 4 ++++ packages/cli/src/Server.ts | 10 ++++++++-- packages/editor-ui/src/Interface.ts | 4 ++++ packages/editor-ui/src/components/CredentialsInput.vue | 2 +- packages/editor-ui/src/store.ts | 8 ++++++++ packages/editor-ui/src/views/NodeView.vue | 1 + 6 files changed, 26 insertions(+), 3 deletions(-) diff --git a/packages/cli/src/Interfaces.ts b/packages/cli/src/Interfaces.ts index bef98af6f..b808a7356 100644 --- a/packages/cli/src/Interfaces.ts +++ b/packages/cli/src/Interfaces.ts @@ -288,6 +288,10 @@ export interface IN8nUISettings { saveManualExecutions: boolean; executionTimeout: number; maxExecutionTimeout: number; + oauthCallbackUrls: { + oauth1: string; + oauth2: string; + }; timezone: string; urlBaseWebhook: string; versionCli: string; diff --git a/packages/cli/src/Server.ts b/packages/cli/src/Server.ts index 67e3f8974..c3e178094 100644 --- a/packages/cli/src/Server.ts +++ b/packages/cli/src/Server.ts @@ -1617,7 +1617,9 @@ class App { // Returns the settings which are needed in the UI this.app.get(`/${this.restEndpoint}/settings`, ResponseHelper.send(async (req: express.Request, res: express.Response): Promise => { - return { + const urlBaseWebhook = WebhookHelpers.getWebhookBaseUrl(); + + const settings: IN8nUISettings = { endpointWebhook: this.endpointWebhook, endpointWebhookTest: this.endpointWebhookTest, saveDataErrorExecution: this.saveDataErrorExecution, @@ -1626,8 +1628,12 @@ class App { executionTimeout: this.executionTimeout, maxExecutionTimeout: this.maxExecutionTimeout, timezone: this.timezone, - urlBaseWebhook: WebhookHelpers.getWebhookBaseUrl(), + urlBaseWebhook, versionCli: this.versions!.cli, + oauthCallbackUrls: { + 'oauth1': urlBaseWebhook + `${this.restEndpoint}/oauth1-credential/callback`, + 'oauth2': urlBaseWebhook + `${this.restEndpoint}/oauth2-credential/callback`, + } }; })); diff --git a/packages/editor-ui/src/Interface.ts b/packages/editor-ui/src/Interface.ts index 2f3973b84..bdb3fe0fa 100644 --- a/packages/editor-ui/src/Interface.ts +++ b/packages/editor-ui/src/Interface.ts @@ -399,6 +399,10 @@ export interface IN8nUISettings { timezone: string; executionTimeout: number; maxExecutionTimeout: number; + oauthCallbackUrls: { + oauth1: string; + oauth2: string; + }; urlBaseWebhook: string; versionCli: string; } diff --git a/packages/editor-ui/src/components/CredentialsInput.vue b/packages/editor-ui/src/components/CredentialsInput.vue index 385f0ff9b..ae678cb25 100644 --- a/packages/editor-ui/src/components/CredentialsInput.vue +++ b/packages/editor-ui/src/components/CredentialsInput.vue @@ -235,7 +235,7 @@ export default mixins( oAuthCallbackUrl (): string { const types = this.parentTypes(this.credentialTypeData.name); const oauthType = (this.credentialTypeData.name === 'oAuth2Api' || types.includes('oAuth2Api')) ? 'oauth2' : 'oauth1'; - return this.$store.getters.getWebhookBaseUrl + `rest/${oauthType}-credential/callback`; + return this.$store.getters.oauthCallbackUrls[oauthType]; }, requiredPropertiesFilled (): boolean { for (const property of this.credentialProperties) { diff --git a/packages/editor-ui/src/store.ts b/packages/editor-ui/src/store.ts index 1b54f3c1a..8aabbdee0 100644 --- a/packages/editor-ui/src/store.ts +++ b/packages/editor-ui/src/store.ts @@ -8,6 +8,7 @@ import { IConnection, IConnections, ICredentialType, + IDataObject, INodeConnections, INodeIssueData, INodeTypeDescription, @@ -55,6 +56,7 @@ export const store = new Vuex.Store({ executionTimeout: -1, maxExecutionTimeout: Number.MAX_SAFE_INTEGER, versionCli: '0.0.0', + oauthCallbackUrls: {}, workflowExecutionData: null as IExecutionResponse | null, lastSelectedNode: null as string | null, lastSelectedNodeOutputIndex: null as number | null, @@ -490,6 +492,9 @@ export const store = new Vuex.Store({ setVersionCli (state, version: string) { Vue.set(state, 'versionCli', version); }, + setOauthCallbackUrls(state, urls: IDataObject) { + Vue.set(state, 'oauthCallbackUrls', urls); + }, addNodeType (state, typeData: INodeTypeDescription) { if (!typeData.hasOwnProperty('name')) { @@ -609,6 +614,9 @@ export const store = new Vuex.Store({ versionCli: (state): string => { return state.versionCli; }, + oauthCallbackUrls: (state): object => { + return state.oauthCallbackUrls; + }, // Push Connection pushConnectionActive: (state): boolean => { diff --git a/packages/editor-ui/src/views/NodeView.vue b/packages/editor-ui/src/views/NodeView.vue index 742e0ae70..92daf07ae 100644 --- a/packages/editor-ui/src/views/NodeView.vue +++ b/packages/editor-ui/src/views/NodeView.vue @@ -1873,6 +1873,7 @@ export default mixins( this.$store.commit('setExecutionTimeout', settings.executionTimeout); this.$store.commit('setMaxExecutionTimeout', settings.maxExecutionTimeout); this.$store.commit('setVersionCli', settings.versionCli); + this.$store.commit('setOauthCallbackUrls', settings.oauthCallbackUrls); }, async loadNodeTypes (): Promise { const nodeTypes = await this.restApi().getNodeTypes(); From 23dbdbc1698d4ce2495f4af96554d75f8083ac33 Mon Sep 17 00:00:00 2001 From: Jan Oberhauser Date: Wed, 30 Sep 2020 16:11:45 +0200 Subject: [PATCH 117/284] :zap: Add hook which allows to overwrite frontend settings --- packages/cli/src/Server.ts | 43 +++++++++++++++++++++----------------- 1 file changed, 24 insertions(+), 19 deletions(-) diff --git a/packages/cli/src/Server.ts b/packages/cli/src/Server.ts index c3e178094..44f5d3ad9 100644 --- a/packages/cli/src/Server.ts +++ b/packages/cli/src/Server.ts @@ -121,7 +121,7 @@ class App { push: Push.Push; versions: IPackageVersions | undefined; restEndpoint: string; - + frontendSettings: IN8nUISettings; protocol: string; sslKey: string; sslCert: string; @@ -155,6 +155,25 @@ class App { this.presetCredentialsLoaded = false; this.endpointPresetCredentials = config.get('credentials.overwrite.endpoint') as string; + + const urlBaseWebhook = WebhookHelpers.getWebhookBaseUrl(); + + this.frontendSettings = { + endpointWebhook: this.endpointWebhook, + endpointWebhookTest: this.endpointWebhookTest, + saveDataErrorExecution: this.saveDataErrorExecution, + saveDataSuccessExecution: this.saveDataSuccessExecution, + saveManualExecutions: this.saveManualExecutions, + executionTimeout: this.executionTimeout, + maxExecutionTimeout: this.maxExecutionTimeout, + timezone: this.timezone, + urlBaseWebhook, + versionCli: '', + oauthCallbackUrls: { + 'oauth1': urlBaseWebhook + `${this.restEndpoint}/oauth1-credential/callback`, + 'oauth2': urlBaseWebhook + `${this.restEndpoint}/oauth2-credential/callback`, + } + }; } @@ -172,6 +191,9 @@ class App { async config(): Promise { this.versions = await GenericHelpers.getVersions(); + this.frontendSettings.versionCli = this.versions.cli; + + await this.externalHooks.run('frontend.settings', [this.frontendSettings]); const excludeEndpoints = config.get('security.excludeEndpoints') as string; @@ -1617,24 +1639,7 @@ class App { // Returns the settings which are needed in the UI this.app.get(`/${this.restEndpoint}/settings`, ResponseHelper.send(async (req: express.Request, res: express.Response): Promise => { - const urlBaseWebhook = WebhookHelpers.getWebhookBaseUrl(); - - const settings: IN8nUISettings = { - endpointWebhook: this.endpointWebhook, - endpointWebhookTest: this.endpointWebhookTest, - saveDataErrorExecution: this.saveDataErrorExecution, - saveDataSuccessExecution: this.saveDataSuccessExecution, - saveManualExecutions: this.saveManualExecutions, - executionTimeout: this.executionTimeout, - maxExecutionTimeout: this.maxExecutionTimeout, - timezone: this.timezone, - urlBaseWebhook, - versionCli: this.versions!.cli, - oauthCallbackUrls: { - 'oauth1': urlBaseWebhook + `${this.restEndpoint}/oauth1-credential/callback`, - 'oauth2': urlBaseWebhook + `${this.restEndpoint}/oauth2-credential/callback`, - } - }; + return this.frontendSettings; })); From 0f5bcd59c87939bfe5165219e4e4b6f047f0df38 Mon Sep 17 00:00:00 2001 From: Ricardo Espinoza Date: Wed, 30 Sep 2020 10:12:39 -0400 Subject: [PATCH 118/284] :bug: Fix issue #987 (#990) --- packages/nodes-base/nodes/Pipedrive/GenericFunctions.ts | 4 ++-- packages/nodes-base/nodes/Pipedrive/PipedriveTrigger.node.ts | 1 - 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/packages/nodes-base/nodes/Pipedrive/GenericFunctions.ts b/packages/nodes-base/nodes/Pipedrive/GenericFunctions.ts index 17033eaf7..4b4d29b98 100644 --- a/packages/nodes-base/nodes/Pipedrive/GenericFunctions.ts +++ b/packages/nodes-base/nodes/Pipedrive/GenericFunctions.ts @@ -34,7 +34,7 @@ export interface ICustomProperties { * @param {object} body * @returns {Promise} */ -export async function pipedriveApiRequest(this: IHookFunctions | IExecuteFunctions | ILoadOptionsFunctions, method: string, endpoint: string, body: IDataObject, query?: IDataObject, formData?: IDataObject, downloadFile?: boolean): Promise { // tslint:disable-line:no-any +export async function pipedriveApiRequest(this: IHookFunctions | IExecuteFunctions | ILoadOptionsFunctions, method: string, endpoint: string, body: IDataObject, query: IDataObject = {}, formData?: IDataObject, downloadFile?: boolean): Promise { // tslint:disable-line:no-any const authenticationMethod = this.getNodeParameter('authentication', 0); const options: OptionsWithUri = { @@ -67,7 +67,7 @@ export async function pipedriveApiRequest(this: IHookFunctions | IExecuteFunctio let responseData; try { - if (authenticationMethod === 'basicAuth' || authenticationMethod === 'apiToken') { + if (authenticationMethod === 'basicAuth' || authenticationMethod === 'apiToken' || authenticationMethod === 'none') { const credentials = this.getCredentials('pipedriveApi'); if (credentials === undefined) { diff --git a/packages/nodes-base/nodes/Pipedrive/PipedriveTrigger.node.ts b/packages/nodes-base/nodes/Pipedrive/PipedriveTrigger.node.ts index 06aa9c923..6a6bfa1a6 100644 --- a/packages/nodes-base/nodes/Pipedrive/PipedriveTrigger.node.ts +++ b/packages/nodes-base/nodes/Pipedrive/PipedriveTrigger.node.ts @@ -215,7 +215,6 @@ export class PipedriveTrigger implements INodeType { async create(this: IHookFunctions): Promise { const webhookUrl = this.getNodeWebhookUrl('default'); const authentication = this.getNodeParameter('authentication', 0) as string; - const eventAction = this.getNodeParameter('action') as string; const eventObject = this.getNodeParameter('object') as string; From 7f6e7583061065c33eaf4e2bc3f7b2ca437ecab1 Mon Sep 17 00:00:00 2001 From: Jan Oberhauser Date: Wed, 30 Sep 2020 16:18:02 +0200 Subject: [PATCH 119/284] :bookmark: Release n8n-nodes-base@0.80.0 --- 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 375d8a66d..2b6eee517 100644 --- a/packages/nodes-base/package.json +++ b/packages/nodes-base/package.json @@ -1,6 +1,6 @@ { "name": "n8n-nodes-base", - "version": "0.79.1", + "version": "0.80.0", "description": "Base nodes of n8n", "license": "SEE LICENSE IN LICENSE.md", "homepage": "https://n8n.io", From c2ba4b64c3602caa0985a39a7b423f010c638788 Mon Sep 17 00:00:00 2001 From: Jan Oberhauser Date: Wed, 30 Sep 2020 16:19:04 +0200 Subject: [PATCH 120/284] :bookmark: Release n8n-editor-ui@0.58.0 --- 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 d5a27b099..6e1518f53 100644 --- a/packages/editor-ui/package.json +++ b/packages/editor-ui/package.json @@ -1,6 +1,6 @@ { "name": "n8n-editor-ui", - "version": "0.57.1", + "version": "0.58.0", "description": "Workflow Editor UI for n8n", "license": "SEE LICENSE IN LICENSE.md", "homepage": "https://n8n.io", From 124bcdc176f62c7785c943c112b66abb7c2e0d20 Mon Sep 17 00:00:00 2001 From: Jan Oberhauser Date: Wed, 30 Sep 2020 16:20:48 +0200 Subject: [PATCH 121/284] :arrow_up: Set n8n-editor-ui@0.58.0 and n8n-nodes-base@0.80.0 on n8n --- packages/cli/package.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/cli/package.json b/packages/cli/package.json index 3e8f853f9..bddc91b7c 100644 --- a/packages/cli/package.json +++ b/packages/cli/package.json @@ -103,8 +103,8 @@ "mongodb": "^3.5.5", "mysql2": "~2.1.0", "n8n-core": "~0.47.0", - "n8n-editor-ui": "~0.57.1", - "n8n-nodes-base": "~0.79.1", + "n8n-editor-ui": "~0.58.0", + "n8n-nodes-base": "~0.80.0", "n8n-workflow": "~0.42.0", "oauth-1.0a": "^2.2.6", "open": "^7.0.0", From 70dc99da41f6e1f1b6c520ecd1e091f3c079e678 Mon Sep 17 00:00:00 2001 From: Jan Oberhauser Date: Wed, 30 Sep 2020 16:21:14 +0200 Subject: [PATCH 122/284] :bookmark: Release n8n@0.85.0 --- 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 bddc91b7c..dfa2e3f0f 100644 --- a/packages/cli/package.json +++ b/packages/cli/package.json @@ -1,6 +1,6 @@ { "name": "n8n", - "version": "0.84.4", + "version": "0.85.0", "description": "n8n Workflow Automation Tool", "license": "SEE LICENSE IN LICENSE.md", "homepage": "https://n8n.io", From 9b5731450fd7d2cb107b24670613a18dea0f6b28 Mon Sep 17 00:00:00 2001 From: Rupenieks <32895755+Rupenieks@users.noreply.github.com> Date: Thu, 1 Oct 2020 09:07:55 +0200 Subject: [PATCH 123/284] :sparkles: Add Wufoo-Trigger node (#597) * Generic functions, credentials, interface, logo, triggernode * TSLint tabs rule, request error handling, webhook done On hold until Wufoo responds with sandbox account * Removed test logging, fixed various errors, cleaned up * Sorted imports, adjusted data display, removed anys, tslint import sort rule * RAAAAAW data * :zap: Fix logo and improve formatting * :zap: Improvements * :zap: Minor improvements to WuFoo-Trigger Co-authored-by: Rupenieks Co-authored-by: ricardo Co-authored-by: Jan Oberhauser --- .../credentials/WufooApi.credentials.ts | 23 ++ .../nodes/Wufoo/GenericFunctions.ts | 45 ++++ packages/nodes-base/nodes/Wufoo/Interface.ts | 27 ++ .../nodes/Wufoo/WufooTrigger.node.ts | 248 ++++++++++++++++++ packages/nodes-base/nodes/Wufoo/wufoo.png | Bin 0 -> 1668 bytes packages/nodes-base/package.json | 2 + packages/nodes-base/tslint.json | 5 + 7 files changed, 350 insertions(+) create mode 100644 packages/nodes-base/credentials/WufooApi.credentials.ts create mode 100644 packages/nodes-base/nodes/Wufoo/GenericFunctions.ts create mode 100644 packages/nodes-base/nodes/Wufoo/Interface.ts create mode 100644 packages/nodes-base/nodes/Wufoo/WufooTrigger.node.ts create mode 100644 packages/nodes-base/nodes/Wufoo/wufoo.png diff --git a/packages/nodes-base/credentials/WufooApi.credentials.ts b/packages/nodes-base/credentials/WufooApi.credentials.ts new file mode 100644 index 000000000..70e0a27da --- /dev/null +++ b/packages/nodes-base/credentials/WufooApi.credentials.ts @@ -0,0 +1,23 @@ +import { + ICredentialType, + NodePropertyTypes, +} from 'n8n-workflow'; + +export class WufooApi implements ICredentialType { + name = 'wufooApi'; + displayName = 'Wufoo API'; + properties = [ + { + displayName: 'API Key', + name: 'apiKey', + type: 'string' as NodePropertyTypes, + default: '', + }, + { + displayName: 'Subdomain', + name: 'subdomain', + type: 'string' as NodePropertyTypes, + default: '', + } + ]; +} diff --git a/packages/nodes-base/nodes/Wufoo/GenericFunctions.ts b/packages/nodes-base/nodes/Wufoo/GenericFunctions.ts new file mode 100644 index 000000000..bcc773c91 --- /dev/null +++ b/packages/nodes-base/nodes/Wufoo/GenericFunctions.ts @@ -0,0 +1,45 @@ +import { + OptionsWithUri, +} from 'request'; + +import { + IExecuteFunctions, + IExecuteSingleFunctions, + IHookFunctions, + ILoadOptionsFunctions, +} from 'n8n-core'; + +import { + IDataObject, +} from 'n8n-workflow'; + +export async function wufooApiRequest(this: IHookFunctions | IExecuteFunctions | IExecuteSingleFunctions | ILoadOptionsFunctions, method: string, resource: string, body: any = {}, qs: IDataObject = {}, uri?: string, option: IDataObject = {}): Promise { // tslint:disable-line:no-any + const credentials = this.getCredentials('wufooApi'); + if (credentials === undefined) { + throw new Error('No credentials got returned!'); + } + + let options: OptionsWithUri = { + auth: { + username: credentials!.apiKey as string, + password: '', + }, + method, + form: body, + body, + qs, + uri: `https://${credentials!.subdomain}.wufoo.com/api/v3/${resource}`, + json: true + }; + + options = Object.assign({}, options, option); + if (Object.keys(options.body).length === 0 || method === 'PUT') { + delete options.body; + } + + try { + return await this.helpers.request!(options); + } catch (error) { + throw new Error(error.message); + } +} diff --git a/packages/nodes-base/nodes/Wufoo/Interface.ts b/packages/nodes-base/nodes/Wufoo/Interface.ts new file mode 100644 index 000000000..88a76965b --- /dev/null +++ b/packages/nodes-base/nodes/Wufoo/Interface.ts @@ -0,0 +1,27 @@ +export interface IFormQuery { + includeTodayCount?: boolean; +} + +export interface IWebhook { + url: string; + handshakeKey?: string; + metadata?: boolean; +} + +interface ISubField { + DefaultVal: string; + ID: string; + Label: string; +} + +export interface IField { + Title: string; + Instructions: string; + IsRequired: number; + ClassNames: string; + DefaultVal: string; + Page: number; + Type: string; + ID: string; + SubFields: [ISubField]; +} diff --git a/packages/nodes-base/nodes/Wufoo/WufooTrigger.node.ts b/packages/nodes-base/nodes/Wufoo/WufooTrigger.node.ts new file mode 100644 index 000000000..b4b0eff98 --- /dev/null +++ b/packages/nodes-base/nodes/Wufoo/WufooTrigger.node.ts @@ -0,0 +1,248 @@ +import { + IHookFunctions, + IWebhookFunctions, +} from 'n8n-core'; + +import { + IDataObject, + ILoadOptionsFunctions, + INodePropertyOptions, + INodeType, + INodeTypeDescription, + IWebhookResponseData, +} from 'n8n-workflow'; + +import { + wufooApiRequest, +} from './GenericFunctions'; + +import { + IField, + IFormQuery, + IWebhook, +} from './Interface'; + +import { + randomBytes, +} from 'crypto'; + +export class WufooTrigger implements INodeType { + description: INodeTypeDescription = { + displayName: 'Wufoo Trigger', + name: 'wufooTrigger', + icon: 'file:wufoo.png', + group: ['trigger'], + version: 1, + description: 'Handle Wufoo events via webhooks', + defaults: { + name: 'Wufoo Trigger', + color: '#c35948', + }, + inputs: [], + outputs: ['main'], + credentials: [ + { + name: 'wufooApi', + required: true, + }, + ], + webhooks: [ + { + name: 'default', + httpMethod: 'POST', + responseMode: 'onReceived', + path: 'webhook', + }, + ], + properties: [ + { + displayName: 'Forms', + name: 'form', + type: 'options', + required: true, + default: '', + typeOptions: { + loadOptionsMethod: 'getForms', + }, + description: 'The form upon which will trigger this node when a new entry is made.', + }, + { + displayName: 'Only Answers', + name: 'onlyAnswers', + type: 'boolean', + default: true, + description: 'Returns only the answers of the form and not any of the other data.', + }, + ], + }; + + methods = { + loadOptions: { + async getForms(this: ILoadOptionsFunctions): Promise { + const returnData: INodePropertyOptions[] = []; + const body: IFormQuery = { includeTodayCount: true }; + // https://wufoo.github.io/docs/#all-forms + const formObject = await wufooApiRequest.call(this, 'GET', 'forms.json', body); + for (const form of formObject.Forms) { + const name = form.Name; + const value = form.Hash; + returnData.push({ + name, + value, + }); + } + // Entries submitted on the same day are present in separate property in data object + if (formObject.EntryCountToday) { + for (const form of formObject.EntryCountToday) { + const name = form.Name; + const value = form.Hash; + returnData.push({ + name, + value, + }); + } + } + return returnData; + }, + }, + }; + + // @ts-ignore + webhookMethods = { + default: { + // No API endpoint to allow checking of existing webhooks. + // Creating new webhook will not overwrite existing one if parameters are the same. + // Otherwise an update occurs. + async checkExists(this: IHookFunctions): Promise { + return false; + }, + async create(this: IHookFunctions): Promise { + const webhookUrl = this.getNodeWebhookUrl('default'); + const webhookData = this.getWorkflowStaticData('node'); + const formHash = this.getNodeParameter('form') as IDataObject; + const endpoint = `forms/${formHash}/webhooks.json`; + + // Handshake key for webhook endpoint protection + webhookData.handshakeKey = randomBytes(20).toString('hex') as string; + const body: IWebhook = { + url: webhookUrl as string, + handshakeKey: webhookData.handshakeKey as string, + metadata: true, + }; + + const result = await wufooApiRequest.call(this, 'PUT', endpoint, body); + webhookData.webhookId = result.WebHookPutResult.Hash; + + return true; + }, + async delete(this: IHookFunctions): Promise { + const webhookData = this.getWorkflowStaticData('node'); + const formHash = this.getNodeParameter('form') as IDataObject; + const endpoint = `forms/${formHash}/webhooks/${webhookData.webhookId}.json`; + try { + await wufooApiRequest.call(this, 'DELETE', endpoint); + } catch (error) { + return false; + } + delete webhookData.webhookId; + delete webhookData.handshakeKey; + return true; + }, + }, + }; + + async webhook(this: IWebhookFunctions): Promise { + const req = this.getRequestObject(); + const body = this.getBodyData(); + const webhookData = this.getWorkflowStaticData('node'); + const onlyAnswers = this.getNodeParameter('onlyAnswers') as boolean; + const entries: IDataObject = {}; + let returnObject: IDataObject = {}; + + if (req.body.HandshakeKey !== webhookData.handshakeKey) { + return {}; + } + + const fieldsObject = JSON.parse(req.body.FieldStructure); + + fieldsObject.Fields.map((field: IField) => { + + // TODO + // Handle docusign field + + if (field.Type === 'file') { + + entries[field.Title] = req.body[`${field.ID}-url`]; + + } else if (field.Type === 'address') { + const address: IDataObject = {}; + + for (const subfield of field.SubFields) { + address[subfield.Label] = body[subfield.ID]; + } + + entries[field.Title] = address; + + } else if (field.Type === 'checkbox') { + + const responses: string[] = []; + + for (const subfield of field.SubFields) { + if (body[subfield.ID] !== '') { + responses.push(body[subfield.ID] as string); + } + } + + entries[field.Title] = responses; + + } else if (field.Type === 'likert') { + + const likert: IDataObject = {}; + + for (const subfield of field.SubFields) { + likert[subfield.Label] = body[subfield.ID]; + } + + entries[field.Title] = likert; + + } else if (field.Type === 'shortname') { + + const shortname: IDataObject = {}; + + for (const subfield of field.SubFields) { + shortname[subfield.Label] = body[subfield.ID]; + } + + entries[field.Title] = shortname; + + } else { + entries[field.Title] = req.body[field.ID]; + } + }); + + if (onlyAnswers === false) { + returnObject = { + createdBy: req.body.CreatedBy as string, + entryId: req.body.EntryId as number, + dateCreated: req.body.DateCreated as Date, + formId: req.body.FormId as string, + formStructure: JSON.parse(req.body.FormStructure), + fieldStructure: JSON.parse(req.body.FieldStructure), + entries + }; + + return { + workflowData: [ + this.helpers.returnJsonArray([returnObject as unknown as IDataObject]), + ], + }; + + } else { + return { + workflowData: [ + this.helpers.returnJsonArray(entries as unknown as IDataObject), + ], + }; + } + } +} diff --git a/packages/nodes-base/nodes/Wufoo/wufoo.png b/packages/nodes-base/nodes/Wufoo/wufoo.png new file mode 100644 index 0000000000000000000000000000000000000000..75a2eba2fc3e10a6e675acfebe73ba4d137b0c14 GIT binary patch literal 1668 zcmV-~27CF5P)%#T*J@hWYg*Q8TH9<}*J@hYYF^uFUejq))@NMXY+BT6Sl4V@)n!`NYFyZE zR?%uy)pbtSZd%!FTG(w|*l1hXZe7)CS=ek_*J@hTXpWY1_=*JfYbicQm3Ov`&c@a_xkSc`ReHQ-rV!j&*-V4<)4+|i-X;J zZ`^im*>7RhXIs)`Qqo^f&qq1^`uX+S*z?uW@XEyPy|?3;kKΜDdeJdu!NhU)N_` z(_>N8T~O3oO43wD(MCPZJumz5@c888_u=CA;NJ1e$LqMS>aD5it)=IroaB^@;gyKr zg?QY3a@%%l+H_{xaAVhDRrJ`^?!mj=j)C5ZeBFU_+=6h`SWnA2Datn|W~q{}0000; zbW%=J015L0`R(V`(QQW|@4Ks`n2mC8U@0g3^zQ83+Re?!zPq5Gmz0Wwcx+QrJ2y2h zB_kXd6A%0Q-nz4|tEHHgl#haeVPIZe_6r5;000B$Nkl#G;NE-h zz4zXlyIzu}P19+2=>SU=QIU-xBHO+9_Mh~<%eBDMw4lGr=a;)Y&wcW}_f0MlCi$-^ z*)@O8niW%*Ppw#ccHzFiQum&(=uIGwJ-&3-#X}QSXUr|Nqp{SkTu?G0cUrm36$^#W z6RUTQuO2)Z&x(GR!=brQ_6BA1$K7&e+)%ZE$0}irC2Pd(H1w*uQ;NGQWu`hjm=J;i zI(h>x3-W7y+WKPl!AchE%wP~1+*iGB)br-6Hm`_0J3(ECHLFTekWJO5JDt_-;`7$3 zg@yZUxDwy5o>Y8MFTJo`6DhadsC03mg3HIa(cXraHFd8PXq$eE$#=pie!PG@R|eOm zE5=r@*D&FcCkXRryZ){Xg1yzE5eLz29c=oe1-3TJRK8G) zn$lg5VNs_K<6xxDkZ(fOL-llawU^3EcAK4}3e*C9VXGeMI5?0J4blo8dO{~R!T}3H z&dnzBu4WXz&(}X!B=zSFe)04=39=GmRx2d?D^1>%`7-jibcl>pzp8$^Dsmnu(HI?{ z3}FqT%S@iuDY&cq8(;rQ(VN69pF{nR=-AOlT0utJ^W`-N>Vap%yA*mS=4gzt5^8jk z0olGaUtU2Uvh6Nk-~H?Di2SUdmC%*M9x89jm#2d4z`k1|qki}S9qO2MwVNfs7{;_? z@|=8mIg>+;JnbWsZ(lR2XlEA+$iQs#WDU*$kN%Ptf!C}I&V3&lq{$odK2oSpN`0HLxk0Aa;VoL08X;6MjYYI?}kxLw~+wI^0a*U41(wg!J(c}azm0l&Ru-4 zkuXg*IaimPL3R^7Cc39B*-c0O#!D_pn0X*y-nW##C2)jn??>b&ok-Puo*Gai^kA$L zOqgYd=m5cz?^;f-(j_H(T4LUygOG;HRG{b2R(r>|2@m)kUI_wXEqx;kQ;#JRZ#D^EII+EVIjYDAX<|%GYh%FZ>l#7H!|UY0m5o^QKKdAWSmJzw!rs%gk6dz&5r3 O0000 Date: Thu, 1 Oct 2020 14:01:39 +0200 Subject: [PATCH 124/284] :shirt: Fix built issue by fixing lint issues --- .../credentials/BitlyOAuth2Api.credentials.ts | 4 +- .../ClickUpOAuth2Api.credentials.ts | 12 +- .../DropboxOAuth2Api.credentials.ts | 4 +- .../credentials/FreshdeskApi.credentials.ts | 2 +- .../HubspotOAuth2Api.credentials.ts | 12 +- .../credentials/Mqtt.credentials.ts | 5 +- .../SpotifyOAuth2Api.credentials.ts | 2 +- .../credentials/WufooApi.credentials.ts | 2 +- .../ActiveCampaign/ActiveCampaign.node.ts | 18 +- .../ActiveCampaignTrigger.node.ts | 2 +- .../AcuitySchedulingTrigger.node.ts | 2 +- .../nodes/Affinity/Affinity.node.ts | 4 +- .../nodes/Affinity/AffinityTrigger.node.ts | 6 +- .../nodes/Affinity/GenericFunctions.ts | 2 +- .../nodes/AgileCrm/GenericFunctions.ts | 2 +- .../nodes/Airtable/Airtable.node.ts | 4 +- .../nodes/Asana/AsanaTrigger.node.ts | 8 +- .../nodes-base/nodes/Aws/AwsLambda.node.ts | 10 +- packages/nodes-base/nodes/Aws/AwsSes.node.ts | 4 +- packages/nodes-base/nodes/Aws/AwsSns.node.ts | 10 +- .../nodes/Aws/AwsSnsTrigger.node.ts | 6 +- .../nodes-base/nodes/Aws/S3/AwsS3.node.ts | 2 +- .../nodes/Bitbucket/BitbucketTrigger.node.ts | 2 +- packages/nodes-base/nodes/Bitly/Bitly.node.ts | 6 +- packages/nodes-base/nodes/Box/Box.node.ts | 4 +- .../nodes-base/nodes/Box/BoxTrigger.node.ts | 2 +- .../nodes-base/nodes/Box/GenericFunctions.ts | 2 +- .../nodes/Calendly/CalendlyTrigger.node.ts | 2 +- .../nodes/Chargebee/Chargebee.node.ts | 2 +- .../nodes/Chargebee/ChargebeeTrigger.node.ts | 2 +- .../nodes/CircleCi/CircleCi.node.ts | 2 +- .../nodes/Clearbit/Clearbit.node.ts | 2 +- .../nodes-base/nodes/Cockpit/Cockpit.node.ts | 2 +- packages/nodes-base/nodes/Coda/Coda.node.ts | 6 +- .../nodes-base/nodes/Coda/GenericFunctions.ts | 2 +- .../nodes/Contentful/Contentful.node.ts | 2 +- .../nodes/ConvertKit/ConvertKit.node.ts | 2 +- .../ConvertKit/ConvertKitTrigger.node.ts | 2 +- .../nodes/Copper/CopperTrigger.node.ts | 2 +- packages/nodes-base/nodes/Crypto.node.ts | 5 +- .../nodes/CustomerIo/CustomerIo.node.ts | 27 +- .../CustomerIo/CustomerIoTrigger.node.ts | 4 +- packages/nodes-base/nodes/DateTime.node.ts | 2 +- .../nodes-base/nodes/Discord/Discord.node.ts | 2 +- .../nodes-base/nodes/Disqus/Disqus.node.ts | 2 +- packages/nodes-base/nodes/Drift/Drift.node.ts | 6 +- .../nodes-base/nodes/Dropbox/Dropbox.node.ts | 2 +- .../nodes-base/nodes/EmailReadImap.node.ts | 8 +- packages/nodes-base/nodes/EmailSend.node.ts | 2 +- .../Eventbrite/EventbriteTrigger.node.ts | 2 +- .../nodes-base/nodes/ExecuteWorkflow.node.ts | 2 +- .../nodes/FileMaker/FileMaker.node.ts | 8 +- .../nodes/FileMaker/GenericFunctions.ts | 3 +- packages/nodes-base/nodes/Flow/Flow.node.ts | 4 +- .../nodes-base/nodes/Flow/FlowTrigger.node.ts | 2 +- .../nodes-base/nodes/Flow/GenericFunctions.ts | 2 +- .../nodes/Freshdesk/Freshdesk.node.ts | 11 +- .../nodes/Freshdesk/GenericFunctions.ts | 2 +- .../nodes-base/nodes/Github/Github.node.ts | 4 +- .../nodes/Github/GithubTrigger.node.ts | 2 +- .../nodes-base/nodes/Gitlab/Gitlab.node.ts | 2 +- .../nodes/Gitlab/GitlabTrigger.node.ts | 2 +- .../Google/Calendar/GoogleCalendar.node.ts | 8 +- .../Google/Contacts/GoogleContacts.node.ts | 8 +- .../nodes/Google/Drive/GoogleDrive.node.ts | 2 +- .../nodes/Google/Gmail/Gmail.node.ts | 10 +- .../nodes/Google/Sheet/GenericFunctions.ts | 34 +- .../nodes/Google/Sheet/GoogleSheet.ts | 46 +- .../nodes/Google/Sheet/GoogleSheets.node.ts | 4 +- .../nodes/Google/Task/GoogleTasks.node.ts | 2 +- .../nodes/Google/YouTube/YouTube.node.ts | 4 +- .../nodes/Gumroad/GumroadTrigger.node.ts | 2 +- .../nodes/HackerNews/HackerNews.node.ts | 2 +- .../nodes-base/nodes/Harvest/Harvest.node.ts | 52 +- .../nodes/HelpScout/GenericFunctions.ts | 2 +- .../nodes/HelpScout/HelpScout.node.ts | 2 +- .../nodes/HtmlExtract/HtmlExtract.node.ts | 2 +- packages/nodes-base/nodes/HttpRequest.node.ts | 2 +- .../nodes-base/nodes/Hubspot/Hubspot.node.ts | 12 +- .../nodes/Hubspot/HubspotTrigger.node.ts | 6 +- .../nodes-base/nodes/Hunter/Hunter.node.ts | 2 +- .../nodes/Intercom/GenericFunctions.ts | 2 +- .../nodes/Intercom/Intercom.node.ts | 25 +- .../nodes/InvoiceNinja/ISOCountryCodes.ts | 2160 ++++++++--------- .../nodes/InvoiceNinja/InvoiceNinja.node.ts | 2 +- .../InvoiceNinja/InvoiceNinjaTrigger.node.ts | 2 +- .../nodes-base/nodes/Jira/GenericFunctions.ts | 2 +- packages/nodes-base/nodes/Jira/Jira.node.ts | 2 +- .../nodes-base/nodes/Jira/JiraTrigger.node.ts | 6 +- .../nodes/JotForm/JotFormTrigger.node.ts | 2 +- packages/nodes-base/nodes/Keap/Keap.node.ts | 36 +- .../nodes-base/nodes/Keap/KeapTrigger.node.ts | 6 +- .../nodes/LinkFish/LinkFish.node.ts | 2 +- .../nodes/LinkedIn/LinkedIn.node.ts | 16 +- .../nodes/Mailchimp/MailchimpTrigger.node.ts | 6 +- .../nodes-base/nodes/Mailgun/Mailgun.node.ts | 2 +- .../nodes-base/nodes/Mailjet/Mailjet.node.ts | 2 +- .../nodes/Mailjet/MailjetTrigger.node.ts | 2 +- .../nodes/Mattermost/Mattermost.node.ts | 4 +- .../nodes/Mautic/GenericFunctions.ts | 2 +- .../nodes-base/nodes/Mautic/Mautic.node.ts | 8 +- .../nodes/Mautic/MauticTrigger.node.ts | 8 +- .../nodes-base/nodes/Medium/Medium.node.ts | 2 +- .../nodes/MessageBird/MessageBird.node.ts | 2 +- .../Microsoft/Excel/MicrosoftExcel.node.ts | 12 +- .../nodes/MongoDb/mongo.node.utils.ts | 4 +- .../nodes/NextCloud/NextCloud.node.ts | 2 +- .../nodes-base/nodes/OpenWeatherMap.node.ts | 2 +- .../nodes/Paddle/GenericFunctions.ts | 2 +- .../nodes/PayPal/GenericFunctions.ts | 4 +- .../nodes-base/nodes/PayPal/PayPal.node.ts | 9 +- .../nodes/PayPal/PayPalTrigger.node.ts | 108 +- .../nodes/PhilipsHue/PhilipsHue.node.ts | 10 +- .../nodes/Pipedrive/Pipedrive.node.ts | 4 +- .../nodes/Pipedrive/PipedriveTrigger.node.ts | 2 +- .../nodes/Postmark/PostmarkTrigger.node.ts | 2 +- .../nodes-base/nodes/ReadBinaryFile.node.ts | 2 +- .../nodes/Rocketchat/GenericFunctions.ts | 2 +- .../nodes/Rocketchat/Rocketchat.node.ts | 4 +- packages/nodes-base/nodes/RssFeedRead.node.ts | 2 +- packages/nodes-base/nodes/S3/S3.node.ts | 2 +- .../nodes/Salesforce/Salesforce.node.ts | 2 +- .../nodes/Salesforce/UserInterface.ts | 16 +- .../nodes-base/nodes/Segment/Segment.node.ts | 7 +- .../nodes/SentryIo/SentryIo.node.ts | 16 +- .../nodes/Shopify/GenericFunctions.ts | 4 +- .../nodes-base/nodes/Shopify/Shopify.node.ts | 8 +- .../nodes/Shopify/ShopifyTrigger.node.ts | 250 +- .../nodes-base/nodes/Signl4/Signl4.node.ts | 2 +- packages/nodes-base/nodes/Slack/Slack.node.ts | 6 +- .../nodes/Spotify/GenericFunctions.ts | 18 +- .../nodes-base/nodes/SpreadsheetFile.node.ts | 4 +- .../nodes/Stripe/StripeTrigger.node.ts | 2 +- .../SurveyMonkey/SurveyMonkeyTrigger.node.ts | 2 +- .../nodes/Taiga/TaigaTrigger.node.ts | 2 +- .../nodes/Telegram/Telegram.node.ts | 4 +- .../nodes-base/nodes/Todoist/Todoist.node.ts | 6 +- .../nodes/Toggl/GenericFunctions.ts | 2 +- .../nodes/Toggl/TogglTrigger.node.ts | 2 +- .../nodes/TravisCi/TravisCi.node.ts | 2 +- .../nodes-base/nodes/Trello/Trello.node.ts | 16 +- .../nodes/Trello/TrelloTrigger.node.ts | 5 +- packages/nodes-base/nodes/Twake/Twake.node.ts | 6 +- .../nodes-base/nodes/Twilio/Twilio.node.ts | 2 +- .../nodes/Twitter/GenericFunctions.ts | 4 +- .../nodes-base/nodes/Twitter/Twitter.node.ts | 4 +- .../nodes/Typeform/TypeformTrigger.node.ts | 6 +- .../UnleashedSoftware.node.ts | 6 +- .../nodes-base/nodes/Uplead/Uplead.node.ts | 2 +- .../nodes-base/nodes/Vero/GenericFunctions.ts | 2 +- packages/nodes-base/nodes/Vero/Vero.node.ts | 8 +- .../nodes/Webflow/WebflowTrigger.node.ts | 6 +- packages/nodes-base/nodes/Webhook.node.ts | 2 +- .../nodes/WooCommerce/GenericFunctions.ts | 14 +- .../nodes/WooCommerce/WooCommerce.node.ts | 4 +- .../WooCommerce/WooCommerceTrigger.node.ts | 4 +- .../nodes/Wordpress/Wordpress.node.ts | 4 +- .../nodes-base/nodes/WriteBinaryFile.node.ts | 2 +- packages/nodes-base/nodes/Xero/Xero.node.ts | 6 +- packages/nodes-base/nodes/Xml.node.ts | 2 +- .../nodes-base/nodes/Zendesk/Zendesk.node.ts | 8 +- .../nodes/Zendesk/ZendeskTrigger.node.ts | 8 +- packages/nodes-base/nodes/Zoom/Zoom.node.ts | 2 +- packages/nodes-base/nodes/Zulip/Zulip.node.ts | 19 +- .../nodes-base/nodes/utils/allCurrencies.ts | 348 +-- 165 files changed, 1930 insertions(+), 1864 deletions(-) diff --git a/packages/nodes-base/credentials/BitlyOAuth2Api.credentials.ts b/packages/nodes-base/credentials/BitlyOAuth2Api.credentials.ts index fc5ef7f15..bdd8b6bfe 100644 --- a/packages/nodes-base/credentials/BitlyOAuth2Api.credentials.ts +++ b/packages/nodes-base/credentials/BitlyOAuth2Api.credentials.ts @@ -6,9 +6,9 @@ import { export class BitlyOAuth2Api implements ICredentialType { name = 'bitlyOAuth2Api'; - displayName = 'Bitly OAuth2 API'; + displayName = 'Bitly OAuth2 API'; documentationUrl = 'bitly'; - extends = [ + extends = [ 'oAuth2Api', ]; properties = [ diff --git a/packages/nodes-base/credentials/ClickUpOAuth2Api.credentials.ts b/packages/nodes-base/credentials/ClickUpOAuth2Api.credentials.ts index b8e703c19..07bb9ef61 100644 --- a/packages/nodes-base/credentials/ClickUpOAuth2Api.credentials.ts +++ b/packages/nodes-base/credentials/ClickUpOAuth2Api.credentials.ts @@ -38,11 +38,11 @@ export class ClickUpOAuth2Api implements ICredentialType { default: '', }, { - displayName: 'Authentication', - name: 'authentication', - type: 'hidden' as NodePropertyTypes, - default: 'body', - description: 'Resource to consume.', - }, + displayName: 'Authentication', + name: 'authentication', + type: 'hidden' as NodePropertyTypes, + default: 'body', + description: 'Resource to consume.', + }, ]; } diff --git a/packages/nodes-base/credentials/DropboxOAuth2Api.credentials.ts b/packages/nodes-base/credentials/DropboxOAuth2Api.credentials.ts index b67bae38f..11e290bd8 100644 --- a/packages/nodes-base/credentials/DropboxOAuth2Api.credentials.ts +++ b/packages/nodes-base/credentials/DropboxOAuth2Api.credentials.ts @@ -25,8 +25,8 @@ export class DropboxOAuth2Api implements ICredentialType { type: 'hidden' as NodePropertyTypes, default: 'https://api.dropboxapi.com/oauth2/token', required: true, - }, - { + }, + { displayName: 'Scope', name: 'scope', type: 'hidden' as NodePropertyTypes, diff --git a/packages/nodes-base/credentials/FreshdeskApi.credentials.ts b/packages/nodes-base/credentials/FreshdeskApi.credentials.ts index 078d4db75..3118b7587 100644 --- a/packages/nodes-base/credentials/FreshdeskApi.credentials.ts +++ b/packages/nodes-base/credentials/FreshdeskApi.credentials.ts @@ -22,6 +22,6 @@ export class FreshdeskApi implements ICredentialType { placeholder: 'company', description: 'If the URL you get displayed on Freshdesk is "https://company.freshdesk.com" enter "company"', default: '' - } + }, ]; } diff --git a/packages/nodes-base/credentials/HubspotOAuth2Api.credentials.ts b/packages/nodes-base/credentials/HubspotOAuth2Api.credentials.ts index bf237ab76..150d33642 100644 --- a/packages/nodes-base/credentials/HubspotOAuth2Api.credentials.ts +++ b/packages/nodes-base/credentials/HubspotOAuth2Api.credentials.ts @@ -44,11 +44,11 @@ export class HubspotOAuth2Api implements ICredentialType { default: 'grant_type=authorization_code', }, { - displayName: 'Authentication', - name: 'authentication', - type: 'hidden' as NodePropertyTypes, - default: 'body', - description: 'Resource to consume.', - }, + displayName: 'Authentication', + name: 'authentication', + type: 'hidden' as NodePropertyTypes, + default: 'body', + description: 'Resource to consume.', + }, ]; } diff --git a/packages/nodes-base/credentials/Mqtt.credentials.ts b/packages/nodes-base/credentials/Mqtt.credentials.ts index 7bdd12ad4..05d4b0ec5 100644 --- a/packages/nodes-base/credentials/Mqtt.credentials.ts +++ b/packages/nodes-base/credentials/Mqtt.credentials.ts @@ -15,8 +15,8 @@ export class Mqtt implements ICredentialType { { displayName: 'Protocol', name: 'protocol', - type: 'options' as NodePropertyTypes, - options: [ + type: 'options' as NodePropertyTypes, + options: [ { name: 'mqtt', value: 'mqtt', @@ -57,4 +57,3 @@ export class Mqtt implements ICredentialType { }, ]; } - diff --git a/packages/nodes-base/credentials/SpotifyOAuth2Api.credentials.ts b/packages/nodes-base/credentials/SpotifyOAuth2Api.credentials.ts index 48e66b0cf..4b37b0881 100644 --- a/packages/nodes-base/credentials/SpotifyOAuth2Api.credentials.ts +++ b/packages/nodes-base/credentials/SpotifyOAuth2Api.credentials.ts @@ -49,6 +49,6 @@ export class SpotifyOAuth2Api implements ICredentialType { name: 'authentication', type: 'hidden' as NodePropertyTypes, default: 'header', - } + }, ]; } diff --git a/packages/nodes-base/credentials/WufooApi.credentials.ts b/packages/nodes-base/credentials/WufooApi.credentials.ts index 70e0a27da..a50ad5835 100644 --- a/packages/nodes-base/credentials/WufooApi.credentials.ts +++ b/packages/nodes-base/credentials/WufooApi.credentials.ts @@ -18,6 +18,6 @@ export class WufooApi implements ICredentialType { name: 'subdomain', type: 'string' as NodePropertyTypes, default: '', - } + }, ]; } diff --git a/packages/nodes-base/nodes/ActiveCampaign/ActiveCampaign.node.ts b/packages/nodes-base/nodes/ActiveCampaign/ActiveCampaign.node.ts index 632c78995..2d00352c5 100644 --- a/packages/nodes-base/nodes/ActiveCampaign/ActiveCampaign.node.ts +++ b/packages/nodes-base/nodes/ActiveCampaign/ActiveCampaign.node.ts @@ -4,11 +4,11 @@ import { import { IDataObject, - INodeTypeDescription, - INodeExecutionData, - INodeType, ILoadOptionsFunctions, + INodeExecutionData, INodePropertyOptions, + INodeType, + INodeTypeDescription, } from 'n8n-workflow'; import { @@ -18,33 +18,33 @@ import { } from './GenericFunctions'; import { + contactFields, contactOperations, - contactFields } from './ContactDescription'; import { + dealFields, dealOperations, - dealFields } from './DealDescription'; import { + ecomOrderFields, ecomOrderOperations, - ecomOrderFields } from './EcomOrderDescription'; import { + ecomCustomerFields, ecomCustomerOperations, - ecomCustomerFields } from './EcomCustomerDescription'; import { + ecomOrderProductsFields, ecomOrderProductsOperations, - ecomOrderProductsFields } from './EcomOrderProductsDescription'; import { + connectionFields, connectionOperations, - connectionFields } from './ConnectionDescription'; import { diff --git a/packages/nodes-base/nodes/ActiveCampaign/ActiveCampaignTrigger.node.ts b/packages/nodes-base/nodes/ActiveCampaign/ActiveCampaignTrigger.node.ts index d27835979..763b04007 100644 --- a/packages/nodes-base/nodes/ActiveCampaign/ActiveCampaignTrigger.node.ts +++ b/packages/nodes-base/nodes/ActiveCampaign/ActiveCampaignTrigger.node.ts @@ -6,9 +6,9 @@ import { import { IDataObject, ILoadOptionsFunctions, + INodePropertyOptions, INodeType, INodeTypeDescription, - INodePropertyOptions, IWebhookResponseData, } from 'n8n-workflow'; diff --git a/packages/nodes-base/nodes/AcuityScheduling/AcuitySchedulingTrigger.node.ts b/packages/nodes-base/nodes/AcuityScheduling/AcuitySchedulingTrigger.node.ts index 75ab74699..289f45221 100644 --- a/packages/nodes-base/nodes/AcuityScheduling/AcuitySchedulingTrigger.node.ts +++ b/packages/nodes-base/nodes/AcuityScheduling/AcuitySchedulingTrigger.node.ts @@ -5,8 +5,8 @@ import { import { IDataObject, - INodeTypeDescription, INodeType, + INodeTypeDescription, IWebhookResponseData, } from 'n8n-workflow'; diff --git a/packages/nodes-base/nodes/Affinity/Affinity.node.ts b/packages/nodes-base/nodes/Affinity/Affinity.node.ts index 972c2e7ec..2e67dfde0 100644 --- a/packages/nodes-base/nodes/Affinity/Affinity.node.ts +++ b/packages/nodes-base/nodes/Affinity/Affinity.node.ts @@ -4,10 +4,10 @@ import { import { IDataObject, ILoadOptionsFunctions, - INodeTypeDescription, INodeExecutionData, - INodeType, INodePropertyOptions, + INodeType, + INodeTypeDescription, } from 'n8n-workflow'; import { affinityApiRequest, diff --git a/packages/nodes-base/nodes/Affinity/AffinityTrigger.node.ts b/packages/nodes-base/nodes/Affinity/AffinityTrigger.node.ts index 3d387e7b4..4d3ba70f1 100644 --- a/packages/nodes-base/nodes/Affinity/AffinityTrigger.node.ts +++ b/packages/nodes-base/nodes/Affinity/AffinityTrigger.node.ts @@ -4,10 +4,10 @@ import { } from 'n8n-core'; import { - INodeTypeDescription, - INodeType, - IWebhookResponseData, IDataObject, + INodeType, + INodeTypeDescription, + IWebhookResponseData, } from 'n8n-workflow'; import { diff --git a/packages/nodes-base/nodes/Affinity/GenericFunctions.ts b/packages/nodes-base/nodes/Affinity/GenericFunctions.ts index 0c2f68061..e33daa75f 100644 --- a/packages/nodes-base/nodes/Affinity/GenericFunctions.ts +++ b/packages/nodes-base/nodes/Affinity/GenericFunctions.ts @@ -1,9 +1,9 @@ import { OptionsWithUri } from 'request'; import { + BINARY_ENCODING, IExecuteFunctions, ILoadOptionsFunctions, - BINARY_ENCODING } from 'n8n-core'; import { IDataObject, IHookFunctions, IWebhookFunctions } from 'n8n-workflow'; diff --git a/packages/nodes-base/nodes/AgileCrm/GenericFunctions.ts b/packages/nodes-base/nodes/AgileCrm/GenericFunctions.ts index 71d5a68c1..09b63e5dd 100644 --- a/packages/nodes-base/nodes/AgileCrm/GenericFunctions.ts +++ b/packages/nodes-base/nodes/AgileCrm/GenericFunctions.ts @@ -4,9 +4,9 @@ import { import { IExecuteFunctions, + IExecuteSingleFunctions, IHookFunctions, ILoadOptionsFunctions, - IExecuteSingleFunctions, } from 'n8n-core'; import { diff --git a/packages/nodes-base/nodes/Airtable/Airtable.node.ts b/packages/nodes-base/nodes/Airtable/Airtable.node.ts index 9f22b3d2b..cd74ebb0e 100644 --- a/packages/nodes-base/nodes/Airtable/Airtable.node.ts +++ b/packages/nodes-base/nodes/Airtable/Airtable.node.ts @@ -3,11 +3,9 @@ import { } from 'n8n-core'; import { IDataObject, - ILoadOptionsFunctions, - INodePropertyOptions, - INodeTypeDescription, INodeExecutionData, INodeType, + INodeTypeDescription, } from 'n8n-workflow'; import { diff --git a/packages/nodes-base/nodes/Asana/AsanaTrigger.node.ts b/packages/nodes-base/nodes/Asana/AsanaTrigger.node.ts index 537831416..746107fb1 100644 --- a/packages/nodes-base/nodes/Asana/AsanaTrigger.node.ts +++ b/packages/nodes-base/nodes/Asana/AsanaTrigger.node.ts @@ -7,8 +7,8 @@ import { IDataObject, ILoadOptionsFunctions, INodePropertyOptions, - INodeTypeDescription, INodeType, + INodeTypeDescription, IWebhookResponseData, } from 'n8n-workflow'; @@ -17,9 +17,9 @@ import { getWorkspaces, } from './GenericFunctions'; -import { - createHmac, -} from 'crypto'; +// import { +// createHmac, +// } from 'crypto'; export class AsanaTrigger implements INodeType { description: INodeTypeDescription = { diff --git a/packages/nodes-base/nodes/Aws/AwsLambda.node.ts b/packages/nodes-base/nodes/Aws/AwsLambda.node.ts index 0bddddede..508f85fc6 100644 --- a/packages/nodes-base/nodes/Aws/AwsLambda.node.ts +++ b/packages/nodes-base/nodes/Aws/AwsLambda.node.ts @@ -1,11 +1,11 @@ import { IExecuteFunctions } from 'n8n-core'; import { - INodeTypeDescription, - INodeExecutionData, - INodeType, - INodePropertyOptions, + IDataObject, ILoadOptionsFunctions, - IDataObject + INodeExecutionData, + INodePropertyOptions, + INodeType, + INodeTypeDescription, } from 'n8n-workflow'; import { awsApiRequestREST } from './GenericFunctions'; diff --git a/packages/nodes-base/nodes/Aws/AwsSes.node.ts b/packages/nodes-base/nodes/Aws/AwsSes.node.ts index 74870cc90..4e87a2dd3 100644 --- a/packages/nodes-base/nodes/Aws/AwsSes.node.ts +++ b/packages/nodes-base/nodes/Aws/AwsSes.node.ts @@ -1,9 +1,9 @@ import { IExecuteFunctions } from 'n8n-core'; import { - INodeTypeDescription, + IDataObject, INodeExecutionData, INodeType, - IDataObject + INodeTypeDescription, } from 'n8n-workflow'; import { awsApiRequestSOAP } from './GenericFunctions'; diff --git a/packages/nodes-base/nodes/Aws/AwsSns.node.ts b/packages/nodes-base/nodes/Aws/AwsSns.node.ts index 358b00458..1a656041a 100644 --- a/packages/nodes-base/nodes/Aws/AwsSns.node.ts +++ b/packages/nodes-base/nodes/Aws/AwsSns.node.ts @@ -1,11 +1,11 @@ import { IExecuteFunctions } from 'n8n-core'; import { - INodeTypeDescription, - INodeExecutionData, - INodeType, - INodePropertyOptions, + IDataObject, ILoadOptionsFunctions, - IDataObject + INodeExecutionData, + INodePropertyOptions, + INodeType, + INodeTypeDescription, } from 'n8n-workflow'; import { awsApiRequestSOAP } from './GenericFunctions'; diff --git a/packages/nodes-base/nodes/Aws/AwsSnsTrigger.node.ts b/packages/nodes-base/nodes/Aws/AwsSnsTrigger.node.ts index 3d4b63b30..42981f315 100644 --- a/packages/nodes-base/nodes/Aws/AwsSnsTrigger.node.ts +++ b/packages/nodes-base/nodes/Aws/AwsSnsTrigger.node.ts @@ -4,11 +4,11 @@ import { } from 'n8n-core'; import { - INodeTypeDescription, - INodeType, - IWebhookResponseData, ILoadOptionsFunctions, INodePropertyOptions, + INodeType, + INodeTypeDescription, + IWebhookResponseData, } from 'n8n-workflow'; import { diff --git a/packages/nodes-base/nodes/Aws/S3/AwsS3.node.ts b/packages/nodes-base/nodes/Aws/S3/AwsS3.node.ts index 5af9ad3d3..6eda1727e 100644 --- a/packages/nodes-base/nodes/Aws/S3/AwsS3.node.ts +++ b/packages/nodes-base/nodes/Aws/S3/AwsS3.node.ts @@ -1,7 +1,7 @@ import { - snakeCase, paramCase, + snakeCase, } from 'change-case'; import { diff --git a/packages/nodes-base/nodes/Bitbucket/BitbucketTrigger.node.ts b/packages/nodes-base/nodes/Bitbucket/BitbucketTrigger.node.ts index 55488c9fd..8321dedac 100644 --- a/packages/nodes-base/nodes/Bitbucket/BitbucketTrigger.node.ts +++ b/packages/nodes-base/nodes/Bitbucket/BitbucketTrigger.node.ts @@ -6,9 +6,9 @@ import { import { IDataObject, ILoadOptionsFunctions, + INodePropertyOptions, INodeType, INodeTypeDescription, - INodePropertyOptions, IWebhookResponseData, } from 'n8n-workflow'; diff --git a/packages/nodes-base/nodes/Bitly/Bitly.node.ts b/packages/nodes-base/nodes/Bitly/Bitly.node.ts index d712238ef..3781a4cb1 100644 --- a/packages/nodes-base/nodes/Bitly/Bitly.node.ts +++ b/packages/nodes-base/nodes/Bitly/Bitly.node.ts @@ -4,11 +4,11 @@ import { import { IDataObject, - INodeTypeDescription, - INodeExecutionData, - INodeType, ILoadOptionsFunctions, + INodeExecutionData, INodePropertyOptions, + INodeType, + INodeTypeDescription, } from 'n8n-workflow'; import { diff --git a/packages/nodes-base/nodes/Box/Box.node.ts b/packages/nodes-base/nodes/Box/Box.node.ts index 374a79612..4483c4e38 100644 --- a/packages/nodes-base/nodes/Box/Box.node.ts +++ b/packages/nodes-base/nodes/Box/Box.node.ts @@ -4,11 +4,11 @@ import { } from 'n8n-core'; import { + IBinaryKeyData, IDataObject, - INodeTypeDescription, INodeExecutionData, INodeType, - IBinaryKeyData, + INodeTypeDescription, } from 'n8n-workflow'; import { diff --git a/packages/nodes-base/nodes/Box/BoxTrigger.node.ts b/packages/nodes-base/nodes/Box/BoxTrigger.node.ts index 7071b57ed..1e4105254 100644 --- a/packages/nodes-base/nodes/Box/BoxTrigger.node.ts +++ b/packages/nodes-base/nodes/Box/BoxTrigger.node.ts @@ -4,8 +4,8 @@ import { } from 'n8n-core'; import { - INodeTypeDescription, INodeType, + INodeTypeDescription, IWebhookResponseData, } from 'n8n-workflow'; diff --git a/packages/nodes-base/nodes/Box/GenericFunctions.ts b/packages/nodes-base/nodes/Box/GenericFunctions.ts index ba0ae1f31..1a586c1df 100644 --- a/packages/nodes-base/nodes/Box/GenericFunctions.ts +++ b/packages/nodes-base/nodes/Box/GenericFunctions.ts @@ -5,8 +5,8 @@ import { import { IExecuteFunctions, IExecuteSingleFunctions, - ILoadOptionsFunctions, IHookFunctions, + ILoadOptionsFunctions, } from 'n8n-core'; import { diff --git a/packages/nodes-base/nodes/Calendly/CalendlyTrigger.node.ts b/packages/nodes-base/nodes/Calendly/CalendlyTrigger.node.ts index 4a7a471e3..8369ab3eb 100644 --- a/packages/nodes-base/nodes/Calendly/CalendlyTrigger.node.ts +++ b/packages/nodes-base/nodes/Calendly/CalendlyTrigger.node.ts @@ -4,8 +4,8 @@ import { } from 'n8n-core'; import { - INodeTypeDescription, INodeType, + INodeTypeDescription, IWebhookResponseData, } from 'n8n-workflow'; diff --git a/packages/nodes-base/nodes/Chargebee/Chargebee.node.ts b/packages/nodes-base/nodes/Chargebee/Chargebee.node.ts index 5bbda0ab7..b233466ed 100644 --- a/packages/nodes-base/nodes/Chargebee/Chargebee.node.ts +++ b/packages/nodes-base/nodes/Chargebee/Chargebee.node.ts @@ -1,9 +1,9 @@ import { IExecuteFunctions } from 'n8n-core'; import { IDataObject, - INodeTypeDescription, INodeExecutionData, INodeType, + INodeTypeDescription, NodeParameterValue, } from 'n8n-workflow'; diff --git a/packages/nodes-base/nodes/Chargebee/ChargebeeTrigger.node.ts b/packages/nodes-base/nodes/Chargebee/ChargebeeTrigger.node.ts index e3ad8b9cd..69c6f4c6e 100644 --- a/packages/nodes-base/nodes/Chargebee/ChargebeeTrigger.node.ts +++ b/packages/nodes-base/nodes/Chargebee/ChargebeeTrigger.node.ts @@ -4,8 +4,8 @@ import { import { IDataObject, - INodeTypeDescription, INodeType, + INodeTypeDescription, IWebhookResponseData, } from 'n8n-workflow'; diff --git a/packages/nodes-base/nodes/CircleCi/CircleCi.node.ts b/packages/nodes-base/nodes/CircleCi/CircleCi.node.ts index a15a9511e..4732133e3 100644 --- a/packages/nodes-base/nodes/CircleCi/CircleCi.node.ts +++ b/packages/nodes-base/nodes/CircleCi/CircleCi.node.ts @@ -4,9 +4,9 @@ import { import { IDataObject, - INodeTypeDescription, INodeExecutionData, INodeType, + INodeTypeDescription, } from 'n8n-workflow'; import { diff --git a/packages/nodes-base/nodes/Clearbit/Clearbit.node.ts b/packages/nodes-base/nodes/Clearbit/Clearbit.node.ts index 4ed7bbc7d..3947f8be2 100644 --- a/packages/nodes-base/nodes/Clearbit/Clearbit.node.ts +++ b/packages/nodes-base/nodes/Clearbit/Clearbit.node.ts @@ -15,8 +15,8 @@ import { companyOperations, } from './CompanyDescription'; import { - personOperations, personFields, + personOperations, } from './PersonDescription'; export class Clearbit implements INodeType { diff --git a/packages/nodes-base/nodes/Cockpit/Cockpit.node.ts b/packages/nodes-base/nodes/Cockpit/Cockpit.node.ts index efd233f74..32335c5e9 100644 --- a/packages/nodes-base/nodes/Cockpit/Cockpit.node.ts +++ b/packages/nodes-base/nodes/Cockpit/Cockpit.node.ts @@ -27,8 +27,8 @@ import { singletonOperations, } from './SingletonDescription'; import { - getSingleton, getAllSingletonNames, + getSingleton, } from './SingletonFunctions'; export class Cockpit implements INodeType { diff --git a/packages/nodes-base/nodes/Coda/Coda.node.ts b/packages/nodes-base/nodes/Coda/Coda.node.ts index e7d8c9c8d..05ea6f71a 100644 --- a/packages/nodes-base/nodes/Coda/Coda.node.ts +++ b/packages/nodes-base/nodes/Coda/Coda.node.ts @@ -3,11 +3,11 @@ import { } from 'n8n-core'; import { IDataObject, - INodeTypeDescription, - INodeExecutionData, - INodeType, ILoadOptionsFunctions, + INodeExecutionData, INodePropertyOptions, + INodeType, + INodeTypeDescription, } from 'n8n-workflow'; import { codaApiRequest, diff --git a/packages/nodes-base/nodes/Coda/GenericFunctions.ts b/packages/nodes-base/nodes/Coda/GenericFunctions.ts index 3c09c6559..81a755085 100644 --- a/packages/nodes-base/nodes/Coda/GenericFunctions.ts +++ b/packages/nodes-base/nodes/Coda/GenericFunctions.ts @@ -1,8 +1,8 @@ import { OptionsWithUri } from 'request'; import { IExecuteFunctions, - ILoadOptionsFunctions, IExecuteSingleFunctions, + ILoadOptionsFunctions, } from 'n8n-core'; import { IDataObject } from 'n8n-workflow'; diff --git a/packages/nodes-base/nodes/Contentful/Contentful.node.ts b/packages/nodes-base/nodes/Contentful/Contentful.node.ts index b4dde2ba0..777cccb05 100644 --- a/packages/nodes-base/nodes/Contentful/Contentful.node.ts +++ b/packages/nodes-base/nodes/Contentful/Contentful.node.ts @@ -10,8 +10,8 @@ import { } from 'n8n-workflow'; import { - contentfulApiRequest, contenfulApiRequestAllItems, + contentfulApiRequest, } from './GenericFunctions'; import * as SpaceDescription from './SpaceDescription'; diff --git a/packages/nodes-base/nodes/ConvertKit/ConvertKit.node.ts b/packages/nodes-base/nodes/ConvertKit/ConvertKit.node.ts index 7933379ad..870e8e2e8 100644 --- a/packages/nodes-base/nodes/ConvertKit/ConvertKit.node.ts +++ b/packages/nodes-base/nodes/ConvertKit/ConvertKit.node.ts @@ -7,8 +7,8 @@ import { IDataObject, INodeExecutionData, INodePropertyOptions, - INodeTypeDescription, INodeType, + INodeTypeDescription, } from 'n8n-workflow'; import { diff --git a/packages/nodes-base/nodes/ConvertKit/ConvertKitTrigger.node.ts b/packages/nodes-base/nodes/ConvertKit/ConvertKitTrigger.node.ts index 84fa78c93..80b4bbfc3 100644 --- a/packages/nodes-base/nodes/ConvertKit/ConvertKitTrigger.node.ts +++ b/packages/nodes-base/nodes/ConvertKit/ConvertKitTrigger.node.ts @@ -7,8 +7,8 @@ import { IDataObject, ILoadOptionsFunctions, INodePropertyOptions, - INodeTypeDescription, INodeType, + INodeTypeDescription, IWebhookResponseData, } from 'n8n-workflow'; diff --git a/packages/nodes-base/nodes/Copper/CopperTrigger.node.ts b/packages/nodes-base/nodes/Copper/CopperTrigger.node.ts index 2c95c25eb..2be001d18 100644 --- a/packages/nodes-base/nodes/Copper/CopperTrigger.node.ts +++ b/packages/nodes-base/nodes/Copper/CopperTrigger.node.ts @@ -5,8 +5,8 @@ import { import { IDataObject, - INodeTypeDescription, INodeType, + INodeTypeDescription, IWebhookResponseData, } from 'n8n-workflow'; diff --git a/packages/nodes-base/nodes/Crypto.node.ts b/packages/nodes-base/nodes/Crypto.node.ts index 511a8a18d..7fd4cbda4 100644 --- a/packages/nodes-base/nodes/Crypto.node.ts +++ b/packages/nodes-base/nodes/Crypto.node.ts @@ -1,12 +1,11 @@ import { set } from 'lodash'; import { IExecuteFunctions } from 'n8n-core'; import { + ILoadOptionsFunctions, INodeExecutionData, + INodePropertyOptions, INodeType, INodeTypeDescription, - IDataObject, - ILoadOptionsFunctions, - INodePropertyOptions, } from 'n8n-workflow'; import { diff --git a/packages/nodes-base/nodes/CustomerIo/CustomerIo.node.ts b/packages/nodes-base/nodes/CustomerIo/CustomerIo.node.ts index f664820bb..da0f40522 100644 --- a/packages/nodes-base/nodes/CustomerIo/CustomerIo.node.ts +++ b/packages/nodes-base/nodes/CustomerIo/CustomerIo.node.ts @@ -3,15 +3,30 @@ import { } from 'n8n-core'; import { IDataObject, - INodeTypeDescription, INodeExecutionData, INodeType, + INodeTypeDescription, } from 'n8n-workflow'; -import { customerIoApiRequest, validateJSON } from './GenericFunctions'; -import { campaignOperations, campaignFields } from './CampaignDescription'; -import { customerOperations, customerFields } from './CustomerDescription'; -import { eventOperations, eventFields } from './EventDescription'; -import { segmentOperations, segmentFields } from './SegmentDescription'; +import { + customerIoApiRequest, + validateJSON, +} from './GenericFunctions'; +import { + campaignFields, + campaignOperations, +} from './CampaignDescription'; +import { + customerFields, + customerOperations, +} from './CustomerDescription'; +import { + eventFields, + eventOperations, +} from './EventDescription'; +import { + segmentFields, + segmentOperations, +} from './SegmentDescription'; export class CustomerIo implements INodeType { diff --git a/packages/nodes-base/nodes/CustomerIo/CustomerIoTrigger.node.ts b/packages/nodes-base/nodes/CustomerIo/CustomerIoTrigger.node.ts index 08990d17b..c9c079d0f 100644 --- a/packages/nodes-base/nodes/CustomerIo/CustomerIoTrigger.node.ts +++ b/packages/nodes-base/nodes/CustomerIo/CustomerIoTrigger.node.ts @@ -4,9 +4,9 @@ import { } from 'n8n-core'; import { - INodeTypeDescription, - INodeType, IDataObject, + INodeType, + INodeTypeDescription, IWebhookResponseData, } from 'n8n-workflow'; diff --git a/packages/nodes-base/nodes/DateTime.node.ts b/packages/nodes-base/nodes/DateTime.node.ts index 15c33dcff..e3a9a299f 100644 --- a/packages/nodes-base/nodes/DateTime.node.ts +++ b/packages/nodes-base/nodes/DateTime.node.ts @@ -6,9 +6,9 @@ import { IDataObject, ILoadOptionsFunctions, INodeExecutionData, + INodePropertyOptions, INodeType, INodeTypeDescription, - INodePropertyOptions, } from 'n8n-workflow'; diff --git a/packages/nodes-base/nodes/Discord/Discord.node.ts b/packages/nodes-base/nodes/Discord/Discord.node.ts index fcd701f27..4e8d1e046 100644 --- a/packages/nodes-base/nodes/Discord/Discord.node.ts +++ b/packages/nodes-base/nodes/Discord/Discord.node.ts @@ -2,9 +2,9 @@ import { get } from 'lodash'; import { IExecuteFunctions } from 'n8n-core'; import { IDataObject, - INodeTypeDescription, INodeExecutionData, INodeType, + INodeTypeDescription, } from 'n8n-workflow'; export class Discord implements INodeType { diff --git a/packages/nodes-base/nodes/Disqus/Disqus.node.ts b/packages/nodes-base/nodes/Disqus/Disqus.node.ts index 22746cfd3..8d5a2692e 100644 --- a/packages/nodes-base/nodes/Disqus/Disqus.node.ts +++ b/packages/nodes-base/nodes/Disqus/Disqus.node.ts @@ -3,9 +3,9 @@ import { } from 'n8n-core'; import { IDataObject, - INodeTypeDescription, INodeExecutionData, INodeType, + INodeTypeDescription, } from 'n8n-workflow'; import { disqusApiRequest, disqusApiRequestAllItems } from './GenericFunctions'; diff --git a/packages/nodes-base/nodes/Drift/Drift.node.ts b/packages/nodes-base/nodes/Drift/Drift.node.ts index 46afa3d51..f91de6897 100644 --- a/packages/nodes-base/nodes/Drift/Drift.node.ts +++ b/packages/nodes-base/nodes/Drift/Drift.node.ts @@ -2,10 +2,10 @@ import { IExecuteFunctions, } from 'n8n-core'; import { - INodeTypeDescription, - INodeType, - INodeExecutionData, IDataObject, + INodeExecutionData, + INodeType, + INodeTypeDescription, } from 'n8n-workflow'; import { driftApiRequest, diff --git a/packages/nodes-base/nodes/Dropbox/Dropbox.node.ts b/packages/nodes-base/nodes/Dropbox/Dropbox.node.ts index 6e05fa5c5..38c5c73f8 100644 --- a/packages/nodes-base/nodes/Dropbox/Dropbox.node.ts +++ b/packages/nodes-base/nodes/Dropbox/Dropbox.node.ts @@ -5,9 +5,9 @@ import { import { IDataObject, - INodeTypeDescription, INodeExecutionData, INodeType, + INodeTypeDescription, } from 'n8n-workflow'; import { diff --git a/packages/nodes-base/nodes/EmailReadImap.node.ts b/packages/nodes-base/nodes/EmailReadImap.node.ts index 0181e6d76..1fb3ba3ec 100644 --- a/packages/nodes-base/nodes/EmailReadImap.node.ts +++ b/packages/nodes-base/nodes/EmailReadImap.node.ts @@ -9,7 +9,13 @@ import { ITriggerResponse, } from 'n8n-workflow'; -import { connect as imapConnect, ImapSimple, ImapSimpleOptions, getParts, Message } from 'imap-simple'; +import { + connect as imapConnect, + getParts, + ImapSimple, + ImapSimpleOptions, + Message, +} from 'imap-simple'; import { simpleParser, Source as ParserSource, diff --git a/packages/nodes-base/nodes/EmailSend.node.ts b/packages/nodes-base/nodes/EmailSend.node.ts index 209706845..4db5679d0 100644 --- a/packages/nodes-base/nodes/EmailSend.node.ts +++ b/packages/nodes-base/nodes/EmailSend.node.ts @@ -4,9 +4,9 @@ import { } from 'n8n-core'; import { IDataObject, - INodeTypeDescription, INodeExecutionData, INodeType, + INodeTypeDescription, } from 'n8n-workflow'; import { createTransport } from 'nodemailer'; diff --git a/packages/nodes-base/nodes/Eventbrite/EventbriteTrigger.node.ts b/packages/nodes-base/nodes/Eventbrite/EventbriteTrigger.node.ts index 0c9158b6c..d7292ad10 100644 --- a/packages/nodes-base/nodes/Eventbrite/EventbriteTrigger.node.ts +++ b/packages/nodes-base/nodes/Eventbrite/EventbriteTrigger.node.ts @@ -7,8 +7,8 @@ import { IDataObject, ILoadOptionsFunctions, INodePropertyOptions, - INodeTypeDescription, INodeType, + INodeTypeDescription, IWebhookResponseData, } from 'n8n-workflow'; diff --git a/packages/nodes-base/nodes/ExecuteWorkflow.node.ts b/packages/nodes-base/nodes/ExecuteWorkflow.node.ts index ec1c161a0..19b9c4d7c 100644 --- a/packages/nodes-base/nodes/ExecuteWorkflow.node.ts +++ b/packages/nodes-base/nodes/ExecuteWorkflow.node.ts @@ -7,10 +7,10 @@ const fsReadFileAsync = promisify(fsReadFile); import { IExecuteFunctions } from 'n8n-core'; import { + IExecuteWorkflowInfo, INodeExecutionData, INodeType, INodeTypeDescription, - IExecuteWorkflowInfo, IWorkflowBase, } from 'n8n-workflow'; diff --git a/packages/nodes-base/nodes/FileMaker/FileMaker.node.ts b/packages/nodes-base/nodes/FileMaker/FileMaker.node.ts index 8eb772b2d..aacbd9377 100644 --- a/packages/nodes-base/nodes/FileMaker/FileMaker.node.ts +++ b/packages/nodes-base/nodes/FileMaker/FileMaker.node.ts @@ -9,17 +9,17 @@ import { import {OptionsWithUri} from 'request'; import { - layoutsApiRequest, getFields, getPortals, getScripts, getToken, - parseSort, + layoutsApiRequest, + logout, + parseFields, parsePortals, parseQuery, parseScripts, - parseFields, - logout + parseSort, } from "./GenericFunctions"; export class FileMaker implements INodeType { diff --git a/packages/nodes-base/nodes/FileMaker/GenericFunctions.ts b/packages/nodes-base/nodes/FileMaker/GenericFunctions.ts index a3144fc3d..00bc43947 100644 --- a/packages/nodes-base/nodes/FileMaker/GenericFunctions.ts +++ b/packages/nodes-base/nodes/FileMaker/GenericFunctions.ts @@ -1,7 +1,7 @@ import { IExecuteFunctions, + IExecuteSingleFunctions, ILoadOptionsFunctions, - IExecuteSingleFunctions } from 'n8n-core'; import { @@ -9,7 +9,6 @@ import { } from 'n8n-workflow'; import {OptionsWithUri} from 'request'; -import {Url} from "url"; interface ScriptsOptions { script?: any; //tslint:disable-line:no-any diff --git a/packages/nodes-base/nodes/Flow/Flow.node.ts b/packages/nodes-base/nodes/Flow/Flow.node.ts index 0d97a5aab..92a0d90ad 100644 --- a/packages/nodes-base/nodes/Flow/Flow.node.ts +++ b/packages/nodes-base/nodes/Flow/Flow.node.ts @@ -3,17 +3,17 @@ import { } from 'n8n-core'; import { IDataObject, - INodeTypeDescription, INodeExecutionData, INodeType, + INodeTypeDescription, } from 'n8n-workflow'; import { flowApiRequest, FlowApiRequestAllItems, } from './GenericFunctions'; import { - taskOpeations, taskFields, + taskOpeations, } from './TaskDescription'; import { ITask, TaskInfo, diff --git a/packages/nodes-base/nodes/Flow/FlowTrigger.node.ts b/packages/nodes-base/nodes/Flow/FlowTrigger.node.ts index 88f07f829..daf3f8940 100644 --- a/packages/nodes-base/nodes/Flow/FlowTrigger.node.ts +++ b/packages/nodes-base/nodes/Flow/FlowTrigger.node.ts @@ -5,8 +5,8 @@ import { import { IDataObject, - INodeTypeDescription, INodeType, + INodeTypeDescription, IWebhookResponseData, } from 'n8n-workflow'; diff --git a/packages/nodes-base/nodes/Flow/GenericFunctions.ts b/packages/nodes-base/nodes/Flow/GenericFunctions.ts index 96edf3f32..af83e474e 100644 --- a/packages/nodes-base/nodes/Flow/GenericFunctions.ts +++ b/packages/nodes-base/nodes/Flow/GenericFunctions.ts @@ -1,9 +1,9 @@ import { OptionsWithUri } from 'request'; import { IExecuteFunctions, + IExecuteSingleFunctions, IHookFunctions, ILoadOptionsFunctions, - IExecuteSingleFunctions, } from 'n8n-core'; import { IDataObject } from 'n8n-workflow'; diff --git a/packages/nodes-base/nodes/Freshdesk/Freshdesk.node.ts b/packages/nodes-base/nodes/Freshdesk/Freshdesk.node.ts index eba94626b..1fa458caf 100644 --- a/packages/nodes-base/nodes/Freshdesk/Freshdesk.node.ts +++ b/packages/nodes-base/nodes/Freshdesk/Freshdesk.node.ts @@ -1,10 +1,10 @@ import { IDataObject, - INodeTypeDescription, - INodeExecutionData, - INodeType, ILoadOptionsFunctions, + INodeExecutionData, INodePropertyOptions, + INodeType, + INodeTypeDescription, } from 'n8n-workflow'; import { @@ -12,10 +12,10 @@ import { } from 'n8n-core'; import { + capitalize, freshdeskApiRequest, freshdeskApiRequestAllItems, // validateJSON, - capitalize } from './GenericFunctions'; import { @@ -27,9 +27,6 @@ import { contactOperations, } from './ContactDescription'; -import * as moment from 'moment-timezone'; -import { response } from 'express'; - enum Status { Open = 2, Pending = 3, diff --git a/packages/nodes-base/nodes/Freshdesk/GenericFunctions.ts b/packages/nodes-base/nodes/Freshdesk/GenericFunctions.ts index e52debfd6..fb3847f5c 100644 --- a/packages/nodes-base/nodes/Freshdesk/GenericFunctions.ts +++ b/packages/nodes-base/nodes/Freshdesk/GenericFunctions.ts @@ -3,9 +3,9 @@ import { } from 'request'; import { + BINARY_ENCODING, IExecuteFunctions, ILoadOptionsFunctions, - BINARY_ENCODING } from 'n8n-core'; import { diff --git a/packages/nodes-base/nodes/Github/Github.node.ts b/packages/nodes-base/nodes/Github/Github.node.ts index 22bc7c0e7..27ae6e668 100644 --- a/packages/nodes-base/nodes/Github/Github.node.ts +++ b/packages/nodes-base/nodes/Github/Github.node.ts @@ -5,13 +5,13 @@ import { import { IDataObject, INodeExecutionData, - INodeTypeDescription, INodeType, + INodeTypeDescription, } from 'n8n-workflow'; import { - githubApiRequest, getFileSha, + githubApiRequest, } from './GenericFunctions'; export class Github implements INodeType { diff --git a/packages/nodes-base/nodes/Github/GithubTrigger.node.ts b/packages/nodes-base/nodes/Github/GithubTrigger.node.ts index 1360d7536..f8173c073 100644 --- a/packages/nodes-base/nodes/Github/GithubTrigger.node.ts +++ b/packages/nodes-base/nodes/Github/GithubTrigger.node.ts @@ -5,8 +5,8 @@ import { import { IDataObject, - INodeTypeDescription, INodeType, + INodeTypeDescription, IWebhookResponseData, } from 'n8n-workflow'; diff --git a/packages/nodes-base/nodes/Gitlab/Gitlab.node.ts b/packages/nodes-base/nodes/Gitlab/Gitlab.node.ts index abb5dcf1a..bba2d0ddc 100644 --- a/packages/nodes-base/nodes/Gitlab/Gitlab.node.ts +++ b/packages/nodes-base/nodes/Gitlab/Gitlab.node.ts @@ -5,8 +5,8 @@ import { import { IDataObject, INodeExecutionData, - INodeTypeDescription, INodeType, + INodeTypeDescription, } from 'n8n-workflow'; import { diff --git a/packages/nodes-base/nodes/Gitlab/GitlabTrigger.node.ts b/packages/nodes-base/nodes/Gitlab/GitlabTrigger.node.ts index 3104a0573..7d2d04235 100644 --- a/packages/nodes-base/nodes/Gitlab/GitlabTrigger.node.ts +++ b/packages/nodes-base/nodes/Gitlab/GitlabTrigger.node.ts @@ -5,8 +5,8 @@ import { import { IDataObject, - INodeTypeDescription, INodeType, + INodeTypeDescription, IWebhookResponseData, } from 'n8n-workflow'; diff --git a/packages/nodes-base/nodes/Google/Calendar/GoogleCalendar.node.ts b/packages/nodes-base/nodes/Google/Calendar/GoogleCalendar.node.ts index 4c8208590..e08ef9beb 100644 --- a/packages/nodes-base/nodes/Google/Calendar/GoogleCalendar.node.ts +++ b/packages/nodes-base/nodes/Google/Calendar/GoogleCalendar.node.ts @@ -4,11 +4,11 @@ import { import { IDataObject, - INodeExecutionData, - INodeTypeDescription, - INodeType, ILoadOptionsFunctions, + INodeExecutionData, INodePropertyOptions, + INodeType, + INodeTypeDescription, } from 'n8n-workflow'; import { @@ -17,8 +17,8 @@ import { } from './GenericFunctions'; import { - eventOperations, eventFields, + eventOperations, } from './EventDescription'; import { diff --git a/packages/nodes-base/nodes/Google/Contacts/GoogleContacts.node.ts b/packages/nodes-base/nodes/Google/Contacts/GoogleContacts.node.ts index b7749662a..0a6bee177 100644 --- a/packages/nodes-base/nodes/Google/Contacts/GoogleContacts.node.ts +++ b/packages/nodes-base/nodes/Google/Contacts/GoogleContacts.node.ts @@ -4,11 +4,11 @@ import { import { IDataObject, - INodeExecutionData, - INodeTypeDescription, - INodeType, ILoadOptionsFunctions, + INodeExecutionData, INodePropertyOptions, + INodeType, + INodeTypeDescription, } from 'n8n-workflow'; import { @@ -19,8 +19,8 @@ import { } from './GenericFunctions'; import { - contactOperations, contactFields, + contactOperations, } from './ContactDescription'; import * as moment from 'moment'; diff --git a/packages/nodes-base/nodes/Google/Drive/GoogleDrive.node.ts b/packages/nodes-base/nodes/Google/Drive/GoogleDrive.node.ts index 479388b09..fe26f12e1 100644 --- a/packages/nodes-base/nodes/Google/Drive/GoogleDrive.node.ts +++ b/packages/nodes-base/nodes/Google/Drive/GoogleDrive.node.ts @@ -5,9 +5,9 @@ import { import { IDataObject, - INodeTypeDescription, INodeExecutionData, INodeType, + INodeTypeDescription, } from 'n8n-workflow'; import { diff --git a/packages/nodes-base/nodes/Google/Gmail/Gmail.node.ts b/packages/nodes-base/nodes/Google/Gmail/Gmail.node.ts index 924ad85c7..552b02203 100644 --- a/packages/nodes-base/nodes/Google/Gmail/Gmail.node.ts +++ b/packages/nodes-base/nodes/Google/Gmail/Gmail.node.ts @@ -4,8 +4,8 @@ import { import { IBinaryKeyData, - ILoadOptionsFunctions, IDataObject, + ILoadOptionsFunctions, INodeExecutionData, INodePropertyOptions, INodeType, @@ -21,23 +21,23 @@ import { } from './GenericFunctions'; import { - messageOperations, messageFields, + messageOperations, } from './MessageDescription'; import { - messageLabelOperations, messageLabelFields, + messageLabelOperations, } from './MessageLabelDescription'; import { - labelOperations, labelFields, + labelOperations, } from './LabelDescription'; import { - draftOperations, draftFields, + draftOperations, } from './DraftDescription'; import { diff --git a/packages/nodes-base/nodes/Google/Sheet/GenericFunctions.ts b/packages/nodes-base/nodes/Google/Sheet/GenericFunctions.ts index dff54fa6a..290c4f3b7 100644 --- a/packages/nodes-base/nodes/Google/Sheet/GenericFunctions.ts +++ b/packages/nodes-base/nodes/Google/Sheet/GenericFunctions.ts @@ -1,6 +1,6 @@ import { OptionsWithUri, - } from 'request'; +} from 'request'; import { IExecuteFunctions, @@ -61,7 +61,7 @@ export async function googleApiRequest(this: IExecuteFunctions | IExecuteSingleF } } -export async function googleApiRequestAllItems(this: IExecuteFunctions | ILoadOptionsFunctions, propertyName: string ,method: string, endpoint: string, body: any = {}, query: IDataObject = {}): Promise { // tslint:disable-line:no-any +export async function googleApiRequestAllItems(this: IExecuteFunctions | ILoadOptionsFunctions, propertyName: string, method: string, endpoint: string, body: any = {}, query: IDataObject = {}): Promise { // tslint:disable-line:no-any const returnData: IDataObject[] = []; @@ -80,7 +80,7 @@ export async function googleApiRequestAllItems(this: IExecuteFunctions | ILoadOp return returnData; } -function getAccessToken(this: IExecuteFunctions | IExecuteSingleFunctions | ILoadOptionsFunctions, credentials: IDataObject) : Promise { +function getAccessToken(this: IExecuteFunctions | IExecuteSingleFunctions | ILoadOptionsFunctions, credentials: IDataObject): Promise { //https://developers.google.com/identity/protocols/oauth2/service-account#httprest const scopes = [ @@ -93,25 +93,25 @@ function getAccessToken(this: IExecuteFunctions | IExecuteSingleFunctions | ILoa const signature = jwt.sign( { - 'iss': credentials.email as string, - 'sub': credentials.email as string, - 'scope': scopes.join(' '), - 'aud': `https://oauth2.googleapis.com/token`, - 'iat': now, - 'exp': now + 3600, + 'iss': credentials.email as string, + 'sub': credentials.email as string, + 'scope': scopes.join(' '), + 'aud': `https://oauth2.googleapis.com/token`, + 'iat': now, + 'exp': now + 3600, }, credentials.privateKey as string, { - algorithm: 'RS256', - header: { - 'kid': credentials.privateKey as string, - 'typ': 'JWT', - 'alg': 'RS256', - }, + algorithm: 'RS256', + header: { + 'kid': credentials.privateKey as string, + 'typ': 'JWT', + 'alg': 'RS256', + }, } - ); + ); - const options: OptionsWithUri = { + const options: OptionsWithUri = { headers: { 'Content-Type': 'application/x-www-form-urlencoded', }, diff --git a/packages/nodes-base/nodes/Google/Sheet/GoogleSheet.ts b/packages/nodes-base/nodes/Google/Sheet/GoogleSheet.ts index 606867e8e..63133c495 100644 --- a/packages/nodes-base/nodes/Google/Sheet/GoogleSheet.ts +++ b/packages/nodes-base/nodes/Google/Sheet/GoogleSheet.ts @@ -5,11 +5,11 @@ import { import { IExecuteFunctions, ILoadOptionsFunctions, - } from 'n8n-core'; +} from 'n8n-core'; import { googleApiRequest, - } from './GenericFunctions'; +} from './GenericFunctions'; import { utils as xlsxUtils, @@ -84,9 +84,9 @@ export class GoogleSheet { return response; } - /** - * Returns the cell values - */ + /** + * Returns the cell values + */ async getData(range: string, valueRenderMode: ValueRenderOption): Promise { const query = { @@ -129,9 +129,9 @@ export class GoogleSheet { } - /** - * Sets the cell values - */ + /** + * Sets the cell values + */ async batchUpdate(updateData: ISheetUpdateData[], valueInputMode: ValueInputOption) { const body = { @@ -145,9 +145,9 @@ export class GoogleSheet { } - /** - * Sets the cell values - */ + /** + * Sets the cell values + */ async setData(range: string, data: string[][], valueInputMode: ValueInputOption) { const body = { @@ -161,9 +161,9 @@ export class GoogleSheet { } - /** - * Appends the cell values - */ + /** + * Appends the cell values + */ async appendData(range: string, data: string[][], valueInputMode: ValueInputOption) { const body = { @@ -180,9 +180,9 @@ export class GoogleSheet { return response; } - /** - * Returns the given sheet data in a structured way - */ + /** + * Returns the given sheet data in a structured way + */ structureData(inputData: string[][], startRow: number, keys: string[], addEmpty?: boolean): IDataObject[] { const returnData = []; @@ -207,10 +207,10 @@ export class GoogleSheet { } - /** - * Returns the given sheet data in a structured way using - * the startRow as the one with the name of the key - */ + /** + * Returns the given sheet data in a structured way using + * the startRow as the one with the name of the key + */ structureArrayDataByColumn(inputData: string[][], keyRow: number, dataStartRow: number): IDataObject[] { const keys: string[] = []; @@ -235,7 +235,7 @@ export class GoogleSheet { } - getColumnWithOffset (startColumn: string, offset: number): string { + getColumnWithOffset(startColumn: string, offset: number): string { const columnIndex = xlsxUtils.decode_col(startColumn) + offset; return xlsxUtils.encode_col(columnIndex); } @@ -303,7 +303,7 @@ export class GoogleSheet { sheetDataKeyColumn.shift(); // Create an Array which all the key-values of the Google Sheet - const keyColumnIndexLookup = sheetDataKeyColumn.map((rowContent) => rowContent[0] ); + const keyColumnIndexLookup = sheetDataKeyColumn.map((rowContent) => rowContent[0]); const updateData: ISheetUpdateData[] = []; let itemKey: string | number | undefined | null; diff --git a/packages/nodes-base/nodes/Google/Sheet/GoogleSheets.node.ts b/packages/nodes-base/nodes/Google/Sheet/GoogleSheets.node.ts index ff12b218e..8149c0f55 100644 --- a/packages/nodes-base/nodes/Google/Sheet/GoogleSheets.node.ts +++ b/packages/nodes-base/nodes/Google/Sheet/GoogleSheets.node.ts @@ -1,7 +1,7 @@ import { IExecuteFunctions, - } from 'n8n-core'; +} from 'n8n-core'; import { IDataObject, @@ -613,7 +613,7 @@ export class GoogleSheets implements INodeType { range = this.getNodeParameter('range', 0) as string; if (range.includes('!')) { const [sheet, ranges] = range.split('!'); - range = `${encodeURIComponent(sheet)}!${ranges}`; + range = `${encodeURIComponent(sheet)}!${ranges}`; } } diff --git a/packages/nodes-base/nodes/Google/Task/GoogleTasks.node.ts b/packages/nodes-base/nodes/Google/Task/GoogleTasks.node.ts index 603bc1af5..9ba91570c 100644 --- a/packages/nodes-base/nodes/Google/Task/GoogleTasks.node.ts +++ b/packages/nodes-base/nodes/Google/Task/GoogleTasks.node.ts @@ -17,8 +17,8 @@ import { } from './GenericFunctions'; import { - taskOperations, taskFields, + taskOperations, } from './TaskDescription'; export class GoogleTasks implements INodeType { diff --git a/packages/nodes-base/nodes/Google/YouTube/YouTube.node.ts b/packages/nodes-base/nodes/Google/YouTube/YouTube.node.ts index 52df08809..2065b685c 100644 --- a/packages/nodes-base/nodes/Google/YouTube/YouTube.node.ts +++ b/packages/nodes-base/nodes/Google/YouTube/YouTube.node.ts @@ -1,6 +1,6 @@ import { - IExecuteFunctions, BINARY_ENCODING, + IExecuteFunctions, } from 'n8n-core'; import { @@ -28,8 +28,8 @@ import { } from './PlaylistDescription'; import { - playlistItemOperations, playlistItemFields, + playlistItemOperations, } from './PlaylistItemDescription'; import { diff --git a/packages/nodes-base/nodes/Gumroad/GumroadTrigger.node.ts b/packages/nodes-base/nodes/Gumroad/GumroadTrigger.node.ts index c4a9b65a9..fb1eb8fe6 100644 --- a/packages/nodes-base/nodes/Gumroad/GumroadTrigger.node.ts +++ b/packages/nodes-base/nodes/Gumroad/GumroadTrigger.node.ts @@ -5,8 +5,8 @@ import { import { IDataObject, - INodeTypeDescription, INodeType, + INodeTypeDescription, IWebhookResponseData, } from 'n8n-workflow'; diff --git a/packages/nodes-base/nodes/HackerNews/HackerNews.node.ts b/packages/nodes-base/nodes/HackerNews/HackerNews.node.ts index b77d5d35b..c607b1b6b 100644 --- a/packages/nodes-base/nodes/HackerNews/HackerNews.node.ts +++ b/packages/nodes-base/nodes/HackerNews/HackerNews.node.ts @@ -3,10 +3,10 @@ import { } from 'n8n-core'; import { + IDataObject, INodeExecutionData, INodeType, INodeTypeDescription, - IDataObject, } from 'n8n-workflow'; import { diff --git a/packages/nodes-base/nodes/Harvest/Harvest.node.ts b/packages/nodes-base/nodes/Harvest/Harvest.node.ts index 336a7838a..91a42840c 100644 --- a/packages/nodes-base/nodes/Harvest/Harvest.node.ts +++ b/packages/nodes-base/nodes/Harvest/Harvest.node.ts @@ -3,22 +3,52 @@ import { } from 'n8n-core'; import { IDataObject, - INodeTypeDescription, INodeExecutionData, INodeType, + INodeTypeDescription, } from 'n8n-workflow'; -import { clientOperations, clientFields } from './ClientDescription'; -import { contactOperations, contactFields } from './ContactDescription'; +import { + clientFields, + clientOperations, +} from './ClientDescription'; +import { + contactFields, + contactOperations, +} from './ContactDescription'; import { companyOperations } from './CompanyDescription'; -import { estimateOperations, estimateFields } from './EstimateDescription'; -import { expenseOperations, expenseFields } from './ExpenseDescription'; -import { harvestApiRequest, harvestApiRequestAllItems } from './GenericFunctions'; -import { invoiceOperations, invoiceFields } from './InvoiceDescription'; -import { projectOperations, projectFields } from './ProjectDescription'; -import { taskOperations, taskFields } from './TaskDescription'; -import { timeEntryOperations, timeEntryFields } from './TimeEntryDescription'; -import { userOperations, userFields } from './UserDescription'; +import { + estimateFields, + estimateOperations, +} from './EstimateDescription'; +import { + expenseFields, + expenseOperations, +} from './ExpenseDescription'; +import { + harvestApiRequest, + harvestApiRequestAllItems, +} from './GenericFunctions'; +import { + invoiceFields, + invoiceOperations, +} from './InvoiceDescription'; +import { + projectFields, + projectOperations, +} from './ProjectDescription'; +import { + taskFields, + taskOperations, +} from './TaskDescription'; +import { + timeEntryFields, + timeEntryOperations, +} from './TimeEntryDescription'; +import { + userFields, + userOperations, +} from './UserDescription'; /** * fetch All resource using paginated calls diff --git a/packages/nodes-base/nodes/HelpScout/GenericFunctions.ts b/packages/nodes-base/nodes/HelpScout/GenericFunctions.ts index a43545a32..e785fff68 100644 --- a/packages/nodes-base/nodes/HelpScout/GenericFunctions.ts +++ b/packages/nodes-base/nodes/HelpScout/GenericFunctions.ts @@ -1,8 +1,8 @@ import { OptionsWithUri } from 'request'; import { - IHookFunctions, IExecuteFunctions, IExecuteSingleFunctions, + IHookFunctions, ILoadOptionsFunctions, } from 'n8n-core'; import { diff --git a/packages/nodes-base/nodes/HelpScout/HelpScout.node.ts b/packages/nodes-base/nodes/HelpScout/HelpScout.node.ts index ce1f02d64..4b7229917 100644 --- a/packages/nodes-base/nodes/HelpScout/HelpScout.node.ts +++ b/packages/nodes-base/nodes/HelpScout/HelpScout.node.ts @@ -8,8 +8,8 @@ import { ILoadOptionsFunctions, INodeExecutionData, INodePropertyOptions, - INodeTypeDescription, INodeType, + INodeTypeDescription, } from 'n8n-workflow'; import { diff --git a/packages/nodes-base/nodes/HtmlExtract/HtmlExtract.node.ts b/packages/nodes-base/nodes/HtmlExtract/HtmlExtract.node.ts index f482829c2..cae98d913 100644 --- a/packages/nodes-base/nodes/HtmlExtract/HtmlExtract.node.ts +++ b/packages/nodes-base/nodes/HtmlExtract/HtmlExtract.node.ts @@ -1,10 +1,10 @@ import * as cheerio from 'cheerio'; import { IExecuteFunctions } from 'n8n-core'; import { + IDataObject, INodeExecutionData, INodeType, INodeTypeDescription, - IDataObject, } from 'n8n-workflow'; type Cheerio = ReturnType; diff --git a/packages/nodes-base/nodes/HttpRequest.node.ts b/packages/nodes-base/nodes/HttpRequest.node.ts index e37e2bd57..b7a75496a 100644 --- a/packages/nodes-base/nodes/HttpRequest.node.ts +++ b/packages/nodes-base/nodes/HttpRequest.node.ts @@ -3,11 +3,11 @@ import { IExecuteFunctions, } from 'n8n-core'; import { + IBinaryData, IDataObject, INodeExecutionData, INodeType, INodeTypeDescription, - IBinaryData, } from 'n8n-workflow'; import { OptionsWithUri } from 'request'; diff --git a/packages/nodes-base/nodes/Hubspot/Hubspot.node.ts b/packages/nodes-base/nodes/Hubspot/Hubspot.node.ts index ad62fd76a..a63809bed 100644 --- a/packages/nodes-base/nodes/Hubspot/Hubspot.node.ts +++ b/packages/nodes-base/nodes/Hubspot/Hubspot.node.ts @@ -17,28 +17,28 @@ import { } from './GenericFunctions'; import { - contactOperations, contactFields, + contactOperations, } from './ContactDescription'; import { - companyOperations, companyFields, + companyOperations, } from './CompanyDescription'; import { - dealOperations, dealFields, + dealOperations, } from './DealDescription'; import { - formOperations, formFields, + formOperations, } from './FormDescription'; import { - ticketOperations, ticketFields, + ticketOperations, } from './TicketDescription'; import { @@ -46,8 +46,8 @@ import { } from './FormInterface'; import { - IDeal, IAssociation, + IDeal, } from './DealInterface'; import { diff --git a/packages/nodes-base/nodes/Hubspot/HubspotTrigger.node.ts b/packages/nodes-base/nodes/Hubspot/HubspotTrigger.node.ts index 0970172ea..1274d929f 100644 --- a/packages/nodes-base/nodes/Hubspot/HubspotTrigger.node.ts +++ b/packages/nodes-base/nodes/Hubspot/HubspotTrigger.node.ts @@ -4,10 +4,10 @@ import { } from 'n8n-core'; import { - INodeTypeDescription, - INodeType, - IWebhookResponseData, IDataObject, + INodeType, + INodeTypeDescription, + IWebhookResponseData, } from 'n8n-workflow'; import { diff --git a/packages/nodes-base/nodes/Hunter/Hunter.node.ts b/packages/nodes-base/nodes/Hunter/Hunter.node.ts index d4e1f8d76..ef3d9a9e2 100644 --- a/packages/nodes-base/nodes/Hunter/Hunter.node.ts +++ b/packages/nodes-base/nodes/Hunter/Hunter.node.ts @@ -3,9 +3,9 @@ import { } from 'n8n-core'; import { IDataObject, - INodeTypeDescription, INodeExecutionData, INodeType, + INodeTypeDescription, } from 'n8n-workflow'; import { hunterApiRequest, diff --git a/packages/nodes-base/nodes/Intercom/GenericFunctions.ts b/packages/nodes-base/nodes/Intercom/GenericFunctions.ts index be68ef0c3..68bed5d7b 100644 --- a/packages/nodes-base/nodes/Intercom/GenericFunctions.ts +++ b/packages/nodes-base/nodes/Intercom/GenericFunctions.ts @@ -2,9 +2,9 @@ import { OptionsWithUri } from 'request'; import { IExecuteFunctions, + IExecuteSingleFunctions, IHookFunctions, ILoadOptionsFunctions, - IExecuteSingleFunctions } from 'n8n-core'; import { diff --git a/packages/nodes-base/nodes/Intercom/Intercom.node.ts b/packages/nodes-base/nodes/Intercom/Intercom.node.ts index d7d1890d5..c98cc027d 100644 --- a/packages/nodes-base/nodes/Intercom/Intercom.node.ts +++ b/packages/nodes-base/nodes/Intercom/Intercom.node.ts @@ -3,15 +3,15 @@ import { } from 'n8n-core'; import { IDataObject, - INodeTypeDescription, - INodeExecutionData, - INodeType, ILoadOptionsFunctions, + INodeExecutionData, INodePropertyOptions, + INodeType, + INodeTypeDescription, } from 'n8n-workflow'; import { - leadOpeations, leadFields, + leadOpeations, } from './LeadDescription'; import { intercomApiRequest, @@ -19,13 +19,22 @@ import { validateJSON, } from './GenericFunctions'; import { + IAvatar, ILead, ILeadCompany, - IAvatar, } from './LeadInterface'; -import { userOpeations, userFields } from './UserDescription'; -import { IUser, IUserCompany } from './UserInterface'; -import { companyOperations, companyFields } from './CompanyDescription'; +import { + userFields, + userOpeations, +} from './UserDescription'; +import { + IUser, + IUserCompany, +} from './UserInterface'; +import { + companyFields, + companyOperations, +} from './CompanyDescription'; import { ICompany } from './CompanyInteface'; export class Intercom implements INodeType { diff --git a/packages/nodes-base/nodes/InvoiceNinja/ISOCountryCodes.ts b/packages/nodes-base/nodes/InvoiceNinja/ISOCountryCodes.ts index 3948ee628..22122653e 100644 --- a/packages/nodes-base/nodes/InvoiceNinja/ISOCountryCodes.ts +++ b/packages/nodes-base/nodes/InvoiceNinja/ISOCountryCodes.ts @@ -2,1580 +2,1580 @@ import { IDataObject } from "n8n-workflow"; export const countryCodes = [ { - "name": "Afghanistan", - "alpha2": "AF", - "alpha3": "AFG", - "numeric": "004" + "name": "Afghanistan", + "alpha2": "AF", + "alpha3": "AFG", + "numeric": "004" }, { - "name": "Åland Islands", - "alpha2": "AX", - "alpha3": "ALA", - "numeric": "248", - "altName": "Aland Islands" + "name": "Åland Islands", + "alpha2": "AX", + "alpha3": "ALA", + "numeric": "248", + "altName": "Aland Islands" }, { - "name": "Albania", - "alpha2": "AL", - "alpha3": "ALB", - "numeric": "008" + "name": "Albania", + "alpha2": "AL", + "alpha3": "ALB", + "numeric": "008" }, { - "name": "Algeria", - "alpha2": "DZ", - "alpha3": "DZA", - "numeric": "012" + "name": "Algeria", + "alpha2": "DZ", + "alpha3": "DZA", + "numeric": "012" }, { - "name": "American Samoa", - "alpha2": "AS", - "alpha3": "ASM", - "numeric": "016" + "name": "American Samoa", + "alpha2": "AS", + "alpha3": "ASM", + "numeric": "016" }, { - "name": "Andorra", - "alpha2": "AD", - "alpha3": "AND", - "numeric": "020" + "name": "Andorra", + "alpha2": "AD", + "alpha3": "AND", + "numeric": "020" }, { - "name": "Angola", - "alpha2": "AO", - "alpha3": "AGO", - "numeric": "024" + "name": "Angola", + "alpha2": "AO", + "alpha3": "AGO", + "numeric": "024" }, { - "name": "Anguilla", - "alpha2": "AI", - "alpha3": "AIA", - "numeric": "660" + "name": "Anguilla", + "alpha2": "AI", + "alpha3": "AIA", + "numeric": "660" }, { - "name": "Antarctica", - "alpha2": "AQ", - "alpha3": "ATA", - "numeric": "010" + "name": "Antarctica", + "alpha2": "AQ", + "alpha3": "ATA", + "numeric": "010" }, { - "name": "Antigua and Barbuda", - "alpha2": "AG", - "alpha3": "ATG", - "numeric": "028" + "name": "Antigua and Barbuda", + "alpha2": "AG", + "alpha3": "ATG", + "numeric": "028" }, { - "name": "Argentina", - "alpha2": "AR", - "alpha3": "ARG", - "numeric": "032" + "name": "Argentina", + "alpha2": "AR", + "alpha3": "ARG", + "numeric": "032" }, { - "name": "Armenia", - "alpha2": "AM", - "alpha3": "ARM", - "numeric": "051" + "name": "Armenia", + "alpha2": "AM", + "alpha3": "ARM", + "numeric": "051" }, { - "name": "Aruba", - "alpha2": "AW", - "alpha3": "ABW", - "numeric": "533" + "name": "Aruba", + "alpha2": "AW", + "alpha3": "ABW", + "numeric": "533" }, { - "name": "Australia", - "alpha2": "AU", - "alpha3": "AUS", - "numeric": "036" + "name": "Australia", + "alpha2": "AU", + "alpha3": "AUS", + "numeric": "036" }, { - "name": "Austria", - "alpha2": "AT", - "alpha3": "AUT", - "numeric": "040" + "name": "Austria", + "alpha2": "AT", + "alpha3": "AUT", + "numeric": "040" }, { - "name": "Azerbaijan", - "alpha2": "AZ", - "alpha3": "AZE", - "numeric": "031" + "name": "Azerbaijan", + "alpha2": "AZ", + "alpha3": "AZE", + "numeric": "031" }, { - "name": "Bahamas (the)", - "alpha2": "BS", - "alpha3": "BHS", - "numeric": "044", - "altName": "Bahamas" + "name": "Bahamas (the)", + "alpha2": "BS", + "alpha3": "BHS", + "numeric": "044", + "altName": "Bahamas" }, { - "name": "Bahrain", - "alpha2": "BH", - "alpha3": "BHR", - "numeric": "048" + "name": "Bahrain", + "alpha2": "BH", + "alpha3": "BHR", + "numeric": "048" }, { - "name": "Bangladesh", - "alpha2": "BD", - "alpha3": "BGD", - "numeric": "050" + "name": "Bangladesh", + "alpha2": "BD", + "alpha3": "BGD", + "numeric": "050" }, { - "name": "Barbados", - "alpha2": "BB", - "alpha3": "BRB", - "numeric": "052" + "name": "Barbados", + "alpha2": "BB", + "alpha3": "BRB", + "numeric": "052" }, { - "name": "Belarus", - "alpha2": "BY", - "alpha3": "BLR", - "numeric": "112" + "name": "Belarus", + "alpha2": "BY", + "alpha3": "BLR", + "numeric": "112" }, { - "name": "Belgium", - "alpha2": "BE", - "alpha3": "BEL", - "numeric": "056" + "name": "Belgium", + "alpha2": "BE", + "alpha3": "BEL", + "numeric": "056" }, { - "name": "Belize", - "alpha2": "BZ", - "alpha3": "BLZ", - "numeric": "084" + "name": "Belize", + "alpha2": "BZ", + "alpha3": "BLZ", + "numeric": "084" }, { - "name": "Benin", - "alpha2": "BJ", - "alpha3": "BEN", - "numeric": "204" + "name": "Benin", + "alpha2": "BJ", + "alpha3": "BEN", + "numeric": "204" }, { - "name": "Bermuda", - "alpha2": "BM", - "alpha3": "BMU", - "numeric": "060" + "name": "Bermuda", + "alpha2": "BM", + "alpha3": "BMU", + "numeric": "060" }, { - "name": "Bhutan", - "alpha2": "BT", - "alpha3": "BTN", - "numeric": "064" + "name": "Bhutan", + "alpha2": "BT", + "alpha3": "BTN", + "numeric": "064" }, { - "name": "Bolivia (Plurinational State of)", - "alpha2": "BO", - "alpha3": "BOL", - "numeric": "068", - "altName": "Bolivia" + "name": "Bolivia (Plurinational State of)", + "alpha2": "BO", + "alpha3": "BOL", + "numeric": "068", + "altName": "Bolivia" }, { - "name": "Bonaire, Sint Eustatius and Saba", - "alpha2": "BQ", - "alpha3": "BES", - "numeric": "535" + "name": "Bonaire, Sint Eustatius and Saba", + "alpha2": "BQ", + "alpha3": "BES", + "numeric": "535" }, { - "name": "Bosnia and Herzegovina", - "alpha2": "BA", - "alpha3": "BIH", - "numeric": "070" + "name": "Bosnia and Herzegovina", + "alpha2": "BA", + "alpha3": "BIH", + "numeric": "070" }, { - "name": "Botswana", - "alpha2": "BW", - "alpha3": "BWA", - "numeric": "072" + "name": "Botswana", + "alpha2": "BW", + "alpha3": "BWA", + "numeric": "072" }, { - "name": "Bouvet Island", - "alpha2": "BV", - "alpha3": "BVT", - "numeric": "074" + "name": "Bouvet Island", + "alpha2": "BV", + "alpha3": "BVT", + "numeric": "074" }, { - "name": "Brazil", - "alpha2": "BR", - "alpha3": "BRA", - "numeric": "076" + "name": "Brazil", + "alpha2": "BR", + "alpha3": "BRA", + "numeric": "076" }, { - "name": "British Indian Ocean Territory (the)", - "alpha2": "IO", - "alpha3": "IOT", - "numeric": "086", - "altName": "British Indian Ocean Territory" + "name": "British Indian Ocean Territory (the)", + "alpha2": "IO", + "alpha3": "IOT", + "numeric": "086", + "altName": "British Indian Ocean Territory" }, { - "name": "Brunei Darussalam", - "alpha2": "BN", - "alpha3": "BRN", - "numeric": "096", - "shortName": "Brunei" + "name": "Brunei Darussalam", + "alpha2": "BN", + "alpha3": "BRN", + "numeric": "096", + "shortName": "Brunei" }, { - "name": "Bulgaria", - "alpha2": "BG", - "alpha3": "BGR", - "numeric": "100" + "name": "Bulgaria", + "alpha2": "BG", + "alpha3": "BGR", + "numeric": "100" }, { - "name": "Burkina Faso", - "alpha2": "BF", - "alpha3": "BFA", - "numeric": "854" + "name": "Burkina Faso", + "alpha2": "BF", + "alpha3": "BFA", + "numeric": "854" }, { - "name": "Burundi", - "alpha2": "BI", - "alpha3": "BDI", - "numeric": "108" + "name": "Burundi", + "alpha2": "BI", + "alpha3": "BDI", + "numeric": "108" }, { - "name": "Cabo Verde", - "alpha2": "CV", - "alpha3": "CPV", - "numeric": "132", - "altName": "Cape Verde" + "name": "Cabo Verde", + "alpha2": "CV", + "alpha3": "CPV", + "numeric": "132", + "altName": "Cape Verde" }, { - "name": "Cambodia", - "alpha2": "KH", - "alpha3": "KHM", - "numeric": "116" + "name": "Cambodia", + "alpha2": "KH", + "alpha3": "KHM", + "numeric": "116" }, { - "name": "Cameroon", - "alpha2": "CM", - "alpha3": "CMR", - "numeric": "120" + "name": "Cameroon", + "alpha2": "CM", + "alpha3": "CMR", + "numeric": "120" }, { - "name": "Canada", - "alpha2": "CA", - "alpha3": "CAN", - "numeric": "124" + "name": "Canada", + "alpha2": "CA", + "alpha3": "CAN", + "numeric": "124" }, { - "name": "Cayman Islands (the)", - "alpha2": "KY", - "alpha3": "CYM", - "numeric": "136", - "altName": "Cayman Islands" + "name": "Cayman Islands (the)", + "alpha2": "KY", + "alpha3": "CYM", + "numeric": "136", + "altName": "Cayman Islands" }, { - "name": "Central African Republic (the)", - "alpha2": "CF", - "alpha3": "CAF", - "numeric": "140", - "altName": "Central African Republic" + "name": "Central African Republic (the)", + "alpha2": "CF", + "alpha3": "CAF", + "numeric": "140", + "altName": "Central African Republic" }, { - "name": "Chad", - "alpha2": "TD", - "alpha3": "TCD", - "numeric": "148" + "name": "Chad", + "alpha2": "TD", + "alpha3": "TCD", + "numeric": "148" }, { - "name": "Chile", - "alpha2": "CL", - "alpha3": "CHL", - "numeric": "152" + "name": "Chile", + "alpha2": "CL", + "alpha3": "CHL", + "numeric": "152" }, { - "name": "China", - "alpha2": "CN", - "alpha3": "CHN", - "numeric": "156" + "name": "China", + "alpha2": "CN", + "alpha3": "CHN", + "numeric": "156" }, { - "name": "Christmas Island", - "alpha2": "CX", - "alpha3": "CXR", - "numeric": "162" + "name": "Christmas Island", + "alpha2": "CX", + "alpha3": "CXR", + "numeric": "162" }, { - "name": "Cocos (Keeling) Islands (the)", - "alpha2": "CC", - "alpha3": "CCK", - "numeric": "166", - "altName": "Cocos (Keeling) Islands", - "shortName": "Cocos Islands" + "name": "Cocos (Keeling) Islands (the)", + "alpha2": "CC", + "alpha3": "CCK", + "numeric": "166", + "altName": "Cocos (Keeling) Islands", + "shortName": "Cocos Islands" }, { - "name": "Colombia", - "alpha2": "CO", - "alpha3": "COL", - "numeric": "170" + "name": "Colombia", + "alpha2": "CO", + "alpha3": "COL", + "numeric": "170" }, { - "name": "Comoros (the)", - "alpha2": "KM", - "alpha3": "COM", - "numeric": "174", - "altName": "Comoros" + "name": "Comoros (the)", + "alpha2": "KM", + "alpha3": "COM", + "numeric": "174", + "altName": "Comoros" }, { - "name": "Congo (the Democratic Republic of the)", - "alpha2": "CD", - "alpha3": "COD", - "numeric": "180", - "altName": "Congo, (Kinshasa)", - "shortName": "Democratic Republic of the Congo" + "name": "Congo (the Democratic Republic of the)", + "alpha2": "CD", + "alpha3": "COD", + "numeric": "180", + "altName": "Congo, (Kinshasa)", + "shortName": "Democratic Republic of the Congo" }, { - "name": "Congo (the)", - "alpha2": "CG", - "alpha3": "COG", - "numeric": "178", - "altName": "Congo (Brazzaville)", - "shortName": "Republic of the Congo" + "name": "Congo (the)", + "alpha2": "CG", + "alpha3": "COG", + "numeric": "178", + "altName": "Congo (Brazzaville)", + "shortName": "Republic of the Congo" }, { - "name": "Cook Islands (the)", - "alpha2": "CK", - "alpha3": "COK", - "numeric": "184", - "altName": "Cook Islands" + "name": "Cook Islands (the)", + "alpha2": "CK", + "alpha3": "COK", + "numeric": "184", + "altName": "Cook Islands" }, { - "name": "Costa Rica", - "alpha2": "CR", - "alpha3": "CRI", - "numeric": "188" + "name": "Costa Rica", + "alpha2": "CR", + "alpha3": "CRI", + "numeric": "188" }, { - "name": "Côte d'Ivoire", - "alpha2": "CI", - "alpha3": "CIV", - "numeric": "384", - "shortName": "Ivory Coast" + "name": "Côte d'Ivoire", + "alpha2": "CI", + "alpha3": "CIV", + "numeric": "384", + "shortName": "Ivory Coast" }, { - "name": "Croatia", - "alpha2": "HR", - "alpha3": "HRV", - "numeric": "191" + "name": "Croatia", + "alpha2": "HR", + "alpha3": "HRV", + "numeric": "191" }, { - "name": "Cuba", - "alpha2": "CU", - "alpha3": "CUB", - "numeric": "192" + "name": "Cuba", + "alpha2": "CU", + "alpha3": "CUB", + "numeric": "192" }, { - "name": "Curaçao", - "alpha2": "CW", - "alpha3": "CUW", - "numeric": "531", - "shortName": "Curacao" + "name": "Curaçao", + "alpha2": "CW", + "alpha3": "CUW", + "numeric": "531", + "shortName": "Curacao" }, { - "name": "Cyprus", - "alpha2": "CY", - "alpha3": "CYP", - "numeric": "196" + "name": "Cyprus", + "alpha2": "CY", + "alpha3": "CYP", + "numeric": "196" }, { - "name": "Czechia", - "alpha2": "CZ", - "alpha3": "CZE", - "numeric": "203", - "altName": "Czech Republic" + "name": "Czechia", + "alpha2": "CZ", + "alpha3": "CZE", + "numeric": "203", + "altName": "Czech Republic" }, { - "name": "Denmark", - "alpha2": "DK", - "alpha3": "DNK", - "numeric": "208" + "name": "Denmark", + "alpha2": "DK", + "alpha3": "DNK", + "numeric": "208" }, { - "name": "Djibouti", - "alpha2": "DJ", - "alpha3": "DJI", - "numeric": "262" + "name": "Djibouti", + "alpha2": "DJ", + "alpha3": "DJI", + "numeric": "262" }, { - "name": "Dominica", - "alpha2": "DM", - "alpha3": "DMA", - "numeric": "212" + "name": "Dominica", + "alpha2": "DM", + "alpha3": "DMA", + "numeric": "212" }, { - "name": "Dominican Republic (the)", - "alpha2": "DO", - "alpha3": "DOM", - "numeric": "214", - "altName": "Dominican Republic" + "name": "Dominican Republic (the)", + "alpha2": "DO", + "alpha3": "DOM", + "numeric": "214", + "altName": "Dominican Republic" }, { - "name": "Ecuador", - "alpha2": "EC", - "alpha3": "ECU", - "numeric": "218" + "name": "Ecuador", + "alpha2": "EC", + "alpha3": "ECU", + "numeric": "218" }, { - "name": "Egypt", - "alpha2": "EG", - "alpha3": "EGY", - "numeric": "818" + "name": "Egypt", + "alpha2": "EG", + "alpha3": "EGY", + "numeric": "818" }, { - "name": "El Salvador", - "alpha2": "SV", - "alpha3": "SLV", - "numeric": "222" + "name": "El Salvador", + "alpha2": "SV", + "alpha3": "SLV", + "numeric": "222" }, { - "name": "Equatorial Guinea", - "alpha2": "GQ", - "alpha3": "GNQ", - "numeric": "226" + "name": "Equatorial Guinea", + "alpha2": "GQ", + "alpha3": "GNQ", + "numeric": "226" }, { - "name": "Eritrea", - "alpha2": "ER", - "alpha3": "ERI", - "numeric": "232" + "name": "Eritrea", + "alpha2": "ER", + "alpha3": "ERI", + "numeric": "232" }, { - "name": "Estonia", - "alpha2": "EE", - "alpha3": "EST", - "numeric": "233" + "name": "Estonia", + "alpha2": "EE", + "alpha3": "EST", + "numeric": "233" }, { - "name": "Ethiopia", - "alpha2": "ET", - "alpha3": "ETH", - "numeric": "231" + "name": "Ethiopia", + "alpha2": "ET", + "alpha3": "ETH", + "numeric": "231" }, { - "name": "Falkland Islands (the) [Malvinas]", - "alpha2": "FK", - "alpha3": "FLK", - "numeric": "238", - "altName": "Falkland Islands (Malvinas)", - "shortName": "Falkland Islands" + "name": "Falkland Islands (the) [Malvinas]", + "alpha2": "FK", + "alpha3": "FLK", + "numeric": "238", + "altName": "Falkland Islands (Malvinas)", + "shortName": "Falkland Islands" }, { - "name": "Faroe Islands (the)", - "alpha2": "FO", - "alpha3": "FRO", - "numeric": "234", - "altName": "Faroe Islands" + "name": "Faroe Islands (the)", + "alpha2": "FO", + "alpha3": "FRO", + "numeric": "234", + "altName": "Faroe Islands" }, { - "name": "Fiji", - "alpha2": "FJ", - "alpha3": "FJI", - "numeric": "242" + "name": "Fiji", + "alpha2": "FJ", + "alpha3": "FJI", + "numeric": "242" }, { - "name": "Finland", - "alpha2": "FI", - "alpha3": "FIN", - "numeric": "246" + "name": "Finland", + "alpha2": "FI", + "alpha3": "FIN", + "numeric": "246" }, { - "name": "France", - "alpha2": "FR", - "alpha3": "FRA", - "numeric": "250" + "name": "France", + "alpha2": "FR", + "alpha3": "FRA", + "numeric": "250" }, { - "name": "French Guiana", - "alpha2": "GF", - "alpha3": "GUF", - "numeric": "254" + "name": "French Guiana", + "alpha2": "GF", + "alpha3": "GUF", + "numeric": "254" }, { - "name": "French Polynesia", - "alpha2": "PF", - "alpha3": "PYF", - "numeric": "258" + "name": "French Polynesia", + "alpha2": "PF", + "alpha3": "PYF", + "numeric": "258" }, { - "name": "French Southern Territories (the)", - "alpha2": "TF", - "alpha3": "ATF", - "numeric": "260", - "altName": "French Southern Territories" + "name": "French Southern Territories (the)", + "alpha2": "TF", + "alpha3": "ATF", + "numeric": "260", + "altName": "French Southern Territories" }, { - "name": "Gabon", - "alpha2": "GA", - "alpha3": "GAB", - "numeric": "266" + "name": "Gabon", + "alpha2": "GA", + "alpha3": "GAB", + "numeric": "266" }, { - "name": "Gambia (the)", - "alpha2": "GM", - "alpha3": "GMB", - "numeric": "270", - "altName": "Gambia" + "name": "Gambia (the)", + "alpha2": "GM", + "alpha3": "GMB", + "numeric": "270", + "altName": "Gambia" }, { - "name": "Georgia", - "alpha2": "GE", - "alpha3": "GEO", - "numeric": "268" + "name": "Georgia", + "alpha2": "GE", + "alpha3": "GEO", + "numeric": "268" }, { - "name": "Germany", - "alpha2": "DE", - "alpha3": "DEU", - "numeric": "276" + "name": "Germany", + "alpha2": "DE", + "alpha3": "DEU", + "numeric": "276" }, { - "name": "Ghana", - "alpha2": "GH", - "alpha3": "GHA", - "numeric": "288" + "name": "Ghana", + "alpha2": "GH", + "alpha3": "GHA", + "numeric": "288" }, { - "name": "Gibraltar", - "alpha2": "GI", - "alpha3": "GIB", - "numeric": "292" + "name": "Gibraltar", + "alpha2": "GI", + "alpha3": "GIB", + "numeric": "292" }, { - "name": "Greece", - "alpha2": "GR", - "alpha3": "GRC", - "numeric": "300" + "name": "Greece", + "alpha2": "GR", + "alpha3": "GRC", + "numeric": "300" }, { - "name": "Greenland", - "alpha2": "GL", - "alpha3": "GRL", - "numeric": "304" + "name": "Greenland", + "alpha2": "GL", + "alpha3": "GRL", + "numeric": "304" }, { - "name": "Grenada", - "alpha2": "GD", - "alpha3": "GRD", - "numeric": "308" + "name": "Grenada", + "alpha2": "GD", + "alpha3": "GRD", + "numeric": "308" }, { - "name": "Guadeloupe", - "alpha2": "GP", - "alpha3": "GLP", - "numeric": "312" + "name": "Guadeloupe", + "alpha2": "GP", + "alpha3": "GLP", + "numeric": "312" }, { - "name": "Guam", - "alpha2": "GU", - "alpha3": "GUM", - "numeric": "316" + "name": "Guam", + "alpha2": "GU", + "alpha3": "GUM", + "numeric": "316" }, { - "name": "Guatemala", - "alpha2": "GT", - "alpha3": "GTM", - "numeric": "320" + "name": "Guatemala", + "alpha2": "GT", + "alpha3": "GTM", + "numeric": "320" }, { - "name": "Guernsey", - "alpha2": "GG", - "alpha3": "GGY", - "numeric": "831" + "name": "Guernsey", + "alpha2": "GG", + "alpha3": "GGY", + "numeric": "831" }, { - "name": "Guinea", - "alpha2": "GN", - "alpha3": "GIN", - "numeric": "324" + "name": "Guinea", + "alpha2": "GN", + "alpha3": "GIN", + "numeric": "324" }, { - "name": "Guinea-Bissau", - "alpha2": "GW", - "alpha3": "GNB", - "numeric": "624" + "name": "Guinea-Bissau", + "alpha2": "GW", + "alpha3": "GNB", + "numeric": "624" }, { - "name": "Guyana", - "alpha2": "GY", - "alpha3": "GUY", - "numeric": "328" + "name": "Guyana", + "alpha2": "GY", + "alpha3": "GUY", + "numeric": "328" }, { - "name": "Haiti", - "alpha2": "HT", - "alpha3": "HTI", - "numeric": "332" + "name": "Haiti", + "alpha2": "HT", + "alpha3": "HTI", + "numeric": "332" }, { - "name": "Heard Island and McDonald Islands", - "alpha2": "HM", - "alpha3": "HMD", - "numeric": "334", - "altName": "Heard and Mcdonald Islands" + "name": "Heard Island and McDonald Islands", + "alpha2": "HM", + "alpha3": "HMD", + "numeric": "334", + "altName": "Heard and Mcdonald Islands" }, { - "name": "Holy See (the)", - "alpha2": "VA", - "alpha3": "VAT", - "numeric": "336", - "altName": "Holy See (Vatican City State)", - "shortName": "Vatican" + "name": "Holy See (the)", + "alpha2": "VA", + "alpha3": "VAT", + "numeric": "336", + "altName": "Holy See (Vatican City State)", + "shortName": "Vatican" }, { - "name": "Honduras", - "alpha2": "HN", - "alpha3": "HND", - "numeric": "340" + "name": "Honduras", + "alpha2": "HN", + "alpha3": "HND", + "numeric": "340" }, { - "name": "Hong Kong", - "alpha2": "HK", - "alpha3": "HKG", - "numeric": "344", - "altName": "Hong Kong, SAR China" + "name": "Hong Kong", + "alpha2": "HK", + "alpha3": "HKG", + "numeric": "344", + "altName": "Hong Kong, SAR China" }, { - "name": "Hungary", - "alpha2": "HU", - "alpha3": "HUN", - "numeric": "348" + "name": "Hungary", + "alpha2": "HU", + "alpha3": "HUN", + "numeric": "348" }, { - "name": "Iceland", - "alpha2": "IS", - "alpha3": "ISL", - "numeric": "352" + "name": "Iceland", + "alpha2": "IS", + "alpha3": "ISL", + "numeric": "352" }, { - "name": "India", - "alpha2": "IN", - "alpha3": "IND", - "numeric": "356" + "name": "India", + "alpha2": "IN", + "alpha3": "IND", + "numeric": "356" }, { - "name": "Indonesia", - "alpha2": "ID", - "alpha3": "IDN", - "numeric": "360" + "name": "Indonesia", + "alpha2": "ID", + "alpha3": "IDN", + "numeric": "360" }, { - "name": "Iran (Islamic Republic of)", - "alpha2": "IR", - "alpha3": "IRN", - "numeric": "364", - "altName": "Iran, Islamic Republic of", - "shortName": "Iran" + "name": "Iran (Islamic Republic of)", + "alpha2": "IR", + "alpha3": "IRN", + "numeric": "364", + "altName": "Iran, Islamic Republic of", + "shortName": "Iran" }, { - "name": "Iraq", - "alpha2": "IQ", - "alpha3": "IRQ", - "numeric": "368" + "name": "Iraq", + "alpha2": "IQ", + "alpha3": "IRQ", + "numeric": "368" }, { - "name": "Ireland", - "alpha2": "IE", - "alpha3": "IRL", - "numeric": "372" + "name": "Ireland", + "alpha2": "IE", + "alpha3": "IRL", + "numeric": "372" }, { - "name": "Isle of Man", - "alpha2": "IM", - "alpha3": "IMN", - "numeric": "833" + "name": "Isle of Man", + "alpha2": "IM", + "alpha3": "IMN", + "numeric": "833" }, { - "name": "Israel", - "alpha2": "IL", - "alpha3": "ISR", - "numeric": "376" + "name": "Israel", + "alpha2": "IL", + "alpha3": "ISR", + "numeric": "376" }, { - "name": "Italy", - "alpha2": "IT", - "alpha3": "ITA", - "numeric": "380" + "name": "Italy", + "alpha2": "IT", + "alpha3": "ITA", + "numeric": "380" }, { - "name": "Jamaica", - "alpha2": "JM", - "alpha3": "JAM", - "numeric": "388" + "name": "Jamaica", + "alpha2": "JM", + "alpha3": "JAM", + "numeric": "388" }, { - "name": "Japan", - "alpha2": "JP", - "alpha3": "JPN", - "numeric": "392" + "name": "Japan", + "alpha2": "JP", + "alpha3": "JPN", + "numeric": "392" }, { - "name": "Jersey", - "alpha2": "JE", - "alpha3": "JEY", - "numeric": "832" + "name": "Jersey", + "alpha2": "JE", + "alpha3": "JEY", + "numeric": "832" }, { - "name": "Jordan", - "alpha2": "JO", - "alpha3": "JOR", - "numeric": "400" + "name": "Jordan", + "alpha2": "JO", + "alpha3": "JOR", + "numeric": "400" }, { - "name": "Kazakhstan", - "alpha2": "KZ", - "alpha3": "KAZ", - "numeric": "398" + "name": "Kazakhstan", + "alpha2": "KZ", + "alpha3": "KAZ", + "numeric": "398" }, { - "name": "Kenya", - "alpha2": "KE", - "alpha3": "KEN", - "numeric": "404" + "name": "Kenya", + "alpha2": "KE", + "alpha3": "KEN", + "numeric": "404" }, { - "name": "Kiribati", - "alpha2": "KI", - "alpha3": "KIR", - "numeric": "296" + "name": "Kiribati", + "alpha2": "KI", + "alpha3": "KIR", + "numeric": "296" }, { - "name": "Korea (the Democratic People's Republic of)", - "alpha2": "KP", - "alpha3": "PRK", - "numeric": "408", - "altName": "Korea (North)", - "shortName": "North Korea" + "name": "Korea (the Democratic People's Republic of)", + "alpha2": "KP", + "alpha3": "PRK", + "numeric": "408", + "altName": "Korea (North)", + "shortName": "North Korea" }, { - "name": "Korea (the Republic of)", - "alpha2": "KR", - "alpha3": "KOR", - "numeric": "410", - "altName": "Korea (South)", - "shortName": "South Korea" + "name": "Korea (the Republic of)", + "alpha2": "KR", + "alpha3": "KOR", + "numeric": "410", + "altName": "Korea (South)", + "shortName": "South Korea" }, { - "name": "Kuwait", - "alpha2": "KW", - "alpha3": "KWT", - "numeric": "414" + "name": "Kuwait", + "alpha2": "KW", + "alpha3": "KWT", + "numeric": "414" }, { - "name": "Kyrgyzstan", - "alpha2": "KG", - "alpha3": "KGZ", - "numeric": "417" + "name": "Kyrgyzstan", + "alpha2": "KG", + "alpha3": "KGZ", + "numeric": "417" }, { - "name": "Lao People's Democratic Republic (the)", - "alpha2": "LA", - "alpha3": "LAO", - "numeric": "418", - "altName": "Lao PDR", - "shortName": "Laos" + "name": "Lao People's Democratic Republic (the)", + "alpha2": "LA", + "alpha3": "LAO", + "numeric": "418", + "altName": "Lao PDR", + "shortName": "Laos" }, { - "name": "Latvia", - "alpha2": "LV", - "alpha3": "LVA", - "numeric": "428" + "name": "Latvia", + "alpha2": "LV", + "alpha3": "LVA", + "numeric": "428" }, { - "name": "Lebanon", - "alpha2": "LB", - "alpha3": "LBN", - "numeric": "422" + "name": "Lebanon", + "alpha2": "LB", + "alpha3": "LBN", + "numeric": "422" }, { - "name": "Lesotho", - "alpha2": "LS", - "alpha3": "LSO", - "numeric": "426" + "name": "Lesotho", + "alpha2": "LS", + "alpha3": "LSO", + "numeric": "426" }, { - "name": "Liberia", - "alpha2": "LR", - "alpha3": "LBR", - "numeric": "430" + "name": "Liberia", + "alpha2": "LR", + "alpha3": "LBR", + "numeric": "430" }, { - "name": "Libya", - "alpha2": "LY", - "alpha3": "LBY", - "numeric": "434" + "name": "Libya", + "alpha2": "LY", + "alpha3": "LBY", + "numeric": "434" }, { - "name": "Liechtenstein", - "alpha2": "LI", - "alpha3": "LIE", - "numeric": "438" + "name": "Liechtenstein", + "alpha2": "LI", + "alpha3": "LIE", + "numeric": "438" }, { - "name": "Lithuania", - "alpha2": "LT", - "alpha3": "LTU", - "numeric": "440" + "name": "Lithuania", + "alpha2": "LT", + "alpha3": "LTU", + "numeric": "440" }, { - "name": "Luxembourg", - "alpha2": "LU", - "alpha3": "LUX", - "numeric": "442" + "name": "Luxembourg", + "alpha2": "LU", + "alpha3": "LUX", + "numeric": "442" }, { - "name": "Macao", - "alpha2": "MO", - "alpha3": "MAC", - "numeric": "446", - "altName": "Macao, SAR China", - "shortName": "Macau" + "name": "Macao", + "alpha2": "MO", + "alpha3": "MAC", + "numeric": "446", + "altName": "Macao, SAR China", + "shortName": "Macau" }, { - "name": "Macedonia (the former Yugoslav Republic of)", - "alpha2": "MK", - "alpha3": "MKD", - "numeric": "807", - "altName": "Macedonia, Republic of", - "shortName": "Macedonia" + "name": "Macedonia (the former Yugoslav Republic of)", + "alpha2": "MK", + "alpha3": "MKD", + "numeric": "807", + "altName": "Macedonia, Republic of", + "shortName": "Macedonia" }, { - "name": "Madagascar", - "alpha2": "MG", - "alpha3": "MDG", - "numeric": "450" + "name": "Madagascar", + "alpha2": "MG", + "alpha3": "MDG", + "numeric": "450" }, { - "name": "Malawi", - "alpha2": "MW", - "alpha3": "MWI", - "numeric": "454" + "name": "Malawi", + "alpha2": "MW", + "alpha3": "MWI", + "numeric": "454" }, { - "name": "Malaysia", - "alpha2": "MY", - "alpha3": "MYS", - "numeric": "458" + "name": "Malaysia", + "alpha2": "MY", + "alpha3": "MYS", + "numeric": "458" }, { - "name": "Maldives", - "alpha2": "MV", - "alpha3": "MDV", - "numeric": "462" + "name": "Maldives", + "alpha2": "MV", + "alpha3": "MDV", + "numeric": "462" }, { - "name": "Mali", - "alpha2": "ML", - "alpha3": "MLI", - "numeric": "466" + "name": "Mali", + "alpha2": "ML", + "alpha3": "MLI", + "numeric": "466" }, { - "name": "Malta", - "alpha2": "MT", - "alpha3": "MLT", - "numeric": "470" + "name": "Malta", + "alpha2": "MT", + "alpha3": "MLT", + "numeric": "470" }, { - "name": "Marshall Islands (the)", - "alpha2": "MH", - "alpha3": "MHL", - "numeric": "584", - "altName": "Marshall Islands" + "name": "Marshall Islands (the)", + "alpha2": "MH", + "alpha3": "MHL", + "numeric": "584", + "altName": "Marshall Islands" }, { - "name": "Martinique", - "alpha2": "MQ", - "alpha3": "MTQ", - "numeric": "474" + "name": "Martinique", + "alpha2": "MQ", + "alpha3": "MTQ", + "numeric": "474" }, { - "name": "Mauritania", - "alpha2": "MR", - "alpha3": "MRT", - "numeric": "478" + "name": "Mauritania", + "alpha2": "MR", + "alpha3": "MRT", + "numeric": "478" }, { - "name": "Mauritius", - "alpha2": "MU", - "alpha3": "MUS", - "numeric": "480" + "name": "Mauritius", + "alpha2": "MU", + "alpha3": "MUS", + "numeric": "480" }, { - "name": "Mayotte", - "alpha2": "YT", - "alpha3": "MYT", - "numeric": "175" + "name": "Mayotte", + "alpha2": "YT", + "alpha3": "MYT", + "numeric": "175" }, { - "name": "Mexico", - "alpha2": "MX", - "alpha3": "MEX", - "numeric": "484" + "name": "Mexico", + "alpha2": "MX", + "alpha3": "MEX", + "numeric": "484" }, { - "name": "Micronesia (Federated States of)", - "alpha2": "FM", - "alpha3": "FSM", - "numeric": "583", - "altName": "Micronesia, Federated States of", - "shortName": "Micronesia" + "name": "Micronesia (Federated States of)", + "alpha2": "FM", + "alpha3": "FSM", + "numeric": "583", + "altName": "Micronesia, Federated States of", + "shortName": "Micronesia" }, { - "name": "Moldova (the Republic of)", - "alpha2": "MD", - "alpha3": "MDA", - "numeric": "498", - "altName": "Moldova" + "name": "Moldova (the Republic of)", + "alpha2": "MD", + "alpha3": "MDA", + "numeric": "498", + "altName": "Moldova" }, { - "name": "Monaco", - "alpha2": "MC", - "alpha3": "MCO", - "numeric": "492" + "name": "Monaco", + "alpha2": "MC", + "alpha3": "MCO", + "numeric": "492" }, { - "name": "Mongolia", - "alpha2": "MN", - "alpha3": "MNG", - "numeric": "496" + "name": "Mongolia", + "alpha2": "MN", + "alpha3": "MNG", + "numeric": "496" }, { - "name": "Montenegro", - "alpha2": "ME", - "alpha3": "MNE", - "numeric": "499" + "name": "Montenegro", + "alpha2": "ME", + "alpha3": "MNE", + "numeric": "499" }, { - "name": "Montserrat", - "alpha2": "MS", - "alpha3": "MSR", - "numeric": "500" + "name": "Montserrat", + "alpha2": "MS", + "alpha3": "MSR", + "numeric": "500" }, { - "name": "Morocco", - "alpha2": "MA", - "alpha3": "MAR", - "numeric": "504" + "name": "Morocco", + "alpha2": "MA", + "alpha3": "MAR", + "numeric": "504" }, { - "name": "Mozambique", - "alpha2": "MZ", - "alpha3": "MOZ", - "numeric": "508" + "name": "Mozambique", + "alpha2": "MZ", + "alpha3": "MOZ", + "numeric": "508" }, { - "name": "Myanmar", - "alpha2": "MM", - "alpha3": "MMR", - "numeric": "104" + "name": "Myanmar", + "alpha2": "MM", + "alpha3": "MMR", + "numeric": "104" }, { - "name": "Namibia", - "alpha2": "NA", - "alpha3": "NAM", - "numeric": "516" + "name": "Namibia", + "alpha2": "NA", + "alpha3": "NAM", + "numeric": "516" }, { - "name": "Nauru", - "alpha2": "NR", - "alpha3": "NRU", - "numeric": "520" + "name": "Nauru", + "alpha2": "NR", + "alpha3": "NRU", + "numeric": "520" }, { - "name": "Nepal", - "alpha2": "NP", - "alpha3": "NPL", - "numeric": "524" + "name": "Nepal", + "alpha2": "NP", + "alpha3": "NPL", + "numeric": "524" }, { - "name": "Netherlands (the)", - "alpha2": "NL", - "alpha3": "NLD", - "numeric": "528", - "altName": "Netherlands" + "name": "Netherlands (the)", + "alpha2": "NL", + "alpha3": "NLD", + "numeric": "528", + "altName": "Netherlands" }, { - "name": "New Caledonia", - "alpha2": "NC", - "alpha3": "NCL", - "numeric": "540" + "name": "New Caledonia", + "alpha2": "NC", + "alpha3": "NCL", + "numeric": "540" }, { - "name": "New Zealand", - "alpha2": "NZ", - "alpha3": "NZL", - "numeric": "554" + "name": "New Zealand", + "alpha2": "NZ", + "alpha3": "NZL", + "numeric": "554" }, { - "name": "Nicaragua", - "alpha2": "NI", - "alpha3": "NIC", - "numeric": "558" + "name": "Nicaragua", + "alpha2": "NI", + "alpha3": "NIC", + "numeric": "558" }, { - "name": "Niger (the)", - "alpha2": "NE", - "alpha3": "NER", - "numeric": "562", - "altName": "Niger" + "name": "Niger (the)", + "alpha2": "NE", + "alpha3": "NER", + "numeric": "562", + "altName": "Niger" }, { - "name": "Nigeria", - "alpha2": "NG", - "alpha3": "NGA", - "numeric": "566" + "name": "Nigeria", + "alpha2": "NG", + "alpha3": "NGA", + "numeric": "566" }, { - "name": "Niue", - "alpha2": "NU", - "alpha3": "NIU", - "numeric": "570" + "name": "Niue", + "alpha2": "NU", + "alpha3": "NIU", + "numeric": "570" }, { - "name": "Norfolk Island", - "alpha2": "NF", - "alpha3": "NFK", - "numeric": "574" + "name": "Norfolk Island", + "alpha2": "NF", + "alpha3": "NFK", + "numeric": "574" }, { - "name": "Northern Mariana Islands (the)", - "alpha2": "MP", - "alpha3": "MNP", - "numeric": "580", - "altName": "Northern Mariana Islands" + "name": "Northern Mariana Islands (the)", + "alpha2": "MP", + "alpha3": "MNP", + "numeric": "580", + "altName": "Northern Mariana Islands" }, { - "name": "Norway", - "alpha2": "NO", - "alpha3": "NOR", - "numeric": "578" + "name": "Norway", + "alpha2": "NO", + "alpha3": "NOR", + "numeric": "578" }, { - "name": "Oman", - "alpha2": "OM", - "alpha3": "OMN", - "numeric": "512" + "name": "Oman", + "alpha2": "OM", + "alpha3": "OMN", + "numeric": "512" }, { - "name": "Pakistan", - "alpha2": "PK", - "alpha3": "PAK", - "numeric": "586" + "name": "Pakistan", + "alpha2": "PK", + "alpha3": "PAK", + "numeric": "586" }, { - "name": "Palau", - "alpha2": "PW", - "alpha3": "PLW", - "numeric": "585" + "name": "Palau", + "alpha2": "PW", + "alpha3": "PLW", + "numeric": "585" }, { - "name": "Palestine, State of", - "alpha2": "PS", - "alpha3": "PSE", - "numeric": "275", - "altName": "Palestinian Territory", - "shortName": "Palestine" + "name": "Palestine, State of", + "alpha2": "PS", + "alpha3": "PSE", + "numeric": "275", + "altName": "Palestinian Territory", + "shortName": "Palestine" }, { - "name": "Panama", - "alpha2": "PA", - "alpha3": "PAN", - "numeric": "591" + "name": "Panama", + "alpha2": "PA", + "alpha3": "PAN", + "numeric": "591" }, { - "name": "Papua New Guinea", - "alpha2": "PG", - "alpha3": "PNG", - "numeric": "598" + "name": "Papua New Guinea", + "alpha2": "PG", + "alpha3": "PNG", + "numeric": "598" }, { - "name": "Paraguay", - "alpha2": "PY", - "alpha3": "PRY", - "numeric": "600" + "name": "Paraguay", + "alpha2": "PY", + "alpha3": "PRY", + "numeric": "600" }, { - "name": "Peru", - "alpha2": "PE", - "alpha3": "PER", - "numeric": "604" + "name": "Peru", + "alpha2": "PE", + "alpha3": "PER", + "numeric": "604" }, { - "name": "Philippines (the)", - "alpha2": "PH", - "alpha3": "PHL", - "numeric": "608", - "altName": "Philippines" + "name": "Philippines (the)", + "alpha2": "PH", + "alpha3": "PHL", + "numeric": "608", + "altName": "Philippines" }, { - "name": "Pitcairn", - "alpha2": "PN", - "alpha3": "PCN", - "numeric": "612" + "name": "Pitcairn", + "alpha2": "PN", + "alpha3": "PCN", + "numeric": "612" }, { - "name": "Poland", - "alpha2": "PL", - "alpha3": "POL", - "numeric": "616" + "name": "Poland", + "alpha2": "PL", + "alpha3": "POL", + "numeric": "616" }, { - "name": "Portugal", - "alpha2": "PT", - "alpha3": "PRT", - "numeric": "620" + "name": "Portugal", + "alpha2": "PT", + "alpha3": "PRT", + "numeric": "620" }, { - "name": "Puerto Rico", - "alpha2": "PR", - "alpha3": "PRI", - "numeric": "630" + "name": "Puerto Rico", + "alpha2": "PR", + "alpha3": "PRI", + "numeric": "630" }, { - "name": "Qatar", - "alpha2": "QA", - "alpha3": "QAT", - "numeric": "634" + "name": "Qatar", + "alpha2": "QA", + "alpha3": "QAT", + "numeric": "634" }, { - "name": "Réunion", - "alpha2": "RE", - "alpha3": "REU", - "numeric": "638", - "shortName": "Reunion" + "name": "Réunion", + "alpha2": "RE", + "alpha3": "REU", + "numeric": "638", + "shortName": "Reunion" }, { - "name": "Romania", - "alpha2": "RO", - "alpha3": "ROU", - "numeric": "642" + "name": "Romania", + "alpha2": "RO", + "alpha3": "ROU", + "numeric": "642" }, { - "name": "Russian Federation (the)", - "alpha2": "RU", - "alpha3": "RUS", - "numeric": "643", - "altName": "Russian Federation", - "shortName": "Russia" + "name": "Russian Federation (the)", + "alpha2": "RU", + "alpha3": "RUS", + "numeric": "643", + "altName": "Russian Federation", + "shortName": "Russia" }, { - "name": "Rwanda", - "alpha2": "RW", - "alpha3": "RWA", - "numeric": "646" + "name": "Rwanda", + "alpha2": "RW", + "alpha3": "RWA", + "numeric": "646" }, { - "name": "Saint Barthélemy", - "alpha2": "BL", - "alpha3": "BLM", - "numeric": "652", - "altName": "Saint-Barthélemy", - "shortName": "Saint Barthelemy" + "name": "Saint Barthélemy", + "alpha2": "BL", + "alpha3": "BLM", + "numeric": "652", + "altName": "Saint-Barthélemy", + "shortName": "Saint Barthelemy" }, { - "name": "Saint Helena, Ascension and Tristan da Cunha", - "alpha2": "SH", - "alpha3": "SHN", - "numeric": "654", - "altName": "Saint Helena" + "name": "Saint Helena, Ascension and Tristan da Cunha", + "alpha2": "SH", + "alpha3": "SHN", + "numeric": "654", + "altName": "Saint Helena" }, { - "name": "Saint Kitts and Nevis", - "alpha2": "KN", - "alpha3": "KNA", - "numeric": "659" + "name": "Saint Kitts and Nevis", + "alpha2": "KN", + "alpha3": "KNA", + "numeric": "659" }, { - "name": "Saint Lucia", - "alpha2": "LC", - "alpha3": "LCA", - "numeric": "662" + "name": "Saint Lucia", + "alpha2": "LC", + "alpha3": "LCA", + "numeric": "662" }, { - "name": "Saint Martin (French part)", - "alpha2": "MF", - "alpha3": "MAF", - "numeric": "663", - "altName": "Saint-Martin (French part)", - "shortName": "Saint Martin" + "name": "Saint Martin (French part)", + "alpha2": "MF", + "alpha3": "MAF", + "numeric": "663", + "altName": "Saint-Martin (French part)", + "shortName": "Saint Martin" }, { - "name": "Saint Pierre and Miquelon", - "alpha2": "PM", - "alpha3": "SPM", - "numeric": "666" + "name": "Saint Pierre and Miquelon", + "alpha2": "PM", + "alpha3": "SPM", + "numeric": "666" }, { - "name": "Saint Vincent and the Grenadines", - "alpha2": "VC", - "alpha3": "VCT", - "numeric": "670", - "altName": "Saint Vincent and Grenadines" + "name": "Saint Vincent and the Grenadines", + "alpha2": "VC", + "alpha3": "VCT", + "numeric": "670", + "altName": "Saint Vincent and Grenadines" }, { - "name": "Samoa", - "alpha2": "WS", - "alpha3": "WSM", - "numeric": "882" + "name": "Samoa", + "alpha2": "WS", + "alpha3": "WSM", + "numeric": "882" }, { - "name": "San Marino", - "alpha2": "SM", - "alpha3": "SMR", - "numeric": "674" + "name": "San Marino", + "alpha2": "SM", + "alpha3": "SMR", + "numeric": "674" }, { - "name": "Sao Tome and Principe", - "alpha2": "ST", - "alpha3": "STP", - "numeric": "678" + "name": "Sao Tome and Principe", + "alpha2": "ST", + "alpha3": "STP", + "numeric": "678" }, { - "name": "Saudi Arabia", - "alpha2": "SA", - "alpha3": "SAU", - "numeric": "682" + "name": "Saudi Arabia", + "alpha2": "SA", + "alpha3": "SAU", + "numeric": "682" }, { - "name": "Senegal", - "alpha2": "SN", - "alpha3": "SEN", - "numeric": "686" + "name": "Senegal", + "alpha2": "SN", + "alpha3": "SEN", + "numeric": "686" }, { - "name": "Serbia", - "alpha2": "RS", - "alpha3": "SRB", - "numeric": "688" + "name": "Serbia", + "alpha2": "RS", + "alpha3": "SRB", + "numeric": "688" }, { - "name": "Seychelles", - "alpha2": "SC", - "alpha3": "SYC", - "numeric": "690" + "name": "Seychelles", + "alpha2": "SC", + "alpha3": "SYC", + "numeric": "690" }, { - "name": "Sierra Leone", - "alpha2": "SL", - "alpha3": "SLE", - "numeric": "694" + "name": "Sierra Leone", + "alpha2": "SL", + "alpha3": "SLE", + "numeric": "694" }, { - "name": "Singapore", - "alpha2": "SG", - "alpha3": "SGP", - "numeric": "702" + "name": "Singapore", + "alpha2": "SG", + "alpha3": "SGP", + "numeric": "702" }, { - "name": "Sint Maarten (Dutch part)", - "alpha2": "SX", - "alpha3": "SXM", - "numeric": "534", - "shortName": "Sint Maarten" + "name": "Sint Maarten (Dutch part)", + "alpha2": "SX", + "alpha3": "SXM", + "numeric": "534", + "shortName": "Sint Maarten" }, { - "name": "Slovakia", - "alpha2": "SK", - "alpha3": "SVK", - "numeric": "703" + "name": "Slovakia", + "alpha2": "SK", + "alpha3": "SVK", + "numeric": "703" }, { - "name": "Slovenia", - "alpha2": "SI", - "alpha3": "SVN", - "numeric": "705" + "name": "Slovenia", + "alpha2": "SI", + "alpha3": "SVN", + "numeric": "705" }, { - "name": "Solomon Islands", - "alpha2": "SB", - "alpha3": "SLB", - "numeric": "090" + "name": "Solomon Islands", + "alpha2": "SB", + "alpha3": "SLB", + "numeric": "090" }, { - "name": "Somalia", - "alpha2": "SO", - "alpha3": "SOM", - "numeric": "706" + "name": "Somalia", + "alpha2": "SO", + "alpha3": "SOM", + "numeric": "706" }, { - "name": "South Africa", - "alpha2": "ZA", - "alpha3": "ZAF", - "numeric": "710" + "name": "South Africa", + "alpha2": "ZA", + "alpha3": "ZAF", + "numeric": "710" }, { - "name": "South Georgia and the South Sandwich Islands", - "alpha2": "GS", - "alpha3": "SGS", - "numeric": "239" + "name": "South Georgia and the South Sandwich Islands", + "alpha2": "GS", + "alpha3": "SGS", + "numeric": "239" }, { - "name": "South Sudan", - "alpha2": "SS", - "alpha3": "SSD", - "numeric": "728" + "name": "South Sudan", + "alpha2": "SS", + "alpha3": "SSD", + "numeric": "728" }, { - "name": "Spain", - "alpha2": "ES", - "alpha3": "ESP", - "numeric": "724" + "name": "Spain", + "alpha2": "ES", + "alpha3": "ESP", + "numeric": "724" }, { - "name": "Sri Lanka", - "alpha2": "LK", - "alpha3": "LKA", - "numeric": "144" + "name": "Sri Lanka", + "alpha2": "LK", + "alpha3": "LKA", + "numeric": "144" }, { - "name": "Sudan (the)", - "alpha2": "SD", - "alpha3": "SDN", - "numeric": "729", - "altName": "Sudan" + "name": "Sudan (the)", + "alpha2": "SD", + "alpha3": "SDN", + "numeric": "729", + "altName": "Sudan" }, { - "name": "Suriname", - "alpha2": "SR", - "alpha3": "SUR", - "numeric": "740" + "name": "Suriname", + "alpha2": "SR", + "alpha3": "SUR", + "numeric": "740" }, { - "name": "Svalbard and Jan Mayen", - "alpha2": "SJ", - "alpha3": "SJM", - "numeric": "744", - "altName": "Svalbard and Jan Mayen Islands" + "name": "Svalbard and Jan Mayen", + "alpha2": "SJ", + "alpha3": "SJM", + "numeric": "744", + "altName": "Svalbard and Jan Mayen Islands" }, { - "name": "Swaziland", - "alpha2": "SZ", - "alpha3": "SWZ", - "numeric": "748" + "name": "Swaziland", + "alpha2": "SZ", + "alpha3": "SWZ", + "numeric": "748" }, { - "name": "Sweden", - "alpha2": "SE", - "alpha3": "SWE", - "numeric": "752" + "name": "Sweden", + "alpha2": "SE", + "alpha3": "SWE", + "numeric": "752" }, { - "name": "Switzerland", - "alpha2": "CH", - "alpha3": "CHE", - "numeric": "756" + "name": "Switzerland", + "alpha2": "CH", + "alpha3": "CHE", + "numeric": "756" }, { - "name": "Syrian Arab Republic", - "alpha2": "SY", - "alpha3": "SYR", - "numeric": "760", - "altName": "Syrian Arab Republic (Syria)", - "shortName": "Syria" + "name": "Syrian Arab Republic", + "alpha2": "SY", + "alpha3": "SYR", + "numeric": "760", + "altName": "Syrian Arab Republic (Syria)", + "shortName": "Syria" }, { - "name": "Taiwan (Province of China)", - "alpha2": "TW", - "alpha3": "TWN", - "numeric": "158", - "altName": "Taiwan, Republic of China", - "shortName": "Taiwan" + "name": "Taiwan (Province of China)", + "alpha2": "TW", + "alpha3": "TWN", + "numeric": "158", + "altName": "Taiwan, Republic of China", + "shortName": "Taiwan" }, { - "name": "Tajikistan", - "alpha2": "TJ", - "alpha3": "TJK", - "numeric": "762" + "name": "Tajikistan", + "alpha2": "TJ", + "alpha3": "TJK", + "numeric": "762" }, { - "name": "Tanzania, United Republic of", - "alpha2": "TZ", - "alpha3": "TZA", - "numeric": "834", - "shortName": "Tanzania" + "name": "Tanzania, United Republic of", + "alpha2": "TZ", + "alpha3": "TZA", + "numeric": "834", + "shortName": "Tanzania" }, { - "name": "Thailand", - "alpha2": "TH", - "alpha3": "THA", - "numeric": "764" + "name": "Thailand", + "alpha2": "TH", + "alpha3": "THA", + "numeric": "764" }, { - "name": "Timor-Leste", - "alpha2": "TL", - "alpha3": "TLS", - "numeric": "626", - "shortName": "East Timor" + "name": "Timor-Leste", + "alpha2": "TL", + "alpha3": "TLS", + "numeric": "626", + "shortName": "East Timor" }, { - "name": "Togo", - "alpha2": "TG", - "alpha3": "TGO", - "numeric": "768" + "name": "Togo", + "alpha2": "TG", + "alpha3": "TGO", + "numeric": "768" }, { - "name": "Tokelau", - "alpha2": "TK", - "alpha3": "TKL", - "numeric": "772" + "name": "Tokelau", + "alpha2": "TK", + "alpha3": "TKL", + "numeric": "772" }, { - "name": "Tonga", - "alpha2": "TO", - "alpha3": "TON", - "numeric": "776" + "name": "Tonga", + "alpha2": "TO", + "alpha3": "TON", + "numeric": "776" }, { - "name": "Trinidad and Tobago", - "alpha2": "TT", - "alpha3": "TTO", - "numeric": "780" + "name": "Trinidad and Tobago", + "alpha2": "TT", + "alpha3": "TTO", + "numeric": "780" }, { - "name": "Tunisia", - "alpha2": "TN", - "alpha3": "TUN", - "numeric": "788" + "name": "Tunisia", + "alpha2": "TN", + "alpha3": "TUN", + "numeric": "788" }, { - "name": "Turkey", - "alpha2": "TR", - "alpha3": "TUR", - "numeric": "792" + "name": "Turkey", + "alpha2": "TR", + "alpha3": "TUR", + "numeric": "792" }, { - "name": "Turkmenistan", - "alpha2": "TM", - "alpha3": "TKM", - "numeric": "795" + "name": "Turkmenistan", + "alpha2": "TM", + "alpha3": "TKM", + "numeric": "795" }, { - "name": "Turks and Caicos Islands (the)", - "alpha2": "TC", - "alpha3": "TCA", - "numeric": "796", - "altName": "Turks and Caicos Islands" + "name": "Turks and Caicos Islands (the)", + "alpha2": "TC", + "alpha3": "TCA", + "numeric": "796", + "altName": "Turks and Caicos Islands" }, { - "name": "Tuvalu", - "alpha2": "TV", - "alpha3": "TUV", - "numeric": "798" + "name": "Tuvalu", + "alpha2": "TV", + "alpha3": "TUV", + "numeric": "798" }, { - "name": "Uganda", - "alpha2": "UG", - "alpha3": "UGA", - "numeric": "800" + "name": "Uganda", + "alpha2": "UG", + "alpha3": "UGA", + "numeric": "800" }, { - "name": "Ukraine", - "alpha2": "UA", - "alpha3": "UKR", - "numeric": "804" + "name": "Ukraine", + "alpha2": "UA", + "alpha3": "UKR", + "numeric": "804" }, { - "name": "United Arab Emirates (the)", - "alpha2": "AE", - "alpha3": "ARE", - "numeric": "784", - "altName": "United Arab Emirates" + "name": "United Arab Emirates (the)", + "alpha2": "AE", + "alpha3": "ARE", + "numeric": "784", + "altName": "United Arab Emirates" }, { - "name": "United Kingdom of Great Britain and Northern Ireland (the)", - "alpha2": "GB", - "alpha3": "GBR", - "numeric": "826", - "altName": "United Kingdom" + "name": "United Kingdom of Great Britain and Northern Ireland (the)", + "alpha2": "GB", + "alpha3": "GBR", + "numeric": "826", + "altName": "United Kingdom" }, { - "name": "United States Minor Outlying Islands (the)", - "alpha2": "UM", - "alpha3": "UMI", - "numeric": "581", - "altName": "US Minor Outlying Islands" + "name": "United States Minor Outlying Islands (the)", + "alpha2": "UM", + "alpha3": "UMI", + "numeric": "581", + "altName": "US Minor Outlying Islands" }, { - "name": "United States of America (the)", - "alpha2": "US", - "alpha3": "USA", - "numeric": "840", - "altName": "United States of America", - "shortName": "United States" + "name": "United States of America (the)", + "alpha2": "US", + "alpha3": "USA", + "numeric": "840", + "altName": "United States of America", + "shortName": "United States" }, { - "name": "Uruguay", - "alpha2": "UY", - "alpha3": "URY", - "numeric": "858" + "name": "Uruguay", + "alpha2": "UY", + "alpha3": "URY", + "numeric": "858" }, { - "name": "Uzbekistan", - "alpha2": "UZ", - "alpha3": "UZB", - "numeric": "860" + "name": "Uzbekistan", + "alpha2": "UZ", + "alpha3": "UZB", + "numeric": "860" }, { - "name": "Vanuatu", - "alpha2": "VU", - "alpha3": "VUT", - "numeric": "548" + "name": "Vanuatu", + "alpha2": "VU", + "alpha3": "VUT", + "numeric": "548" }, { - "name": "Venezuela (Bolivarian Republic of)", - "alpha2": "VE", - "alpha3": "VEN", - "numeric": "862", - "altName": "Venezuela (Bolivarian Republic)", - "shortName": "Venezuela" + "name": "Venezuela (Bolivarian Republic of)", + "alpha2": "VE", + "alpha3": "VEN", + "numeric": "862", + "altName": "Venezuela (Bolivarian Republic)", + "shortName": "Venezuela" }, { - "name": "Viet Nam", - "alpha2": "VN", - "alpha3": "VNM", - "numeric": "704", - "shortName": "Vietnam" + "name": "Viet Nam", + "alpha2": "VN", + "alpha3": "VNM", + "numeric": "704", + "shortName": "Vietnam" }, { - "name": "Virgin Islands (British)", - "alpha2": "VG", - "alpha3": "VGB", - "numeric": "092", - "altName": "British Virgin Islands" + "name": "Virgin Islands (British)", + "alpha2": "VG", + "alpha3": "VGB", + "numeric": "092", + "altName": "British Virgin Islands" }, { - "name": "Virgin Islands (U.S.)", - "alpha2": "VI", - "alpha3": "VIR", - "numeric": "850", - "altName": "Virgin Islands, US", - "shortName": "U.S. Virgin Islands" + "name": "Virgin Islands (U.S.)", + "alpha2": "VI", + "alpha3": "VIR", + "numeric": "850", + "altName": "Virgin Islands, US", + "shortName": "U.S. Virgin Islands" }, { - "name": "Wallis and Futuna", - "alpha2": "WF", - "alpha3": "WLF", - "numeric": "876", - "altName": "Wallis and Futuna Islands" + "name": "Wallis and Futuna", + "alpha2": "WF", + "alpha3": "WLF", + "numeric": "876", + "altName": "Wallis and Futuna Islands" }, { - "name": "Western Sahara*", - "alpha2": "EH", - "alpha3": "ESH", - "numeric": "732", - "altName": "Western Sahara" + "name": "Western Sahara*", + "alpha2": "EH", + "alpha3": "ESH", + "numeric": "732", + "altName": "Western Sahara" }, { - "name": "Yemen", - "alpha2": "YE", - "alpha3": "YEM", - "numeric": "887" + "name": "Yemen", + "alpha2": "YE", + "alpha3": "YEM", + "numeric": "887" }, { - "name": "Zambia", - "alpha2": "ZM", - "alpha3": "ZMB", - "numeric": "894" + "name": "Zambia", + "alpha2": "ZM", + "alpha3": "ZMB", + "numeric": "894" }, { - "name": "Zimbabwe", - "alpha2": "ZW", - "alpha3": "ZWE", - "numeric": "716" + "name": "Zimbabwe", + "alpha2": "ZW", + "alpha3": "ZWE", + "numeric": "716" } - ] as IDataObject[]; +] as IDataObject[]; diff --git a/packages/nodes-base/nodes/InvoiceNinja/InvoiceNinja.node.ts b/packages/nodes-base/nodes/InvoiceNinja/InvoiceNinja.node.ts index b1be5bdcd..3979e165c 100644 --- a/packages/nodes-base/nodes/InvoiceNinja/InvoiceNinja.node.ts +++ b/packages/nodes-base/nodes/InvoiceNinja/InvoiceNinja.node.ts @@ -3,8 +3,8 @@ import { } from 'n8n-core'; import { IDataObject, - INodeExecutionData, ILoadOptionsFunctions, + INodeExecutionData, INodePropertyOptions, INodeType, INodeTypeDescription, diff --git a/packages/nodes-base/nodes/InvoiceNinja/InvoiceNinjaTrigger.node.ts b/packages/nodes-base/nodes/InvoiceNinja/InvoiceNinjaTrigger.node.ts index f84982c0f..3833dddcf 100644 --- a/packages/nodes-base/nodes/InvoiceNinja/InvoiceNinjaTrigger.node.ts +++ b/packages/nodes-base/nodes/InvoiceNinja/InvoiceNinjaTrigger.node.ts @@ -4,8 +4,8 @@ import { } from 'n8n-core'; import { - INodeTypeDescription, INodeType, + INodeTypeDescription, IWebhookResponseData, } from 'n8n-workflow'; diff --git a/packages/nodes-base/nodes/Jira/GenericFunctions.ts b/packages/nodes-base/nodes/Jira/GenericFunctions.ts index 88e247f1c..4d1692dc7 100644 --- a/packages/nodes-base/nodes/Jira/GenericFunctions.ts +++ b/packages/nodes-base/nodes/Jira/GenericFunctions.ts @@ -10,8 +10,8 @@ import { } from 'n8n-core'; import { - IDataObject, ICredentialDataDecryptedObject, + IDataObject, } from 'n8n-workflow'; export async function jiraSoftwareCloudApiRequest(this: IHookFunctions | IExecuteFunctions | IExecuteSingleFunctions | ILoadOptionsFunctions, endpoint: string, method: string, body: any = {}, query?: IDataObject, uri?: string): Promise { // tslint:disable-line:no-any diff --git a/packages/nodes-base/nodes/Jira/Jira.node.ts b/packages/nodes-base/nodes/Jira/Jira.node.ts index e1e21ec01..c41581412 100644 --- a/packages/nodes-base/nodes/Jira/Jira.node.ts +++ b/packages/nodes-base/nodes/Jira/Jira.node.ts @@ -18,8 +18,8 @@ import { } from './GenericFunctions'; import { - issueOperations, issueFields, + issueOperations, } from './IssueDescription'; import { diff --git a/packages/nodes-base/nodes/Jira/JiraTrigger.node.ts b/packages/nodes-base/nodes/Jira/JiraTrigger.node.ts index 41ecaecc3..bdda07bb2 100644 --- a/packages/nodes-base/nodes/Jira/JiraTrigger.node.ts +++ b/packages/nodes-base/nodes/Jira/JiraTrigger.node.ts @@ -5,16 +5,16 @@ import { import { IDataObject, - INodeTypeDescription, INodeType, + INodeTypeDescription, IWebhookResponseData, } from 'n8n-workflow'; import { - jiraSoftwareCloudApiRequest, + allEvents, eventExists, getId, - allEvents, + jiraSoftwareCloudApiRequest, } from './GenericFunctions'; import * as queryString from 'querystring'; diff --git a/packages/nodes-base/nodes/JotForm/JotFormTrigger.node.ts b/packages/nodes-base/nodes/JotForm/JotFormTrigger.node.ts index 9d05edcac..d03406584 100644 --- a/packages/nodes-base/nodes/JotForm/JotFormTrigger.node.ts +++ b/packages/nodes-base/nodes/JotForm/JotFormTrigger.node.ts @@ -9,8 +9,8 @@ import { IDataObject, ILoadOptionsFunctions, INodePropertyOptions, - INodeTypeDescription, INodeType, + INodeTypeDescription, IWebhookResponseData, } from 'n8n-workflow'; diff --git a/packages/nodes-base/nodes/Keap/Keap.node.ts b/packages/nodes-base/nodes/Keap/Keap.node.ts index 3ba98cda7..59692d0df 100644 --- a/packages/nodes-base/nodes/Keap/Keap.node.ts +++ b/packages/nodes-base/nodes/Keap/Keap.node.ts @@ -3,13 +3,13 @@ import { } from 'n8n-core'; import { - IDataObject, - INodeExecutionData, - INodeTypeDescription, - INodeType, - ILoadOptionsFunctions, - INodePropertyOptions, IBinaryKeyData, + IDataObject, + ILoadOptionsFunctions, + INodeExecutionData, + INodePropertyOptions, + INodeType, + INodeTypeDescription, } from 'n8n-workflow'; import { @@ -19,57 +19,57 @@ import { } from './GenericFunctions'; import { - contactOperations, contactFields, + contactOperations, } from './ContactDescription'; import { - contactNoteOperations, contactNoteFields, + contactNoteOperations, } from './ContactNoteDescription'; import { - contactTagOperations, contactTagFields, + contactTagOperations, } from './ContactTagDescription'; import { - ecommerceOrderOperations, ecommerceOrderFields, + ecommerceOrderOperations, } from './EcommerceOrderDescripion'; import { - ecommerceProductOperations, ecommerceProductFields, + ecommerceProductOperations, } from './EcommerceProductDescription'; import { - emailOperations, emailFields, + emailOperations, } from './EmailDescription'; import { - fileOperations, fileFields, + fileOperations, } from './FileDescription'; import { - companyOperations, companyFields, + companyOperations, } from './CompanyDescription'; import { - IContact, IAddress, - IFax, + IContact, IEmailContact, - ISocialAccount, + IFax, IPhone, + ISocialAccount, } from './ConctactInterface'; import { - IEmail, IAttachment, + IEmail, } from './EmaiIInterface'; import { diff --git a/packages/nodes-base/nodes/Keap/KeapTrigger.node.ts b/packages/nodes-base/nodes/Keap/KeapTrigger.node.ts index c5818165a..fe608e58a 100644 --- a/packages/nodes-base/nodes/Keap/KeapTrigger.node.ts +++ b/packages/nodes-base/nodes/Keap/KeapTrigger.node.ts @@ -5,11 +5,11 @@ import { import { IDataObject, - INodeTypeDescription, - INodeType, - IWebhookResponseData, ILoadOptionsFunctions, INodePropertyOptions, + INodeType, + INodeTypeDescription, + IWebhookResponseData, } from 'n8n-workflow'; import { diff --git a/packages/nodes-base/nodes/LinkFish/LinkFish.node.ts b/packages/nodes-base/nodes/LinkFish/LinkFish.node.ts index 345124492..5d97ce834 100644 --- a/packages/nodes-base/nodes/LinkFish/LinkFish.node.ts +++ b/packages/nodes-base/nodes/LinkFish/LinkFish.node.ts @@ -1,9 +1,9 @@ import { IExecuteSingleFunctions } from 'n8n-core'; import { IDataObject, - INodeTypeDescription, INodeExecutionData, INodeType, + INodeTypeDescription, } from 'n8n-workflow'; export class LinkFish implements INodeType { diff --git a/packages/nodes-base/nodes/LinkedIn/LinkedIn.node.ts b/packages/nodes-base/nodes/LinkedIn/LinkedIn.node.ts index 6220b6771..919d5f48d 100644 --- a/packages/nodes-base/nodes/LinkedIn/LinkedIn.node.ts +++ b/packages/nodes-base/nodes/LinkedIn/LinkedIn.node.ts @@ -1,14 +1,20 @@ -import { IExecuteFunctions, BINARY_ENCODING } from 'n8n-core'; +import { + BINARY_ENCODING, + IExecuteFunctions, +} from 'n8n-core'; import { IDataObject, - INodeTypeDescription, - INodeExecutionData, - INodeType, ILoadOptionsFunctions, + INodeExecutionData, INodePropertyOptions, + INodeType, + INodeTypeDescription, } from 'n8n-workflow'; import { linkedInApiRequest } from './GenericFunctions'; -import { postOperations, postFields } from './PostDescription'; +import { + postFields, + postOperations, +} from './PostDescription'; export class LinkedIn implements INodeType { description: INodeTypeDescription = { diff --git a/packages/nodes-base/nodes/Mailchimp/MailchimpTrigger.node.ts b/packages/nodes-base/nodes/Mailchimp/MailchimpTrigger.node.ts index 870078dc0..1d3794bea 100644 --- a/packages/nodes-base/nodes/Mailchimp/MailchimpTrigger.node.ts +++ b/packages/nodes-base/nodes/Mailchimp/MailchimpTrigger.node.ts @@ -5,11 +5,11 @@ import { import { IDataObject, - INodeTypeDescription, - INodeType, - IWebhookResponseData, ILoadOptionsFunctions, INodePropertyOptions, + INodeType, + INodeTypeDescription, + IWebhookResponseData, } from 'n8n-workflow'; import { mailchimpApiRequest, diff --git a/packages/nodes-base/nodes/Mailgun/Mailgun.node.ts b/packages/nodes-base/nodes/Mailgun/Mailgun.node.ts index 07c5445da..b05857ac1 100644 --- a/packages/nodes-base/nodes/Mailgun/Mailgun.node.ts +++ b/packages/nodes-base/nodes/Mailgun/Mailgun.node.ts @@ -4,9 +4,9 @@ import { } from 'n8n-core'; import { IDataObject, - INodeTypeDescription, INodeExecutionData, INodeType, + INodeTypeDescription, } from 'n8n-workflow'; diff --git a/packages/nodes-base/nodes/Mailjet/Mailjet.node.ts b/packages/nodes-base/nodes/Mailjet/Mailjet.node.ts index 841494b67..a30d7ada1 100644 --- a/packages/nodes-base/nodes/Mailjet/Mailjet.node.ts +++ b/packages/nodes-base/nodes/Mailjet/Mailjet.node.ts @@ -3,9 +3,9 @@ import { } from 'n8n-core'; import { IDataObject, - INodeTypeDescription, INodeExecutionData, INodeType, + INodeTypeDescription, } from 'n8n-workflow'; import { mailjetApiRequest, diff --git a/packages/nodes-base/nodes/Mailjet/MailjetTrigger.node.ts b/packages/nodes-base/nodes/Mailjet/MailjetTrigger.node.ts index aba063502..f25b39b0b 100644 --- a/packages/nodes-base/nodes/Mailjet/MailjetTrigger.node.ts +++ b/packages/nodes-base/nodes/Mailjet/MailjetTrigger.node.ts @@ -5,8 +5,8 @@ import { import { IDataObject, - INodeTypeDescription, INodeType, + INodeTypeDescription, IWebhookResponseData, } from 'n8n-workflow'; diff --git a/packages/nodes-base/nodes/Mattermost/Mattermost.node.ts b/packages/nodes-base/nodes/Mattermost/Mattermost.node.ts index cc7bc65ee..771059c95 100644 --- a/packages/nodes-base/nodes/Mattermost/Mattermost.node.ts +++ b/packages/nodes-base/nodes/Mattermost/Mattermost.node.ts @@ -5,10 +5,10 @@ import { import { IDataObject, ILoadOptionsFunctions, - INodeTypeDescription, - INodePropertyOptions, INodeExecutionData, + INodePropertyOptions, INodeType, + INodeTypeDescription, } from 'n8n-workflow'; import { diff --git a/packages/nodes-base/nodes/Mautic/GenericFunctions.ts b/packages/nodes-base/nodes/Mautic/GenericFunctions.ts index 986169025..3bf736783 100644 --- a/packages/nodes-base/nodes/Mautic/GenericFunctions.ts +++ b/packages/nodes-base/nodes/Mautic/GenericFunctions.ts @@ -2,9 +2,9 @@ import { OptionsWithUri } from 'request'; import { IExecuteFunctions, + IExecuteSingleFunctions, IHookFunctions, ILoadOptionsFunctions, - IExecuteSingleFunctions } from 'n8n-core'; import { diff --git a/packages/nodes-base/nodes/Mautic/Mautic.node.ts b/packages/nodes-base/nodes/Mautic/Mautic.node.ts index 7bdafe760..8a6bd76e1 100644 --- a/packages/nodes-base/nodes/Mautic/Mautic.node.ts +++ b/packages/nodes-base/nodes/Mautic/Mautic.node.ts @@ -3,17 +3,17 @@ import { } from 'n8n-core'; import { IDataObject, - INodeTypeDescription, - INodeExecutionData, - INodeType, ILoadOptionsFunctions, + INodeExecutionData, INodePropertyOptions, + INodeType, + INodeTypeDescription, } from 'n8n-workflow'; import { + getErrors, mauticApiRequest, mauticApiRequestAllItems, validateJSON, - getErrors, } from './GenericFunctions'; import { diff --git a/packages/nodes-base/nodes/Mautic/MauticTrigger.node.ts b/packages/nodes-base/nodes/Mautic/MauticTrigger.node.ts index 4f844e118..75ea573d9 100644 --- a/packages/nodes-base/nodes/Mautic/MauticTrigger.node.ts +++ b/packages/nodes-base/nodes/Mautic/MauticTrigger.node.ts @@ -8,12 +8,12 @@ import { } from 'n8n-core'; import { - INodeTypeDescription, - INodeType, - IWebhookResponseData, IDataObject, - INodePropertyOptions, ILoadOptionsFunctions, + INodePropertyOptions, + INodeType, + INodeTypeDescription, + IWebhookResponseData, } from 'n8n-workflow'; import { diff --git a/packages/nodes-base/nodes/Medium/Medium.node.ts b/packages/nodes-base/nodes/Medium/Medium.node.ts index c44d256e3..1316a3b03 100644 --- a/packages/nodes-base/nodes/Medium/Medium.node.ts +++ b/packages/nodes-base/nodes/Medium/Medium.node.ts @@ -6,8 +6,8 @@ import { IDataObject, ILoadOptionsFunctions, INodeExecutionData, - INodeType, INodePropertyOptions, + INodeType, INodeTypeDescription, } from 'n8n-workflow'; diff --git a/packages/nodes-base/nodes/MessageBird/MessageBird.node.ts b/packages/nodes-base/nodes/MessageBird/MessageBird.node.ts index 46f70c85d..fa8895e58 100644 --- a/packages/nodes-base/nodes/MessageBird/MessageBird.node.ts +++ b/packages/nodes-base/nodes/MessageBird/MessageBird.node.ts @@ -4,9 +4,9 @@ import { import { IDataObject, - INodeTypeDescription, INodeExecutionData, INodeType, + INodeTypeDescription, } from 'n8n-workflow'; import { diff --git a/packages/nodes-base/nodes/Microsoft/Excel/MicrosoftExcel.node.ts b/packages/nodes-base/nodes/Microsoft/Excel/MicrosoftExcel.node.ts index 26e58ef9a..e242d1c58 100644 --- a/packages/nodes-base/nodes/Microsoft/Excel/MicrosoftExcel.node.ts +++ b/packages/nodes-base/nodes/Microsoft/Excel/MicrosoftExcel.node.ts @@ -4,11 +4,11 @@ import { import { IDataObject, - INodeExecutionData, - INodeTypeDescription, - INodeType, ILoadOptionsFunctions, + INodeExecutionData, INodePropertyOptions, + INodeType, + INodeTypeDescription, } from 'n8n-workflow'; import { @@ -18,18 +18,18 @@ import { } from './GenericFunctions'; import { - workbookOperations, workbookFields, + workbookOperations, } from './WorkbookDescription'; import { - worksheetOperations, worksheetFields, + worksheetOperations, } from './WorksheetDescription'; import { - tableOperations, tableFields, + tableOperations, } from './TableDescription'; export class MicrosoftExcel implements INodeType { diff --git a/packages/nodes-base/nodes/MongoDb/mongo.node.utils.ts b/packages/nodes-base/nodes/MongoDb/mongo.node.utils.ts index 17ccc0dc4..fcc080558 100644 --- a/packages/nodes-base/nodes/MongoDb/mongo.node.utils.ts +++ b/packages/nodes-base/nodes/MongoDb/mongo.node.utils.ts @@ -1,12 +1,12 @@ import { + ICredentialDataDecryptedObject, IDataObject, INodeExecutionData, - ICredentialDataDecryptedObject } from 'n8n-workflow'; import { + IMongoCredentials, IMongoCredentialsType, IMongoParametricCredentials, - IMongoCredentials } from './mongo.node.types'; /** diff --git a/packages/nodes-base/nodes/NextCloud/NextCloud.node.ts b/packages/nodes-base/nodes/NextCloud/NextCloud.node.ts index 47e8fe47a..d938ead9d 100644 --- a/packages/nodes-base/nodes/NextCloud/NextCloud.node.ts +++ b/packages/nodes-base/nodes/NextCloud/NextCloud.node.ts @@ -5,9 +5,9 @@ import { import { IDataObject, - INodeTypeDescription, INodeExecutionData, INodeType, + INodeTypeDescription, } from 'n8n-workflow'; import { diff --git a/packages/nodes-base/nodes/OpenWeatherMap.node.ts b/packages/nodes-base/nodes/OpenWeatherMap.node.ts index 38e883e53..f643ecb76 100644 --- a/packages/nodes-base/nodes/OpenWeatherMap.node.ts +++ b/packages/nodes-base/nodes/OpenWeatherMap.node.ts @@ -3,9 +3,9 @@ import { } from 'n8n-core'; import { IDataObject, - INodeTypeDescription, INodeExecutionData, INodeType, + INodeTypeDescription, } from 'n8n-workflow'; import { OptionsWithUri } from 'request'; diff --git a/packages/nodes-base/nodes/Paddle/GenericFunctions.ts b/packages/nodes-base/nodes/Paddle/GenericFunctions.ts index 243dc56ff..a481b4e98 100644 --- a/packages/nodes-base/nodes/Paddle/GenericFunctions.ts +++ b/packages/nodes-base/nodes/Paddle/GenericFunctions.ts @@ -4,9 +4,9 @@ import { import { IExecuteFunctions, + IExecuteSingleFunctions, IHookFunctions, ILoadOptionsFunctions, - IExecuteSingleFunctions, IWebhookFunctions, } from 'n8n-core'; diff --git a/packages/nodes-base/nodes/PayPal/GenericFunctions.ts b/packages/nodes-base/nodes/PayPal/GenericFunctions.ts index b1b5a2091..90e4c33a2 100644 --- a/packages/nodes-base/nodes/PayPal/GenericFunctions.ts +++ b/packages/nodes-base/nodes/PayPal/GenericFunctions.ts @@ -1,12 +1,12 @@ import { OptionsWithUri } from 'request'; import { + BINARY_ENCODING, IExecuteFunctions, + IExecuteSingleFunctions, IHookFunctions, ILoadOptionsFunctions, - IExecuteSingleFunctions, IWebhookFunctions, - BINARY_ENCODING } from 'n8n-core'; import { diff --git a/packages/nodes-base/nodes/PayPal/PayPal.node.ts b/packages/nodes-base/nodes/PayPal/PayPal.node.ts index 2f1f00147..dd10ef9af 100644 --- a/packages/nodes-base/nodes/PayPal/PayPal.node.ts +++ b/packages/nodes-base/nodes/PayPal/PayPal.node.ts @@ -3,20 +3,21 @@ import { } from 'n8n-core'; import { IDataObject, - INodeTypeDescription, INodeExecutionData, INodeType, + INodeTypeDescription, } from 'n8n-workflow'; import { - payoutOperations, - payoutItemOperations, payoutFields, payoutItemFields, + payoutItemOperations, + payoutOperations, } from './PaymentDescription'; import { + IAmount, + IItem, IPaymentBatch, ISenderBatchHeader, - IItem, IAmount, RecipientType, RecipientWallet, } from './PaymentInteface'; diff --git a/packages/nodes-base/nodes/PayPal/PayPalTrigger.node.ts b/packages/nodes-base/nodes/PayPal/PayPalTrigger.node.ts index 979af52e7..a55b93d15 100644 --- a/packages/nodes-base/nodes/PayPal/PayPalTrigger.node.ts +++ b/packages/nodes-base/nodes/PayPal/PayPalTrigger.node.ts @@ -1,63 +1,63 @@ import { IHookFunctions, IWebhookFunctions, - } from 'n8n-core'; +} from 'n8n-core'; - import { +import { IDataObject, - INodeTypeDescription, - INodeType, - IWebhookResponseData, ILoadOptionsFunctions, INodePropertyOptions, - } from 'n8n-workflow'; - import { + INodeType, + INodeTypeDescription, + IWebhookResponseData, +} from 'n8n-workflow'; +import { payPalApiRequest, upperFist - } from './GenericFunctions'; +} from './GenericFunctions'; - export class PayPalTrigger implements INodeType { +export class PayPalTrigger implements INodeType { description: INodeTypeDescription = { - displayName: 'PayPal Trigger', - name: 'payPalTrigger', - icon: 'file:paypal.png', - group: ['trigger'], - version: 1, - description: 'Handle PayPal events via webhooks', - defaults: { - name: 'PayPal Trigger', - color: '#32325d', - }, - inputs: [], - outputs: ['main'], - credentials: [ - { - name: 'payPalApi', - required: true, - } - ], - webhooks: [ - { - name: 'default', - httpMethod: 'POST', - reponseMode: 'onReceived', - path: 'webhook', + displayName: 'PayPal Trigger', + name: 'payPalTrigger', + icon: 'file:paypal.png', + group: ['trigger'], + version: 1, + description: 'Handle PayPal events via webhooks', + defaults: { + name: 'PayPal Trigger', + color: '#32325d', }, - ], - properties: [ - { - displayName: 'Events', - name: 'events', - type: 'multiOptions', - required: true, - default: [], - description: 'The event to listen to.', - typeOptions: { - loadOptionsMethod: 'getEvents' + inputs: [], + outputs: ['main'], + credentials: [ + { + name: 'payPalApi', + required: true, + } + ], + webhooks: [ + { + name: 'default', + httpMethod: 'POST', + reponseMode: 'onReceived', + path: 'webhook', }, - options: [], - }, - ], + ], + properties: [ + { + displayName: 'Events', + name: 'events', + type: 'multiOptions', + required: true, + default: [], + description: 'The event to listen to.', + typeOptions: { + loadOptionsMethod: 'getEvents' + }, + options: [], + }, + ], }; methods = { @@ -126,7 +126,7 @@ import { url: webhookUrl, event_types: events.map(event => { return { name: event }; - }), + }), }; const endpoint = '/notifications/webhooks'; try { @@ -157,7 +157,7 @@ import { return true; }, }, - }; + }; async webhook(this: IWebhookFunctions): Promise { let webhook; @@ -168,10 +168,10 @@ import { const endpoint = '/notifications/verify-webhook-signature'; if (headerData['PAYPAL-AUTH-ALGO'] !== undefined - && headerData['PAYPAL-CERT-URL'] !== undefined - && headerData['PAYPAL-TRANSMISSION-ID'] !== undefined - && headerData['PAYPAL-TRANSMISSION-SIG'] !== undefined - && headerData['PAYPAL-TRANSMISSION-TIME'] !== undefined) { + && headerData['PAYPAL-CERT-URL'] !== undefined + && headerData['PAYPAL-TRANSMISSION-ID'] !== undefined + && headerData['PAYPAL-TRANSMISSION-SIG'] !== undefined + && headerData['PAYPAL-TRANSMISSION-TIME'] !== undefined) { const body = { auth_algo: headerData['PAYPAL-AUTH-ALGO'], cert_url: headerData['PAYPAL-CERT-URL'], @@ -198,4 +198,4 @@ import { ], }; } - } +} diff --git a/packages/nodes-base/nodes/PhilipsHue/PhilipsHue.node.ts b/packages/nodes-base/nodes/PhilipsHue/PhilipsHue.node.ts index 27189d4b3..f636ab287 100644 --- a/packages/nodes-base/nodes/PhilipsHue/PhilipsHue.node.ts +++ b/packages/nodes-base/nodes/PhilipsHue/PhilipsHue.node.ts @@ -4,21 +4,21 @@ import { import { IDataObject, - INodeExecutionData, - INodeTypeDescription, - INodeType, ILoadOptionsFunctions, + INodeExecutionData, INodePropertyOptions, + INodeType, + INodeTypeDescription, } from 'n8n-workflow'; import { - philipsHueApiRequest, getUser, + philipsHueApiRequest, } from './GenericFunctions'; import { - lightOperations, lightFields, + lightOperations, } from './LightDescription'; export class PhilipsHue implements INodeType { diff --git a/packages/nodes-base/nodes/Pipedrive/Pipedrive.node.ts b/packages/nodes-base/nodes/Pipedrive/Pipedrive.node.ts index 919558e70..dba5142e4 100644 --- a/packages/nodes-base/nodes/Pipedrive/Pipedrive.node.ts +++ b/packages/nodes-base/nodes/Pipedrive/Pipedrive.node.ts @@ -5,10 +5,10 @@ import { } from 'n8n-core'; import { IDataObject, - INodeTypeDescription, INodeExecutionData, - INodeType, INodePropertyOptions, + INodeType, + INodeTypeDescription, } from 'n8n-workflow'; import { diff --git a/packages/nodes-base/nodes/Pipedrive/PipedriveTrigger.node.ts b/packages/nodes-base/nodes/Pipedrive/PipedriveTrigger.node.ts index 6a6bfa1a6..99f10d68d 100644 --- a/packages/nodes-base/nodes/Pipedrive/PipedriveTrigger.node.ts +++ b/packages/nodes-base/nodes/Pipedrive/PipedriveTrigger.node.ts @@ -4,8 +4,8 @@ import { } from 'n8n-core'; import { - INodeTypeDescription, INodeType, + INodeTypeDescription, IWebhookResponseData, } from 'n8n-workflow'; diff --git a/packages/nodes-base/nodes/Postmark/PostmarkTrigger.node.ts b/packages/nodes-base/nodes/Postmark/PostmarkTrigger.node.ts index 87ed571c2..951e19496 100644 --- a/packages/nodes-base/nodes/Postmark/PostmarkTrigger.node.ts +++ b/packages/nodes-base/nodes/Postmark/PostmarkTrigger.node.ts @@ -4,8 +4,8 @@ import { } from 'n8n-core'; import { - INodeTypeDescription, INodeType, + INodeTypeDescription, IWebhookResponseData, } from 'n8n-workflow'; diff --git a/packages/nodes-base/nodes/ReadBinaryFile.node.ts b/packages/nodes-base/nodes/ReadBinaryFile.node.ts index 8bf1a8a84..a96c17457 100644 --- a/packages/nodes-base/nodes/ReadBinaryFile.node.ts +++ b/packages/nodes-base/nodes/ReadBinaryFile.node.ts @@ -1,8 +1,8 @@ import { IExecuteSingleFunctions } from 'n8n-core'; import { INodeExecutionData, - INodeTypeDescription, INodeType, + INodeTypeDescription, } from 'n8n-workflow'; import { diff --git a/packages/nodes-base/nodes/Rocketchat/GenericFunctions.ts b/packages/nodes-base/nodes/Rocketchat/GenericFunctions.ts index 0c0e8fb63..509151ede 100644 --- a/packages/nodes-base/nodes/Rocketchat/GenericFunctions.ts +++ b/packages/nodes-base/nodes/Rocketchat/GenericFunctions.ts @@ -1,9 +1,9 @@ import { OptionsWithUri } from 'request'; import { IExecuteFunctions, + IExecuteSingleFunctions, IHookFunctions, ILoadOptionsFunctions, - IExecuteSingleFunctions, } from 'n8n-core'; export async function rocketchatApiRequest(this: IHookFunctions | IExecuteFunctions | IExecuteSingleFunctions | ILoadOptionsFunctions, resource: string, method: string, operation: string, body: any = {}, headers?: object): Promise { // tslint:disable-line:no-any diff --git a/packages/nodes-base/nodes/Rocketchat/Rocketchat.node.ts b/packages/nodes-base/nodes/Rocketchat/Rocketchat.node.ts index 65dd1a682..fb179307c 100644 --- a/packages/nodes-base/nodes/Rocketchat/Rocketchat.node.ts +++ b/packages/nodes-base/nodes/Rocketchat/Rocketchat.node.ts @@ -3,9 +3,9 @@ import { } from 'n8n-core'; import { IDataObject, - INodeTypeDescription, INodeExecutionData, - INodeType + INodeType, + INodeTypeDescription, } from 'n8n-workflow'; import { rocketchatApiRequest, diff --git a/packages/nodes-base/nodes/RssFeedRead.node.ts b/packages/nodes-base/nodes/RssFeedRead.node.ts index b96f3d6e0..96bcb4550 100644 --- a/packages/nodes-base/nodes/RssFeedRead.node.ts +++ b/packages/nodes-base/nodes/RssFeedRead.node.ts @@ -1,9 +1,9 @@ import { IExecuteFunctions } from 'n8n-core'; import { IDataObject, + INodeExecutionData, INodeType, INodeTypeDescription, - INodeExecutionData, } from 'n8n-workflow'; import * as Parser from 'rss-parser'; diff --git a/packages/nodes-base/nodes/S3/S3.node.ts b/packages/nodes-base/nodes/S3/S3.node.ts index accb8494f..2a71489a7 100644 --- a/packages/nodes-base/nodes/S3/S3.node.ts +++ b/packages/nodes-base/nodes/S3/S3.node.ts @@ -1,7 +1,7 @@ import { - snakeCase, paramCase, + snakeCase, } from 'change-case'; import { diff --git a/packages/nodes-base/nodes/Salesforce/Salesforce.node.ts b/packages/nodes-base/nodes/Salesforce/Salesforce.node.ts index 4f6ddecc8..1fafc216d 100644 --- a/packages/nodes-base/nodes/Salesforce/Salesforce.node.ts +++ b/packages/nodes-base/nodes/Salesforce/Salesforce.node.ts @@ -6,9 +6,9 @@ import { IDataObject, ILoadOptionsFunctions, INodeExecutionData, + INodePropertyOptions, INodeType, INodeTypeDescription, - INodePropertyOptions, } from 'n8n-workflow'; import { diff --git a/packages/nodes-base/nodes/Salesforce/UserInterface.ts b/packages/nodes-base/nodes/Salesforce/UserInterface.ts index 035a1b6c9..985607008 100644 --- a/packages/nodes-base/nodes/Salesforce/UserInterface.ts +++ b/packages/nodes-base/nodes/Salesforce/UserInterface.ts @@ -1,10 +1,10 @@ export interface IUser { - Alias?: string; - Department?: string; - Division?: string; - Email?: string; - IsActive?: boolean; - MobilePhone?: string; - Title?: string; - Username?: string; + Alias?: string; + Department?: string; + Division?: string; + Email?: string; + IsActive?: boolean; + MobilePhone?: string; + Title?: string; + Username?: string; } diff --git a/packages/nodes-base/nodes/Segment/Segment.node.ts b/packages/nodes-base/nodes/Segment/Segment.node.ts index e8c11613a..f4c3cc41f 100644 --- a/packages/nodes-base/nodes/Segment/Segment.node.ts +++ b/packages/nodes-base/nodes/Segment/Segment.node.ts @@ -14,8 +14,8 @@ import { } from './GenericFunctions'; import { - groupOperations, groupFields, + groupOperations, } from './GroupDescription'; import { @@ -28,12 +28,13 @@ import { } from './IdentifyInterface'; import { - trackOperations, trackFields, + trackOperations, } from './TrackDescription'; import { - ITrack, IGroup, + IGroup, + ITrack, } from './TrackInterface'; import * as uuid from 'uuid/v4'; diff --git a/packages/nodes-base/nodes/SentryIo/SentryIo.node.ts b/packages/nodes-base/nodes/SentryIo/SentryIo.node.ts index 3996ad294..f5353dc05 100644 --- a/packages/nodes-base/nodes/SentryIo/SentryIo.node.ts +++ b/packages/nodes-base/nodes/SentryIo/SentryIo.node.ts @@ -4,21 +4,21 @@ import { import { IDataObject, + ILoadOptionsFunctions, INodeExecutionData, + INodePropertyOptions, INodeType, INodeTypeDescription, - ILoadOptionsFunctions, - INodePropertyOptions, } from 'n8n-workflow'; import { - eventOperations, eventFields, + eventOperations, } from './EventDescription'; import { - issueOperations, issueFields, + issueOperations, } from './IssueDescription'; import { @@ -27,23 +27,23 @@ import { } from './OrganizationDescription'; import { - projectOperations, projectFields, + projectOperations, } from './ProjectDescription'; import { - releaseOperations, releaseFields, + releaseOperations, } from './ReleaseDescription'; import { - teamOperations, teamFields, + teamOperations, } from './TeamDescription'; import { - sentryIoApiRequest, sentryApiRequestAllItems, + sentryIoApiRequest, } from './GenericFunctions'; import { diff --git a/packages/nodes-base/nodes/Shopify/GenericFunctions.ts b/packages/nodes-base/nodes/Shopify/GenericFunctions.ts index 909220431..ebb8bde4b 100644 --- a/packages/nodes-base/nodes/Shopify/GenericFunctions.ts +++ b/packages/nodes-base/nodes/Shopify/GenericFunctions.ts @@ -3,11 +3,11 @@ import { } from 'request'; import { + BINARY_ENCODING, IExecuteFunctions, + IExecuteSingleFunctions, IHookFunctions, ILoadOptionsFunctions, - IExecuteSingleFunctions, - BINARY_ENCODING } from 'n8n-core'; import { diff --git a/packages/nodes-base/nodes/Shopify/Shopify.node.ts b/packages/nodes-base/nodes/Shopify/Shopify.node.ts index dc093bb12..9ae04b763 100644 --- a/packages/nodes-base/nodes/Shopify/Shopify.node.ts +++ b/packages/nodes-base/nodes/Shopify/Shopify.node.ts @@ -5,10 +5,10 @@ import { import { IDataObject, ILoadOptionsFunctions, - INodeTypeDescription, INodeExecutionData, - INodeType, INodePropertyOptions, + INodeType, + INodeTypeDescription, } from 'n8n-workflow'; import { @@ -28,10 +28,10 @@ import { } from './ProductDescription'; import { - IOrder, - IDiscountCode, IAddress, + IDiscountCode, ILineItem, + IOrder, } from './OrderInterface'; import { diff --git a/packages/nodes-base/nodes/Shopify/ShopifyTrigger.node.ts b/packages/nodes-base/nodes/Shopify/ShopifyTrigger.node.ts index 07b7939a7..412c58211 100644 --- a/packages/nodes-base/nodes/Shopify/ShopifyTrigger.node.ts +++ b/packages/nodes-base/nodes/Shopify/ShopifyTrigger.node.ts @@ -5,8 +5,8 @@ import { import { IDataObject, - INodeTypeDescription, INodeType, + INodeTypeDescription, IWebhookResponseData, } from 'n8n-workflow'; @@ -16,7 +16,7 @@ import { import { createHmac, - } from 'crypto'; +} from 'crypto'; export class ShopifyTrigger implements INodeType { description: INodeTypeDescription = { @@ -55,240 +55,240 @@ export class ShopifyTrigger implements INodeType { options: [ { - name: 'App uninstalled', - value: 'app/uninstalled', + name: 'App uninstalled', + value: 'app/uninstalled', }, { - name: 'Carts create', - value: 'carts/create', + name: 'Carts create', + value: 'carts/create', }, { - name: 'Carts update', - value: 'carts/update', + name: 'Carts update', + value: 'carts/update', }, { - name: 'Checkouts create', - value: 'checkouts/create', + name: 'Checkouts create', + value: 'checkouts/create', }, { - name: 'Checkouts delete', - value: 'checkouts/delete', + name: 'Checkouts delete', + value: 'checkouts/delete', }, { - name: 'Checkouts update', - value: 'checkouts/update', + name: 'Checkouts update', + value: 'checkouts/update', }, { - name: 'Collection listings add', - value: 'collection_listings/add', + name: 'Collection listings add', + value: 'collection_listings/add', }, { - name: 'Collection listings remove', - value: 'collection_listings/remove', + name: 'Collection listings remove', + value: 'collection_listings/remove', }, { - name: 'Collection listings update', - value: 'collection_listings/update', + name: 'Collection listings update', + value: 'collection_listings/update', }, { - name: 'Collections create', - value: 'collections/create', + name: 'Collections create', + value: 'collections/create', }, { - name: 'Collections delete', - value: 'collections/delete', + name: 'Collections delete', + value: 'collections/delete', }, { - name: 'Collections update', - value: 'collections/update', + name: 'Collections update', + value: 'collections/update', }, { - name: 'Customer groups create', - value: 'customer_groups/create', + name: 'Customer groups create', + value: 'customer_groups/create', }, { - name: 'Customer groups delete', - value: 'customer_groups/delete', + name: 'Customer groups delete', + value: 'customer_groups/delete', }, { - name: 'Customer groups update', - value: 'customer_groups/update', + name: 'Customer groups update', + value: 'customer_groups/update', }, { - name: 'Customers create', - value: 'customers/create', + name: 'Customers create', + value: 'customers/create', }, { - name: 'Customers delete', - value: 'customers/delete', + name: 'Customers delete', + value: 'customers/delete', }, { - name: 'Customers disable', - value: 'customers/disable', + name: 'Customers disable', + value: 'customers/disable', }, { - name: 'Customers enable', - value: 'customers/enable', + name: 'Customers enable', + value: 'customers/enable', }, { - name: 'Customers update', - value: 'customers/update', + name: 'Customers update', + value: 'customers/update', }, { - name: 'Draft orders create', - value: 'draft_orders/create', + name: 'Draft orders create', + value: 'draft_orders/create', }, { - name: 'Draft orders delete', - value: 'draft_orders/delete', + name: 'Draft orders delete', + value: 'draft_orders/delete', }, { - name: 'Draft orders update', - value: 'draft_orders/update', + name: 'Draft orders update', + value: 'draft_orders/update', }, { - name: 'Fulfillment events create', - value: 'fulfillment_events/create', + name: 'Fulfillment events create', + value: 'fulfillment_events/create', }, { - name: 'Fulfillment events delete', - value: 'fulfillment_events/delete', + name: 'Fulfillment events delete', + value: 'fulfillment_events/delete', }, { - name: 'Fulfillments create', - value: 'fulfillments/create', + name: 'Fulfillments create', + value: 'fulfillments/create', }, { - name: 'Fulfillments update', - value: 'fulfillments/update', + name: 'Fulfillments update', + value: 'fulfillments/update', }, { - name: 'Inventory_items create', - value: 'inventory_items/create', + name: 'Inventory_items create', + value: 'inventory_items/create', }, { - name: 'Inventory_items delete', - value: 'inventory_items/delete', + name: 'Inventory_items delete', + value: 'inventory_items/delete', }, { - name: 'Inventory_items update', - value: 'inventory_items/update', + name: 'Inventory_items update', + value: 'inventory_items/update', }, { - name: 'Inventory_levels connect', - value: 'inventory_levels/connect', + name: 'Inventory_levels connect', + value: 'inventory_levels/connect', }, { - name: 'Inventory_levels disconnect', - value: 'inventory_levels/disconnect', + name: 'Inventory_levels disconnect', + value: 'inventory_levels/disconnect', }, { - name: 'Inventory_levels update', - value: 'inventory_levels/update', + name: 'Inventory_levels update', + value: 'inventory_levels/update', }, { - name: 'Locales create', - value: 'locales/create', + name: 'Locales create', + value: 'locales/create', }, { - name: 'Locales update', - value: 'locales/update', + name: 'Locales update', + value: 'locales/update', }, { - name: 'Locations create', - value: 'locations/create', + name: 'Locations create', + value: 'locations/create', }, { - name: 'Locations delete', - value: 'locations/delete', + name: 'Locations delete', + value: 'locations/delete', }, { - name: 'Locations update', - value: 'locations/update', + name: 'Locations update', + value: 'locations/update', }, { - name: 'Order transactions create', - value: 'order_transactions/create', + name: 'Order transactions create', + value: 'order_transactions/create', }, { - name: 'Orders cancelled', - value: 'orders/cancelled', + name: 'Orders cancelled', + value: 'orders/cancelled', }, { - name: 'Orders create', - value: 'orders/create', + name: 'Orders create', + value: 'orders/create', }, { - name: 'Orders delete', - value: 'orders/delete', + name: 'Orders delete', + value: 'orders/delete', }, { - name: 'Orders fulfilled', - value: 'orders/fulfilled', + name: 'Orders fulfilled', + value: 'orders/fulfilled', }, { - name: 'Orders paid', - value: 'orders/paid', + name: 'Orders paid', + value: 'orders/paid', }, { - name: 'Orders partially fulfilled', - value: 'orders/partially_fulfilled', + name: 'Orders partially fulfilled', + value: 'orders/partially_fulfilled', }, { - name: 'Orders updated', - value: 'orders/updated', + name: 'Orders updated', + value: 'orders/updated', }, { - name: 'Product listings add', - value: 'product_listings/add', + name: 'Product listings add', + value: 'product_listings/add', }, { - name: 'Product listings remove', - value: 'product_listings/remove', + name: 'Product listings remove', + value: 'product_listings/remove', }, { - name: 'Product listings update', - value: 'product_listings/update', + name: 'Product listings update', + value: 'product_listings/update', }, { - name: 'Products create', - value: 'products/create', + name: 'Products create', + value: 'products/create', }, { - name: 'Products delete', - value: 'products/delete', + name: 'Products delete', + value: 'products/delete', }, { - name: 'Products update', - value: 'products/update', + name: 'Products update', + value: 'products/update', }, { - name: 'Refunds create', - value: 'refunds/create', + name: 'Refunds create', + value: 'refunds/create', }, { - name: 'Shop update', - value: 'shop/update', + name: 'Shop update', + value: 'shop/update', }, { - name: 'Tender transactions create', - value: 'tender_transactions/create', + name: 'Tender transactions create', + value: 'tender_transactions/create', }, { - name: 'Themes create', - value: 'themes/create', + name: 'Themes create', + value: 'themes/create', }, { - name: 'Themes delete', - value: 'themes/delete', + name: 'Themes delete', + value: 'themes/delete', }, { - name: 'Themes publish', - value: 'themes/publish', + name: 'Themes publish', + value: 'themes/publish', }, { - name: 'Themes update', - value: 'themes/update', + name: 'Themes update', + value: 'themes/update', }, ], description: 'Event that triggers the webhook', @@ -331,8 +331,8 @@ export class ShopifyTrigger implements INodeType { let responseData; try { - responseData = await shopifyApiRequest.call(this, 'POST', endpoint, body); - } catch(error) { + responseData = await shopifyApiRequest.call(this, 'POST', endpoint, body); + } catch (error) { return false; } @@ -370,9 +370,9 @@ export class ShopifyTrigger implements INodeType { const req = this.getRequestObject(); const webhookData = this.getWorkflowStaticData('node') as IDataObject; if (headerData['x-shopify-topic'] !== undefined - && headerData['x-shopify-hmac-sha256'] !== undefined - && headerData['x-shopify-shop-domain'] !== undefined - && headerData['x-shopify-api-version'] !== undefined) { + && headerData['x-shopify-hmac-sha256'] !== undefined + && headerData['x-shopify-shop-domain'] !== undefined + && headerData['x-shopify-api-version'] !== undefined) { // @ts-ignore const computedSignature = createHmac('sha256', webhookData.sharedSecret as string).update(req.rawBody).digest('base64'); if (headerData['x-shopify-hmac-sha256'] !== computedSignature) { diff --git a/packages/nodes-base/nodes/Signl4/Signl4.node.ts b/packages/nodes-base/nodes/Signl4/Signl4.node.ts index b4022ff0e..143146ad2 100644 --- a/packages/nodes-base/nodes/Signl4/Signl4.node.ts +++ b/packages/nodes-base/nodes/Signl4/Signl4.node.ts @@ -1,6 +1,6 @@ import { - IExecuteFunctions, BINARY_ENCODING, + IExecuteFunctions, } from 'n8n-core'; import { diff --git a/packages/nodes-base/nodes/Slack/Slack.node.ts b/packages/nodes-base/nodes/Slack/Slack.node.ts index f95558b64..391cb1589 100644 --- a/packages/nodes-base/nodes/Slack/Slack.node.ts +++ b/packages/nodes-base/nodes/Slack/Slack.node.ts @@ -5,11 +5,11 @@ import { import { IDataObject, - INodeTypeDescription, - INodeExecutionData, - INodeType, ILoadOptionsFunctions, + INodeExecutionData, INodePropertyOptions, + INodeType, + INodeTypeDescription, } from 'n8n-workflow'; import { diff --git a/packages/nodes-base/nodes/Spotify/GenericFunctions.ts b/packages/nodes-base/nodes/Spotify/GenericFunctions.ts index f4c86a9d2..b822e63e7 100644 --- a/packages/nodes-base/nodes/Spotify/GenericFunctions.ts +++ b/packages/nodes-base/nodes/Spotify/GenericFunctions.ts @@ -48,17 +48,17 @@ export async function spotifyApiRequest(this: IHookFunctions | IExecuteFunctions return await this.helpers.requestOAuth2.call(this, 'spotifyOAuth2Api', options); } catch (error) { if (error.statusCode === 401) { - // Return a clear error - throw new Error('The Spotify credentials are not valid!'); - } + // Return a clear error + throw new Error('The Spotify credentials are not valid!'); + } - if (error.error && error.error.error && error.error.error.message) { - // Try to return the error prettier - throw new Error(`Spotify error response [${error.error.error.status}]: ${error.error.error.message}`); - } + if (error.error && error.error.error && error.error.error.message) { + // Try to return the error prettier + throw new Error(`Spotify error response [${error.error.error.status}]: ${error.error.error.message}`); + } - // If that data does not exist for some reason return the actual error - throw error; + // If that data does not exist for some reason return the actual error + throw error; } } diff --git a/packages/nodes-base/nodes/SpreadsheetFile.node.ts b/packages/nodes-base/nodes/SpreadsheetFile.node.ts index b1d1c1419..daf602657 100644 --- a/packages/nodes-base/nodes/SpreadsheetFile.node.ts +++ b/packages/nodes-base/nodes/SpreadsheetFile.node.ts @@ -4,18 +4,18 @@ import { } from 'n8n-core'; import { + IDataObject, INodeExecutionData, INodeType, INodeTypeDescription, - IDataObject, } from 'n8n-workflow'; import { read as xlsxRead, utils as xlsxUtils, + WorkBook, write as xlsxWrite, WritingOptions, - WorkBook, } from 'xlsx'; diff --git a/packages/nodes-base/nodes/Stripe/StripeTrigger.node.ts b/packages/nodes-base/nodes/Stripe/StripeTrigger.node.ts index 304af3720..b9cc24663 100644 --- a/packages/nodes-base/nodes/Stripe/StripeTrigger.node.ts +++ b/packages/nodes-base/nodes/Stripe/StripeTrigger.node.ts @@ -5,8 +5,8 @@ import { import { IDataObject, - INodeTypeDescription, INodeType, + INodeTypeDescription, IWebhookResponseData, } from 'n8n-workflow'; diff --git a/packages/nodes-base/nodes/SurveyMonkey/SurveyMonkeyTrigger.node.ts b/packages/nodes-base/nodes/SurveyMonkey/SurveyMonkeyTrigger.node.ts index c0f722dd8..94320d4b4 100644 --- a/packages/nodes-base/nodes/SurveyMonkey/SurveyMonkeyTrigger.node.ts +++ b/packages/nodes-base/nodes/SurveyMonkey/SurveyMonkeyTrigger.node.ts @@ -22,9 +22,9 @@ import { import { IAnswer, IChoice, + IOther, IQuestion, IRow, - IOther, } from './Interfaces'; import { diff --git a/packages/nodes-base/nodes/Taiga/TaigaTrigger.node.ts b/packages/nodes-base/nodes/Taiga/TaigaTrigger.node.ts index 8dbdd6e2f..a26ddb148 100644 --- a/packages/nodes-base/nodes/Taiga/TaigaTrigger.node.ts +++ b/packages/nodes-base/nodes/Taiga/TaigaTrigger.node.ts @@ -14,8 +14,8 @@ import { } from 'n8n-core'; import { - taigaApiRequest, getAutomaticSecret, + taigaApiRequest, } from './GenericFunctions'; // import { diff --git a/packages/nodes-base/nodes/Telegram/Telegram.node.ts b/packages/nodes-base/nodes/Telegram/Telegram.node.ts index 2597b0a0c..e7b7825a8 100644 --- a/packages/nodes-base/nodes/Telegram/Telegram.node.ts +++ b/packages/nodes-base/nodes/Telegram/Telegram.node.ts @@ -5,13 +5,13 @@ import { import { IDataObject, INodeExecutionData, - INodeTypeDescription, INodeType, + INodeTypeDescription, } from 'n8n-workflow'; import { - apiRequest, addAdditionalFields, + apiRequest, } from './GenericFunctions'; diff --git a/packages/nodes-base/nodes/Todoist/Todoist.node.ts b/packages/nodes-base/nodes/Todoist/Todoist.node.ts index d2dbe002e..fb46fe7b5 100644 --- a/packages/nodes-base/nodes/Todoist/Todoist.node.ts +++ b/packages/nodes-base/nodes/Todoist/Todoist.node.ts @@ -4,11 +4,11 @@ import { import { IDataObject, - INodeTypeDescription, - INodeExecutionData, - INodeType, ILoadOptionsFunctions, + INodeExecutionData, INodePropertyOptions, + INodeType, + INodeTypeDescription, } from 'n8n-workflow'; import { diff --git a/packages/nodes-base/nodes/Toggl/GenericFunctions.ts b/packages/nodes-base/nodes/Toggl/GenericFunctions.ts index 37c6bdb80..d8d3ed693 100644 --- a/packages/nodes-base/nodes/Toggl/GenericFunctions.ts +++ b/packages/nodes-base/nodes/Toggl/GenericFunctions.ts @@ -2,9 +2,9 @@ import { OptionsWithUri } from 'request'; import { IExecuteFunctions, + IExecuteSingleFunctions, IHookFunctions, ILoadOptionsFunctions, - IExecuteSingleFunctions, IPollFunctions, ITriggerFunctions, } from 'n8n-core'; diff --git a/packages/nodes-base/nodes/Toggl/TogglTrigger.node.ts b/packages/nodes-base/nodes/Toggl/TogglTrigger.node.ts index c32734bc2..70bd4945e 100644 --- a/packages/nodes-base/nodes/Toggl/TogglTrigger.node.ts +++ b/packages/nodes-base/nodes/Toggl/TogglTrigger.node.ts @@ -1,9 +1,9 @@ import { IPollFunctions } from 'n8n-core'; import { + IDataObject, INodeExecutionData, INodeType, INodeTypeDescription, - IDataObject, } from 'n8n-workflow'; import * as moment from 'moment'; diff --git a/packages/nodes-base/nodes/TravisCi/TravisCi.node.ts b/packages/nodes-base/nodes/TravisCi/TravisCi.node.ts index 140a4cd0d..313eab17d 100644 --- a/packages/nodes-base/nodes/TravisCi/TravisCi.node.ts +++ b/packages/nodes-base/nodes/TravisCi/TravisCi.node.ts @@ -4,9 +4,9 @@ import { import { IDataObject, - INodeTypeDescription, INodeExecutionData, INodeType, + INodeTypeDescription, } from 'n8n-workflow'; import { diff --git a/packages/nodes-base/nodes/Trello/Trello.node.ts b/packages/nodes-base/nodes/Trello/Trello.node.ts index c39b51a6c..0fa85a8de 100644 --- a/packages/nodes-base/nodes/Trello/Trello.node.ts +++ b/packages/nodes-base/nodes/Trello/Trello.node.ts @@ -4,9 +4,9 @@ import { import { IDataObject, - INodeTypeDescription, INodeExecutionData, INodeType, + INodeTypeDescription, } from 'n8n-workflow'; import { @@ -14,38 +14,38 @@ import { } from './GenericFunctions'; import { - attachmentOperations, attachmentFields, + attachmentOperations, } from './AttachmentDescription'; import { - boardOperations, boardFields, + boardOperations, } from './BoardDescription'; import { - cardOperations, cardFields, + cardOperations, } from './CardDescription'; import { - cardCommentOperations, cardCommentFields, + cardCommentOperations, } from './CardCommentDescription'; import { - checklistOperations, checklistFields, + checklistOperations, } from './ChecklistDescription'; import { - labelOperations, labelFields, + labelOperations, } from './LabelDescription'; import { - listOperations, listFields, + listOperations, } from './ListDescription'; export class Trello implements INodeType { diff --git a/packages/nodes-base/nodes/Trello/TrelloTrigger.node.ts b/packages/nodes-base/nodes/Trello/TrelloTrigger.node.ts index 9d58c935b..feb80219d 100644 --- a/packages/nodes-base/nodes/Trello/TrelloTrigger.node.ts +++ b/packages/nodes-base/nodes/Trello/TrelloTrigger.node.ts @@ -4,9 +4,8 @@ import { } from 'n8n-core'; import { - IDataObject, - INodeTypeDescription, INodeType, + INodeTypeDescription, IWebhookResponseData, } from 'n8n-workflow'; @@ -14,7 +13,7 @@ import { apiRequest, } from './GenericFunctions'; -import { createHmac } from 'crypto'; +// import { createHmac } from 'crypto'; export class TrelloTrigger implements INodeType { diff --git a/packages/nodes-base/nodes/Twake/Twake.node.ts b/packages/nodes-base/nodes/Twake/Twake.node.ts index 3b5459cde..7d1da3182 100644 --- a/packages/nodes-base/nodes/Twake/Twake.node.ts +++ b/packages/nodes-base/nodes/Twake/Twake.node.ts @@ -3,12 +3,12 @@ import { } from 'n8n-core'; import { - INodeExecutionData, IDataObject, + ILoadOptionsFunctions, + INodeExecutionData, + INodePropertyOptions, INodeType, INodeTypeDescription, - ILoadOptionsFunctions, - INodePropertyOptions, } from 'n8n-workflow'; import { diff --git a/packages/nodes-base/nodes/Twilio/Twilio.node.ts b/packages/nodes-base/nodes/Twilio/Twilio.node.ts index 479cb11e6..cf468c20e 100644 --- a/packages/nodes-base/nodes/Twilio/Twilio.node.ts +++ b/packages/nodes-base/nodes/Twilio/Twilio.node.ts @@ -3,9 +3,9 @@ import { } from 'n8n-core'; import { IDataObject, - INodeTypeDescription, INodeExecutionData, INodeType, + INodeTypeDescription, } from 'n8n-workflow'; import { diff --git a/packages/nodes-base/nodes/Twitter/GenericFunctions.ts b/packages/nodes-base/nodes/Twitter/GenericFunctions.ts index 655345ec9..d05a3c32e 100644 --- a/packages/nodes-base/nodes/Twitter/GenericFunctions.ts +++ b/packages/nodes-base/nodes/Twitter/GenericFunctions.ts @@ -3,9 +3,9 @@ import { } from 'request'; import { - IHookFunctions, IExecuteFunctions, IExecuteSingleFunctions, + IHookFunctions, ILoadOptionsFunctions, } from 'n8n-core'; @@ -74,5 +74,3 @@ export function chunks (buffer: Buffer, chunkSize: number) { return result; } - - diff --git a/packages/nodes-base/nodes/Twitter/Twitter.node.ts b/packages/nodes-base/nodes/Twitter/Twitter.node.ts index 88a4770e3..22ce72657 100644 --- a/packages/nodes-base/nodes/Twitter/Twitter.node.ts +++ b/packages/nodes-base/nodes/Twitter/Twitter.node.ts @@ -1,17 +1,17 @@ import { + BINARY_ENCODING, IExecuteFunctions, ILoadOptionsFunctions, - BINARY_ENCODING, } from 'n8n-core'; import { IBinaryKeyData, IDataObject, INodeExecutionData, + INodePropertyOptions, INodeType, INodeTypeDescription, - INodePropertyOptions, } from 'n8n-workflow'; import { diff --git a/packages/nodes-base/nodes/Typeform/TypeformTrigger.node.ts b/packages/nodes-base/nodes/Typeform/TypeformTrigger.node.ts index 9c8f6e16f..4af0342d8 100644 --- a/packages/nodes-base/nodes/Typeform/TypeformTrigger.node.ts +++ b/packages/nodes-base/nodes/Typeform/TypeformTrigger.node.ts @@ -4,10 +4,10 @@ import { } from 'n8n-core'; import { - INodeTypeDescription, - INodeType, - IWebhookResponseData, IDataObject, + INodeType, + INodeTypeDescription, + IWebhookResponseData, } from 'n8n-workflow'; import { diff --git a/packages/nodes-base/nodes/UnleashedSoftware/UnleashedSoftware.node.ts b/packages/nodes-base/nodes/UnleashedSoftware/UnleashedSoftware.node.ts index e5ef6b3f9..fd3bf6525 100644 --- a/packages/nodes-base/nodes/UnleashedSoftware/UnleashedSoftware.node.ts +++ b/packages/nodes-base/nodes/UnleashedSoftware/UnleashedSoftware.node.ts @@ -10,19 +10,19 @@ import { } from 'n8n-workflow'; import { + convertNETDates, unleashedApiRequest, unleashedApiRequestAllItems, - convertNETDates, } from './GenericFunctions'; import { - salesOrderOperations, salesOrderFields, + salesOrderOperations, } from './SalesOrderDescription'; import { - stockOnHandOperations, stockOnHandFields, + stockOnHandOperations, } from './StockOnHandDescription'; import * as moment from 'moment'; diff --git a/packages/nodes-base/nodes/Uplead/Uplead.node.ts b/packages/nodes-base/nodes/Uplead/Uplead.node.ts index 93350e99d..2c4197244 100644 --- a/packages/nodes-base/nodes/Uplead/Uplead.node.ts +++ b/packages/nodes-base/nodes/Uplead/Uplead.node.ts @@ -15,8 +15,8 @@ import { companyOperations, } from './CompanyDesciption'; import { + personFields, personOperations, - personFields, } from './PersonDescription'; export class Uplead implements INodeType { diff --git a/packages/nodes-base/nodes/Vero/GenericFunctions.ts b/packages/nodes-base/nodes/Vero/GenericFunctions.ts index 2e5053fb7..ecac55953 100644 --- a/packages/nodes-base/nodes/Vero/GenericFunctions.ts +++ b/packages/nodes-base/nodes/Vero/GenericFunctions.ts @@ -1,8 +1,8 @@ import { OptionsWithUri } from 'request'; import { IExecuteFunctions, - ILoadOptionsFunctions, IExecuteSingleFunctions, + ILoadOptionsFunctions, } from 'n8n-core'; import { IDataObject } from 'n8n-workflow'; diff --git a/packages/nodes-base/nodes/Vero/Vero.node.ts b/packages/nodes-base/nodes/Vero/Vero.node.ts index 6638e22b5..ea6e7a8d8 100644 --- a/packages/nodes-base/nodes/Vero/Vero.node.ts +++ b/packages/nodes-base/nodes/Vero/Vero.node.ts @@ -3,21 +3,21 @@ import { } from 'n8n-core'; import { IDataObject, - INodeTypeDescription, INodeExecutionData, INodeType, + INodeTypeDescription, } from 'n8n-workflow'; import { - veroApiRequest, validateJSON, + veroApiRequest, } from './GenericFunctions'; import { - userOperations, userFields, + userOperations, } from './UserDescription'; import { + eventFields, eventOperations, - eventFields } from './EventDescripion'; export class Vero implements INodeType { diff --git a/packages/nodes-base/nodes/Webflow/WebflowTrigger.node.ts b/packages/nodes-base/nodes/Webflow/WebflowTrigger.node.ts index 73b2efef6..becb8a962 100644 --- a/packages/nodes-base/nodes/Webflow/WebflowTrigger.node.ts +++ b/packages/nodes-base/nodes/Webflow/WebflowTrigger.node.ts @@ -5,11 +5,11 @@ import { import { IDataObject, - INodeTypeDescription, - INodeType, - IWebhookResponseData, ILoadOptionsFunctions, INodePropertyOptions, + INodeType, + INodeTypeDescription, + IWebhookResponseData, } from 'n8n-workflow'; import { diff --git a/packages/nodes-base/nodes/Webhook.node.ts b/packages/nodes-base/nodes/Webhook.node.ts index 1778d4429..0294d6556 100644 --- a/packages/nodes-base/nodes/Webhook.node.ts +++ b/packages/nodes-base/nodes/Webhook.node.ts @@ -6,8 +6,8 @@ import { import { IDataObject, INodeExecutionData, - INodeTypeDescription, INodeType, + INodeTypeDescription, IWebhookResponseData, } from 'n8n-workflow'; diff --git a/packages/nodes-base/nodes/WooCommerce/GenericFunctions.ts b/packages/nodes-base/nodes/WooCommerce/GenericFunctions.ts index 804392fea..f16887284 100644 --- a/packages/nodes-base/nodes/WooCommerce/GenericFunctions.ts +++ b/packages/nodes-base/nodes/WooCommerce/GenericFunctions.ts @@ -10,14 +10,14 @@ import { IWebhookFunctions, } from 'n8n-core'; import { + ICredentialDataDecryptedObject, IDataObject, - ICredentialDataDecryptedObject } from 'n8n-workflow'; import { - IShoppingLine, - IFeeLine, - ILineItem, - ICouponLine + ICouponLine, + IFeeLine, + ILineItem, + IShoppingLine, } from './OrderInterface'; export async function woocommerceApiRequest(this: IHookFunctions | IExecuteFunctions | IExecuteSingleFunctions | ILoadOptionsFunctions | IWebhookFunctions, method: string, resource: string, body: any = {}, qs: IDataObject = {}, uri?: string, option: IDataObject = {}): Promise { // tslint:disable-line:no-any @@ -33,7 +33,7 @@ export async function woocommerceApiRequest(this: IHookFunctions | IExecuteFunct method, qs, body, - uri: uri ||`${credentials.url}/wp-json/wc/v3${resource}`, + uri: uri || `${credentials.url}/wp-json/wc/v3${resource}`, json: true }; if (!Object.keys(body).length) { @@ -67,7 +67,7 @@ export async function woocommerceApiRequestAllItems(this: IExecuteFunctions | IL query.per_page = 100; do { responseData = await woocommerceApiRequest.call(this, method, endpoint, body, query, uri, { resolveWithFullResponse: true }); - uri = responseData.headers['link'].split(';')[0].replace('<', '').replace('>',''); + uri = responseData.headers['link'].split(';')[0].replace('<', '').replace('>', ''); returnData.push.apply(returnData, responseData.body); } while ( responseData.headers['link'] !== undefined && diff --git a/packages/nodes-base/nodes/WooCommerce/WooCommerce.node.ts b/packages/nodes-base/nodes/WooCommerce/WooCommerce.node.ts index bbb3d8668..9b9b8019b 100644 --- a/packages/nodes-base/nodes/WooCommerce/WooCommerce.node.ts +++ b/packages/nodes-base/nodes/WooCommerce/WooCommerce.node.ts @@ -4,10 +4,10 @@ import { import { IDataObject, ILoadOptionsFunctions, - INodeTypeDescription, INodeExecutionData, - INodeType, INodePropertyOptions, + INodeType, + INodeTypeDescription, } from 'n8n-workflow'; import { setMetadata, diff --git a/packages/nodes-base/nodes/WooCommerce/WooCommerceTrigger.node.ts b/packages/nodes-base/nodes/WooCommerce/WooCommerceTrigger.node.ts index 6a09e525d..15277e5ae 100644 --- a/packages/nodes-base/nodes/WooCommerce/WooCommerceTrigger.node.ts +++ b/packages/nodes-base/nodes/WooCommerce/WooCommerceTrigger.node.ts @@ -5,14 +5,14 @@ import { import { IDataObject, - INodeTypeDescription, INodeType, + INodeTypeDescription, IWebhookResponseData, } from 'n8n-workflow'; import { - woocommerceApiRequest, getAutomaticSecret, + woocommerceApiRequest, } from './GenericFunctions'; import { createHmac } from 'crypto'; diff --git a/packages/nodes-base/nodes/Wordpress/Wordpress.node.ts b/packages/nodes-base/nodes/Wordpress/Wordpress.node.ts index d7f930aee..36f845325 100644 --- a/packages/nodes-base/nodes/Wordpress/Wordpress.node.ts +++ b/packages/nodes-base/nodes/Wordpress/Wordpress.node.ts @@ -4,10 +4,10 @@ import { import { IDataObject, ILoadOptionsFunctions, - INodeTypeDescription, INodeExecutionData, - INodeType, INodePropertyOptions, + INodeType, + INodeTypeDescription, } from 'n8n-workflow'; import { wordpressApiRequest, diff --git a/packages/nodes-base/nodes/WriteBinaryFile.node.ts b/packages/nodes-base/nodes/WriteBinaryFile.node.ts index 2bcb8f8e9..4c6fba99f 100644 --- a/packages/nodes-base/nodes/WriteBinaryFile.node.ts +++ b/packages/nodes-base/nodes/WriteBinaryFile.node.ts @@ -4,9 +4,9 @@ import { } from 'n8n-core'; import { IDataObject, - INodeTypeDescription, INodeExecutionData, INodeType, + INodeTypeDescription, } from 'n8n-workflow'; import { diff --git a/packages/nodes-base/nodes/Xero/Xero.node.ts b/packages/nodes-base/nodes/Xero/Xero.node.ts index 31a1df36e..46825973c 100644 --- a/packages/nodes-base/nodes/Xero/Xero.node.ts +++ b/packages/nodes-base/nodes/Xero/Xero.node.ts @@ -4,11 +4,11 @@ import { import { IDataObject, - INodeTypeDescription, - INodeExecutionData, - INodeType, ILoadOptionsFunctions, + INodeExecutionData, INodePropertyOptions, + INodeType, + INodeTypeDescription, } from 'n8n-workflow'; import { diff --git a/packages/nodes-base/nodes/Xml.node.ts b/packages/nodes-base/nodes/Xml.node.ts index f684228d5..1b2fd2ab8 100644 --- a/packages/nodes-base/nodes/Xml.node.ts +++ b/packages/nodes-base/nodes/Xml.node.ts @@ -1,10 +1,10 @@ import { Builder, Parser } from 'xml2js'; import { IExecuteFunctions } from 'n8n-core'; import { + IDataObject, INodeExecutionData, INodeType, INodeTypeDescription, - IDataObject, } from 'n8n-workflow'; diff --git a/packages/nodes-base/nodes/Zendesk/Zendesk.node.ts b/packages/nodes-base/nodes/Zendesk/Zendesk.node.ts index 632660a34..0bb6e01fe 100644 --- a/packages/nodes-base/nodes/Zendesk/Zendesk.node.ts +++ b/packages/nodes-base/nodes/Zendesk/Zendesk.node.ts @@ -4,11 +4,11 @@ import { import { IDataObject, - INodeTypeDescription, - INodeExecutionData, - INodeType, ILoadOptionsFunctions, + INodeExecutionData, INodePropertyOptions, + INodeType, + INodeTypeDescription, } from 'n8n-workflow'; import { @@ -33,8 +33,8 @@ import { } from './UserDescription'; import { - ITicket, IComment, + ITicket, } from './TicketInterface'; export class Zendesk implements INodeType { diff --git a/packages/nodes-base/nodes/Zendesk/ZendeskTrigger.node.ts b/packages/nodes-base/nodes/Zendesk/ZendeskTrigger.node.ts index c8d1a8b37..8b4f4103f 100644 --- a/packages/nodes-base/nodes/Zendesk/ZendeskTrigger.node.ts +++ b/packages/nodes-base/nodes/Zendesk/ZendeskTrigger.node.ts @@ -8,12 +8,12 @@ import { } from 'n8n-core'; import { - INodeTypeDescription, - INodeType, - IWebhookResponseData, IDataObject, - INodePropertyOptions, ILoadOptionsFunctions, + INodePropertyOptions, + INodeType, + INodeTypeDescription, + IWebhookResponseData, } from 'n8n-workflow'; import { diff --git a/packages/nodes-base/nodes/Zoom/Zoom.node.ts b/packages/nodes-base/nodes/Zoom/Zoom.node.ts index 10c773f4c..e5d59abaf 100644 --- a/packages/nodes-base/nodes/Zoom/Zoom.node.ts +++ b/packages/nodes-base/nodes/Zoom/Zoom.node.ts @@ -17,8 +17,8 @@ import { } from './GenericFunctions'; import { - meetingOperations, meetingFields, + meetingOperations, } from './MeetingDescription'; // import { diff --git a/packages/nodes-base/nodes/Zulip/Zulip.node.ts b/packages/nodes-base/nodes/Zulip/Zulip.node.ts index 3c6261a37..3c716613b 100644 --- a/packages/nodes-base/nodes/Zulip/Zulip.node.ts +++ b/packages/nodes-base/nodes/Zulip/Zulip.node.ts @@ -5,10 +5,10 @@ import { import { IDataObject, ILoadOptionsFunctions, - INodeTypeDescription, INodeExecutionData, - INodeType, INodePropertyOptions, + INodeType, + INodeTypeDescription, } from 'n8n-workflow'; import { zulipApiRequest, @@ -21,9 +21,18 @@ import { IMessage, } from './MessageInterface'; import { snakeCase } from 'change-case'; -import { streamFields, streamOperations } from './StreamDescription'; -import { userOperations, userFields } from './UserDescription'; -import { IStream, IPrincipal } from './StreamInterface'; +import { + streamFields, + streamOperations, +} from './StreamDescription'; +import { + userFields, + userOperations, +} from './UserDescription'; +import { + IPrincipal, + IStream, +} from './StreamInterface'; import { validateJSON } from './GenericFunctions'; import { IUser } from './UserInterface'; diff --git a/packages/nodes-base/nodes/utils/allCurrencies.ts b/packages/nodes-base/nodes/utils/allCurrencies.ts index 2924edc32..88dacd318 100644 --- a/packages/nodes-base/nodes/utils/allCurrencies.ts +++ b/packages/nodes-base/nodes/utils/allCurrencies.ts @@ -1,174 +1,174 @@ -const allCurrencies = [ - { name: 'Euro', value: 'eur' }, - { name: 'United States Dollar', value: 'usd' }, - { name: 'British Pound Sterling', value: 'gbp' }, - { name: 'Swiss Franc', value: 'chf' }, - { name: 'Renminbi', value: 'cny' }, - { name: '--------', value: '' }, - { name: 'United Arab Emirates Dirham', value: 'aed' }, - { name: 'Afghan Afghani', value: 'afn' }, - { name: 'Albanian Lek', value: 'all' }, - { name: 'Armenian Dram', value: 'amd' }, - { name: 'Netherlands Antillean Guilder', value: 'ang' }, - { name: 'Angolan Kwanza', value: 'aoa' }, - { name: 'Argentine Peso', value: 'ars' }, - { name: 'Australian Dollar', value: 'aud' }, - { name: 'Aruban Florin', value: 'awg' }, - { name: 'Azerbaijani Manat', value: 'azn' }, - { name: 'Bosnia-Herzegovina Convertible Mark', value: 'bam' }, - { name: 'Barbadian Dollar', value: 'bbd' }, - { name: 'Bangladeshi Taka', value: 'bdt' }, - { name: 'Bulgarian Lev', value: 'bgn' }, - { name: 'Bahraini Dinar', value: 'bhd' }, - { name: 'Burundian Franc', value: 'bif' }, - { name: 'Bermudan Dollar', value: 'bmd' }, - { name: 'Brunei Dollar', value: 'bnd' }, - { name: 'Bolivian Boliviano', value: 'bob' }, - { name: 'Brazilian Real', value: 'brl' }, - { name: 'Bahamian Dollar', value: 'bsd' }, - { name: 'Bitcoin', value: 'btc' }, - { name: 'Bhutanese Ngultrum', value: 'btn' }, - { name: 'Botswanan Pula', value: 'bwp' }, - { name: 'Belarusian Ruble', value: 'byn' }, - { name: 'Belize Dollar', value: 'bzd' }, - { name: 'Canadian Dollar', value: 'cad' }, - { name: 'Congolese Franc', value: 'cdf' }, - { name: 'Chilean Unit of Account (UF)', value: 'clf' }, - { name: 'Chilean Peso', value: 'clp' }, - { name: 'Chinese Yuan (Offshore)', value: 'cnh' }, - { name: 'Colombian Peso', value: 'cop' }, - { name: 'Costa Rican Colón', value: 'crc' }, - { name: 'Cuban Convertible Peso', value: 'cuc' }, - { name: 'Cuban Peso', value: 'cup' }, - { name: 'Cape Verdean Escudo', value: 'cve' }, - { name: 'Czech Republic Koruna', value: 'czk' }, - { name: 'Djiboutian Franc', value: 'djf' }, - { name: 'Danish Krone', value: 'dkk' }, - { name: 'Dominican Peso', value: 'dop' }, - { name: 'Algerian Dinar', value: 'dzd' }, - { name: 'Egyptian Pound', value: 'egp' }, - { name: 'Eritrean Nakfa', value: 'ern' }, - { name: 'Ethiopian Birr', value: 'etb' }, - { name: 'Fijian Dollar', value: 'fjd' }, - { name: 'Falkland Islands Pound', value: 'fkp' }, - { name: 'Georgian Lari', value: 'gel' }, - { name: 'Guernsey Pound', value: 'ggp' }, - { name: 'Ghanaian Cedi', value: 'ghs' }, - { name: 'Gibraltar Pound', value: 'gip' }, - { name: 'Gambian Dalasi', value: 'gmd' }, - { name: 'Guinean Franc', value: 'gnf' }, - { name: 'Guatemalan Quetzal', value: 'gtq' }, - { name: 'Guyanaese Dollar', value: 'gyd' }, - { name: 'Hong Kong Dollar', value: 'hkd' }, - { name: 'Honduran Lempira', value: 'hnl' }, - { name: 'Croatian Kuna', value: 'hrk' }, - { name: 'Haitian Gourde', value: 'htg' }, - { name: 'Hungarian Forint', value: 'huf' }, - { name: 'Indonesian Rupiah', value: 'idr' }, - { name: 'Israeli New Sheqel', value: 'ils' }, - { name: 'Manx pound', value: 'imp' }, - { name: 'Indian Rupee', value: 'inr' }, - { name: 'Iraqi Dinar', value: 'iqd' }, - { name: 'Iranian Rial', value: 'irr' }, - { name: 'Icelandic Króna', value: 'isk' }, - { name: 'Jersey Pound', value: 'jep' }, - { name: 'Jamaican Dollar', value: 'jmd' }, - { name: 'Jordanian Dinar', value: 'jod' }, - { name: 'Japanese Yen', value: 'jpy' }, - { name: 'Kenyan Shilling', value: 'kes' }, - { name: 'Kyrgystani Som', value: 'kgs' }, - { name: 'Cambodian Riel', value: 'khr' }, - { name: 'Comorian Franc', value: 'kmf' }, - { name: 'North Korean Won', value: 'kpw' }, - { name: 'South Korean Won', value: 'krw' }, - { name: 'Kuwaiti Dinar', value: 'kwd' }, - { name: 'Cayman Islands Dollar', value: 'kyd' }, - { name: 'Kazakhstani Tenge', value: 'kzt' }, - { name: 'Laotian Kip', value: 'lak' }, - { name: 'Lebanese Pound', value: 'lbp' }, - { name: 'Sri Lankan Rupee', value: 'lkr' }, - { name: 'Liberian Dollar', value: 'lrd' }, - { name: 'Lesotho Loti', value: 'lsl' }, - { name: 'Libyan Dinar', value: 'lyd' }, - { name: 'Moroccan Dirham', value: 'mad' }, - { name: 'Moldovan Leu', value: 'mdl' }, - { name: 'Malagasy Ariary', value: 'mga' }, - { name: 'Macedonian Denar', value: 'mkd' }, - { name: 'Myanma Kyat', value: 'mmk' }, - { name: 'Mongolian Tugrik', value: 'mnt' }, - { name: 'Macanese Pataca', value: 'mop' }, - { name: 'Mauritanian Ouguiya (pre-2018)', value: 'mro' }, - { name: 'Mauritanian Ouguiya', value: 'mru' }, - { name: 'Mauritian Rupee', value: 'mur' }, - { name: 'Maldivian Rufiyaa', value: 'mvr' }, - { name: 'Malawian Kwacha', value: 'mwk' }, - { name: 'Mexican Peso', value: 'mxn' }, - { name: 'Malaysian Ringgit', value: 'myr' }, - { name: 'Mozambican Metical', value: 'mzn' }, - { name: 'Namibian Dollar', value: 'nad' }, - { name: 'Nigerian Naira', value: 'ngn' }, - { name: 'Nicaraguan Córdoba', value: 'nio' }, - { name: 'Norwegian Krone', value: 'nok' }, - { name: 'Nepalese Rupee', value: 'npr' }, - { name: 'New Zealand Dollar', value: 'nzd' }, - { name: 'Omani Rial', value: 'omr' }, - { name: 'Panamanian Balboa', value: 'pab' }, - { name: 'Peruvian Nuevo Sol', value: 'pen' }, - { name: 'Papua New Guinean Kina', value: 'pgk' }, - { name: 'Philippine Peso', value: 'php' }, - { name: 'Pakistani Rupee', value: 'pkr' }, - { name: 'Polish Zloty', value: 'pln' }, - { name: 'Paraguayan Guarani', value: 'pyg' }, - { name: 'Qatari Rial', value: 'qar' }, - { name: 'Romanian Leu', value: 'ron' }, - { name: 'Serbian Dinar', value: 'rsd' }, - { name: 'Russian Ruble', value: 'rub' }, - { name: 'Rwandan Franc', value: 'rwf' }, - { name: 'Saudi Riyal', value: 'sar' }, - { name: 'Solomon Islands Dollar', value: 'sbd' }, - { name: 'Seychellois Rupee', value: 'scr' }, - { name: 'Sudanese Pound', value: 'sdg' }, - { name: 'Swedish Krona', value: 'sek' }, - { name: 'Singapore Dollar', value: 'sgd' }, - { name: 'Saint Helena Pound', value: 'shp' }, - { name: 'Sierra Leonean Leone', value: 'sll' }, - { name: 'Somali Shilling', value: 'sos' }, - { name: 'Surinamese Dollar', value: 'srd' }, - { name: 'South Sudanese Pound', value: 'ssp' }, - { name: 'São Tomé and Príncipe Dobra (pre-2018)', value: 'std' }, - { name: 'São Tomé and Príncipe Dobra', value: 'stn' }, - { name: 'Salvadoran Colón', value: 'svc' }, - { name: 'Syrian Pound', value: 'syp' }, - { name: 'Swazi Lilangeni', value: 'szl' }, - { name: 'Thai Baht', value: 'thb' }, - { name: 'Tajikistani Somoni', value: 'tjs' }, - { name: 'Turkmenistani Manat', value: 'tmt' }, - { name: 'Tunisian Dinar', value: 'tnd' }, - { name: "Tongan Pa'anga", value: 'top' }, - { name: 'Turkish Lira', value: 'try' }, - { name: 'Trinidad and Tobago Dollar', value: 'ttd' }, - { name: 'New Taiwan Dollar', value: 'twd' }, - { name: 'Tanzanian Shilling', value: 'tzs' }, - { name: 'Ukrainian Hryvnia', value: 'uah' }, - { name: 'Ugandan Shilling', value: 'ugx' }, - { name: 'Uruguayan Peso', value: 'uyu' }, - { name: 'Uzbekistan Som', value: 'uzs' }, - { name: 'Venezuelan Bolívar Fuerte', value: 'vef' }, - { name: 'Vietnamese Dong', value: 'vnd' }, - { name: 'Vanuatu Vatu', value: 'vuv' }, - { name: 'Samoan Tala', value: 'wst' }, - { name: 'CFA Franc BEAC', value: 'xaf' }, - { name: 'Silver Ounce', value: 'xag' }, - { name: 'Gold Ounce', value: 'xau' }, - { name: 'East Caribbean Dollar', value: 'xcd' }, - { name: 'Special Drawing Rights', value: 'xdr' }, - { name: 'CFA Franc BCEAO', value: 'xof' }, - { name: 'Palladium Ounce', value: 'xpd' }, - { name: 'CFP Franc', value: 'xpf' }, - { name: 'Platinum Ounce', value: 'xpt' }, - { name: 'Yemeni Rial', value: 'yer' }, - { name: 'South African Rand', value: 'zar' }, - { name: 'Zambian Kwacha', value: 'zmw' }, - { name: 'Zimbabwean Dollar', value: 'zwl' } -// tslint:disable-next-line: semicolon -] \ No newline at end of file +const allCurrencies = [ + { name: 'Euro', value: 'eur' }, + { name: 'United States Dollar', value: 'usd' }, + { name: 'British Pound Sterling', value: 'gbp' }, + { name: 'Swiss Franc', value: 'chf' }, + { name: 'Renminbi', value: 'cny' }, + { name: '--------', value: '' }, + { name: 'United Arab Emirates Dirham', value: 'aed' }, + { name: 'Afghan Afghani', value: 'afn' }, + { name: 'Albanian Lek', value: 'all' }, + { name: 'Armenian Dram', value: 'amd' }, + { name: 'Netherlands Antillean Guilder', value: 'ang' }, + { name: 'Angolan Kwanza', value: 'aoa' }, + { name: 'Argentine Peso', value: 'ars' }, + { name: 'Australian Dollar', value: 'aud' }, + { name: 'Aruban Florin', value: 'awg' }, + { name: 'Azerbaijani Manat', value: 'azn' }, + { name: 'Bosnia-Herzegovina Convertible Mark', value: 'bam' }, + { name: 'Barbadian Dollar', value: 'bbd' }, + { name: 'Bangladeshi Taka', value: 'bdt' }, + { name: 'Bulgarian Lev', value: 'bgn' }, + { name: 'Bahraini Dinar', value: 'bhd' }, + { name: 'Burundian Franc', value: 'bif' }, + { name: 'Bermudan Dollar', value: 'bmd' }, + { name: 'Brunei Dollar', value: 'bnd' }, + { name: 'Bolivian Boliviano', value: 'bob' }, + { name: 'Brazilian Real', value: 'brl' }, + { name: 'Bahamian Dollar', value: 'bsd' }, + { name: 'Bitcoin', value: 'btc' }, + { name: 'Bhutanese Ngultrum', value: 'btn' }, + { name: 'Botswanan Pula', value: 'bwp' }, + { name: 'Belarusian Ruble', value: 'byn' }, + { name: 'Belize Dollar', value: 'bzd' }, + { name: 'Canadian Dollar', value: 'cad' }, + { name: 'Congolese Franc', value: 'cdf' }, + { name: 'Chilean Unit of Account (UF)', value: 'clf' }, + { name: 'Chilean Peso', value: 'clp' }, + { name: 'Chinese Yuan (Offshore)', value: 'cnh' }, + { name: 'Colombian Peso', value: 'cop' }, + { name: 'Costa Rican Colón', value: 'crc' }, + { name: 'Cuban Convertible Peso', value: 'cuc' }, + { name: 'Cuban Peso', value: 'cup' }, + { name: 'Cape Verdean Escudo', value: 'cve' }, + { name: 'Czech Republic Koruna', value: 'czk' }, + { name: 'Djiboutian Franc', value: 'djf' }, + { name: 'Danish Krone', value: 'dkk' }, + { name: 'Dominican Peso', value: 'dop' }, + { name: 'Algerian Dinar', value: 'dzd' }, + { name: 'Egyptian Pound', value: 'egp' }, + { name: 'Eritrean Nakfa', value: 'ern' }, + { name: 'Ethiopian Birr', value: 'etb' }, + { name: 'Fijian Dollar', value: 'fjd' }, + { name: 'Falkland Islands Pound', value: 'fkp' }, + { name: 'Georgian Lari', value: 'gel' }, + { name: 'Guernsey Pound', value: 'ggp' }, + { name: 'Ghanaian Cedi', value: 'ghs' }, + { name: 'Gibraltar Pound', value: 'gip' }, + { name: 'Gambian Dalasi', value: 'gmd' }, + { name: 'Guinean Franc', value: 'gnf' }, + { name: 'Guatemalan Quetzal', value: 'gtq' }, + { name: 'Guyanaese Dollar', value: 'gyd' }, + { name: 'Hong Kong Dollar', value: 'hkd' }, + { name: 'Honduran Lempira', value: 'hnl' }, + { name: 'Croatian Kuna', value: 'hrk' }, + { name: 'Haitian Gourde', value: 'htg' }, + { name: 'Hungarian Forint', value: 'huf' }, + { name: 'Indonesian Rupiah', value: 'idr' }, + { name: 'Israeli New Sheqel', value: 'ils' }, + { name: 'Manx pound', value: 'imp' }, + { name: 'Indian Rupee', value: 'inr' }, + { name: 'Iraqi Dinar', value: 'iqd' }, + { name: 'Iranian Rial', value: 'irr' }, + { name: 'Icelandic Króna', value: 'isk' }, + { name: 'Jersey Pound', value: 'jep' }, + { name: 'Jamaican Dollar', value: 'jmd' }, + { name: 'Jordanian Dinar', value: 'jod' }, + { name: 'Japanese Yen', value: 'jpy' }, + { name: 'Kenyan Shilling', value: 'kes' }, + { name: 'Kyrgystani Som', value: 'kgs' }, + { name: 'Cambodian Riel', value: 'khr' }, + { name: 'Comorian Franc', value: 'kmf' }, + { name: 'North Korean Won', value: 'kpw' }, + { name: 'South Korean Won', value: 'krw' }, + { name: 'Kuwaiti Dinar', value: 'kwd' }, + { name: 'Cayman Islands Dollar', value: 'kyd' }, + { name: 'Kazakhstani Tenge', value: 'kzt' }, + { name: 'Laotian Kip', value: 'lak' }, + { name: 'Lebanese Pound', value: 'lbp' }, + { name: 'Sri Lankan Rupee', value: 'lkr' }, + { name: 'Liberian Dollar', value: 'lrd' }, + { name: 'Lesotho Loti', value: 'lsl' }, + { name: 'Libyan Dinar', value: 'lyd' }, + { name: 'Moroccan Dirham', value: 'mad' }, + { name: 'Moldovan Leu', value: 'mdl' }, + { name: 'Malagasy Ariary', value: 'mga' }, + { name: 'Macedonian Denar', value: 'mkd' }, + { name: 'Myanma Kyat', value: 'mmk' }, + { name: 'Mongolian Tugrik', value: 'mnt' }, + { name: 'Macanese Pataca', value: 'mop' }, + { name: 'Mauritanian Ouguiya (pre-2018)', value: 'mro' }, + { name: 'Mauritanian Ouguiya', value: 'mru' }, + { name: 'Mauritian Rupee', value: 'mur' }, + { name: 'Maldivian Rufiyaa', value: 'mvr' }, + { name: 'Malawian Kwacha', value: 'mwk' }, + { name: 'Mexican Peso', value: 'mxn' }, + { name: 'Malaysian Ringgit', value: 'myr' }, + { name: 'Mozambican Metical', value: 'mzn' }, + { name: 'Namibian Dollar', value: 'nad' }, + { name: 'Nigerian Naira', value: 'ngn' }, + { name: 'Nicaraguan Córdoba', value: 'nio' }, + { name: 'Norwegian Krone', value: 'nok' }, + { name: 'Nepalese Rupee', value: 'npr' }, + { name: 'New Zealand Dollar', value: 'nzd' }, + { name: 'Omani Rial', value: 'omr' }, + { name: 'Panamanian Balboa', value: 'pab' }, + { name: 'Peruvian Nuevo Sol', value: 'pen' }, + { name: 'Papua New Guinean Kina', value: 'pgk' }, + { name: 'Philippine Peso', value: 'php' }, + { name: 'Pakistani Rupee', value: 'pkr' }, + { name: 'Polish Zloty', value: 'pln' }, + { name: 'Paraguayan Guarani', value: 'pyg' }, + { name: 'Qatari Rial', value: 'qar' }, + { name: 'Romanian Leu', value: 'ron' }, + { name: 'Serbian Dinar', value: 'rsd' }, + { name: 'Russian Ruble', value: 'rub' }, + { name: 'Rwandan Franc', value: 'rwf' }, + { name: 'Saudi Riyal', value: 'sar' }, + { name: 'Solomon Islands Dollar', value: 'sbd' }, + { name: 'Seychellois Rupee', value: 'scr' }, + { name: 'Sudanese Pound', value: 'sdg' }, + { name: 'Swedish Krona', value: 'sek' }, + { name: 'Singapore Dollar', value: 'sgd' }, + { name: 'Saint Helena Pound', value: 'shp' }, + { name: 'Sierra Leonean Leone', value: 'sll' }, + { name: 'Somali Shilling', value: 'sos' }, + { name: 'Surinamese Dollar', value: 'srd' }, + { name: 'South Sudanese Pound', value: 'ssp' }, + { name: 'São Tomé and Príncipe Dobra (pre-2018)', value: 'std' }, + { name: 'São Tomé and Príncipe Dobra', value: 'stn' }, + { name: 'Salvadoran Colón', value: 'svc' }, + { name: 'Syrian Pound', value: 'syp' }, + { name: 'Swazi Lilangeni', value: 'szl' }, + { name: 'Thai Baht', value: 'thb' }, + { name: 'Tajikistani Somoni', value: 'tjs' }, + { name: 'Turkmenistani Manat', value: 'tmt' }, + { name: 'Tunisian Dinar', value: 'tnd' }, + { name: "Tongan Pa'anga", value: 'top' }, + { name: 'Turkish Lira', value: 'try' }, + { name: 'Trinidad and Tobago Dollar', value: 'ttd' }, + { name: 'New Taiwan Dollar', value: 'twd' }, + { name: 'Tanzanian Shilling', value: 'tzs' }, + { name: 'Ukrainian Hryvnia', value: 'uah' }, + { name: 'Ugandan Shilling', value: 'ugx' }, + { name: 'Uruguayan Peso', value: 'uyu' }, + { name: 'Uzbekistan Som', value: 'uzs' }, + { name: 'Venezuelan Bolívar Fuerte', value: 'vef' }, + { name: 'Vietnamese Dong', value: 'vnd' }, + { name: 'Vanuatu Vatu', value: 'vuv' }, + { name: 'Samoan Tala', value: 'wst' }, + { name: 'CFA Franc BEAC', value: 'xaf' }, + { name: 'Silver Ounce', value: 'xag' }, + { name: 'Gold Ounce', value: 'xau' }, + { name: 'East Caribbean Dollar', value: 'xcd' }, + { name: 'Special Drawing Rights', value: 'xdr' }, + { name: 'CFA Franc BCEAO', value: 'xof' }, + { name: 'Palladium Ounce', value: 'xpd' }, + { name: 'CFP Franc', value: 'xpf' }, + { name: 'Platinum Ounce', value: 'xpt' }, + { name: 'Yemeni Rial', value: 'yer' }, + { name: 'South African Rand', value: 'zar' }, + { name: 'Zambian Kwacha', value: 'zmw' }, + { name: 'Zimbabwean Dollar', value: 'zwl' } + // tslint:disable-next-line: semicolon +] From 39c173a2727effb3180d53733a23498a4d02c6ad Mon Sep 17 00:00:00 2001 From: Ricardo Espinoza Date: Fri, 2 Oct 2020 01:31:41 -0400 Subject: [PATCH 125/284] Feature/slack status change (#995) * Added methods to set and get the user's status. * Fixed set status message * Improvements to #993 * :lipstick: small cosmetic change Co-authored-by: Tobias Schulz-Hess Co-authored-by: Tobias Schulz-Hess --- .../credentials/SlackOAuth2Api.credentials.ts | 2 + packages/nodes-base/nodes/Slack/Slack.node.ts | 83 ++++++++ .../nodes/Slack/UserProfileDescription.ts | 183 ++++++++++++++++++ 3 files changed, 268 insertions(+) create mode 100644 packages/nodes-base/nodes/Slack/UserProfileDescription.ts diff --git a/packages/nodes-base/credentials/SlackOAuth2Api.credentials.ts b/packages/nodes-base/credentials/SlackOAuth2Api.credentials.ts index 3e600122f..d9e4fb551 100644 --- a/packages/nodes-base/credentials/SlackOAuth2Api.credentials.ts +++ b/packages/nodes-base/credentials/SlackOAuth2Api.credentials.ts @@ -10,6 +10,8 @@ const userScopes = [ 'files:write', 'stars:read', 'stars:write', + 'users.profile:read', + 'users.profile:write' ]; export class SlackOAuth2Api implements ICredentialType { diff --git a/packages/nodes-base/nodes/Slack/Slack.node.ts b/packages/nodes-base/nodes/Slack/Slack.node.ts index 391cb1589..577076919 100644 --- a/packages/nodes-base/nodes/Slack/Slack.node.ts +++ b/packages/nodes-base/nodes/Slack/Slack.node.ts @@ -16,26 +16,37 @@ import { channelFields, channelOperations, } from './ChannelDescription'; + import { messageFields, messageOperations, } from './MessageDescription'; + import { starFields, starOperations, } from './StarDescription'; + import { fileFields, fileOperations, } from './FileDescription'; + +import { + userProfileFields, + userProfileOperations, +} from './UserProfileDescription'; + import { slackApiRequest, slackApiRequestAllItems, validateJSON, } from './GenericFunctions'; + import { IAttachment, } from './MessageInterface'; +import moment = require('moment'); interface Attachment { fields: { @@ -156,6 +167,10 @@ export class Slack implements INodeType { name: 'Star', value: 'star', }, + { + name: 'User Profile', + value: 'userProfile', + }, ], default: 'message', description: 'The resource to operate on.', @@ -169,6 +184,8 @@ export class Slack implements INodeType { ...starFields, ...fileOperations, ...fileFields, + ...userProfileOperations, + ...userProfileFields, ], }; @@ -218,6 +235,22 @@ export class Slack implements INodeType { return returnData; }, + // Get all the team fields to display them to user so that he can + // select them easily + async getTeamFields(this: ILoadOptionsFunctions): Promise { + const returnData: INodePropertyOptions[] = []; + const { profile: { fields } } = await slackApiRequest.call(this, 'GET', '/team.profile.get'); + console.log(fields); + for (const field of fields) { + const fieldName = field.label; + const fieldId = field.id; + returnData.push({ + name: fieldName, + value: fieldId, + }); + } + return returnData; + }, } }; @@ -232,6 +265,7 @@ export class Slack implements INodeType { const operation = this.getNodeParameter('operation', 0) as string; for (let i = 0; i < length; i++) { + responseData = { error: 'Resource ' + resource + ' / operation ' + operation + ' not found!'}; qs = {}; if (resource === 'channel') { //https://api.slack.com/methods/conversations.archive @@ -848,6 +882,55 @@ export class Slack implements INodeType { responseData = responseData.file; } } + if (resource === 'userProfile') { + //https://api.slack.com/methods/users.profile.set + if (operation === 'update') { + const additionalFields = this.getNodeParameter('additionalFields', i) as IDataObject; + + const timezone = this.getTimezone(); + + const body: IDataObject = {}; + + Object.assign(body, additionalFields); + + if (body.status_expiration === undefined) { + body.status_expiration = 0; + + } else { + body.status_expiration = moment.tz(body.status_expiration as string, timezone).unix(); + } + + if (body.customFieldUi) { + const customFields = (body.customFieldUi as IDataObject).customFieldValues as IDataObject[]; + + body.fields = {}; + + for (const customField of customFields) { + //@ts-ignore + body.fields[customField.id] = { + value: customField.value, + alt: customField.alt, + }; + } + } + + responseData = await slackApiRequest.call(this, 'POST', '/users.profile.set', { profile: body }, qs); + + responseData = responseData.profile; + } + //https://api.slack.com/methods/users.profile.get + if (operation === 'get') { + const additionalFields = this.getNodeParameter('additionalFields', i) as IDataObject; + + const body: IDataObject = {}; + + Object.assign(body, additionalFields); + + responseData = await slackApiRequest.call(this, 'POST', '/users.profile.get', body); + + responseData = responseData.profile; + } + } if (Array.isArray(responseData)) { returnData.push.apply(returnData, responseData as IDataObject[]); } else { diff --git a/packages/nodes-base/nodes/Slack/UserProfileDescription.ts b/packages/nodes-base/nodes/Slack/UserProfileDescription.ts new file mode 100644 index 000000000..a3475ee99 --- /dev/null +++ b/packages/nodes-base/nodes/Slack/UserProfileDescription.ts @@ -0,0 +1,183 @@ +import { + INodeProperties, +} from 'n8n-workflow'; +import { text } from 'express'; + +export const userProfileOperations = [ + { + displayName: 'Operation', + name: 'operation', + type: 'options', + displayOptions: { + show: { + resource: [ + 'userProfile', + ], + }, + }, + options: [ + { + name: 'Get', + value: 'get', + description: `Get your user's profile`, + }, + { + name: 'Update', + value: 'update', + description: `Update user's profile`, + }, + ], + default: 'get', + description: 'The operation to perform.', + }, +] as INodeProperties[]; + +export const userProfileFields = [ + +/* -------------------------------------------------------------------------- */ +/* userProfile:update */ +/* -------------------------------------------------------------------------- */ + { + displayName: 'Additional Fields', + name: 'additionalFields', + type: 'collection', + placeholder: 'Add Field', + default: {}, + displayOptions: { + show: { + resource: [ + 'userProfile', + ], + operation: [ + 'update', + ], + }, + }, + options: [ + { + displayName: 'Custom Fields', + name: 'customFieldUi', + placeholder: 'Add Custom Fields', + type: 'fixedCollection', + typeOptions: { + multipleValues: true, + }, + default: {}, + options: [ + { + name: 'customFieldValues', + displayName: 'Custom Field', + values: [ + { + displayName: 'Field ID', + name: 'id', + type: 'options', + typeOptions: { + loadOptionsMethod: 'getTeamFields', + }, + default: '', + description: 'ID of the field to set.', + }, + { + displayName: 'Field Value', + name: 'value', + type: 'string', + default: '', + description: 'Value of the field to set.', + }, + { + displayName: 'Alt', + name: 'alt', + type: 'string', + default: '', + }, + ], + }, + ], + }, + { + displayName: 'Email', + name: 'email', + type: 'string', + default: '', + description: `This field can only be changed by admins for users on paid teams.`, + }, + { + displayName: 'First Name', + name: 'first_name', + type: 'string', + default: '', + }, + { + displayName: 'Last Name', + name: 'last_name', + type: 'string', + default: '', + }, + { + displayName: 'Status Emoji', + name: 'status_emoji', + type: 'string', + default: '', + description: `is a string referencing an emoji enabled for the Slack team, such as :mountain_railway:`, + }, + { + displayName: 'Status Expiration', + name: 'status_expiration', + type: 'dateTime', + default: '', + description: `is an integer specifying seconds since the epoch, more commonly known as "UNIX time". Providing 0 or omitting this field results in a custom status that will not expire`, + }, + { + displayName: 'Status Text', + name: 'status_text', + type: 'string', + default: '', + description: `allows up to 100 characters, though we strongly encourage brevity.`, + }, + { + displayName: 'User ID', + name: 'user', + type: 'string', + default: '', + description: `ID of user to change. This argument may only be specified by team admins on paid teams.`, + }, + ], + }, +/* -------------------------------------------------------------------------- */ +/* userProfile:get */ +/* -------------------------------------------------------------------------- */ + { + displayName: 'Additional Fields', + name: 'additionalFields', + type: 'collection', + placeholder: 'Add Field', + default: {}, + displayOptions: { + show: { + resource: [ + 'userProfile', + ], + operation: [ + 'get', + ], + }, + }, + options: [ + { + displayName: 'Include Labels', + name: 'include_labels', + type: 'boolean', + default: false, + description: `Include labels for each ID in custom profile fields`, + }, + { + displayName: 'User ID', + name: 'user', + type: 'string', + default: '', + description: `User to retrieve profile info for`, + }, + ], + }, +] as INodeProperties[]; From 383ee9533f48d5ad9b97fbcb1424b6e1a318a85b Mon Sep 17 00:00:00 2001 From: Jan Oberhauser Date: Fri, 2 Oct 2020 08:50:37 +0200 Subject: [PATCH 126/284] :zap: Error if Slack-Requests fail --- packages/nodes-base/nodes/Slack/GenericFunctions.ts | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/packages/nodes-base/nodes/Slack/GenericFunctions.ts b/packages/nodes-base/nodes/Slack/GenericFunctions.ts index 5686d9c0e..be43f8b18 100644 --- a/packages/nodes-base/nodes/Slack/GenericFunctions.ts +++ b/packages/nodes-base/nodes/Slack/GenericFunctions.ts @@ -35,6 +35,9 @@ export async function slackApiRequest(this: IExecuteFunctions | IExecuteSingleFu delete options.qs; } try { + + let response: any; // tslint:disable-line:no-any + if (authenticationMethod === 'accessToken') { const credentials = this.getCredentials('slackApi'); if (credentials === undefined) { @@ -42,7 +45,7 @@ export async function slackApiRequest(this: IExecuteFunctions | IExecuteSingleFu } options.headers!.Authorization = `Bearer ${credentials.accessToken}`; //@ts-ignore - return await this.helpers.request(options); + response = await this.helpers.request(options); } else { const oAuth2Options: IOAuth2Options = { @@ -50,8 +53,14 @@ export async function slackApiRequest(this: IExecuteFunctions | IExecuteSingleFu property: 'authed_user.access_token', }; //@ts-ignore - return await this.helpers.requestOAuth2.call(this, 'slackOAuth2Api', options, oAuth2Options); + response = await this.helpers.requestOAuth2.call(this, 'slackOAuth2Api', options, oAuth2Options); } + + if (response.ok === false) { + throw new Error('Slack error response: ' + JSON.stringify(response)); + } + + return response; } catch (error) { if (error.statusCode === 401) { // Return a clear error From ff5eb0a5ecc500db0b8f31a5a7140e9b53283af3 Mon Sep 17 00:00:00 2001 From: Harshil Date: Sat, 3 Oct 2020 04:15:58 +0530 Subject: [PATCH 127/284] =?UTF-8?q?=E2=9C=A8=20Add=20Campaign=20Resource?= =?UTF-8?q?=20with=20get=20all=20and=20send=20operation?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../nodes/Mailchimp/Mailchimp.node.ts | 317 ++++++++++++++++++ 1 file changed, 317 insertions(+) diff --git a/packages/nodes-base/nodes/Mailchimp/Mailchimp.node.ts b/packages/nodes-base/nodes/Mailchimp/Mailchimp.node.ts index ff5ee6364..db57d73a3 100644 --- a/packages/nodes-base/nodes/Mailchimp/Mailchimp.node.ts +++ b/packages/nodes-base/nodes/Mailchimp/Mailchimp.node.ts @@ -125,6 +125,10 @@ export class Mailchimp implements INodeType { name: 'Member Tag', value: 'memberTag', }, + { + name: 'Campaign', + value: 'campaign' + } ], default: 'member', required: true, @@ -221,6 +225,89 @@ export class Mailchimp implements INodeType { default: 'getAll', description: 'The operation to perform.', }, + { + displayName: 'Operation', + name: 'operation', + type:'options', + required: true, + displayOptions : { + show: { + resource: [ + 'campaign', + ], + }, + + }, + options: [ + { + name: 'Create', + value: 'create', + description: 'Create a new campaign' + }, + { + name: 'Delete', + value: 'delete', + description: 'Delete a campaign' + }, + { + name: 'Get', + value: 'get', + description: 'Get a campaign' + }, + { + name: 'Get All', + value: 'getAll', + description: 'Get all the campaigns' + }, + { + name: 'Schedule', + value: 'schedule', + description: 'Schedule a campaign' + }, + { + name: 'Pause RSS', + value: 'pauseRss', + description: 'Pause an RSS-driven campaign' + }, + { + name: 'Replicate', + value: 'replicate', + description: 'Replicate a campaign in saved or send status' + }, + { + name: 'Resend', + value: 'resend', + description: 'Creates a Resend to Non-Openers version of this campaign.' + }, + { + name: 'Resume RSS', + value: 'resumeRss', + description: 'Resume an RSS-driven campaign' + }, + { + name: 'Send', + value: 'send', + description: 'Send a campaign' + }, + { + name: 'Send Test Email', + value: 'sendTestEmail', + description: 'Send a test email' + }, + { + name: 'Unschedule', + value: 'unschedule', + description: 'Unschedule a scheduled campaign' + }, + { + name: 'Update', + value: 'update', + description: 'Update a campaign' + }, + ], + default: 'getAll', + description: 'The operation to perform.' + }, /* -------------------------------------------------------------------------- */ /* member:create */ /* -------------------------------------------------------------------------- */ @@ -1500,6 +1587,176 @@ export class Mailchimp implements INodeType { default: 500, description: 'How many results to return.', }, +/* -------------------------------------------------------------------------- */ +/* campaign:getAll */ +/* -------------------------------------------------------------------------- */ + { + displayName: 'Return All', + name: 'returnAll', + type: 'boolean', + displayOptions: { + show: { + resource: [ + 'campaign', + ], + 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: [ + 'campaign', + ], + operation: [ + 'getAll', + ], + returnAll: [ + false, + ], + }, + }, + typeOptions: { + minValue: 1, + maxValue: 1000, + }, + default: 10, + description: 'How many results to return.', + }, + { + displayName: 'Options', + name: 'options', + type: 'collection', + placeholder: 'Add Option', + default: {}, + displayOptions: { + show: { + resource:[ + 'campaign', + ], + operation: [ + 'getAll', + ], + }, + }, + options: [ + { + displayName: 'Before Create Time', + name: 'beforeCreateTime', + type: 'dateTime', + default: '', + description: 'Restrict the response to campaigns created before the set time.', + }, + { + displayName: 'Before Send Time', + name: 'beforeSendTime', + type: 'dateTime', + default: '', + description: 'Restrict the response to campaigns sent before the set time.', + }, + { + displayName: 'Exclude Fields', + name: 'excludeFields', + type: 'string', + default: '', + description: 'A comma-separated list of fields to exclude.', + }, + { + displayName: 'Fields', + name: 'fields', + type: 'string', + default: '', + description: 'A comma-separated list of fields to return.', + }, + { + displayName: 'List ID', + name: 'listId', + type: 'options', + typeOptions: { + loadOptionsMethod: 'getLists', + }, + default: '', + description: 'List of lists' + }, + { + displayName: 'Since Create Time', + name: 'sinceCreateTime', + type: 'dateTime', + default: '', + description: 'Restrict the response to campaigns created after the set time.', + }, + { + displayName: 'Since Send Time', + name: 'sinceSendTime', + type: 'dateTime', + default: '', + description: 'Restrict the response to campaigns sent after the set time.', + }, + { + displayName: 'Status', + name: 'status', + type: 'options', + options: [ + { + name: 'Save', + value: 'save', + }, + { + name: 'Sending', + value: 'sending', + }, + { + name: 'Sent', + value: 'sent', + }, + { + name: 'Schedule', + value: 'schedule', + }, + ], + default: '', + description: 'The status of the campaign.', + }, + ], + }, +/* -------------------------------------------------------------------------- */ +/* campaign:send */ +/* -------------------------------------------------------------------------- */ + { + displayName: 'Campaign ID', + name: 'campaignId', + type: 'options', + typeOptions: { + loadOptionsMethod: 'getCampaigns', + }, + displayOptions: { + show: { + resource: [ + 'campaign' + ], + operation: [ + 'send' + ] + } + }, + required: true, + default: '', + description: 'List of Campaigns', + options:[], + }, +/* -------------------------------------------------------------------------- */ +/* campaign:create */ +/* -------------------------------------------------------------------------- */ + + ], }; @@ -1556,6 +1813,21 @@ export class Mailchimp implements INodeType { } return returnData; }, + + // Get all the available campaigns to display them to users so that they can select them easily + async getCampaigns(this: ILoadOptionsFunctions): Promise { + const returnData: INodePropertyOptions[] = []; + const campaigns = await mailchimpApiRequestAllItems.call(this, '/campaigns', 'GET', 'campaigns'); + for (const campaign of campaigns) { + const campaignName = campaign.settings.title; + const campaignId = campaign.id; + returnData.push({ + name: campaignName, + value: campaignId, + }); + } + return returnData + } } }; @@ -1884,6 +2156,51 @@ export class Mailchimp implements INodeType { responseData = { success: true }; } } + if (resource === 'campaign') { + if (operation === 'getAll') { + const returnAll = this.getNodeParameter('returnAll', i) as boolean; + const options = this.getNodeParameter('options', i) as IDataObject; + if (options.status) { + qs.status = options.status as string; + } + if (options.beforeCreateTime) { + qs.beforeCreateTime = options.beforeCreateTime as string; + } + if (options.beforeSendTime) { + qs.beforeSendTime = options.beforeSendTime as string; + } + // TODO + // Figure out how to make excludeFields and fileds work + // if (options.excludeFields) { + // qs.excludeFields = options.excludeFields as string; + // } + // if (options.fields) { + // qs.fields = options.fields as string; + // } + if (options.listId) { + qs.listId = options.listId as string; + } + if (options.sinceCreateTime) { + qs.sinceCreateTime = options.sinceCreateTime as string; + } + if (options.sinceSendTime) { + qs.sinceSendTime = options.sinceSendTime as string; + } + // TODO + // Make the options work when returnAll is false + if (returnAll === true) { + responseData = await mailchimpApiRequestAllItems.call(this, `/campaigns`, 'GET', 'campaigns', {}, qs) + } else { + qs.count = this.getNodeParameter('limit', i) as number; + responseData = await mailchimpApiRequest.call(this, `/campaigns`, 'GET', 'campaigns', {}, qs) + responseData = responseData.campaigns + } + } + if (operation === 'send') { + const campaignId = this.getNodeParameter('campaignId', i) as string; + responseData = await mailchimpApiRequest.call(this, `/campaigns/${campaignId}/actions/send`, 'POST', {}) + } + } if (Array.isArray(responseData)) { returnData.push.apply(returnData, responseData as IDataObject[]); From 016102a647c4bc10a77f90cbee30a8a3a9dc559a Mon Sep 17 00:00:00 2001 From: Harshil Date: Sat, 3 Oct 2020 04:19:53 +0530 Subject: [PATCH 128/284] =?UTF-8?q?=E2=9C=A8=20Add=20Campaign=20Resource?= =?UTF-8?q?=20with=20get=20all=20and=20send=20operation?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../nodes/Mailchimp/Mailchimp.node.ts | 55 ------------------- 1 file changed, 55 deletions(-) diff --git a/packages/nodes-base/nodes/Mailchimp/Mailchimp.node.ts b/packages/nodes-base/nodes/Mailchimp/Mailchimp.node.ts index db57d73a3..09a960278 100644 --- a/packages/nodes-base/nodes/Mailchimp/Mailchimp.node.ts +++ b/packages/nodes-base/nodes/Mailchimp/Mailchimp.node.ts @@ -239,71 +239,16 @@ export class Mailchimp implements INodeType { }, options: [ - { - name: 'Create', - value: 'create', - description: 'Create a new campaign' - }, - { - name: 'Delete', - value: 'delete', - description: 'Delete a campaign' - }, - { - name: 'Get', - value: 'get', - description: 'Get a campaign' - }, { name: 'Get All', value: 'getAll', description: 'Get all the campaigns' }, - { - name: 'Schedule', - value: 'schedule', - description: 'Schedule a campaign' - }, - { - name: 'Pause RSS', - value: 'pauseRss', - description: 'Pause an RSS-driven campaign' - }, - { - name: 'Replicate', - value: 'replicate', - description: 'Replicate a campaign in saved or send status' - }, - { - name: 'Resend', - value: 'resend', - description: 'Creates a Resend to Non-Openers version of this campaign.' - }, - { - name: 'Resume RSS', - value: 'resumeRss', - description: 'Resume an RSS-driven campaign' - }, { name: 'Send', value: 'send', description: 'Send a campaign' }, - { - name: 'Send Test Email', - value: 'sendTestEmail', - description: 'Send a test email' - }, - { - name: 'Unschedule', - value: 'unschedule', - description: 'Unschedule a scheduled campaign' - }, - { - name: 'Update', - value: 'update', - description: 'Update a campaign' - }, ], default: 'getAll', description: 'The operation to perform.' From 49914559f547431755ed33aa46de0697180fe358 Mon Sep 17 00:00:00 2001 From: Harshil Date: Sat, 3 Oct 2020 05:10:10 +0530 Subject: [PATCH 129/284] =?UTF-8?q?=F0=9F=9A=A7=20Add=20Get=20and=20Delete?= =?UTF-8?q?=20operation?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../nodes/Mailchimp/Mailchimp.node.ts | 40 ++++++++++++++----- 1 file changed, 30 insertions(+), 10 deletions(-) diff --git a/packages/nodes-base/nodes/Mailchimp/Mailchimp.node.ts b/packages/nodes-base/nodes/Mailchimp/Mailchimp.node.ts index 09a960278..086e341a3 100644 --- a/packages/nodes-base/nodes/Mailchimp/Mailchimp.node.ts +++ b/packages/nodes-base/nodes/Mailchimp/Mailchimp.node.ts @@ -113,6 +113,10 @@ export class Mailchimp implements INodeType { name: 'resource', type: 'options', options: [ + { + name: 'Campaign', + value: 'campaign' + }, { name: 'List Group', value: 'listGroup', @@ -125,10 +129,6 @@ export class Mailchimp implements INodeType { name: 'Member Tag', value: 'memberTag', }, - { - name: 'Campaign', - value: 'campaign' - } ], default: 'member', required: true, @@ -239,6 +239,16 @@ export class Mailchimp implements INodeType { }, options: [ + { + name: 'Delete', + value: 'delete', + description: 'Delete a campaign' + }, + { + name: 'Get', + value: 'get', + description: 'Get a campaign' + }, { name: 'Get All', value: 'getAll', @@ -1688,7 +1698,9 @@ export class Mailchimp implements INodeType { 'campaign' ], operation: [ - 'send' + 'send', + 'get', + 'delete' ] } }, @@ -1697,11 +1709,6 @@ export class Mailchimp implements INodeType { description: 'List of Campaigns', options:[], }, -/* -------------------------------------------------------------------------- */ -/* campaign:create */ -/* -------------------------------------------------------------------------- */ - - ], }; @@ -2145,6 +2152,19 @@ export class Mailchimp implements INodeType { const campaignId = this.getNodeParameter('campaignId', i) as string; responseData = await mailchimpApiRequest.call(this, `/campaigns/${campaignId}/actions/send`, 'POST', {}) } + if (operation === 'get') { + const campaignId = this.getNodeParameter('campaignId', i) as string; + if(!campaignId){ + // TODO + // Display error message. + throw new Error("Campaign ID is required"); + } + responseData = await mailchimpApiRequest.call(this, `/campaigns/${campaignId}`, 'GET', {}) + } + if (operation === 'delete') { + const campaignId = this.getNodeParameter('campaignId', i) as string; + responseData = await mailchimpApiRequest.call(this, `/campaigns/${campaignId}`, 'Delete', {}) + } } if (Array.isArray(responseData)) { From 7c51964bd200860f698422d34271c7a8610b8aaf Mon Sep 17 00:00:00 2001 From: Jan Date: Sat, 3 Oct 2020 11:50:57 +0200 Subject: [PATCH 130/284] :sparkles: Add CoinGecko-Node * Add CoinGecko node * CoinGecko improvements and add events * :zap: Small improvements * :zap: Improvements to CoinGecko-Node Co-authored-by: Dokime Co-authored-by: ricardo --- .../nodes/CoinGecko/CoinDescription.ts | 740 ++++++++++++++++++ .../nodes/CoinGecko/CoinGecko.node.ts | 537 +++++++++++++ .../nodes/CoinGecko/EventDescription.ts | 130 +++ .../nodes/CoinGecko/GenericFunctions.ts | 67 ++ .../nodes-base/nodes/CoinGecko/coinGecko.png | Bin 0 -> 3955 bytes packages/nodes-base/package.json | 1 + 6 files changed, 1475 insertions(+) create mode 100644 packages/nodes-base/nodes/CoinGecko/CoinDescription.ts create mode 100644 packages/nodes-base/nodes/CoinGecko/CoinGecko.node.ts create mode 100644 packages/nodes-base/nodes/CoinGecko/EventDescription.ts create mode 100644 packages/nodes-base/nodes/CoinGecko/GenericFunctions.ts create mode 100644 packages/nodes-base/nodes/CoinGecko/coinGecko.png diff --git a/packages/nodes-base/nodes/CoinGecko/CoinDescription.ts b/packages/nodes-base/nodes/CoinGecko/CoinDescription.ts new file mode 100644 index 000000000..6b11cea60 --- /dev/null +++ b/packages/nodes-base/nodes/CoinGecko/CoinDescription.ts @@ -0,0 +1,740 @@ +import { + INodeProperties, +} from 'n8n-workflow'; + +export const coinOperations = [ + { + displayName: 'Operation', + name: 'operation', + type: 'options', + displayOptions: { + show: { + resource: [ + 'coin', + ], + }, + }, + options: [ + { + name: 'Candlestick', + value: 'candlestick', + description: 'Get a candlestick open-high-low-close chart for the selected currency', + }, + { + name: 'Get', + value: 'get', + description: 'Get current data for a coin', + }, + { + name: 'Get All', + value: 'getAll', + description: 'Get all coins', + }, + { + name: 'History', + value: 'history', + description: 'Get historical data (name, price, market, stats) at a given date for a coin', + }, + { + name: 'Market', + value: 'market', + description: 'Get prices and market related data for all trading pairs that match the selected currency', + }, + { + name: 'Market Chart', + value: 'marketChart', + description: 'Get historical market data include price, market cap, and 24h volume (granularity auto)', + }, + { + name: 'Price', + value: 'price', + description: 'Get the current price of any cryptocurrencies in any other supported currencies that you need', + }, + { + name: 'Ticker', + value: 'ticker', + description: 'Get coin tickers', + }, + ], + default: 'getAll', + }, +] as INodeProperties[]; + +export const coinFields = [ + { + displayName: 'Search By', + name: 'searchBy', + required: true, + type: 'options', + options: [ + { + name: 'Coin ID', + value: 'coinId', + }, + { + name: 'Contract address', + value: 'contractAddress', + }, + ], + displayOptions: { + show: { + operation: [ + 'get', + 'marketChart', + 'price', + ], + resource: [ + 'coin', + ], + }, + }, + default: 'coinId', + description: 'Search by coin ID or contract address.', + }, + { + displayName: 'Coin ID', + name: 'coinId', + required: true, + type: 'options', + typeOptions: { + loadOptionsMethod: 'getCoins', + }, + displayOptions: { + show: { + operation: [ + 'get', + ], + resource: [ + 'coin', + ], + }, + }, + default: '', + placeholder: 'bitcoin', + description: 'Coin ID', + }, + { + displayName: 'Coin ID', + name: 'coinId', + required: true, + type: 'options', + typeOptions: { + loadOptionsMethod: 'getCoins', + }, + displayOptions: { + show: { + operation: [ + 'ticker', + 'history', + 'candlestick', + ], + resource: [ + 'coin', + ], + }, + }, + default: '', + placeholder: 'bitcoin', + description: 'Coin ID', + }, + { + displayName: 'Coin IDs', + name: 'coinIds', + required: true, + type: 'multiOptions', + typeOptions: { + loadOptionsMethod: 'getCoins', + }, + displayOptions: { + show: { + operation: [ + 'price', + ], + resource: [ + 'coin', + ], + searchBy: [ + 'coinId' + ], + }, + }, + default: [], + placeholder: 'bitcoin', + description: 'ID of coins, comma-separated. Refers to Coin / GetAll.', + }, + { + displayName: 'Platform ID', + name: 'platformId', + required: true, + displayOptions: { + show: { + operation: [ + 'get', + 'marketChart', + 'price', + ], + resource: [ + 'coin', + ], + searchBy: [ + 'contractAddress' + ], + }, + }, + type: 'options', + options: [ + { + name: 'Ethereum', + value: 'ethereum', + }, + ], + default: 'ethereum', + description: 'The id of the platform issuing tokens.', + }, + { + displayName: 'Contract address', + name: 'contractAddress', + required: true, + type: 'string', + displayOptions: { + show: { + operation: [ + 'get', + 'marketChart', + ], + resource: [ + 'coin', + ], + searchBy: [ + 'contractAddress' + ], + }, + }, + description: 'Token\'s contract address.', + }, + { + displayName: 'Contract addresses', + name: 'contractAddresses', + required: true, + type: 'string', + displayOptions: { + show: { + operation: [ + 'price', + ], + resource: [ + 'coin', + ], + searchBy: [ + 'contractAddress' + ], + }, + }, + description: 'The contract address of tokens, comma separated.', + }, + { + displayName: 'Base Currency', + name: 'baseCurrency', + required: true, + type: 'options', + typeOptions: { + loadOptionsMethod: 'getCoins', + }, + displayOptions: { + show: { + operation: [ + 'marketChart', + ], + resource: [ + 'coin', + ], + searchBy: [ + 'coinId' + ], + }, + hide: { + searchBy: [ + 'contractAddress', + ], + }, + }, + default: '', + description: 'The first currency in the pair. For BTC:ETH this is BTC.', + }, + { + displayName: 'Quote Currency', + name: 'quoteCurrency', + required: true, + type: 'options', + typeOptions: { + loadOptionsMethod: 'getCurrencies', + }, + displayOptions: { + show: { + operation: [ + 'market', + 'marketChart', + 'candlestick', + ], + resource: [ + 'coin', + ], + }, + }, + default: '', + description: 'The second currency in the pair. For BTC:ETH this is ETH.', + }, + { + displayName: 'Currencies', + name: 'currencies', + type: 'multiOptions', + typeOptions: { + loadOptionsMethod: 'getCurrencies', + }, + required: true, + displayOptions: { + show: { + operation: [ + 'price', + ], + resource: [ + 'coin', + ], + }, + }, + default: [], + description: 'Currencies of coin.', + }, + { + displayName: 'Historical Range (days)', + name: 'days', + required: true, + type: 'options', + options: [ + { + name: '1', + value: '1', + }, + { + name: '7', + value: '7', + }, + { + name: '14', + value: '14', + }, + { + name: '30', + value: '30', + }, + { + name: '90', + value: '90', + }, + { + name: '180', + value: '180', + }, + { + name: '365', + value: '365', + }, + { + name: 'Max', + value: 'max', + }, + ], + displayOptions: { + show: { + operation: [ + 'marketChart', + 'candlestick', + ], + resource: [ + 'coin', + ], + }, + }, + default: '', + description: 'Return data for this many days in the past from now.', + }, + { + displayName: 'Date', + name: 'date', + required: true, + type: 'dateTime', + displayOptions: { + show: { + operation: [ + 'history', + ], + resource: [ + 'coin', + ], + }, + }, + default: '', + description: 'The date of data snapshot.', + }, + { + displayName: 'Return All', + name: 'returnAll', + type: 'boolean', + displayOptions: { + show: { + operation: [ + 'getAll', + 'market', + 'ticker', + ], + resource: [ + 'coin', + ], + }, + }, + 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', + 'market', + 'ticker', + ], + resource: [ + 'coin', + ], + returnAll: [ + false, + ], + }, + }, + typeOptions: { + minValue: 1, + maxValue: 500, + }, + default: 100, + description: 'How many results to return.', + }, + { + displayName: 'Options', + name: 'options', + type: 'collection', + placeholder: 'Add Option', + default: {}, + displayOptions: { + show: { + resource: [ + 'coin', + ], + operation: [ + 'market', + ], + }, + }, + options: [ + { + displayName: 'Coin IDs', + name: 'ids', + type: 'string', + placeholder: 'bitcoin', + default: '', + description: 'Filter results by comma separated list of coin ID.', + }, + { + displayName: 'Category', + name: 'category', + type: 'options', + options: [ + { + name: 'Decentralized Finance Defi', + value: 'decentralized_finance_defi', + }, + ], + default: 'decentralized_finance_defi', + description: 'Filter by coin category.', + }, + { + displayName: 'Order', + name: 'order', + type: 'options', + options: [ + { + name: 'Market Cap Desc', + value: 'market_cap_desc', + }, + { + name: 'Gecko Desc', + value: 'gecko_desc', + }, + { + name: 'Gecko Asc', + value: 'gecko_asc', + }, + { + name: 'Market Cap Asc', + value: 'market_cap_asc', + }, + { + name: 'Market Cap Desc', + value: 'market_cap_desc', + }, + { + name: 'Volume Asc', + value: 'volume_asc', + }, + { + name: 'Volume Desc', + value: 'volume_desc', + }, + { + name: 'Id Asc', + value: 'id_asc', + }, + { + name: 'Id Desc', + value: 'id_desc', + } + ], + default: '', + description: 'Sort results by field.', + }, + { + displayName: 'Sparkline', + name: 'sparkline', + type: 'boolean', + default: false, + description: 'Include sparkline 7 days data.', + }, + { + displayName: 'Price Change Percentage', + name: 'price_change_percentage', + type: 'multiOptions', + options: [ + { + name: '1h', + value: '1h', + }, + { + name: '24h', + value: '24h', + }, + { + name: '7d', + value: '7d', + }, + { + name: '14d', + value: '14d', + }, + { + name: '30d', + value: '30d', + }, + { + name: '200d', + value: '200d', + }, + { + name: '1y', + value: '1y', + }, + ], + default: [], + description: 'Include price change percentage for specified times.', + }, + ], + }, + { + displayName: 'Options', + name: 'options', + type: 'collection', + placeholder: 'Add Field', + default: {}, + displayOptions: { + show: { + resource: [ + 'coin', + ], + operation: [ + 'price', + ], + }, + }, + options: [ + { + displayName: 'Include 24hr Change', + name: 'include_24hr_change', + type: 'boolean', + default: false, + }, + { + displayName: 'Include 24hr Vol', + name: 'include_24hr_vol', + type: 'boolean', + default: false, + }, + { + displayName: 'Include Last Updated At', + name: 'include_last_updated_at', + type: 'boolean', + default: false, + }, + { + displayName: 'Include Market Cap', + name: 'include_market_cap', + type: 'boolean', + default: false, + }, + ], + }, + { + displayName: 'Options', + name: 'options', + type: 'collection', + placeholder: 'Add Option', + default: {}, + displayOptions: { + show: { + resource: [ + 'coin', + ], + operation: [ + 'ticker', + ], + }, + }, + options: [ + { + displayName: 'Exchange IDs', + name: 'exchange_ids', + type: 'multiOptions', + typeOptions: { + loadOptionsMethod: 'getExchanges', + }, + default: [], + description: 'Filter results by exchange IDs.', + }, + { + displayName: 'Include Exchange Logo', + name: 'include_exchange_logo', + type: 'boolean', + default: false, + description: 'Include exchange logo.', + }, + { + displayName: 'Order', + name: 'order', + type: 'options', + options: [ + { + name: 'Trust Score Desc', + value: 'trust_score_desc', + }, + { + name: 'Trust Score Asc', + value: 'trust_score_asc', + }, + { + name: 'Volume Desc', + value: 'volume_desc', + }, + ], + default: 'trust_score_desc', + description: 'Sorts results by the selected rule.', + }, + ], + }, + { + displayName: 'Options', + name: 'options', + type: 'collection', + placeholder: 'Add Option', + default: {}, + displayOptions: { + show: { + resource: [ + 'coin', + ], + operation: [ + 'history', + ], + }, + }, + options: [ + { + displayName: 'Localization', + name: 'localization', + type: 'boolean', + default: true, + description: 'Set to false to exclude localized languages in response.', + }, + ], + }, + { + displayName: 'Options', + name: 'options', + type: 'collection', + placeholder: 'Add Field', + default: {}, + displayOptions: { + show: { + operation: [ + 'get', + ], + resource: [ + 'coin', + ], + }, + }, + options: [ + { + displayName: 'Community data', + name: 'community_data', + type: 'boolean', + default: false, + description: 'Include community data.' + }, + { + displayName: 'Developer data', + name: 'developer_data', + type: 'boolean', + default: false, + description: 'Include developer data.' + }, + { + displayName: 'Localization', + name: 'localization', + type: 'boolean', + default: false, + description: 'Include all localized languages in response.' + }, + { + displayName: 'Market data', + name: 'market_data', + type: 'boolean', + default: false, + description: 'Include market data.' + }, + { + displayName: 'Sparkline', + name: 'sparkline', + type: 'boolean', + default: false, + description: 'Include sparkline 7 days data (eg. true, false).' + }, + { + displayName: 'Tickers', + name: 'tickers', + type: 'boolean', + default: false, + description: 'Include tickers data.' + }, + ], + }, +] as INodeProperties[]; diff --git a/packages/nodes-base/nodes/CoinGecko/CoinGecko.node.ts b/packages/nodes-base/nodes/CoinGecko/CoinGecko.node.ts new file mode 100644 index 000000000..aaf8e537c --- /dev/null +++ b/packages/nodes-base/nodes/CoinGecko/CoinGecko.node.ts @@ -0,0 +1,537 @@ +import { + IExecuteFunctions, +} from 'n8n-core'; + +import { + IDataObject, + ILoadOptionsFunctions, + INodeExecutionData, + INodePropertyOptions, + INodeType, + INodeTypeDescription, +} from 'n8n-workflow'; + +import { + coinFields, + coinOperations, +} from './CoinDescription'; + +import { + eventFields, + eventOperations, +} from './EventDescription'; + +import { + coinGeckoApiRequest, + coinGeckoRequestAllItems, +} from './GenericFunctions'; + +import * as moment from 'moment-timezone'; + +export class CoinGecko implements INodeType { + description: INodeTypeDescription = { + displayName: 'CoinGecko', + name: 'coinGecko', + icon: 'file:coinGecko.png', + group: ['output'], + version: 1, + description: 'Consume CoinGecko API', + subtitle: '={{$parameter["operation"] + ": " + $parameter["resource"]}}', + defaults: { + name: 'CoinGecko', + color: '#8bc53f', + }, + inputs: ['main'], + outputs: ['main'], + properties: [ + { + displayName: 'Resource', + name: 'resource', + type: 'options', + options: [ + { + name: 'Coin', + value: 'coin', + }, + { + name: 'Event', + value: 'event', + }, + ], + default: 'coin', + }, + ...coinOperations, + ...coinFields, + ...eventOperations, + ...eventFields, + ], + }; + + methods = { + loadOptions: { + async getCurrencies(this: ILoadOptionsFunctions): Promise { + const returnData: INodePropertyOptions[] = []; + const currencies = await coinGeckoApiRequest.call( + this, + 'GET', + '/simple/supported_vs_currencies' + ); + currencies.sort(); + for (const currency of currencies) { + returnData.push({ + name: currency.toUpperCase(), + value: currency, + }); + } + return returnData; + }, + + async getCoins(this: ILoadOptionsFunctions): Promise { + const returnData: INodePropertyOptions[] = []; + const coins = await coinGeckoApiRequest.call( + this, + 'GET', + '/coins/list' + ); + for (const coin of coins) { + returnData.push({ + name: coin.symbol.toUpperCase(), + value: coin.id, + }); + } + returnData.sort((a, b) => { + if (a.name < b.name) { return -1; } + if (a.name > b.name) { return 1; } + return 0; + }); + return returnData; + }, + + async getExchanges(this: ILoadOptionsFunctions): Promise { + const returnData: INodePropertyOptions[] = []; + const exchanges = await coinGeckoApiRequest.call( + this, + 'GET', + '/exchanges/list' + ); + for (const exchange of exchanges) { + returnData.push({ + name: exchange.name, + value: exchange.id, + }); + } + return returnData; + }, + + async getEventCountryCodes(this: ILoadOptionsFunctions): Promise { + const returnData: INodePropertyOptions[] = []; + const countryCodes = await coinGeckoApiRequest.call( + this, + 'GET', + '/events/countries' + ); + for (const code of countryCodes.data) { + if (!code.code) { + continue; + } + returnData.push({ + name: code.country, + value: code.code, + }); + } + return returnData; + }, + + async getEventTypes(this: ILoadOptionsFunctions): Promise { + const returnData: INodePropertyOptions[] = []; + const eventTypes = await coinGeckoApiRequest.call( + this, + 'GET', + '/events/types' + ); + for (const type of eventTypes.data) { + returnData.push({ + name: type, + value: type, + }); + } + 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 === 'coin') { + //https://www.coingecko.com/api/documentations/v3#/coins/get_coins__id_ + //https://www.coingecko.com/api/documentations/v3#/contract/get_coins__id__contract__contract_address_ + if (operation === 'get') { + + const options = this.getNodeParameter('options', i) as IDataObject; + + qs.community_data = false; + qs.developer_data = false; + qs.localization = false; + qs.market_data = false; + qs.sparkline = false; + qs.tickers = false; + + Object.assign(qs, options); + + const searchBy = this.getNodeParameter('searchBy', i) as string; + + if (searchBy === 'coinId') { + const coinId = this.getNodeParameter('coinId', i) as string; + + responseData = await coinGeckoApiRequest.call( + this, + 'GET', + `/coins/${coinId}`, + {}, + qs + ); + } + + if (searchBy === 'contractAddress') { + const platformId = this.getNodeParameter('platformId', i) as string; + const contractAddress = this.getNodeParameter('contractAddress', i) as string; + + responseData = await coinGeckoApiRequest.call( + this, + 'GET', + `/coins/${platformId}/contract/${contractAddress}`, + {}, + qs + ); + } + } + //https://www.coingecko.com/api/documentations/v3#/coins/get_coins_list + if (operation === 'getAll') { + + const returnAll = this.getNodeParameter('returnAll', i) as boolean; + + let limit; + + responseData = await coinGeckoApiRequest.call( + this, + 'GET', + '/coins/list', + {}, + qs + ); + + if (returnAll === false) { + limit = this.getNodeParameter('limit', i) as number; + responseData = responseData.splice(0, limit); + } + } + + //https://www.coingecko.com/api/documentations/v3#/coins/get_coins_list + if (operation === 'market') { + + const returnAll = this.getNodeParameter('returnAll', i) as boolean; + const quoteCurrency = this.getNodeParameter('quoteCurrency', i) as string; + const options = this.getNodeParameter('options', i) as IDataObject; + + qs.vs_currency = quoteCurrency; + + Object.assign(qs, options); + + if (options.price_change_percentage) { + qs.price_change_percentage = (options.price_change_percentage as string[]).join(','); + } + + if (returnAll) { + responseData = await coinGeckoRequestAllItems.call( + this, + '', + 'GET', + `/coins/markets`, + {}, + qs + ); + } else { + const limit = this.getNodeParameter('limit', i) as number; + + qs.per_page = limit; + + responseData = await coinGeckoApiRequest.call( + this, + 'GET', + `/coins/markets`, + {}, + qs + ); + } + } + + //https://www.coingecko.com/api/documentations/v3#/simple/get_simple_price + //https://www.coingecko.com/api/documentations/v3#/simple/get_simple_token_price__id_ + if (operation === 'price') { + + const searchBy = this.getNodeParameter('searchBy', i) as string; + const currencies = this.getNodeParameter('currencies', i) as string[]; + const options = this.getNodeParameter('options', i) as IDataObject; + + qs.vs_currencies = currencies.join(','); + + Object.assign(qs, options); + + if (searchBy === 'coinId') { + const coinIds = this.getNodeParameter('coinIds', i) as string[]; + + qs.ids = coinIds.join(','); + + responseData = await coinGeckoApiRequest.call( + this, + 'GET', + '/simple/price', + {}, + qs + ); + } + + if (searchBy === 'contractAddress') { + const platformId = this.getNodeParameter('platformId', i) as string; + const contractAddresses = this.getNodeParameter('contractAddresses', i) as string; + + qs.contract_addresses = contractAddresses; + + responseData = await coinGeckoApiRequest.call( + this, + 'GET', + `/simple/token_price/${platformId}`, + {}, + qs + ); + } + } + + //https://www.coingecko.com/api/documentations/v3#/coins/get_coins__id__tickers + if (operation === 'ticker') { + + const returnAll = this.getNodeParameter('returnAll', i) as boolean; + const coinId = this.getNodeParameter('coinId', i) as string; + const options = this.getNodeParameter('options', i) as IDataObject; + + Object.assign(qs, options); + + if (options.exchange_ids) { + qs.exchange_ids = (options.exchange_ids as string[]).join(','); + } + + if (returnAll) { + + responseData = await coinGeckoRequestAllItems.call( + this, + 'tickers', + 'GET', + `/coins/${coinId}/tickers`, + {}, + qs, + ); + } else { + const limit = this.getNodeParameter('limit', i) as number; + + responseData = await coinGeckoApiRequest.call( + this, + 'GET', + `/coins/${coinId}/tickers`, + {}, + qs + ); + + responseData = responseData.tickers; + responseData = responseData.splice(0, limit); + } + } + + //https://www.coingecko.com/api/documentations/v3#/coins/get_coins__id__history + if (operation === 'history') { + + const coinId = this.getNodeParameter('coinId', i) as string; + const date = this.getNodeParameter('date', i) as string; + const options = this.getNodeParameter('options', i) as IDataObject; + + Object.assign(qs, options); + + qs.date = moment(date).format('DD-MM-YYYY'); + + responseData = await coinGeckoApiRequest.call( + this, + 'GET', + `/coins/${coinId}/history`, + {}, + qs + ); + } + + //https://www.coingecko.com/api/documentations/v3#/coins/get_coins__id__market_chart + //https://www.coingecko.com/api/documentations/v3#/contract/get_coins__id__contract__contract_address__market_chart_ + if (operation === 'marketChart') { + + let respData; + + const searchBy = this.getNodeParameter('searchBy', i) as string; + const quoteCurrency = this.getNodeParameter('quoteCurrency', i) as string; + const days = this.getNodeParameter('days', i) as string; + + qs.vs_currency = quoteCurrency; + qs.days = days; + + if (searchBy === 'coinId') { + const coinId = this.getNodeParameter('baseCurrency', i) as string; + + respData = await coinGeckoApiRequest.call( + this, + 'GET', + `/coins/${coinId}/market_chart`, + {}, + qs + ); + } + + if (searchBy === 'contractAddress') { + const platformId = this.getNodeParameter('platformId', i) as string; + const contractAddress = this.getNodeParameter('contractAddress', i) as string; + + respData = await coinGeckoApiRequest.call( + this, + 'GET', + `/coins/${platformId}/contract/${contractAddress}/market_chart`, + {}, + qs + ); + } + + responseData = []; + for (let idx = 0; idx < respData.prices.length; idx++) { + const [time, price] = respData.prices[idx]; + const marketCaps = respData.market_caps[idx][1]; + const totalVolume = respData.total_volumes[idx][1]; + responseData.push({ time: moment(time).toISOString(), price, marketCaps, totalVolume } as IDataObject); + } + } + + //https://www.coingecko.com/api/documentations/v3#/coins/get_coins__id__ohlc + if (operation === 'candlestick') { + + const coinId = this.getNodeParameter('coinId', i) as string; + const quoteCurrency = this.getNodeParameter('quoteCurrency', i) as string; + const days = this.getNodeParameter('days', i) as string; + + qs.vs_currency = quoteCurrency; + qs.days = days; + + responseData = await coinGeckoApiRequest.call( + this, + 'GET', + `/coins/${coinId}/ohlc`, + {}, + qs + ); + + for (let idx = 0; idx < responseData.length; idx++) { + const [time, open, high, low, close] = responseData[idx]; + responseData[idx] = { time: moment(time).toISOString(), open, high, low, close } as IDataObject; + } + } + } + + if (resource === 'event') { + //https://www.coingecko.com/api/documentations/v3#/events/get_events + if (operation === 'getAll') { + + const returnAll = this.getNodeParameter('returnAll', i) as boolean; + const options = this.getNodeParameter('options', i) as IDataObject; + + Object.assign(qs, options); + + if (returnAll) { + responseData = await coinGeckoRequestAllItems.call( + this, + 'data', + 'GET', + '/events', + {}, + qs + ); + } else { + const limit = this.getNodeParameter('limit', i) as number; + + qs.per_page = limit; + + responseData = await coinGeckoApiRequest.call( + this, + 'GET', + '/events', + {}, + qs + ); + responseData = responseData.data; + } + } + } + + if (resource === 'simple') { + //https://www.coingecko.com/api/documentations/v3#/simple/get_simple_price + if (operation === 'price') { + + const ids = this.getNodeParameter('ids', i) as string; + const currencies = this.getNodeParameter('currencies', i) as string[]; + const options = this.getNodeParameter('options', i) as IDataObject; + + qs.ids = ids, + qs.vs_currencies = currencies.join(','); + + Object.assign(qs, options); + + responseData = await coinGeckoApiRequest.call( + this, + 'GET', + '/simple/price', + {}, + qs + ); + } + + //https://www.coingecko.com/api/documentations/v3#/simple/get_simple_token_price__id_ + if (operation === 'tokenPrice') { + + const id = this.getNodeParameter('id', i) as string; + const contractAddresses = this.getNodeParameter('contractAddresses', i) as string; + const currencies = this.getNodeParameter('currencies', i) as string[]; + const options = this.getNodeParameter('options', i) as IDataObject; + + qs.contract_addresses = contractAddresses; + qs.vs_currencies = currencies.join(','); + + Object.assign(qs, options); + + responseData = await coinGeckoApiRequest.call( + this, + 'GET', + `/simple/token_price/${id}`, + {}, + qs + ); + } + } + + } + 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/CoinGecko/EventDescription.ts b/packages/nodes-base/nodes/CoinGecko/EventDescription.ts new file mode 100644 index 000000000..4a6f5dc59 --- /dev/null +++ b/packages/nodes-base/nodes/CoinGecko/EventDescription.ts @@ -0,0 +1,130 @@ +import { + INodeProperties, +} from 'n8n-workflow'; + +export const eventOperations = [ + { + displayName: 'Operation', + name: 'operation', + type: 'options', + displayOptions: { + show: { + resource: [ + 'event', + ], + }, + }, + options: [ + { + name: 'Get All', + value: 'getAll', + description: 'Get all events', + }, + ], + default: 'getAll', + }, +] as INodeProperties[]; + +export const eventFields = [ + { + displayName: 'Return All', + name: 'returnAll', + type: 'boolean', + displayOptions: { + show: { + operation: [ + 'getAll', + ], + resource: [ + 'event', + ], + }, + }, + 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: [ + 'event', + ], + returnAll: [ + false, + ], + }, + }, + typeOptions: { + minValue: 1, + maxValue: 500, + }, + default: 100, + description: 'How many results to return.', + }, + { + displayName: 'Options', + name: 'options', + type: 'collection', + placeholder: 'Add Option', + default: {}, + displayOptions: { + show: { + operation: [ + 'getAll', + ], + resource: [ + 'event', + ], + }, + }, + options: [ + { + displayName: 'Country code', + name: 'country_code', + type: 'options', + typeOptions: { + loadOptionsMethod: 'getEventCountryCodes', + }, + default: '', + description: 'Country code of event.', + }, + { + displayName: 'From date', + name: 'from_date', + type: 'dateTime', + default: '', + description: 'Lists events after this date.', + }, + { + displayName: 'To date', + name: 'to_date', + type: 'dateTime', + default: '', + description: 'Lists events before this date.', + }, + { + displayName: 'Type', + name: 'type', + type: 'options', + typeOptions: { + loadOptionsMethod: 'getEventTypes', + }, + default: '', + description: 'Type of event.', + }, + { + displayName: 'Upcoming events only', + name: 'upcoming_events_only', + type: 'boolean', + default: true, + description: 'Lists only upcoming events.', + }, + ], + }, +] as INodeProperties[]; diff --git a/packages/nodes-base/nodes/CoinGecko/GenericFunctions.ts b/packages/nodes-base/nodes/CoinGecko/GenericFunctions.ts new file mode 100644 index 000000000..d5f3e0b51 --- /dev/null +++ b/packages/nodes-base/nodes/CoinGecko/GenericFunctions.ts @@ -0,0 +1,67 @@ +import { + OptionsWithUri, +} from 'request'; + +import { + IExecuteFunctions, + IExecuteSingleFunctions, + ILoadOptionsFunctions, +} from 'n8n-core'; + +import { + IDataObject, +} from 'n8n-workflow'; + +export async function coinGeckoApiRequest(this: IExecuteFunctions | IExecuteSingleFunctions | ILoadOptionsFunctions, method: string, + endpoint: string, body: any = {}, qs: IDataObject = {}, uri?: string, option: IDataObject = {}): Promise { // tslint:disable-line:no-any + let options: OptionsWithUri = { + headers: { + 'Accept': 'application/json', + 'Content-Type': 'application/json', + }, + method, + body, + qs, + uri: uri || `https://api.coingecko.com/api/v3${endpoint}`, + json: true, + }; + + options = Object.assign({}, options, option); + + try { + if (Object.keys(body).length === 0) { + delete options.body; + } + + //@ts-ignore + return await this.helpers.request.call(this, options); + + } catch (error) { + + throw error; + } +} + +export async function coinGeckoRequestAllItems(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 respData; + query.per_page = 250; + query.page = 1; + + do { + responseData = await coinGeckoApiRequest.call(this, method, endpoint, body, query); + query.page++; + respData = responseData; + if (propertyName !== '') { + respData = responseData[propertyName]; + } + returnData.push.apply(returnData, respData); + } while ( + respData.length !== 0 + ); + + return returnData; +} diff --git a/packages/nodes-base/nodes/CoinGecko/coinGecko.png b/packages/nodes-base/nodes/CoinGecko/coinGecko.png new file mode 100644 index 0000000000000000000000000000000000000000..332730847f0e8fde323715e399839633a6498134 GIT binary patch literal 3955 zcmV-(4~+1MP)EX>4Tx04R}tkv&MmKpe$iTcuhmf_4yb$WWauh>D1l zR-p(LLaorMgUO{|(4-+rad8w}3l4rPRvlcNb#-tR1i=pwCr2km7b)?7NufoI2gm(* zckglc4)E8@Of@^k09CV$R3a{9va3Sy72W7V2*8ZQOnpuilkgm0_we!cF2=LG&;2=i zl$^-`pFljzbi*RvAfDQ^bk6(45muBG;&b9rgDyz?$aUG}H_ioz{X8>bq*L?65n`dx z#&R38qM;H`5=RwPqkMnHWrgz=XSG~q&3p0}hI87=GS_JiA%R6KL4*JqRg_SMg&3_G zDJD|1ANTMNI)0H{GPz1%j1(w)-Q(R|?Y;ebrrF;QS6*_QHBDz800006VoOIv05AYm z0LM2KxUm2L010qNS#tmY3ljhU3ljkVnw%H_000McNliruRM=>7Hy3b0g48$ zf;vtbxJA&qje?>;kOrxkq=u8YP2whzC09~viIytMl&r(nL5ZSnUJ}VAmwV36%-av# zT~Xw6xhs)UKJ)?h!|cp^{O5Vz=N`dbqOAuXo${G2&8;)O4>7~w%7K-@eLy2n1Ng_S z8v%NOPT&*Z2yhiJXW|8jX$C2PCg6GC31B&(+{Sd5fZf2)fqj5JUEuE+@K@hnL!ip# z0$v2ZnC2z`77~}RQR(nA%jUytbK+9%XhH?Rlom-VMZ`!js7L74hX|W-lv}rT4QK(r z3-sP8@Y@FbS8uMQy53_4UIJcCiz^bSwFg+_siD>#z;7=>6$&!v>JCu62n5I|mKh;p z#OaBLIUnt%GuDT^IWn3C{SUyv?E)8fAij?R-vO2aXhP+F-#nK1YVq4krk+IcqS%)q zY*h$bB|`IoG?2y+mfnp_o*|a#pkt(uBg36U%tU770PqUXvZc9I&M4qn^E6)h@j5&3 zI`Ef(qM)$4w1K9;TpX(HHswWv;zO}7MmQQ!?6n{?q%nXQZNp3)=WMu#1B30vEj=@6 z7w|_wUoqSkMRK(kcpF$FfqG{Jk5(+fXLC=Z9+eJ1(&hhKYOMuTUPA}Z4QcSCBi-%bL7{cpXFL? zkazoz6SI;4N#Kuxw+aDWDBuTxJwTaTaqwi-N-A8X+&z+@l%keAf;2){@dHE+ot-_$ z=+Q#}W?FgvU@Fj}0M0H2_5)>pt%RqmSK+g}xi=&cwnZrRxtP)aA!Vi5)q8>q@xBb` zKb(rwQ#l0{z)qmdr@DErcFjEmJIWuKI){}whU$F^o9baxbrbcjN&pr3A@FcMH&)5R zT@`o>nCnz*JX^Dx63xkP0!gzUD|Qgg|NE$Fkd0OMQEd+ZIDnr5RYd_#zoA!w^#Z}~ zR<69qoXrKg4>Q_^7TAoYmGimk`*AA{(qR96>%m7wF&th2{0+dmz#^*MfjbaFfe`6m z4waF)cUz$KQ%L^r~pRv=gr+tQjNwxG}Y-?if($Z(r53;#-9pz46!3KRukI^3P=49k55i>ER&9fv7 zMFm~M3P%SOx^%&tO^tk}v5sPVM#XU-YUxJI$cH2%dwApW`@}{)vVR6%9;>x);qct0 zr2_amz;aI=6;5A43lHltJ{&wxd$ceO!b#;Hj>gpG$_pe7K!1eg)?28#oZadTkEc%UMJgOa^ z&4nVA0+m=8#kB&|00DW4GvRJ@Y1n}HXa98G-rX&iZw36?_J;ws0!td*)vWN>Z2JLiZHH+HHc{zX zRj@*gm?O@)#K+ts0hb08cHh1+GTaD^4ML0 z;7p_|KL>RxSno{K2`w!spc-Xx8g$Z01Lh$Uf&!C$L+6Q+|n{Rz0ayfmb z6|CeXgz7~R4l3>0xPAH7gO6sl^a6l@&4Vh$q#AlikL7eA#?zV5&saL}7#lZk#OL$z z&W@e*_xIDda3Rk<_Y4gU^J%^C6B6e2X-%;ACnI=lE~MTGB~73N2->}zkM{yJj5SMD z06xu?chV6fSx|~ZAoyj+U-S6XDMM zfaPdzIlmhhGxVZE!=Yq5NBY{QMQl&@zm4Bn&BdXQZUGCh&|Qs3b7nnxnYs=bXo@m< zcsNy{5G#ViGeNo);=L>1n0Ujm^H6Z%E&G>7FIce&n3SOkF`*p{#w*N^$xk1mp!I1cZ&)ZxB!qsi=buOM z?gkh(qLYfXl#+-UpT1373PQ1p8e{4XhOfFY;~Jj2D4$+YN^^Pr6qY4Ewn~u&d=?;- ziegx1jz|PjmPx`grwy^-AQu9M&!Cia=?k^O%bS%V)rCi z?Q7tn^2Vucv!0F9&u(dM%>q6L7zR=cr(%>jyg4*<5({hE3M;mf7=eZ)gPmTw4pm{s z6@*PsXD~FRp`rPccxqDkYElFj^`VH-;%T#g5vzms1(-&Q-elG@{ABzYKBK^j-uQ4% zz>xx-j$%krO=dL?5o{a+Q^iQQKnfI_fnrY}#KZ&kuzxWtgYyfTe@Ks!kSP>|2^=1u zrrif{DRyI$!OJSW(P{EG?(3vewqnCT`s3G`e& zG*tq0M0)NCcQ;iuQDQ5;VtIV<62@p6>N4=7T)y;JF8>{1bn79`hx>>cNjgUh&UaTy z`Rb`F2^KwWNRM(Pd?9oB_l&kgOv>Smfo=iVJ8+6)p-W?XrS1xORTmEi7ZmTVBH4TW zRA#>)1K%It4KM+4`hl+i-Em9jqnmH*uF+$&mSHRM+yZ^A@>yCwZfACD^@|(b+mX}Xc5a9p(#(5a+?fB+h4f}w# zWyOo~upZ@|zK=4ZydU`bWQ{G8=5*2nUQGkPE9BSQ%r2Q#)R=H8#?QOkNLZ7@hJ<# zJJmuGP*8P&dRD>KYRCQVETwkeHF+xb@B5>DwR#px zY;_iTKuX;OQV8Kp3Y1|5+3ghm^LCt_pI^Hzy#4TNJh|p?`G1-Q`ah-bL>(8!WlI16 N002ovPDHLkV1iClWHSH& literal 0 HcmV?d00001 diff --git a/packages/nodes-base/package.json b/packages/nodes-base/package.json index a703f528d..c54d6f6a2 100644 --- a/packages/nodes-base/package.json +++ b/packages/nodes-base/package.json @@ -220,6 +220,7 @@ "dist/nodes/Clockify/ClockifyTrigger.node.js", "dist/nodes/Cockpit/Cockpit.node.js", "dist/nodes/Coda/Coda.node.js", + "dist/nodes/CoinGecko/CoinGecko.node.js", "dist/nodes/Contentful/Contentful.node.js", "dist/nodes/ConvertKit/ConvertKit.node.js", "dist/nodes/ConvertKit/ConvertKitTrigger.node.js", From 419b58024a4984435ae573fc0f1c1b9cebf89345 Mon Sep 17 00:00:00 2001 From: Ricardo Espinoza Date: Sat, 3 Oct 2020 08:08:50 -0400 Subject: [PATCH 131/284] :sparkles: Add Mindee-Node (#1004) * :sparkles: Mindee-Node * :zap: Improvements --- .../MindeeInvoiceApi.credentials.ts | 17 ++ .../MindeeReceiptApi.credentials.ts | 17 ++ .../nodes/Mindee/GenericFunctions.ts | 84 +++++++ .../nodes-base/nodes/Mindee/Mindee.node.ts | 220 ++++++++++++++++++ packages/nodes-base/nodes/Mindee/mindee.png | Bin 0 -> 1909 bytes packages/nodes-base/package.json | 3 + 6 files changed, 341 insertions(+) create mode 100644 packages/nodes-base/credentials/MindeeInvoiceApi.credentials.ts create mode 100644 packages/nodes-base/credentials/MindeeReceiptApi.credentials.ts create mode 100644 packages/nodes-base/nodes/Mindee/GenericFunctions.ts create mode 100644 packages/nodes-base/nodes/Mindee/Mindee.node.ts create mode 100644 packages/nodes-base/nodes/Mindee/mindee.png diff --git a/packages/nodes-base/credentials/MindeeInvoiceApi.credentials.ts b/packages/nodes-base/credentials/MindeeInvoiceApi.credentials.ts new file mode 100644 index 000000000..527551aea --- /dev/null +++ b/packages/nodes-base/credentials/MindeeInvoiceApi.credentials.ts @@ -0,0 +1,17 @@ +import { + ICredentialType, + NodePropertyTypes, +} from 'n8n-workflow'; + +export class MindeeInvoiceApi implements ICredentialType { + name = 'mindeeInvoiceApi'; + displayName = 'Mindee Invoice API'; + properties = [ + { + displayName: 'API Key', + name: 'apiKey', + type: 'string' as NodePropertyTypes, + default: '', + }, + ]; +} diff --git a/packages/nodes-base/credentials/MindeeReceiptApi.credentials.ts b/packages/nodes-base/credentials/MindeeReceiptApi.credentials.ts new file mode 100644 index 000000000..bdd133707 --- /dev/null +++ b/packages/nodes-base/credentials/MindeeReceiptApi.credentials.ts @@ -0,0 +1,17 @@ +import { + ICredentialType, + NodePropertyTypes, +} from 'n8n-workflow'; + +export class MindeeReceiptApi implements ICredentialType { + name = 'mindeeReceiptApi'; + displayName = 'Mindee Receipt API'; + properties = [ + { + displayName: 'API Key', + name: 'apiKey', + type: 'string' as NodePropertyTypes, + default: '', + }, + ]; +} diff --git a/packages/nodes-base/nodes/Mindee/GenericFunctions.ts b/packages/nodes-base/nodes/Mindee/GenericFunctions.ts new file mode 100644 index 000000000..33055addd --- /dev/null +++ b/packages/nodes-base/nodes/Mindee/GenericFunctions.ts @@ -0,0 +1,84 @@ +import { + OptionsWithUri, + } from 'request'; + +import { + IExecuteFunctions, + IExecuteSingleFunctions, + ILoadOptionsFunctions, +} from 'n8n-core'; + +import { + IDataObject, +} from 'n8n-workflow'; + +export async function mindeeApiRequest(this: IExecuteFunctions | IExecuteSingleFunctions | ILoadOptionsFunctions, method: string, path: string, body: any = {}, qs: IDataObject = {}, option = {}): Promise { // tslint:disable-line:no-any + + const resource = this.getNodeParameter('resource', 0) as string; + + let credentials; + + if (resource === 'receipt') { + credentials = this.getCredentials('mindeeReceiptApi') as IDataObject; + } else { + credentials = this.getCredentials('mindeeInvoiceApi') as IDataObject; + } + + const options: OptionsWithUri = { + headers: { + 'X-Inferuser-Token': credentials.apiKey, + }, + method, + body, + qs, + uri: `https://api.mindee.net/products${path}`, + json: true, + }; + try { + if (Object.keys(body).length === 0) { + delete options.body; + } + if (Object.keys(qs).length === 0) { + delete options.qs; + } + if (Object.keys(option).length !== 0) { + Object.assign(options, option); + } + //@ts-ignore + return await this.helpers.request.call(this, options); + } catch (error) { + if (error.response && error.response.body && error.response.body.error) { + + let errors = error.response.body.error.errors; + + errors = errors.map((e: IDataObject) => e.message); + // Try to return the error prettier + throw new Error( + `Mindee error response [${error.statusCode}]: ${errors.join('|')}` + ); + } + throw error; + } +} + +export function cleanData(predictions: IDataObject[]) { + + const newData: IDataObject = {}; + + for (const key of Object.keys(predictions[0])) { + + const data = predictions[0][key] as IDataObject | IDataObject[]; + + if (key === 'taxes' && data.length) { + newData[key] = { + amount: (data as IDataObject[])[0].amount, + rate: (data as IDataObject[])[0].rate, + }; + } else { + //@ts-ignore + newData[key] = data.value || data.name || data.raw || data.degrees || data.amount || data.iban; + } + } + + return newData; +} diff --git a/packages/nodes-base/nodes/Mindee/Mindee.node.ts b/packages/nodes-base/nodes/Mindee/Mindee.node.ts new file mode 100644 index 000000000..c59ecb61b --- /dev/null +++ b/packages/nodes-base/nodes/Mindee/Mindee.node.ts @@ -0,0 +1,220 @@ +import { + BINARY_ENCODING, + IExecuteFunctions, +} from 'n8n-core'; + +import { + IBinaryData, + IBinaryKeyData, + IDataObject, + INodeExecutionData, + INodeType, + INodeTypeDescription, +} from 'n8n-workflow'; + +import { + cleanData, + mindeeApiRequest, +} from './GenericFunctions'; + +export class Mindee implements INodeType { + description: INodeTypeDescription = { + displayName: 'Mindee', + name: 'mindee', + icon: 'file:mindee.png', + group: ['input'], + version: 1, + subtitle: '={{$parameter["operation"] + ": " + $parameter["resource"]}}', + description: 'Consume Mindee API.', + defaults: { + name: 'Mindee', + color: '#e94950', + }, + inputs: ['main'], + outputs: ['main'], + credentials: [ + { + name: 'mindeeReceiptApi', + required: true, + displayOptions: { + show: { + resource: [ + 'receipt', + ], + }, + }, + }, + { + name: 'mindeeInvoiceApi', + required: true, + displayOptions: { + show: { + resource: [ + 'invoice', + ], + }, + }, + }, + ], + properties: [ + { + displayName: 'Resource', + name: 'resource', + type: 'options', + options: [ + { + name: 'Receipt', + value: 'receipt', + }, + { + name: 'Invoice', + value: 'invoice', + }, + ], + default: 'receipt', + description: 'The resource to operate on.' + }, + { + displayName: 'Operation', + name: 'operation', + type: 'options', + options: [ + { + name: 'Predict', + value: 'predict', + }, + ], + default: 'predict', + description: 'The resource to operate on.' + }, + { + displayName: 'Binary Property', + name: 'binaryPropertyName', + type: 'string', + required: true, + default: 'data', + displayOptions: { + show: { + operation: [ + 'predict' + ], + resource: [ + 'receipt', + 'invoice', + ], + }, + }, + description: 'Name of the binary property which containsthe data for the file to be uploaded.', + }, + { + displayName: 'RAW Data', + name: 'rawData', + type: 'boolean', + default: false, + description: `Returns the data exactly in the way it got received from the API.`, + }, + ], + }; + + 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 === 'receipt') { + if (operation === 'predict') { + const binaryPropertyName = this.getNodeParameter('binaryPropertyName', i) as string; + + const rawData = this.getNodeParameter('rawData', i) as boolean; + + if (items[i].binary === undefined) { + throw new Error('No binary data exists on item!'); + } + + const item = items[i].binary as IBinaryKeyData; + + const binaryData = item[binaryPropertyName] as IBinaryData; + + if (binaryData === undefined) { + throw new Error(`No binary data property "${binaryPropertyName}" does not exists on item!`); + } + + responseData = await mindeeApiRequest.call( + this, + 'POST', + `/expense_receipts/v2/predict`, + {}, + {}, + { + formData: { + file: { + value: Buffer.from(binaryData.data, BINARY_ENCODING), + options: { + filename: binaryData.fileName, + } + }, + }, + }, + ); + + if (rawData === false) { + responseData = cleanData(responseData.predictions); + } + } + } + + if (resource === 'invoice') { + if (operation === 'predict') { + const binaryPropertyName = this.getNodeParameter('binaryPropertyName', i) as string; + + const rawData = this.getNodeParameter('rawData', i) as boolean; + + if (items[i].binary === undefined) { + throw new Error('No binary data exists on item!'); + } + + const item = items[i].binary as IBinaryKeyData; + + const binaryData = item[binaryPropertyName] as IBinaryData; + + if (binaryData === undefined) { + throw new Error(`No binary data property "${binaryPropertyName}" does not exists on item!`); + } + + responseData = await mindeeApiRequest.call( + this, + 'POST', + `/invoices/v1/predict`, + {}, + {}, + { + formData: { + file: { + value: Buffer.from(binaryData.data, BINARY_ENCODING), + options: { + filename: binaryData.fileName, + } + }, + }, + }, + ); + + if (rawData === false) { + responseData = cleanData(responseData.predictions); + } + } + } + } + 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/Mindee/mindee.png b/packages/nodes-base/nodes/Mindee/mindee.png new file mode 100644 index 0000000000000000000000000000000000000000..26d8060cda5dbbb84e51686960a3fa4037e9c8c5 GIT binary patch literal 1909 zcmeAS@N?(olHy`uVBq!ia0vp^k|4~%1|*NXY)uAIEa{HEjtmSN`?>!lvI6-E$sR$z z3=CCj3=9n|3=F@3LJcn%7)lKo7+xhXFj&oCU=S~uvn$XBD3OsG;hE;^%b*2hb1<+l zvN13NS&R%!Ktc%0W(2Y|5aR8b3@l(Z3=DQant_3N0V6`?0w%buH|Sk5DQD z1Jk3-kcg59UmvUF{9L`nl>DSry^7odkOBsq3M(KpH?<^Dp&~aYuh^=>Rtc=a3djZt z>nkaMm6T-LDnNc-DA*LGq*(>IxIwi8dA3R!B_#z``ugSN z<$C4Ddih1^`i7R4mih)p`bI{&Koz>hm3bwJ6}oxF$`C_f=D4I5Cl_TFlw{`TDS*sP zOv*1Uu~kw6Sp)|Vca~(PA#BPkhI$L=L4A;nzM-ChJ~nNs6`44+fn*@s!2W_*X9F_K zDj*}jBp(Vr~?^K(i;#)sx*mcSi|CXA#DYXzR?C&@hBw@_$? zBy(J!LSGeE?t9i-S&cD$et{lw5nB3+9>M`JIsOu+d-L;`=scag)ZyOwhW~|&y@TFg z^;Zy7_vl;Et+`;{!snTLH2gL+q~Fo9O6J`Wa3|%H!>mt_J}>t+e5X2n`PvqlxrKAf zd>5?ak(k}G(~#kE{iU-{-AqKz32*6o7`ziw#^Dgho>73H2w5+O5@aJcrr>9nx z`|V>Fy8iU+C4tZPuRJhZ{BM`r%21b|*Pdx`F6hY&vgkUY9{HVr#_gPI)^UrUf70>U z#eFyY!?`s*G6GAdmc6>gQTfF6QIY7b^AioHzU)qH+v}vcY@xu5_o6eLe=|;7p3W|>9(xHigMGH3{QdqU_;Bf|Tk=2?r zlG_`ZtM{5O_$Ii_SEhq4@P^5oJGCr5HR<>EZ{X%X#Brqdl%PW5g#+_C^&ObJHmxe# z(Bqn=`rq^7#1k`S#fGZ~gg@rf4wlTA5Y5ufwo%olpz&s6=ca-^$BwUGmDer8uyE5} zo7&W#{M6SjvDeQX+gq8pv-(`6_1~Pm+DyCubv={zEoeISa`)V6S3WC6K1g|Ull}S4 z?1Zkf9se2D*z4Rqdr0WaO1lN$7qBmttbcB&J)!yJKW3%(s=H^gO?y$$w7>QdgPPx@ z7iZTj@{fOLQ+d2YBOy5a_%jPbo;NRVN(ipxnX~ZuffxMes+O6Y{<+veWNXRoSAVQ? z3g`AFm+{T0IlcAv+G&XehN2zQsUS4WZAQqTYlZt zPg6Z!@>TZfftO5P7G}(qOIqBe&VE);xE1}DGgb2E&C3q|3-w)@I3~>rc=hbu0h4Qw Z`L*wJ_56LJd<0bcc)I$ztaD0e0szVw Date: Sat, 3 Oct 2020 14:09:37 +0200 Subject: [PATCH 132/284] :zap: Small improvements to Mindee-Node --- .../nodes-base/nodes/Mindee/GenericFunctions.ts | 2 +- packages/nodes-base/nodes/Mindee/Mindee.node.ts | 10 +++++----- packages/nodes-base/nodes/Mindee/mindee.png | Bin 1909 -> 0 bytes packages/nodes-base/nodes/Mindee/mindee.svg | 3 +++ 4 files changed, 9 insertions(+), 6 deletions(-) delete mode 100644 packages/nodes-base/nodes/Mindee/mindee.png create mode 100644 packages/nodes-base/nodes/Mindee/mindee.svg diff --git a/packages/nodes-base/nodes/Mindee/GenericFunctions.ts b/packages/nodes-base/nodes/Mindee/GenericFunctions.ts index 33055addd..703cc20a4 100644 --- a/packages/nodes-base/nodes/Mindee/GenericFunctions.ts +++ b/packages/nodes-base/nodes/Mindee/GenericFunctions.ts @@ -1,6 +1,6 @@ import { OptionsWithUri, - } from 'request'; +} from 'request'; import { IExecuteFunctions, diff --git a/packages/nodes-base/nodes/Mindee/Mindee.node.ts b/packages/nodes-base/nodes/Mindee/Mindee.node.ts index c59ecb61b..66ce2bbf8 100644 --- a/packages/nodes-base/nodes/Mindee/Mindee.node.ts +++ b/packages/nodes-base/nodes/Mindee/Mindee.node.ts @@ -21,7 +21,7 @@ export class Mindee implements INodeType { description: INodeTypeDescription = { displayName: 'Mindee', name: 'mindee', - icon: 'file:mindee.png', + icon: 'file:mindee.svg', group: ['input'], version: 1, subtitle: '={{$parameter["operation"] + ": " + $parameter["resource"]}}', @@ -62,14 +62,14 @@ export class Mindee implements INodeType { name: 'resource', type: 'options', options: [ - { - name: 'Receipt', - value: 'receipt', - }, { name: 'Invoice', value: 'invoice', }, + { + name: 'Receipt', + value: 'receipt', + }, ], default: 'receipt', description: 'The resource to operate on.' diff --git a/packages/nodes-base/nodes/Mindee/mindee.png b/packages/nodes-base/nodes/Mindee/mindee.png deleted file mode 100644 index 26d8060cda5dbbb84e51686960a3fa4037e9c8c5..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1909 zcmeAS@N?(olHy`uVBq!ia0vp^k|4~%1|*NXY)uAIEa{HEjtmSN`?>!lvI6-E$sR$z z3=CCj3=9n|3=F@3LJcn%7)lKo7+xhXFj&oCU=S~uvn$XBD3OsG;hE;^%b*2hb1<+l zvN13NS&R%!Ktc%0W(2Y|5aR8b3@l(Z3=DQant_3N0V6`?0w%buH|Sk5DQD z1Jk3-kcg59UmvUF{9L`nl>DSry^7odkOBsq3M(KpH?<^Dp&~aYuh^=>Rtc=a3djZt z>nkaMm6T-LDnNc-DA*LGq*(>IxIwi8dA3R!B_#z``ugSN z<$C4Ddih1^`i7R4mih)p`bI{&Koz>hm3bwJ6}oxF$`C_f=D4I5Cl_TFlw{`TDS*sP zOv*1Uu~kw6Sp)|Vca~(PA#BPkhI$L=L4A;nzM-ChJ~nNs6`44+fn*@s!2W_*X9F_K zDj*}jBp(Vr~?^K(i;#)sx*mcSi|CXA#DYXzR?C&@hBw@_$? zBy(J!LSGeE?t9i-S&cD$et{lw5nB3+9>M`JIsOu+d-L;`=scag)ZyOwhW~|&y@TFg z^;Zy7_vl;Et+`;{!snTLH2gL+q~Fo9O6J`Wa3|%H!>mt_J}>t+e5X2n`PvqlxrKAf zd>5?ak(k}G(~#kE{iU-{-AqKz32*6o7`ziw#^Dgho>73H2w5+O5@aJcrr>9nx z`|V>Fy8iU+C4tZPuRJhZ{BM`r%21b|*Pdx`F6hY&vgkUY9{HVr#_gPI)^UrUf70>U z#eFyY!?`s*G6GAdmc6>gQTfF6QIY7b^AioHzU)qH+v}vcY@xu5_o6eLe=|;7p3W|>9(xHigMGH3{QdqU_;Bf|Tk=2?r zlG_`ZtM{5O_$Ii_SEhq4@P^5oJGCr5HR<>EZ{X%X#Brqdl%PW5g#+_C^&ObJHmxe# z(Bqn=`rq^7#1k`S#fGZ~gg@rf4wlTA5Y5ufwo%olpz&s6=ca-^$BwUGmDer8uyE5} zo7&W#{M6SjvDeQX+gq8pv-(`6_1~Pm+DyCubv={zEoeISa`)V6S3WC6K1g|Ull}S4 z?1Zkf9se2D*z4Rqdr0WaO1lN$7qBmttbcB&J)!yJKW3%(s=H^gO?y$$w7>QdgPPx@ z7iZTj@{fOLQ+d2YBOy5a_%jPbo;NRVN(ipxnX~ZuffxMes+O6Y{<+veWNXRoSAVQ? z3g`AFm+{T0IlcAv+G&XehN2zQsUS4WZAQqTYlZt zPg6Z!@>TZfftO5P7G}(qOIqBe&VE);xE1}DGgb2E&C3q|3-w)@I3~>rc=hbu0h4Qw Z`L*wJ_56LJd<0bcc)I$ztaD0e0szVw + + \ No newline at end of file From 1a80a6afed76ad463b6dce68cecd773ebcd98e1c Mon Sep 17 00:00:00 2001 From: Jan Oberhauser Date: Sat, 3 Oct 2020 14:10:08 +0200 Subject: [PATCH 133/284] :zap: Improve support for svg-icons --- packages/editor-ui/src/components/NodeIcon.vue | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/packages/editor-ui/src/components/NodeIcon.vue b/packages/editor-ui/src/components/NodeIcon.vue index 4d7a95f47..d32a1a40e 100644 --- a/packages/editor-ui/src/components/NodeIcon.vue +++ b/packages/editor-ui/src/components/NodeIcon.vue @@ -1,5 +1,5 @@