From 3865add250eb012393d65e6837a29a0d9643b42d Mon Sep 17 00:00:00 2001 From: Jon Date: Wed, 26 Jul 2023 11:59:55 +0100 Subject: [PATCH 1/4] fix(Lemlist Node): Fix pagination issues with campaigns and activities (#6734) --- .../nodes/Lemlist/GenericFunctions.ts | 2 +- .../nodes-base/nodes/Lemlist/Lemlist.node.ts | 26 ++++++++++--------- 2 files changed, 15 insertions(+), 13 deletions(-) diff --git a/packages/nodes-base/nodes/Lemlist/GenericFunctions.ts b/packages/nodes-base/nodes/Lemlist/GenericFunctions.ts index 70e6e5c46501b..5257c0234e71e 100644 --- a/packages/nodes-base/nodes/Lemlist/GenericFunctions.ts +++ b/packages/nodes-base/nodes/Lemlist/GenericFunctions.ts @@ -51,11 +51,11 @@ export async function lemlistApiRequestAllItems( this: IExecuteFunctions | ILoadOptionsFunctions | IHookFunctions, method: string, endpoint: string, + qs: IDataObject = {}, ) { const returnData: IDataObject[] = []; let responseData; - const qs: IDataObject = {}; qs.limit = 100; qs.offset = 0; diff --git a/packages/nodes-base/nodes/Lemlist/Lemlist.node.ts b/packages/nodes-base/nodes/Lemlist/Lemlist.node.ts index 3343d185aa6f2..e1b3faf0de506 100644 --- a/packages/nodes-base/nodes/Lemlist/Lemlist.node.ts +++ b/packages/nodes-base/nodes/Lemlist/Lemlist.node.ts @@ -123,7 +123,7 @@ export class Lemlist implements INodeType { // https://developer.lemlist.com/#activities - const returnAll = this.getNodeParameter('returnAll', 0); + const returnAll = this.getNodeParameter('returnAll', i); const qs = {} as IDataObject; const filters = this.getNodeParameter('filters', i); @@ -132,11 +132,11 @@ export class Lemlist implements INodeType { Object.assign(qs, filters); } - responseData = await lemlistApiRequest.call(this, 'GET', '/activities', {}, qs); - - if (!returnAll) { - const limit = this.getNodeParameter('limit', 0); - responseData = responseData.slice(0, limit); + if (returnAll) { + responseData = await lemlistApiRequestAllItems.call(this, 'GET', '/activities', qs); + } else { + qs.limit = this.getNodeParameter('limit', i); + responseData = await lemlistApiRequest.call(this, 'GET', '/activities', {}, qs); } } } else if (resource === 'campaign') { @@ -151,13 +151,15 @@ export class Lemlist implements INodeType { // https://developer.lemlist.com/#list-all-campaigns - responseData = await lemlistApiRequest.call(this, 'GET', '/campaigns'); - const returnAll = this.getNodeParameter('returnAll', i); - if (!returnAll) { - const limit = this.getNodeParameter('limit', i); - responseData = responseData.slice(0, limit); + if (returnAll) { + responseData = await lemlistApiRequestAllItems.call(this, 'GET', '/campaigns', {}); + } else { + const qs = { + limit: this.getNodeParameter('limit', i), + }; + responseData = await lemlistApiRequest.call(this, 'GET', '/campaigns', {}, qs); } } } else if (resource === 'lead') { @@ -277,7 +279,7 @@ export class Lemlist implements INodeType { const returnAll = this.getNodeParameter('returnAll', i); if (returnAll) { - responseData = await lemlistApiRequestAllItems.call(this, 'GET', '/unsubscribes'); + responseData = await lemlistApiRequestAllItems.call(this, 'GET', '/unsubscribes', {}); } else { const qs = { limit: this.getNodeParameter('limit', i), From 45dcbd4eb0dfd802ed2d535e6e65e7eb62f13782 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E0=A4=95=E0=A4=BE=E0=A4=B0=E0=A4=A4=E0=A5=8B=E0=A4=AB?= =?UTF-8?q?=E0=A5=8D=E0=A4=AB=E0=A5=87=E0=A4=B2=E0=A4=B8=E0=A5=8D=E0=A4=95?= =?UTF-8?q?=E0=A5=8D=E0=A4=B0=E0=A4=BF=E0=A4=AA=E0=A5=8D=E0=A4=9F=E2=84=A2?= Date: Wed, 26 Jul 2023 17:56:59 +0200 Subject: [PATCH 2/4] fix(core): Allow ignoring SSL issues on generic oauth2 credentials (#6702) --- packages/@n8n/client-oauth2/src/ClientOAuth2.ts | 16 ++++++++++++++-- packages/@n8n/client-oauth2/src/CodeFlow.ts | 8 ++++---- packages/@n8n/client-oauth2/src/utils.ts | 5 +++-- .../cli/src/credentials/oauth2Credential.api.ts | 1 + packages/core/src/NodeExecuteFunctions.ts | 4 ++++ .../credentials/OAuth2Api.credentials.ts | 7 +++++++ packages/workflow/src/Interfaces.ts | 1 + packages/workflow/src/NodeHelpers.ts | 2 ++ 8 files changed, 36 insertions(+), 8 deletions(-) diff --git a/packages/@n8n/client-oauth2/src/ClientOAuth2.ts b/packages/@n8n/client-oauth2/src/ClientOAuth2.ts index 6074cf11cdf7b..c9e21a6a42a63 100644 --- a/packages/@n8n/client-oauth2/src/ClientOAuth2.ts +++ b/packages/@n8n/client-oauth2/src/ClientOAuth2.ts @@ -4,7 +4,9 @@ /* eslint-disable @typescript-eslint/restrict-plus-operands */ /* eslint-disable @typescript-eslint/no-explicit-any */ import * as qs from 'querystring'; +import { Agent } from 'https'; import axios from 'axios'; +import type { AxiosRequestConfig } from 'axios'; import { getAuthError } from './utils'; import type { ClientOAuth2TokenData } from './ClientOAuth2Token'; import { ClientOAuth2Token } from './ClientOAuth2Token'; @@ -18,6 +20,7 @@ export interface ClientOAuth2RequestObject { body?: Record; query?: qs.ParsedUrlQuery; headers?: Headers; + ignoreSSLIssues?: boolean; } export interface ClientOAuth2Options { @@ -32,6 +35,7 @@ export interface ClientOAuth2Options { state?: string; body?: Record; query?: qs.ParsedUrlQuery; + ignoreSSLIssues?: boolean; } class ResponseError extends Error { @@ -40,6 +44,8 @@ class ResponseError extends Error { } } +const sslIgnoringAgent = new Agent({ rejectUnauthorized: false }); + /** * Construct an object that can handle the multiple OAuth 2.0 flows. */ @@ -86,7 +92,7 @@ export class ClientOAuth2 { url += (url.indexOf('?') === -1 ? '?' : '&') + query; } - const response = await axios.request({ + const requestConfig: AxiosRequestConfig = { url, method: options.method, data: qs.stringify(options.body), @@ -95,7 +101,13 @@ export class ClientOAuth2 { // Axios rejects the promise by default for all status codes 4xx. // We override this to reject promises only on 5xxs validateStatus: (status) => status < 500, - }); + }; + + if (options.ignoreSSLIssues) { + requestConfig.httpsAgent = sslIgnoringAgent; + } + + const response = await axios.request(requestConfig); const body = this.parseResponseBody(response.data); diff --git a/packages/@n8n/client-oauth2/src/CodeFlow.ts b/packages/@n8n/client-oauth2/src/CodeFlow.ts index 7d3b8423296ca..20679188a879e 100644 --- a/packages/@n8n/client-oauth2/src/CodeFlow.ts +++ b/packages/@n8n/client-oauth2/src/CodeFlow.ts @@ -53,13 +53,13 @@ export class CodeFlow { * the user access token. */ async getToken( - uri: string | URL, + urlString: string, opts?: Partial, ): Promise { - const options = { ...this.client.options, ...opts }; + const options: ClientOAuth2Options = { ...this.client.options, ...opts }; expects(options, 'clientId', 'accessTokenUri'); - const url = uri instanceof URL ? uri : new URL(uri, DEFAULT_URL_BASE); + const url = new URL(urlString, DEFAULT_URL_BASE); if ( typeof options.redirectUri === 'string' && typeof url.pathname === 'string' && @@ -70,7 +70,7 @@ export class CodeFlow { if (!url.search?.substring(1)) { // eslint-disable-next-line @typescript-eslint/restrict-template-expressions - throw new TypeError(`Unable to process uri: ${uri.toString()}`); + throw new TypeError(`Unable to process uri: ${urlString}`); } const data = diff --git a/packages/@n8n/client-oauth2/src/utils.ts b/packages/@n8n/client-oauth2/src/utils.ts index 44317e5dc6e8a..9d57c78b224bf 100644 --- a/packages/@n8n/client-oauth2/src/utils.ts +++ b/packages/@n8n/client-oauth2/src/utils.ts @@ -63,14 +63,15 @@ export function auth(username: string, password: string): string { */ export function getRequestOptions( { url, method, body, query, headers }: ClientOAuth2RequestObject, - options: any, + options: ClientOAuth2Options, ): ClientOAuth2RequestObject { const rOptions = { url, method, body: { ...body, ...options.body }, query: { ...query, ...options.query }, - headers: { ...headers, ...options.headers }, + headers: headers ?? {}, + ignoreSSLIssues: options.ignoreSSLIssues, }; // if request authorization was overridden delete it from header if (rOptions.headers.Authorization === '') { diff --git a/packages/cli/src/credentials/oauth2Credential.api.ts b/packages/cli/src/credentials/oauth2Credential.api.ts index e2cf481ceef7d..b6a9573ba6837 100644 --- a/packages/cli/src/credentials/oauth2Credential.api.ts +++ b/packages/cli/src/credentials/oauth2Credential.api.ts @@ -265,6 +265,7 @@ oauth2CredentialController.get( redirectUri: `${getInstanceBaseUrl()}/${restEndpoint}/oauth2-credential/callback`, scopes: split(scopes, ','), scopesSeparator: scopes.includes(',') ? ',' : ' ', + ignoreSSLIssues: get(oauthCredentials, 'ignoreSSLIssues') as boolean, }; if (oauthCredentials.grantType === 'pkce') { diff --git a/packages/core/src/NodeExecuteFunctions.ts b/packages/core/src/NodeExecuteFunctions.ts index 7f2e66265d121..aefb5d5003c4d 100644 --- a/packages/core/src/NodeExecuteFunctions.ts +++ b/packages/core/src/NodeExecuteFunctions.ts @@ -1092,6 +1092,7 @@ export async function requestOAuth2( clientSecret: credentials.clientSecret as string, accessTokenUri: credentials.accessTokenUrl as string, scopes: (credentials.scope as string).split(' '), + ignoreSSLIssues: credentials.ignoreSSLIssues as boolean, }); let oauthTokenData = credentials.oauthTokenData as ClientOAuth2TokenData; @@ -1131,6 +1132,9 @@ export async function requestOAuth2( }, oAuth2Options?.tokenType || oauthTokenData.tokenType, ); + + (requestOptions as OptionsWithUri).rejectUnauthorized = !credentials.ignoreSSLIssues; + // Signs the request by adding authorization headers or query parameters depending // on the token-type used. const newRequestOptions = token.sign(requestOptions as ClientOAuth2RequestObject); diff --git a/packages/nodes-base/credentials/OAuth2Api.credentials.ts b/packages/nodes-base/credentials/OAuth2Api.credentials.ts index 22b0d8258c8d4..ed61e2d639353 100644 --- a/packages/nodes-base/credentials/OAuth2Api.credentials.ts +++ b/packages/nodes-base/credentials/OAuth2Api.credentials.ts @@ -104,5 +104,12 @@ export class OAuth2Api implements ICredentialType { ], default: 'header', }, + { + displayName: 'Ignore SSL Issues', + name: 'ignoreSSLIssues', + type: 'boolean', + default: false, + doNotInherit: true, + }, ]; } diff --git a/packages/workflow/src/Interfaces.ts b/packages/workflow/src/Interfaces.ts index 5dfc701eef033..ed2f1f88a7a56 100644 --- a/packages/workflow/src/Interfaces.ts +++ b/packages/workflow/src/Interfaces.ts @@ -1116,6 +1116,7 @@ export interface INodeProperties { extractValue?: INodePropertyValueExtractor; modes?: INodePropertyMode[]; requiresDataPath?: 'single' | 'multiple'; + doNotInherit?: boolean; } export interface INodePropertyModeTypeOptions { diff --git a/packages/workflow/src/NodeHelpers.ts b/packages/workflow/src/NodeHelpers.ts index 4b10ce92a9134..8e658c0e43dbe 100644 --- a/packages/workflow/src/NodeHelpers.ts +++ b/packages/workflow/src/NodeHelpers.ts @@ -1601,6 +1601,8 @@ export function mergeNodeProperties( ): void { let existingIndex: number; for (const property of addProperties) { + if (property.doNotInherit) continue; + existingIndex = mainProperties.findIndex((element) => element.name === property.name); if (existingIndex === -1) { From 41e3c439f2bb94e039b951d5bb910752264018ce Mon Sep 17 00:00:00 2001 From: Omar Ajoue Date: Thu, 27 Jul 2023 12:11:43 +0200 Subject: [PATCH 3/4] fix: Display source control buttons properly (#6756) --- .../src/components/MainSidebarSourceControl.vue | 9 ++------- .../__tests__/MainSidebarSourceControl.test.ts | 12 +++++------- 2 files changed, 7 insertions(+), 14 deletions(-) diff --git a/packages/editor-ui/src/components/MainSidebarSourceControl.vue b/packages/editor-ui/src/components/MainSidebarSourceControl.vue index e04e64907bb70..6fd0a1d66517f 100644 --- a/packages/editor-ui/src/components/MainSidebarSourceControl.vue +++ b/packages/editor-ui/src/components/MainSidebarSourceControl.vue @@ -31,11 +31,6 @@ const tooltipOpenDelay = ref(300); const currentBranch = computed(() => { return sourceControlStore.preferences.branchName; }); -const featureEnabled = computed(() => window.localStorage.getItem('source-control')); -// TODO: use this for release -// const featureEnabled = computed( -// () => sourceControlStore.preferences.connected && sourceControlStore.preferences.branchName, -// ); const isInstanceOwner = computed(() => usersStore.isInstanceOwner); const setupButtonTooltipPlacement = computed(() => (props.isCollapsed ? 'right' : 'top')); @@ -125,11 +120,11 @@ const goToSourceControlSetup = async () => {