From 420b4271a90fd2902f6b4ae855fe17716c99695b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Iv=C3=A1n=20Ovejero?= Date: Tue, 16 Jan 2024 13:35:43 +0100 Subject: [PATCH 01/69] refactor(core): Move `typeorm` operators from Public API (no-changelog) (#8319) --- .../handlers/executions/executions.handler.ts | 15 +- .../handlers/executions/executions.service.ts | 110 --------------- .../repositories/execution.repository.ts | 130 +++++++++++++++++- packages/cli/src/databases/types.ts | 2 + .../cli/src/executions/executions.service.ts | 41 ++---- 5 files changed, 150 insertions(+), 148 deletions(-) delete mode 100644 packages/cli/src/PublicApi/v1/handlers/executions/executions.service.ts diff --git a/packages/cli/src/PublicApi/v1/handlers/executions/executions.handler.ts b/packages/cli/src/PublicApi/v1/handlers/executions/executions.handler.ts index 4acad28696..a54a57fbca 100644 --- a/packages/cli/src/PublicApi/v1/handlers/executions/executions.handler.ts +++ b/packages/cli/src/PublicApi/v1/handlers/executions/executions.handler.ts @@ -2,7 +2,6 @@ import type express from 'express'; import { Container } from 'typedi'; import { replaceCircularReferences } from 'n8n-workflow'; -import { getExecutions, getExecutionInWorkflows, getExecutionsCount } from './executions.service'; import { ActiveExecutions } from '@/ActiveExecutions'; import { authorize, validCursor } from '../../shared/middlewares/global.middleware'; import type { ExecutionRequest } from '../../../types'; @@ -26,7 +25,9 @@ export = { const { id } = req.params; // look for the execution on the workflow the user owns - const execution = await getExecutionInWorkflows(id, sharedWorkflowsIds, false); + const execution = await Container.get( + ExecutionRepository, + ).getExecutionInWorkflowsForPublicApi(id, sharedWorkflowsIds, false); if (!execution) { return res.status(404).json({ message: 'Not Found' }); @@ -57,7 +58,9 @@ export = { const { includeData = false } = req.query; // look for the execution on the workflow the user owns - const execution = await getExecutionInWorkflows(id, sharedWorkflowsIds, includeData); + const execution = await Container.get( + ExecutionRepository, + ).getExecutionInWorkflowsForPublicApi(id, sharedWorkflowsIds, includeData); if (!execution) { return res.status(404).json({ message: 'Not Found' }); @@ -105,13 +108,15 @@ export = { excludedExecutionsIds: runningExecutionsIds, }; - const executions = await getExecutions(filters); + const executions = + await Container.get(ExecutionRepository).getExecutionsForPublicApi(filters); const newLastId = !executions.length ? '0' : executions.slice(-1)[0].id; filters.lastId = newLastId; - const count = await getExecutionsCount(filters); + const count = + await Container.get(ExecutionRepository).getExecutionsCountForPublicApi(filters); void Container.get(InternalHooks).onUserRetrievedAllExecutions({ user_id: req.user.id, diff --git a/packages/cli/src/PublicApi/v1/handlers/executions/executions.service.ts b/packages/cli/src/PublicApi/v1/handlers/executions/executions.service.ts deleted file mode 100644 index b2811bdcda..0000000000 --- a/packages/cli/src/PublicApi/v1/handlers/executions/executions.service.ts +++ /dev/null @@ -1,110 +0,0 @@ -import type { FindOptionsWhere } from 'typeorm'; -import { In, Not, Raw, LessThan } from 'typeorm'; -import { Container } from 'typedi'; -import type { ExecutionStatus } from 'n8n-workflow'; - -import type { IExecutionBase, IExecutionFlattedDb } from '@/Interfaces'; -import { ExecutionRepository } from '@db/repositories/execution.repository'; - -function getStatusCondition(status: ExecutionStatus) { - const condition: Pick, 'status'> = {}; - - if (status === 'success') { - condition.status = 'success'; - } else if (status === 'waiting') { - condition.status = 'waiting'; - } else if (status === 'error') { - condition.status = In(['error', 'crashed', 'failed']); - } - - return condition; -} - -export async function getExecutions(params: { - limit: number; - includeData?: boolean; - lastId?: string; - workflowIds?: string[]; - status?: ExecutionStatus; - excludedExecutionsIds?: string[]; -}): Promise { - let where: FindOptionsWhere = {}; - - if (params.lastId && params.excludedExecutionsIds?.length) { - where.id = Raw((id) => `${id} < :lastId AND ${id} NOT IN (:...excludedExecutionsIds)`, { - lastId: params.lastId, - excludedExecutionsIds: params.excludedExecutionsIds, - }); - } else if (params.lastId) { - where.id = LessThan(params.lastId); - } else if (params.excludedExecutionsIds?.length) { - where.id = Not(In(params.excludedExecutionsIds)); - } - - if (params.status) { - where = { ...where, ...getStatusCondition(params.status) }; - } - - if (params.workflowIds) { - where = { ...where, workflowId: In(params.workflowIds) }; - } - - return Container.get(ExecutionRepository).findMultipleExecutions( - { - select: [ - 'id', - 'mode', - 'retryOf', - 'retrySuccessId', - 'startedAt', - 'stoppedAt', - 'workflowId', - 'waitTill', - 'finished', - ], - where, - order: { id: 'DESC' }, - take: params.limit, - relations: ['executionData'], - }, - { - includeData: params.includeData, - unflattenData: true, - }, - ); -} - -export async function getExecutionsCount(data: { - limit: number; - lastId?: string; - workflowIds?: string[]; - status?: ExecutionStatus; - excludedWorkflowIds?: string[]; -}): Promise { - // TODO: Consider moving this to the repository as well - const executions = await Container.get(ExecutionRepository).count({ - where: { - ...(data.lastId && { id: LessThan(data.lastId) }), - ...(data.status && { ...getStatusCondition(data.status) }), - ...(data.workflowIds && { workflowId: In(data.workflowIds) }), - ...(data.excludedWorkflowIds && { workflowId: Not(In(data.excludedWorkflowIds)) }), - }, - take: data.limit, - }); - - return executions; -} - -export async function getExecutionInWorkflows( - id: string, - workflowIds: string[], - includeData?: boolean, -): Promise { - return Container.get(ExecutionRepository).findSingleExecution(id, { - where: { - workflowId: In(workflowIds), - }, - includeData, - unflattenData: true, - }); -} diff --git a/packages/cli/src/databases/repositories/execution.repository.ts b/packages/cli/src/databases/repositories/execution.repository.ts index 8021433f3b..9266604d7f 100644 --- a/packages/cli/src/databases/repositories/execution.repository.ts +++ b/packages/cli/src/databases/repositories/execution.repository.ts @@ -4,15 +4,18 @@ import { DataSource, In, IsNull, + LessThan, LessThanOrEqual, MoreThanOrEqual, Not, + Raw, Repository, } from 'typeorm'; import { DateUtils } from 'typeorm/util/DateUtils'; import type { FindManyOptions, FindOneOptions, + FindOperator, FindOptionsWhere, SelectQueryBuilder, } from 'typeorm'; @@ -32,7 +35,6 @@ import type { } from '@/Interfaces'; import config from '@/config'; -import type { IGetExecutionsQueryFilter } from '@/executions/executions.service'; import { isAdvancedExecutionFiltersEnabled } from '@/executions/executionHelpers'; import type { ExecutionData } from '../entities/ExecutionData'; import { ExecutionEntity } from '../entities/ExecutionEntity'; @@ -540,4 +542,130 @@ export class ExecutionRepository extends Repository { }, }); } + + async getExecutionsCountForPublicApi(data: { + limit: number; + lastId?: string; + workflowIds?: string[]; + status?: ExecutionStatus; + excludedWorkflowIds?: string[]; + }): Promise { + const executions = await this.count({ + where: { + ...(data.lastId && { id: LessThan(data.lastId) }), + ...(data.status && { ...this.getStatusCondition(data.status) }), + ...(data.workflowIds && { workflowId: In(data.workflowIds) }), + ...(data.excludedWorkflowIds && { workflowId: Not(In(data.excludedWorkflowIds)) }), + }, + take: data.limit, + }); + + return executions; + } + + private getStatusCondition(status: ExecutionStatus) { + const condition: Pick, 'status'> = {}; + + if (status === 'success') { + condition.status = 'success'; + } else if (status === 'waiting') { + condition.status = 'waiting'; + } else if (status === 'error') { + condition.status = In(['error', 'crashed', 'failed']); + } + + return condition; + } + + async getExecutionsForPublicApi(params: { + limit: number; + includeData?: boolean; + lastId?: string; + workflowIds?: string[]; + status?: ExecutionStatus; + excludedExecutionsIds?: string[]; + }): Promise { + let where: FindOptionsWhere = {}; + + if (params.lastId && params.excludedExecutionsIds?.length) { + where.id = Raw((id) => `${id} < :lastId AND ${id} NOT IN (:...excludedExecutionsIds)`, { + lastId: params.lastId, + excludedExecutionsIds: params.excludedExecutionsIds, + }); + } else if (params.lastId) { + where.id = LessThan(params.lastId); + } else if (params.excludedExecutionsIds?.length) { + where.id = Not(In(params.excludedExecutionsIds)); + } + + if (params.status) { + where = { ...where, ...this.getStatusCondition(params.status) }; + } + + if (params.workflowIds) { + where = { ...where, workflowId: In(params.workflowIds) }; + } + + return this.findMultipleExecutions( + { + select: [ + 'id', + 'mode', + 'retryOf', + 'retrySuccessId', + 'startedAt', + 'stoppedAt', + 'workflowId', + 'waitTill', + 'finished', + ], + where, + order: { id: 'DESC' }, + take: params.limit, + relations: ['executionData'], + }, + { + includeData: params.includeData, + unflattenData: true, + }, + ); + } + + async getExecutionInWorkflowsForPublicApi( + id: string, + workflowIds: string[], + includeData?: boolean, + ): Promise { + return this.findSingleExecution(id, { + where: { + workflowId: In(workflowIds), + }, + includeData, + unflattenData: true, + }); + } + + async findIfShared(executionId: string, sharedWorkflowIds: string[]) { + return this.findSingleExecution(executionId, { + where: { + workflowId: In(sharedWorkflowIds), + }, + includeData: true, + unflattenData: false, + }); + } +} + +export interface IGetExecutionsQueryFilter { + id?: FindOperator | string; + finished?: boolean; + mode?: string; + retryOf?: string; + retrySuccessId?: string; + status?: ExecutionStatus[]; + workflowId?: string; + waitTill?: FindOperator | boolean; + metadata?: Array<{ key: string; value: string }>; + startedAfter?: string; + startedBefore?: string; } diff --git a/packages/cli/src/databases/types.ts b/packages/cli/src/databases/types.ts index 9574e5f201..25a9363a00 100644 --- a/packages/cli/src/databases/types.ts +++ b/packages/cli/src/databases/types.ts @@ -59,3 +59,5 @@ export interface Migration extends Function { } export type InsertResult = Array<{ insertId: number }>; + +export { QueryFailedError } from 'typeorm/error/QueryFailedError'; diff --git a/packages/cli/src/executions/executions.service.ts b/packages/cli/src/executions/executions.service.ts index 64fb3ff6c9..0b39da6ce9 100644 --- a/packages/cli/src/executions/executions.service.ts +++ b/packages/cli/src/executions/executions.service.ts @@ -2,15 +2,12 @@ import { validate as jsonSchemaValidate } from 'jsonschema'; import type { IWorkflowBase, JsonObject, - ExecutionStatus, ExecutionError, INode, IRunExecutionData, WorkflowExecuteMode, } from 'n8n-workflow'; import { ApplicationError, jsonParse, Workflow, WorkflowOperationError } from 'n8n-workflow'; -import type { FindOperator } from 'typeorm'; -import { In } from 'typeorm'; import { ActiveExecutions } from '@/ActiveExecutions'; import config from '@/config'; import type { User } from '@db/entities/User'; @@ -30,26 +27,13 @@ import { WorkflowRunner } from '@/WorkflowRunner'; import * as GenericHelpers from '@/GenericHelpers'; import { Container, Service } from 'typedi'; import { getStatusUsingPreviousExecutionStatusMethod } from './executionHelpers'; +import type { IGetExecutionsQueryFilter } from '@db/repositories/execution.repository'; import { ExecutionRepository } from '@db/repositories/execution.repository'; import { WorkflowRepository } from '@db/repositories/workflow.repository'; import { Logger } from '@/Logger'; import { InternalServerError } from '@/errors/response-errors/internal-server.error'; import { NotFoundError } from '@/errors/response-errors/not-found.error'; -export interface IGetExecutionsQueryFilter { - id?: FindOperator | string; - finished?: boolean; - mode?: string; - retryOf?: string; - retrySuccessId?: string; - status?: ExecutionStatus[]; - workflowId?: string; - waitTill?: FindOperator | boolean; - metadata?: Array<{ key: string; value: string }>; - startedAfter?: string; - startedBefore?: string; -} - const schemaGetExecutionsQueryFilter = { $id: '/IGetExecutionsQueryFilter', type: 'object', @@ -193,14 +177,10 @@ export class ExecutionsService { if (!sharedWorkflowIds.length) return undefined; const { id: executionId } = req.params; - const execution = await Container.get(ExecutionRepository).findSingleExecution(executionId, { - where: { - id: executionId, - workflowId: In(sharedWorkflowIds), - }, - includeData: true, - unflattenData: false, - }); + const execution = await Container.get(ExecutionRepository).findIfShared( + executionId, + sharedWorkflowIds, + ); if (!execution) { Container.get(Logger).info( @@ -225,13 +205,10 @@ export class ExecutionsService { if (!sharedWorkflowIds.length) return false; const { id: executionId } = req.params; - const execution = await Container.get(ExecutionRepository).findSingleExecution(executionId, { - where: { - workflowId: In(sharedWorkflowIds), - }, - includeData: true, - unflattenData: true, - }); + const execution = (await Container.get(ExecutionRepository).findIfShared( + executionId, + sharedWorkflowIds, + )) as unknown as IExecutionResponse; if (!execution) { Container.get(Logger).info( From 64ceb16af6343761ba5efd567dfa2a919e97a2d1 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: Tue, 16 Jan 2024 14:15:29 +0100 Subject: [PATCH 02/69] refactor(core): Use DI in PermissionChecker (no-changelog) (#8344) --- .../src/UserManagement/PermissionChecker.ts | 34 ++++++++----- .../cli/src/WorkflowExecuteAdditionalData.ts | 4 +- packages/cli/src/WorkflowHelpers.ts | 2 +- packages/cli/src/WorkflowRunner.ts | 2 +- packages/cli/src/WorkflowRunnerProcess.ts | 2 +- packages/cli/src/commands/worker.ts | 2 +- .../cli/test/unit/PermissionChecker.test.ts | 48 ++++++++++--------- 7 files changed, 52 insertions(+), 42 deletions(-) diff --git a/packages/cli/src/UserManagement/PermissionChecker.ts b/packages/cli/src/UserManagement/PermissionChecker.ts index a5e1ffdb8a..e2e42a4481 100644 --- a/packages/cli/src/UserManagement/PermissionChecker.ts +++ b/packages/cli/src/UserManagement/PermissionChecker.ts @@ -1,22 +1,32 @@ +import { Service } from 'typedi'; import type { INode, Workflow } from 'n8n-workflow'; import { NodeOperationError, WorkflowOperationError } from 'n8n-workflow'; + import config from '@/config'; import { isSharingEnabled } from './UserManagementHelper'; import { OwnershipService } from '@/services/ownership.service'; -import Container from 'typedi'; import { RoleService } from '@/services/role.service'; import { UserRepository } from '@db/repositories/user.repository'; import { SharedCredentialsRepository } from '@db/repositories/sharedCredentials.repository'; import { SharedWorkflowRepository } from '@db/repositories/sharedWorkflow.repository'; +@Service() export class PermissionChecker { + constructor( + private readonly userRepository: UserRepository, + private readonly sharedCredentialsRepository: SharedCredentialsRepository, + private readonly sharedWorkflowRepository: SharedWorkflowRepository, + private readonly roleService: RoleService, + private readonly ownershipService: OwnershipService, + ) {} + /** * Check if a user is permitted to execute a workflow. */ - static async check(workflow: Workflow, userId: string) { + async check(workflow: Workflow, userId: string) { // allow if no nodes in this workflow use creds - const credIdsToNodes = PermissionChecker.mapCredIdsToNodes(workflow); + const credIdsToNodes = this.mapCredIdsToNodes(workflow); const workflowCredIds = Object.keys(credIdsToNodes); @@ -24,7 +34,7 @@ export class PermissionChecker { // allow if requesting user is instance owner - const user = await Container.get(UserRepository).findOneOrFail({ + const user = await this.userRepository.findOneOrFail({ where: { id: userId }, relations: ['globalRole'], }); @@ -37,7 +47,7 @@ export class PermissionChecker { let workflowUserIds = [userId]; if (workflow.id && isSharingEnabled()) { - const workflowSharings = await Container.get(SharedWorkflowRepository).find({ + const workflowSharings = await this.sharedWorkflowRepository.find({ relations: ['workflow'], where: { workflowId: workflow.id }, select: ['userId'], @@ -45,9 +55,9 @@ export class PermissionChecker { workflowUserIds = workflowSharings.map((s) => s.userId); } - const roleId = await Container.get(RoleService).findCredentialOwnerRoleId(); + const roleId = await this.roleService.findCredentialOwnerRoleId(); - const credentialSharings = await Container.get(SharedCredentialsRepository).findSharings( + const credentialSharings = await this.sharedCredentialsRepository.findSharings( workflowUserIds, roleId, ); @@ -68,7 +78,7 @@ export class PermissionChecker { }); } - static async checkSubworkflowExecutePolicy( + async checkSubworkflowExecutePolicy( subworkflow: Workflow, parentWorkflowId: string, node?: INode, @@ -94,11 +104,9 @@ export class PermissionChecker { } const parentWorkflowOwner = - await Container.get(OwnershipService).getWorkflowOwnerCached(parentWorkflowId); + await this.ownershipService.getWorkflowOwnerCached(parentWorkflowId); - const subworkflowOwner = await Container.get(OwnershipService).getWorkflowOwnerCached( - subworkflow.id, - ); + const subworkflowOwner = await this.ownershipService.getWorkflowOwnerCached(subworkflow.id); const description = subworkflowOwner.id === parentWorkflowOwner.id @@ -134,7 +142,7 @@ export class PermissionChecker { } } - private static mapCredIdsToNodes(workflow: Workflow) { + private mapCredIdsToNodes(workflow: Workflow) { return Object.values(workflow.nodes).reduce<{ [credentialId: string]: INode[] }>( (map, node) => { if (node.disabled || !node.credentials) return map; diff --git a/packages/cli/src/WorkflowExecuteAdditionalData.ts b/packages/cli/src/WorkflowExecuteAdditionalData.ts index 143ef169a4..a170d01f85 100644 --- a/packages/cli/src/WorkflowExecuteAdditionalData.ts +++ b/packages/cli/src/WorkflowExecuteAdditionalData.ts @@ -795,8 +795,8 @@ async function executeWorkflow( let data; try { - await PermissionChecker.check(workflow, additionalData.userId); - await PermissionChecker.checkSubworkflowExecutePolicy( + await Container.get(PermissionChecker).check(workflow, additionalData.userId); + await Container.get(PermissionChecker).checkSubworkflowExecutePolicy( workflow, options.parentWorkflowId, options.node, diff --git a/packages/cli/src/WorkflowHelpers.ts b/packages/cli/src/WorkflowHelpers.ts index 010dee308a..9186ea5e8b 100644 --- a/packages/cli/src/WorkflowHelpers.ts +++ b/packages/cli/src/WorkflowHelpers.ts @@ -176,7 +176,7 @@ export async function executeErrorWorkflow( const failedNode = workflowErrorData.execution?.lastNodeExecuted ? workflowInstance.getNode(workflowErrorData.execution?.lastNodeExecuted) : undefined; - await PermissionChecker.checkSubworkflowExecutePolicy( + await Container.get(PermissionChecker).checkSubworkflowExecutePolicy( workflowInstance, workflowErrorData.workflow.id!, failedNode ?? undefined, diff --git a/packages/cli/src/WorkflowRunner.ts b/packages/cli/src/WorkflowRunner.ts index 1242bc876d..a6534af337 100644 --- a/packages/cli/src/WorkflowRunner.ts +++ b/packages/cli/src/WorkflowRunner.ts @@ -327,7 +327,7 @@ export class WorkflowRunner { ); try { - await PermissionChecker.check(workflow, data.userId); + await Container.get(PermissionChecker).check(workflow, data.userId); } catch (error) { ErrorReporter.error(error); // Create a failed execution with the data for the node diff --git a/packages/cli/src/WorkflowRunnerProcess.ts b/packages/cli/src/WorkflowRunnerProcess.ts index 4bb8ba24cc..199a418416 100644 --- a/packages/cli/src/WorkflowRunnerProcess.ts +++ b/packages/cli/src/WorkflowRunnerProcess.ts @@ -142,7 +142,7 @@ class WorkflowRunnerProcess { pinData: this.data.pinData, }); try { - await PermissionChecker.check(this.workflow, userId); + await Container.get(PermissionChecker).check(this.workflow, userId); } catch (error) { const caughtError = error as NodeOperationError; const failedExecutionData = generateFailedExecutionFromError( diff --git a/packages/cli/src/commands/worker.ts b/packages/cli/src/commands/worker.ts index 4848ff7d10..d1fe4e935c 100644 --- a/packages/cli/src/commands/worker.ts +++ b/packages/cli/src/commands/worker.ts @@ -185,7 +185,7 @@ export class Worker extends BaseCommand { ); try { - await PermissionChecker.check(workflow, workflowOwner.id); + await Container.get(PermissionChecker).check(workflow, workflowOwner.id); } catch (error) { if (error instanceof NodeOperationError) { const failedExecution = generateFailedExecutionFromError( diff --git a/packages/cli/test/unit/PermissionChecker.test.ts b/packages/cli/test/unit/PermissionChecker.test.ts index d411db0ea3..07c24fa11f 100644 --- a/packages/cli/test/unit/PermissionChecker.test.ts +++ b/packages/cli/test/unit/PermissionChecker.test.ts @@ -1,15 +1,20 @@ import { v4 as uuid } from 'uuid'; import { Container } from 'typedi'; -import type { INodeTypes, WorkflowSettings } from 'n8n-workflow'; +import type { WorkflowSettings } from 'n8n-workflow'; import { SubworkflowOperationError, Workflow } from 'n8n-workflow'; import config from '@/config'; import type { Role } from '@db/entities/Role'; import { User } from '@db/entities/User'; +import { WorkflowRepository } from '@db/repositories/workflow.repository'; +import { SharedWorkflowRepository } from '@db/repositories/sharedWorkflow.repository'; +import { UserRepository } from '@/databases/repositories/user.repository'; +import { generateNanoId } from '@/databases/utils/generators'; +import { License } from '@/License'; import { LoadNodesAndCredentials } from '@/LoadNodesAndCredentials'; import { NodeTypes } from '@/NodeTypes'; -import { PermissionChecker } from '@/UserManagement/PermissionChecker'; import { OwnershipService } from '@/services/ownership.service'; +import { PermissionChecker } from '@/UserManagement/PermissionChecker'; import { mockInstance } from '../shared/mocking'; import { @@ -17,18 +22,13 @@ import { randomName, randomPositiveDigit, } from '../integration/shared/random'; +import { LicenseMocker } from '../integration/shared/license'; import * as testDb from '../integration/shared/testDb'; import type { SaveCredentialFunction } from '../integration/shared/types'; import { mockNodeTypesData } from './Helpers'; import { affixRoleToSaveCredential } from '../integration/shared/db/credentials'; import { getCredentialOwnerRole, getWorkflowOwnerRole } from '../integration/shared/db/roles'; import { createOwner, createUser } from '../integration/shared/db/users'; -import { WorkflowRepository } from '@db/repositories/workflow.repository'; -import { SharedWorkflowRepository } from '@db/repositories/sharedWorkflow.repository'; -import { UserRepository } from '@/databases/repositories/user.repository'; -import { LicenseMocker } from '../integration/shared/license'; -import { License } from '@/License'; -import { generateNanoId } from '@/databases/utils/generators'; export const toTargetCallErrorMsg = (subworkflowId: string) => `Target workflow ID ${subworkflowId} may not be called`; @@ -71,24 +71,26 @@ export function createSubworkflow({ }); } -let mockNodeTypes: INodeTypes; let credentialOwnerRole: Role; let workflowOwnerRole: Role; let saveCredential: SaveCredentialFunction; +const mockNodeTypes = mockInstance(NodeTypes); mockInstance(LoadNodesAndCredentials, { loadedNodes: mockNodeTypesData(['start', 'actionNetwork']), }); +let permissionChecker: PermissionChecker; + beforeAll(async () => { await testDb.init(); - mockNodeTypes = Container.get(NodeTypes); - credentialOwnerRole = await getCredentialOwnerRole(); workflowOwnerRole = await getWorkflowOwnerRole(); saveCredential = affixRoleToSaveCredential(credentialOwnerRole); + + permissionChecker = Container.get(PermissionChecker); }); describe('check()', () => { @@ -121,7 +123,7 @@ describe('check()', () => { ], }); - expect(async () => PermissionChecker.check(workflow, userId)).not.toThrow(); + expect(async () => permissionChecker.check(workflow, userId)).not.toThrow(); }); test('should allow if requesting user is instance owner', async () => { @@ -151,7 +153,7 @@ describe('check()', () => { ], }); - expect(async () => PermissionChecker.check(workflow, owner.id)).not.toThrow(); + expect(async () => permissionChecker.check(workflow, owner.id)).not.toThrow(); }); test('should allow if workflow creds are valid subset', async () => { @@ -198,7 +200,7 @@ describe('check()', () => { ], }); - expect(async () => PermissionChecker.check(workflow, owner.id)).not.toThrow(); + expect(async () => permissionChecker.check(workflow, owner.id)).not.toThrow(); }); test('should deny if workflow creds are not valid subset', async () => { @@ -254,7 +256,7 @@ describe('check()', () => { const workflow = new Workflow(workflowDetails); - await expect(PermissionChecker.check(workflow, member.id)).rejects.toThrow(); + await expect(permissionChecker.check(workflow, member.id)).rejects.toThrow(); }); }); @@ -278,7 +280,7 @@ describe('checkSubworkflowExecutePolicy()', () => { ownershipService.getWorkflowOwnerCached.mockResolvedValue(new User()); - const check = PermissionChecker.checkSubworkflowExecutePolicy(subworkflow, parentWorkflow.id); + const check = permissionChecker.checkSubworkflowExecutePolicy(subworkflow, parentWorkflow.id); await expect(check).rejects.toThrow(toTargetCallErrorMsg(subworkflow.id)); @@ -299,12 +301,12 @@ describe('checkSubworkflowExecutePolicy()', () => { ownershipService.getWorkflowOwnerCached.mockResolvedValueOnce(firstUser); // parent workflow ownershipService.getWorkflowOwnerCached.mockResolvedValueOnce(secondUser); // subworkflow - const check = PermissionChecker.checkSubworkflowExecutePolicy(subworkflow, parentWorkflow.id); + const check = permissionChecker.checkSubworkflowExecutePolicy(subworkflow, parentWorkflow.id); await expect(check).rejects.toThrow(toTargetCallErrorMsg(subworkflow.id)); try { - await PermissionChecker.checkSubworkflowExecutePolicy(subworkflow, uuid()); + await permissionChecker.checkSubworkflowExecutePolicy(subworkflow, uuid()); } catch (error) { if (error instanceof SubworkflowOperationError) { expect(error.description).toBe( @@ -326,7 +328,7 @@ describe('checkSubworkflowExecutePolicy()', () => { callerIds: `123,456,bcdef, ${parentWorkflow.id}`, }); - const check = PermissionChecker.checkSubworkflowExecutePolicy(subworkflow, parentWorkflow.id); + const check = permissionChecker.checkSubworkflowExecutePolicy(subworkflow, parentWorkflow.id); await expect(check).resolves.not.toThrow(); }); @@ -339,7 +341,7 @@ describe('checkSubworkflowExecutePolicy()', () => { callerIds: 'xyz', }); - const check = PermissionChecker.checkSubworkflowExecutePolicy(subworkflow, parentWorkflow.id); + const check = permissionChecker.checkSubworkflowExecutePolicy(subworkflow, parentWorkflow.id); await expect(check).rejects.toThrow(); }); @@ -351,7 +353,7 @@ describe('checkSubworkflowExecutePolicy()', () => { const subworkflow = createSubworkflow({ policy: 'any' }); ownershipService.getWorkflowOwnerCached.mockResolvedValue(new User()); - const check = PermissionChecker.checkSubworkflowExecutePolicy(subworkflow, parentWorkflow.id); + const check = permissionChecker.checkSubworkflowExecutePolicy(subworkflow, parentWorkflow.id); await expect(check).resolves.not.toThrow(); }); @@ -367,7 +369,7 @@ describe('checkSubworkflowExecutePolicy()', () => { const subworkflow = createSubworkflow({ policy: 'workflowsFromSameOwner' }); - const check = PermissionChecker.checkSubworkflowExecutePolicy(subworkflow, uuid()); + const check = permissionChecker.checkSubworkflowExecutePolicy(subworkflow, uuid()); await expect(check).rejects.toThrow(toTargetCallErrorMsg(subworkflow.id)); }); @@ -382,7 +384,7 @@ describe('checkSubworkflowExecutePolicy()', () => { const subworkflow = createSubworkflow({ policy: 'workflowsFromSameOwner' }); - const check = PermissionChecker.checkSubworkflowExecutePolicy(subworkflow, parentWorkflow.id); + const check = permissionChecker.checkSubworkflowExecutePolicy(subworkflow, parentWorkflow.id); await expect(check).resolves.not.toThrow(); }); From b267bf07e365d8bb82a9847fb3c490437dc1010e 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: Tue, 16 Jan 2024 16:18:34 +0100 Subject: [PATCH 03/69] fix(core): Prevent NodeErrors from being wrapped multiple times (#8301) --- .../workflow/src/errors/abstract/node.error.ts | 18 +++++++++--------- .../workflow/test/errors/node.error.test.ts | 16 ++++++++++++++++ 2 files changed, 25 insertions(+), 9 deletions(-) create mode 100644 packages/workflow/test/errors/node.error.test.ts diff --git a/packages/workflow/src/errors/abstract/node.error.ts b/packages/workflow/src/errors/abstract/node.error.ts index 6b22e71ada..adaa2e4516 100644 --- a/packages/workflow/src/errors/abstract/node.error.ts +++ b/packages/workflow/src/errors/abstract/node.error.ts @@ -35,16 +35,16 @@ const COMMON_ERRORS: IDataObject = { * a value recursively inside an error object. */ export abstract class NodeError extends ExecutionBaseError { - node: INode; + constructor( + readonly node: INode, + error: Error | JsonObject, + ) { + if (error instanceof NodeError) return error; - constructor(node: INode, error: Error | JsonObject) { - if (error instanceof Error) { - super(error.message, { cause: error }); - } else { - super('', { errorResponse: error }); - } - - this.node = node; + const isError = error instanceof Error; + const message = isError ? error.message : ''; + const options = isError ? { cause: error } : { errorResponse: error }; + super(message, options); } /** diff --git a/packages/workflow/test/errors/node.error.test.ts b/packages/workflow/test/errors/node.error.test.ts new file mode 100644 index 0000000000..d5dcf1652d --- /dev/null +++ b/packages/workflow/test/errors/node.error.test.ts @@ -0,0 +1,16 @@ +import { mock } from 'jest-mock-extended'; +import type { INode } from '@/Interfaces'; +import { NodeApiError } from '@/errors/node-api.error'; +import { NodeOperationError } from '@/errors/node-operation.error'; + +describe('NodeError', () => { + const node = mock(); + + it('should prevent errors from being re-wrapped', () => { + const apiError = new NodeApiError(node, mock({ message: 'Some error happened', code: 500 })); + const opsError = new NodeOperationError(node, mock()); + + expect(new NodeOperationError(node, apiError)).toEqual(apiError); + expect(new NodeOperationError(node, opsError)).toEqual(opsError); + }); +}); From 7bb2d1799e83ae1a8655444ced972e7096bf8e0a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Iv=C3=A1n=20Ovejero?= Date: Tue, 16 Jan 2024 16:52:21 +0100 Subject: [PATCH 04/69] refactor(core): Consolidate executions controllers (no-changelog) (#8349) --- packages/cli/src/Server.ts | 12 +-- .../workflowStatistics.controller.ts | 2 +- .../cli/src/executions/execution.request.ts | 29 +++++++ .../executions/executions.controller.ee.ts | 68 ----------------- .../src/executions/executions.controller.ts | 76 ++++++------------- .../src/executions/executions.service.ee.ts | 4 +- .../cli/src/executions/executions.service.ts | 2 +- packages/cli/src/requests.ts | 33 +------- .../integration/shared/utils/testServer.ts | 4 +- 9 files changed, 64 insertions(+), 166 deletions(-) create mode 100644 packages/cli/src/executions/execution.request.ts delete mode 100644 packages/cli/src/executions/executions.controller.ee.ts diff --git a/packages/cli/src/Server.ts b/packages/cli/src/Server.ts index 1de23822f3..cb11e29c5c 100644 --- a/packages/cli/src/Server.ts +++ b/packages/cli/src/Server.ts @@ -39,7 +39,8 @@ import { TEMPLATES_DIR, } from '@/constants'; import { credentialsController } from '@/credentials/credentials.controller'; -import type { CurlHelper, ExecutionRequest } from '@/requests'; +import type { CurlHelper } from '@/requests'; +import type { ExecutionRequest } from '@/executions/execution.request'; import { registerController } from '@/decorators'; import { AuthController } from '@/controllers/auth.controller'; import { BinaryDataController } from '@/controllers/binaryData.controller'; @@ -56,7 +57,7 @@ import { TranslationController } from '@/controllers/translation.controller'; import { UsersController } from '@/controllers/users.controller'; import { WorkflowStatisticsController } from '@/controllers/workflowStatistics.controller'; import { ExternalSecretsController } from '@/ExternalSecrets/ExternalSecrets.controller.ee'; -import { executionsController } from '@/executions/executions.controller'; +import { ExecutionsController } from '@/executions/executions.controller'; import { isApiEnabled, loadPublicApiVersions } from '@/PublicApi'; import type { ICredentialsOverwrite, IDiagnosticInfo, IExecutionsStopData } from '@/Interfaces'; import { ActiveExecutions } from '@/ActiveExecutions'; @@ -247,6 +248,7 @@ export class Server extends AbstractServer { RoleController, ActiveWorkflowsController, WorkflowsController, + ExecutionsController, ]; if (process.env.NODE_ENV !== 'production' && Container.get(MultiMainSetup).isEnabled) { @@ -397,12 +399,6 @@ export class Server extends AbstractServer { }), ); - // ---------------------------------------- - // Executions - // ---------------------------------------- - - this.app.use(`/${this.restEndpoint}/executions`, executionsController); - // ---------------------------------------- // Executing Workflows // ---------------------------------------- diff --git a/packages/cli/src/controllers/workflowStatistics.controller.ts b/packages/cli/src/controllers/workflowStatistics.controller.ts index a5ad163823..1fa97db2d5 100644 --- a/packages/cli/src/controllers/workflowStatistics.controller.ts +++ b/packages/cli/src/controllers/workflowStatistics.controller.ts @@ -4,7 +4,7 @@ import type { WorkflowStatistics } from '@db/entities/WorkflowStatistics'; import { StatisticsNames } from '@db/entities/WorkflowStatistics'; import { SharedWorkflowRepository } from '@db/repositories/sharedWorkflow.repository'; import { WorkflowStatisticsRepository } from '@db/repositories/workflowStatistics.repository'; -import { ExecutionRequest } from '@/requests'; +import { ExecutionRequest } from '@/executions/execution.request'; import type { IWorkflowStatisticsDataLoaded } from '@/Interfaces'; import { Logger } from '@/Logger'; import { NotFoundError } from '@/errors/response-errors/not-found.error'; diff --git a/packages/cli/src/executions/execution.request.ts b/packages/cli/src/executions/execution.request.ts new file mode 100644 index 0000000000..96e1a058ae --- /dev/null +++ b/packages/cli/src/executions/execution.request.ts @@ -0,0 +1,29 @@ +import type { IExecutionDeleteFilter } from '@/Interfaces'; +import type { AuthenticatedRequest } from '@/requests'; + +export declare namespace ExecutionRequest { + namespace QueryParam { + type GetAll = { + filter: string; // '{ waitTill: string; finished: boolean, [other: string]: string }' + limit: string; + lastId: string; + firstId: string; + }; + + type GetAllCurrent = { + filter: string; // '{ workflowId: string }' + }; + } + + type GetAll = AuthenticatedRequest<{}, {}, {}, QueryParam.GetAll>; + + type Get = AuthenticatedRequest<{ id: string }, {}, {}, { unflattedResponse: 'true' | 'false' }>; + + type Delete = AuthenticatedRequest<{}, {}, IExecutionDeleteFilter>; + + type Retry = AuthenticatedRequest<{ id: string }, {}, { loadWorkflow: boolean }, {}>; + + type Stop = AuthenticatedRequest<{ id: string }>; + + type GetAllCurrent = AuthenticatedRequest<{}, {}, {}, QueryParam.GetAllCurrent>; +} diff --git a/packages/cli/src/executions/executions.controller.ee.ts b/packages/cli/src/executions/executions.controller.ee.ts deleted file mode 100644 index 673d74d15c..0000000000 --- a/packages/cli/src/executions/executions.controller.ee.ts +++ /dev/null @@ -1,68 +0,0 @@ -import express from 'express'; -import type { - IExecutionFlattedResponse, - IExecutionResponse, - IExecutionsListResponse, -} from '@/Interfaces'; -import type { ExecutionRequest } from '@/requests'; -import * as ResponseHelper from '@/ResponseHelper'; -import { isSharingEnabled } from '@/UserManagement/UserManagementHelper'; -import { EEExecutionsService } from './executions.service.ee'; - -export const EEExecutionsController = express.Router(); - -EEExecutionsController.use((req, res, next) => { - if (!isSharingEnabled()) { - // skip ee router and use free one - next('router'); - return; - } - // use ee router - next(); -}); - -/** - * GET /executions - */ -EEExecutionsController.get( - '/', - ResponseHelper.send(async (req: ExecutionRequest.GetAll): Promise => { - return EEExecutionsService.getExecutionsList(req); - }), -); - -/** - * GET /executions/:id - */ -EEExecutionsController.get( - '/:id(\\d+)', - ResponseHelper.send( - async ( - req: ExecutionRequest.Get, - ): Promise => { - return EEExecutionsService.getExecution(req); - }, - ), -); - -/** - * POST /executions/:id/retry - */ -EEExecutionsController.post( - '/:id/retry', - ResponseHelper.send(async (req: ExecutionRequest.Retry): Promise => { - return EEExecutionsService.retryExecution(req); - }), -); - -/** - * POST /executions/delete - * INFORMATION: We use POST instead of DELETE to not run into any issues with the query data - * getting too long - */ -EEExecutionsController.post( - '/delete', - ResponseHelper.send(async (req: ExecutionRequest.Delete): Promise => { - await EEExecutionsService.deleteExecutions(req); - }), -); diff --git a/packages/cli/src/executions/executions.controller.ts b/packages/cli/src/executions/executions.controller.ts index fffd493fd1..d068c75ec0 100644 --- a/packages/cli/src/executions/executions.controller.ts +++ b/packages/cli/src/executions/executions.controller.ts @@ -1,59 +1,31 @@ -import express from 'express'; -import type { - IExecutionFlattedResponse, - IExecutionResponse, - IExecutionsListResponse, -} from '@/Interfaces'; -import * as ResponseHelper from '@/ResponseHelper'; -import type { ExecutionRequest } from '@/requests'; -import { EEExecutionsController } from './executions.controller.ee'; +import { ExecutionRequest } from './execution.request'; import { ExecutionsService } from './executions.service'; +import { Authorized, Get, Post, RestController } from '@/decorators'; +import { EnterpriseExecutionsService } from './executions.service.ee'; +import { isSharingEnabled } from '@/UserManagement/UserManagementHelper'; -export const executionsController = express.Router(); -executionsController.use('/', EEExecutionsController); - -/** - * GET /executions - */ -executionsController.get( - '/', - ResponseHelper.send(async (req: ExecutionRequest.GetAll): Promise => { +@Authorized() +@RestController('/executions') +export class ExecutionsController { + @Get('/') + async getExecutionsList(req: ExecutionRequest.GetAll) { return ExecutionsService.getExecutionsList(req); - }), -); + } -/** - * GET /executions/:id - */ -executionsController.get( - '/:id(\\d+)', - ResponseHelper.send( - async ( - req: ExecutionRequest.Get, - ): Promise => { - return ExecutionsService.getExecution(req); - }, - ), -); + @Get('/:id') + async getExecution(req: ExecutionRequest.Get) { + return isSharingEnabled() + ? EnterpriseExecutionsService.getExecution(req) + : ExecutionsService.getExecution(req); + } -/** - * POST /executions/:id/retry - */ -executionsController.post( - '/:id/retry', - ResponseHelper.send(async (req: ExecutionRequest.Retry): Promise => { + @Post('/:id/retry') + async retryExecution(req: ExecutionRequest.Retry) { return ExecutionsService.retryExecution(req); - }), -); + } -/** - * POST /executions/delete - * INFORMATION: We use POST instead of DELETE to not run into any issues with the query data - * getting too long - */ -executionsController.post( - '/delete', - ResponseHelper.send(async (req: ExecutionRequest.Delete): Promise => { - await ExecutionsService.deleteExecutions(req); - }), -); + @Post('/delete') + async deleteExecutions(req: ExecutionRequest.Delete) { + return ExecutionsService.deleteExecutions(req); + } +} diff --git a/packages/cli/src/executions/executions.service.ee.ts b/packages/cli/src/executions/executions.service.ee.ts index 662ce38759..17e8179bfc 100644 --- a/packages/cli/src/executions/executions.service.ee.ts +++ b/packages/cli/src/executions/executions.service.ee.ts @@ -1,14 +1,14 @@ import type { User } from '@db/entities/User'; import { getSharedWorkflowIds } from '@/WorkflowHelpers'; import { ExecutionsService } from './executions.service'; -import type { ExecutionRequest } from '@/requests'; +import type { ExecutionRequest } from './execution.request'; import type { IExecutionResponse, IExecutionFlattedResponse } from '@/Interfaces'; import { EnterpriseWorkflowService } from '../workflows/workflow.service.ee'; import type { WorkflowWithSharingsAndCredentials } from '@/workflows/workflows.types'; import Container from 'typedi'; import { WorkflowRepository } from '@/databases/repositories/workflow.repository'; -export class EEExecutionsService extends ExecutionsService { +export class EnterpriseExecutionsService extends ExecutionsService { /** * Function to get the workflow Ids for a User regardless of role */ diff --git a/packages/cli/src/executions/executions.service.ts b/packages/cli/src/executions/executions.service.ts index 0b39da6ce9..a0a836856a 100644 --- a/packages/cli/src/executions/executions.service.ts +++ b/packages/cli/src/executions/executions.service.ts @@ -21,7 +21,7 @@ import type { } from '@/Interfaces'; import { NodeTypes } from '@/NodeTypes'; import { Queue } from '@/Queue'; -import type { ExecutionRequest } from '@/requests'; +import type { ExecutionRequest } from './execution.request'; import { getSharedWorkflowIds } from '@/WorkflowHelpers'; import { WorkflowRunner } from '@/WorkflowRunner'; import * as GenericHelpers from '@/GenericHelpers'; diff --git a/packages/cli/src/requests.ts b/packages/cli/src/requests.ts index 641d1c72fd..ef89d276b3 100644 --- a/packages/cli/src/requests.ts +++ b/packages/cli/src/requests.ts @@ -13,12 +13,7 @@ import type { import { IsBoolean, IsEmail, IsIn, IsOptional, IsString, Length } from 'class-validator'; import { NoXss } from '@db/utils/customValidators'; -import type { - PublicUser, - IExecutionDeleteFilter, - SecretsProvider, - SecretsProviderState, -} from '@/Interfaces'; +import type { PublicUser, SecretsProvider, SecretsProviderState } from '@/Interfaces'; import type { Role, RoleNames } from '@db/entities/Role'; import type { User } from '@db/entities/User'; import type { UserManagementMailer } from '@/UserManagement/email'; @@ -171,32 +166,6 @@ export declare namespace CredentialRequest { type Share = AuthenticatedRequest<{ credentialId: string }, {}, { shareWithIds: string[] }>; } -// ---------------------------------- -// /executions -// ---------------------------------- - -export declare namespace ExecutionRequest { - namespace QueryParam { - type GetAll = { - filter: string; // '{ waitTill: string; finished: boolean, [other: string]: string }' - limit: string; - lastId: string; - firstId: string; - }; - - type GetAllCurrent = { - filter: string; // '{ workflowId: string }' - }; - } - - type GetAll = AuthenticatedRequest<{}, {}, {}, QueryParam.GetAll>; - type Get = AuthenticatedRequest<{ id: string }, {}, {}, { unflattedResponse: 'true' | 'false' }>; - type Delete = AuthenticatedRequest<{}, {}, IExecutionDeleteFilter>; - type Retry = AuthenticatedRequest<{ id: string }, {}, { loadWorkflow: boolean }, {}>; - type Stop = AuthenticatedRequest<{ id: string }>; - type GetAllCurrent = AuthenticatedRequest<{}, {}, {}, QueryParam.GetAllCurrent>; -} - // ---------------------------------- // /me // ---------------------------------- diff --git a/packages/cli/test/integration/shared/utils/testServer.ts b/packages/cli/test/integration/shared/utils/testServer.ts index cb6dc1355f..496c3711f6 100644 --- a/packages/cli/test/integration/shared/utils/testServer.ts +++ b/packages/cli/test/integration/shared/utils/testServer.ts @@ -131,8 +131,8 @@ export const setupTestServer = ({ break; case 'executions': - const { executionsController } = await import('@/executions/executions.controller'); - app.use(`/${REST_PATH_SEGMENT}/executions`, executionsController); + const { ExecutionsController } = await import('@/executions/executions.controller'); + registerController(app, ExecutionsController); break; case 'variables': From d4c93b16071081002b4bd316be0921bc7867dd82 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Iv=C3=A1n=20Ovejero?= Date: Tue, 16 Jan 2024 18:25:53 +0100 Subject: [PATCH 05/69] fix(core): Prevent issues with missing or mismatching encryption key (#8332) --- packages/cli/src/commands/worker.ts | 7 +++ packages/cli/src/config/index.ts | 2 +- packages/cli/test/setup-test-folder.ts | 2 +- packages/core/src/Credentials.ts | 4 +- packages/core/src/InstanceSettings.ts | 54 +++++++++++++------ packages/core/test/InstanceSettings.test.ts | 6 +++ .../HttpRequest/V3/HttpRequestV3.node.ts | 32 +++-------- 7 files changed, 62 insertions(+), 45 deletions(-) diff --git a/packages/cli/src/commands/worker.ts b/packages/cli/src/commands/worker.ts index d1fe4e935c..2988108855 100644 --- a/packages/cli/src/commands/worker.ts +++ b/packages/cli/src/commands/worker.ts @@ -259,6 +259,13 @@ export class Worker extends BaseCommand { constructor(argv: string[], cmdConfig: IConfig) { super(argv, cmdConfig); + + if (!process.env.N8N_ENCRYPTION_KEY) { + throw new ApplicationError( + 'Missing encryption key. Worker started without the required N8N_ENCRYPTION_KEY env var. More information: https://docs.n8n.io/hosting/environment-variables/configuration-methods/#encryption-key', + ); + } + this.setInstanceType('worker'); this.setInstanceQueueModeId(); } diff --git a/packages/cli/src/config/index.ts b/packages/cli/src/config/index.ts index 44d341afcc..59962ff774 100644 --- a/packages/cli/src/config/index.ts +++ b/packages/cli/src/config/index.ts @@ -14,7 +14,7 @@ if (inE2ETests) { process.env.N8N_AI_ENABLED = 'true'; } else if (inTest) { process.env.N8N_LOG_LEVEL = 'silent'; - process.env.N8N_ENCRYPTION_KEY = 'test-encryption-key'; + process.env.N8N_ENCRYPTION_KEY = 'test_key'; process.env.N8N_PUBLIC_API_DISABLED = 'true'; process.env.SKIP_STATISTICS_EVENTS = 'true'; } else { diff --git a/packages/cli/test/setup-test-folder.ts b/packages/cli/test/setup-test-folder.ts index 07a8095373..3709ed250b 100644 --- a/packages/cli/test/setup-test-folder.ts +++ b/packages/cli/test/setup-test-folder.ts @@ -11,6 +11,6 @@ process.env.N8N_USER_FOLDER = testDir; writeFileSync( join(testDir, '.n8n/config'), - JSON.stringify({ encryptionKey: 'testkey', instanceId: '123' }), + JSON.stringify({ encryptionKey: 'test_key', instanceId: '123' }), 'utf-8', ); diff --git a/packages/core/src/Credentials.ts b/packages/core/src/Credentials.ts index 9bf58284df..7714df6898 100644 --- a/packages/core/src/Credentials.ts +++ b/packages/core/src/Credentials.ts @@ -41,9 +41,9 @@ export class Credentials extends ICredentials { throw new ApplicationError('No data is set so nothing can be returned.'); } - const decryptedData = this.cipher.decrypt(this.data); - try { + const decryptedData = this.cipher.decrypt(this.data); + return jsonParse(decryptedData); } catch (e) { throw new ApplicationError( diff --git a/packages/core/src/InstanceSettings.ts b/packages/core/src/InstanceSettings.ts index 4731ad91f2..e8ab9aa553 100644 --- a/packages/core/src/InstanceSettings.ts +++ b/packages/core/src/InstanceSettings.ts @@ -2,7 +2,7 @@ import path from 'path'; import { existsSync, mkdirSync, readFileSync, writeFileSync } from 'fs'; import { createHash, randomBytes } from 'crypto'; import { Service } from 'typedi'; -import { jsonParse } from 'n8n-workflow'; +import { ApplicationError, jsonParse } from 'n8n-workflow'; interface ReadOnlySettings { encryptionKey: string; @@ -14,6 +14,8 @@ interface WritableSettings { type Settings = ReadOnlySettings & WritableSettings; +const inTest = process.env.NODE_ENV === 'test'; + @Service() export class InstanceSettings { private readonly userHome = this.getUserHome(); @@ -57,26 +59,44 @@ export class InstanceSettings { return process.env.N8N_USER_FOLDER ?? process.env[homeVarName] ?? process.cwd(); } + /** + * Load instance settings from the settings file. If missing, create a new + * settings file with an auto-generated encryption key. + */ private loadOrCreate(): Settings { - let settings: Settings; - const { settingsFile } = this; - if (existsSync(settingsFile)) { - const content = readFileSync(settingsFile, 'utf8'); - settings = jsonParse(content, { - errorMessage: `Error parsing n8n-config file "${settingsFile}". It does not seem to be valid JSON.`, + if (existsSync(this.settingsFile)) { + const content = readFileSync(this.settingsFile, 'utf8'); + + const settings = jsonParse(content, { + errorMessage: `Error parsing n8n-config file "${this.settingsFile}". It does not seem to be valid JSON.`, }); - } else { - // Ensure that the `.n8n` folder exists - mkdirSync(this.n8nFolder, { recursive: true }); - // If file doesn't exist, create new settings - const encryptionKey = process.env.N8N_ENCRYPTION_KEY ?? randomBytes(24).toString('base64'); - settings = { encryptionKey }; - this.save(settings); - // console.info(`UserSettings were generated and saved to: ${settingsFile}`); + + if (!inTest) console.info(`User settings loaded from: ${this.settingsFile}`); + + const { encryptionKey, tunnelSubdomain } = settings; + + if (process.env.N8N_ENCRYPTION_KEY && encryptionKey !== process.env.N8N_ENCRYPTION_KEY) { + throw new ApplicationError( + `Mismatching encryption keys. The encryption key in the settings file ${this.settingsFile} does not match the N8N_ENCRYPTION_KEY env var. Please make sure both keys match. More information: https://docs.n8n.io/hosting/environment-variables/configuration-methods/#encryption-key`, + ); + } + + return { encryptionKey, tunnelSubdomain }; } - const { encryptionKey, tunnelSubdomain } = settings; - return { encryptionKey, tunnelSubdomain }; + mkdirSync(this.n8nFolder, { recursive: true }); + + const encryptionKey = process.env.N8N_ENCRYPTION_KEY ?? randomBytes(24).toString('base64'); + + const settings: Settings = { encryptionKey }; + + this.save(settings); + + if (!inTest && !process.env.N8N_ENCRYPTION_KEY) { + console.info(`No encryption key found - Auto-generated and saved to: ${this.settingsFile}`); + } + + return settings; } private generateInstanceId() { diff --git a/packages/core/test/InstanceSettings.test.ts b/packages/core/test/InstanceSettings.test.ts index 05899e9c89..414f875274 100644 --- a/packages/core/test/InstanceSettings.test.ts +++ b/packages/core/test/InstanceSettings.test.ts @@ -24,6 +24,12 @@ describe('InstanceSettings', () => { readSpy.mockReturnValue('{"encryptionKey":"test_key"'); expect(() => new InstanceSettings()).toThrowError(); }); + + it('should throw if the env and file keys do not match', () => { + readSpy.mockReturnValue(JSON.stringify({ encryptionKey: 'key_1' })); + process.env.N8N_ENCRYPTION_KEY = 'key_2'; + expect(() => new InstanceSettings()).toThrowError(); + }); }); describe('If the settings file does not exist', () => { diff --git a/packages/nodes-base/nodes/HttpRequest/V3/HttpRequestV3.node.ts b/packages/nodes-base/nodes/HttpRequest/V3/HttpRequestV3.node.ts index c6fabfa41e..38412b1d8a 100644 --- a/packages/nodes-base/nodes/HttpRequest/V3/HttpRequestV3.node.ts +++ b/packages/nodes-base/nodes/HttpRequest/V3/HttpRequestV3.node.ts @@ -1259,38 +1259,22 @@ export class HttpRequestV3 implements INodeType { genericCredentialType = this.getNodeParameter('genericAuthType', 0) as string; if (genericCredentialType === 'httpBasicAuth') { - try { - httpBasicAuth = await this.getCredentials('httpBasicAuth', itemIndex); - } catch {} + httpBasicAuth = await this.getCredentials('httpBasicAuth', itemIndex); } else if (genericCredentialType === 'httpDigestAuth') { - try { - httpDigestAuth = await this.getCredentials('httpDigestAuth', itemIndex); - } catch {} + httpDigestAuth = await this.getCredentials('httpDigestAuth', itemIndex); } else if (genericCredentialType === 'httpHeaderAuth') { - try { - httpHeaderAuth = await this.getCredentials('httpHeaderAuth', itemIndex); - } catch {} + httpHeaderAuth = await this.getCredentials('httpHeaderAuth', itemIndex); } else if (genericCredentialType === 'httpQueryAuth') { - try { - httpQueryAuth = await this.getCredentials('httpQueryAuth', itemIndex); - } catch {} + httpQueryAuth = await this.getCredentials('httpQueryAuth', itemIndex); } else if (genericCredentialType === 'httpCustomAuth') { - try { - httpCustomAuth = await this.getCredentials('httpCustomAuth', itemIndex); - } catch {} + httpCustomAuth = await this.getCredentials('httpCustomAuth', itemIndex); } else if (genericCredentialType === 'oAuth1Api') { - try { - oAuth1Api = await this.getCredentials('oAuth1Api', itemIndex); - } catch {} + oAuth1Api = await this.getCredentials('oAuth1Api', itemIndex); } else if (genericCredentialType === 'oAuth2Api') { - try { - oAuth2Api = await this.getCredentials('oAuth2Api', itemIndex); - } catch {} + oAuth2Api = await this.getCredentials('oAuth2Api', itemIndex); } } else if (authentication === 'predefinedCredentialType') { - try { - nodeCredentialType = this.getNodeParameter('nodeCredentialType', 0) as string; - } catch {} + nodeCredentialType = this.getNodeParameter('nodeCredentialType', 0) as string; } const requestMethod = this.getNodeParameter('method', itemIndex) as string; From 3734c89cf64514489831b5339d722c89b300cc54 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Iv=C3=A1n=20Ovejero?= Date: Tue, 16 Jan 2024 18:28:19 +0100 Subject: [PATCH 06/69] fix(core): Ensure waiting executions account for workflow timezone (#8340) --- packages/core/package.json | 1 + packages/core/src/NodeExecuteFunctions.ts | 3 +- packages/core/src/utils.ts | 5 ++++ packages/core/test/utils.test.ts | 35 +++++++++++++++++++++++ pnpm-lock.yaml | 8 ++++++ 5 files changed, 51 insertions(+), 1 deletion(-) create mode 100644 packages/core/src/utils.ts create mode 100644 packages/core/test/utils.test.ts diff --git a/packages/core/package.json b/packages/core/package.json index 2c336663fc..5039d4cf79 100644 --- a/packages/core/package.json +++ b/packages/core/package.json @@ -57,6 +57,7 @@ "file-type": "16.5.4", "form-data": "4.0.0", "lodash": "4.17.21", + "luxon": "^3.4.4", "mime-types": "2.1.35", "n8n-workflow": "workspace:*", "oauth-1.0a": "2.2.6", diff --git a/packages/core/src/NodeExecuteFunctions.ts b/packages/core/src/NodeExecuteFunctions.ts index f539b30a94..746d30eede 100644 --- a/packages/core/src/NodeExecuteFunctions.ts +++ b/packages/core/src/NodeExecuteFunctions.ts @@ -151,6 +151,7 @@ import Container from 'typedi'; import type { BinaryData } from './BinaryData/types'; import merge from 'lodash/merge'; import { InstanceSettings } from './InstanceSettings'; +import { toUtcDate } from './utils'; axios.defaults.timeout = 300000; // Prevent axios from adding x-form-www-urlencoded headers by default @@ -3489,7 +3490,7 @@ export function getExecuteFunctions( binaryToBuffer: async (body: Buffer | Readable) => Container.get(BinaryDataService).toBuffer(body), async putExecutionToWait(waitTill: Date): Promise { - runExecutionData.waitTill = waitTill; + runExecutionData.waitTill = toUtcDate(waitTill, getTimezone(workflow)); if (additionalData.setExecutionStatus) { additionalData.setExecutionStatus('waiting'); } diff --git a/packages/core/src/utils.ts b/packages/core/src/utils.ts new file mode 100644 index 0000000000..991044ad41 --- /dev/null +++ b/packages/core/src/utils.ts @@ -0,0 +1,5 @@ +import { DateTime } from 'luxon'; + +export function toUtcDate(datetime: Date, tz: string) { + return DateTime.fromISO(datetime.toISOString().slice(0, -1), { zone: tz }).toUTC().toJSDate(); +} diff --git a/packages/core/test/utils.test.ts b/packages/core/test/utils.test.ts new file mode 100644 index 0000000000..a155b72e1c --- /dev/null +++ b/packages/core/test/utils.test.ts @@ -0,0 +1,35 @@ +import { toUtcDate } from '@/utils'; + +describe('utils', () => { + describe('toUtcDate()', () => { + test('should convert to UTC date by adding', () => { + const originalDate = new Date('2020-01-01T00:00:00.000Z'); + const timezone = 'America/New_York'; // +5 to reach Z + + const utcDate = toUtcDate(originalDate, timezone); + + expect(utcDate).toBeInstanceOf(Date); + expect(utcDate.toISOString()).toBe('2020-01-01T05:00:00.000Z'); + }); + + test('should convert to UTC date by subtracting', () => { + const originalDate = new Date('2020-01-01T00:00:00.000Z'); + const timezone = 'Europe/Paris'; // -1 to reach Z + + const utcDate = toUtcDate(originalDate, timezone); + + expect(utcDate).toBeInstanceOf(Date); + expect(utcDate.toISOString()).toBe('2019-12-31T23:00:00.000Z'); + }); + + test('should convert to UTC date when already UTC', () => { + const originalDate = new Date('2020-01-01T00:00:00.000Z'); + const timezone = 'UTC'; // already at Z + + const utcDate = toUtcDate(originalDate, timezone); + + expect(utcDate).toBeInstanceOf(Date); + expect(utcDate.toISOString()).toBe('2020-01-01T00:00:00.000Z'); + }); + }); +}); diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index ccd855c9a5..f418fe6ba0 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -767,6 +767,9 @@ importers: lodash: specifier: 4.17.21 version: 4.17.21 + luxon: + specifier: ^3.4.4 + version: 3.4.4 mime-types: specifier: 2.1.35 version: 2.1.35 @@ -19831,6 +19834,11 @@ packages: engines: {node: '>=12'} dev: false + /luxon@3.4.4: + resolution: {integrity: sha512-zobTr7akeGHnv7eBOXcRgMeCP6+uyYsczwmeRCauvpvaAltgNyTbLH/+VaEAPUeWBT+1GuNmz4wC/6jtQzbbVA==} + engines: {node: '>=12'} + dev: false + /lz-string@1.5.0: resolution: {integrity: sha512-h5bgJWpxJNswbU7qCrV0tIKQCaS3blPDrqKWx+QxzuzL1zGUzij9XCWLrSLsJPu5t+eWA/ycetzYAO5IOMcWAQ==} hasBin: true From ab52aaf7e9846526122c52e975230aad1e239957 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, 17 Jan 2024 10:15:20 +0100 Subject: [PATCH 07/69] feat(Redis Node): Update node-redis (no-changelog) (#8269) Co-authored-by: Michael Kret --- packages/@n8n/nodes-langchain/package.json | 2 +- packages/nodes-base/nodes/Redis/Redis.node.ts | 484 +++++------------- .../nodes/Redis/RedisTrigger.node.ts | 47 +- .../nodes/Redis/test/Redis.node.test.ts | 158 ++++++ packages/nodes-base/nodes/Redis/utils.ts | 168 ++++++ packages/nodes-base/package.json | 3 +- pnpm-lock.yaml | 81 ++- 7 files changed, 517 insertions(+), 426 deletions(-) create mode 100644 packages/nodes-base/nodes/Redis/test/Redis.node.test.ts create mode 100644 packages/nodes-base/nodes/Redis/utils.ts diff --git a/packages/@n8n/nodes-langchain/package.json b/packages/@n8n/nodes-langchain/package.json index 1dcdff0266..6dfa670242 100644 --- a/packages/@n8n/nodes-langchain/package.json +++ b/packages/@n8n/nodes-langchain/package.json @@ -148,7 +148,7 @@ "openai": "4.20.0", "pdf-parse": "1.1.1", "pg": "8.11.3", - "redis": "4.6.11", + "redis": "4.6.12", "sqlite3": "5.1.6", "temp": "0.9.4", "typeorm": "0.3.17", diff --git a/packages/nodes-base/nodes/Redis/Redis.node.ts b/packages/nodes-base/nodes/Redis/Redis.node.ts index 9d7a3fc65b..414176db94 100644 --- a/packages/nodes-base/nodes/Redis/Redis.node.ts +++ b/packages/nodes-base/nodes/Redis/Redis.node.ts @@ -1,19 +1,19 @@ -import util from 'util'; import type { IExecuteFunctions, - ICredentialDataDecryptedObject, - ICredentialsDecrypted, - ICredentialTestFunctions, - IDataObject, - INodeCredentialTestResult, INodeExecutionData, INodeType, INodeTypeDescription, } from 'n8n-workflow'; -import { NodeOperationError } from 'n8n-workflow'; import set from 'lodash/set'; -import redis from 'redis'; + +import { + setupRedisClient, + redisConnectionTest, + convertInfoToObject, + getValue, + setValue, +} from './utils'; export class Redis implements INodeType { description: INodeTypeDescription = { @@ -100,6 +100,23 @@ export class Redis implements INodeType { default: 'info', }, + // ---------------------------------- + // delete + // ---------------------------------- + { + displayName: 'Key', + name: 'key', + type: 'string', + displayOptions: { + show: { + operation: ['delete'], + }, + }, + default: '', + required: true, + description: 'Name of the key to delete from Redis', + }, + // ---------------------------------- // get // ---------------------------------- @@ -117,19 +134,6 @@ export class Redis implements INodeType { description: 'Name of the property to write received data to. Supports dot-notation. Example: "data.person[0].name".', }, - { - displayName: 'Key', - name: 'key', - type: 'string', - displayOptions: { - show: { - operation: ['delete'], - }, - }, - default: '', - required: true, - description: 'Name of the key to delete from Redis', - }, { displayName: 'Key', name: 'key', @@ -498,345 +502,135 @@ export class Redis implements INodeType { }; methods = { - credentialTest: { - async redisConnectionTest( - this: ICredentialTestFunctions, - credential: ICredentialsDecrypted, - ): Promise { - const credentials = credential.data as ICredentialDataDecryptedObject; - const redisOptions: redis.ClientOpts = { - host: credentials.host as string, - port: credentials.port as number, - db: credentials.database as number, - }; - - if (credentials.password) { - redisOptions.password = credentials.password as string; - } - try { - const client = redis.createClient(redisOptions); - - await new Promise((resolve, reject): any => { - client.on('connect', async () => { - client.ping('ping', (error, pong) => { - if (error) reject(error); - resolve(pong); - client.quit(); - }); - }); - client.on('error', async (err) => { - client.quit(); - reject(err); - }); - }); - } catch (error) { - return { - status: 'Error', - message: error.message, - }; - } - return { - status: 'OK', - message: 'Connection successful!', - }; - }, - }, + credentialTest: { redisConnectionTest }, }; - async execute(this: IExecuteFunctions): Promise { - // Parses the given value in a number if it is one else returns a string - function getParsedValue(value: string): string | number { - if (value.match(/^[\d\.]+$/) === null) { - // Is a string - return value; - } else { - // Is a number - return parseFloat(value); - } - } + async execute(this: IExecuteFunctions) { + // TODO: For array and object fields it should not have a "value" field it should + // have a parameter field for a path. Because it is not possible to set + // array, object via parameter directly (should maybe be possible?!?!) + // Should maybe have a parameter which is JSON. + const credentials = await this.getCredentials('redis'); - // Converts the Redis Info String into an object - function convertInfoToObject(stringData: string): IDataObject { - const returnData: IDataObject = {}; + const client = setupRedisClient(credentials); + await client.connect(); + await client.ping(); - let key: string, value: string; - for (const line of stringData.split('\n')) { - if (['#', ''].includes(line.charAt(0))) { - continue; - } - [key, value] = line.split(':'); - if (key === undefined || value === undefined) { - continue; - } - value = value.trim(); + const operation = this.getNodeParameter('operation', 0); + const returnItems: INodeExecutionData[] = []; - if (value.includes('=')) { - returnData[key] = {}; - let key2: string, value2: string; - for (const keyValuePair of value.split(',')) { - [key2, value2] = keyValuePair.split('='); - (returnData[key] as IDataObject)[key2] = getParsedValue(value2); - } - } else { - returnData[key] = getParsedValue(value); - } - } + try { + if (operation === 'info') { + const result = await client.info(); + returnItems.push({ json: convertInfoToObject(result) }); + } else if ( + ['delete', 'get', 'keys', 'set', 'incr', 'publish', 'push', 'pop'].includes(operation) + ) { + const items = this.getInputData(); - return returnData; - } + let item: INodeExecutionData; + for (let itemIndex = 0; itemIndex < items.length; itemIndex++) { + item = { json: {} }; - async function getValue(client: redis.RedisClient, keyName: string, type?: string) { - if (type === undefined || type === 'automatic') { - // Request the type first - const clientType = util.promisify(client.type).bind(client); - type = await clientType(keyName); - } + if (operation === 'delete') { + const keyDelete = this.getNodeParameter('key', itemIndex) as string; - if (type === 'string') { - const clientGet = util.promisify(client.get).bind(client); - return clientGet(keyName); - } else if (type === 'hash') { - const clientHGetAll = util.promisify(client.hgetall).bind(client); - return clientHGetAll(keyName); - } else if (type === 'list') { - const clientLRange = util.promisify(client.lrange).bind(client); - return clientLRange(keyName, 0, -1); - } else if (type === 'sets') { - const clientSMembers = util.promisify(client.smembers).bind(client); - return clientSMembers(keyName); - } - } + await client.del(keyDelete); + returnItems.push(items[itemIndex]); + } else if (operation === 'get') { + const propertyName = this.getNodeParameter('propertyName', itemIndex) as string; + const keyGet = this.getNodeParameter('key', itemIndex) as string; + const keyType = this.getNodeParameter('keyType', itemIndex) as string; - const setValue = async ( - client: redis.RedisClient, - keyName: string, - value: string | number | object | string[] | number[], - expire: boolean, - ttl: number, - type?: string, - valueIsJSON?: boolean, - ) => { - if (type === undefined || type === 'automatic') { - // Request the type first - if (typeof value === 'string') { - type = 'string'; - } else if (Array.isArray(value)) { - type = 'list'; - } else if (typeof value === 'object') { - type = 'hash'; - } else { - throw new NodeOperationError( - this.getNode(), - 'Could not identify the type to set. Please set it manually!', - ); - } - } + const value = (await getValue(client, keyGet, keyType)) ?? null; - if (type === 'string') { - const clientSet = util.promisify(client.set).bind(client); - await clientSet(keyName, value.toString()); - } else if (type === 'hash') { - const clientHset = util.promisify(client.hset).bind(client); - if (valueIsJSON) { - let values: unknown; - if (typeof value === 'string') { + const options = this.getNodeParameter('options', itemIndex, {}); + + if (options.dotNotation === false) { + item.json[propertyName] = value; + } else { + set(item.json, propertyName, value); + } + + returnItems.push(item); + } else if (operation === 'keys') { + const keyPattern = this.getNodeParameter('keyPattern', itemIndex) as string; + const getValues = this.getNodeParameter('getValues', itemIndex, true) as boolean; + + const keys = await client.keys(keyPattern); + + if (!getValues) { + returnItems.push({ json: { keys } }); + continue; + } + + for (const keyName of keys) { + item.json[keyName] = await getValue(client, keyName); + } + returnItems.push(item); + } else if (operation === 'set') { + const keySet = this.getNodeParameter('key', itemIndex) as string; + const value = this.getNodeParameter('value', itemIndex) as string; + const keyType = this.getNodeParameter('keyType', itemIndex) as string; + const valueIsJSON = this.getNodeParameter('valueIsJSON', itemIndex, true) as boolean; + const expire = this.getNodeParameter('expire', itemIndex, false) as boolean; + const ttl = this.getNodeParameter('ttl', itemIndex, -1) as number; + + await setValue.call(this, client, keySet, value, expire, ttl, keyType, valueIsJSON); + returnItems.push(items[itemIndex]); + } else if (operation === 'incr') { + const keyIncr = this.getNodeParameter('key', itemIndex) as string; + const expire = this.getNodeParameter('expire', itemIndex, false) as boolean; + const ttl = this.getNodeParameter('ttl', itemIndex, -1) as number; + const incrementVal = await client.incr(keyIncr); + if (expire && ttl > 0) { + await client.expire(keyIncr, ttl); + } + returnItems.push({ json: { [keyIncr]: incrementVal } }); + } else if (operation === 'publish') { + const channel = this.getNodeParameter('channel', itemIndex) as string; + const messageData = this.getNodeParameter('messageData', itemIndex) as string; + await client.publish(channel, messageData); + returnItems.push(items[itemIndex]); + } else if (operation === 'push') { + const redisList = this.getNodeParameter('list', itemIndex) as string; + const messageData = this.getNodeParameter('messageData', itemIndex) as string; + const tail = this.getNodeParameter('tail', itemIndex, false) as boolean; + await client[tail ? 'rPush' : 'lPush'](redisList, messageData); + returnItems.push(items[itemIndex]); + } else if (operation === 'pop') { + const redisList = this.getNodeParameter('list', itemIndex) as string; + const tail = this.getNodeParameter('tail', itemIndex, false) as boolean; + const propertyName = this.getNodeParameter( + 'propertyName', + itemIndex, + 'propertyName', + ) as string; + + const value = await client[tail ? 'rPop' : 'lPop'](redisList); + + let outputValue; try { - values = JSON.parse(value); + outputValue = value && JSON.parse(value); } catch { - // This is how we originally worked and prevents a breaking change - values = value; + outputValue = value; } - } else { - values = value; - } - for (const key of Object.keys(values as object)) { - // @ts-ignore - await clientHset(keyName, key, (values as IDataObject)[key]!.toString()); - } - } else { - const values = value.toString().split(' '); - //@ts-ignore - await clientHset(keyName, values); - } - } else if (type === 'list') { - const clientLset = util.promisify(client.lset).bind(client); - for (let index = 0; index < (value as string[]).length; index++) { - await clientLset(keyName, index, (value as IDataObject)[index]!.toString()); - } - } else if (type === 'sets') { - const clientSadd = util.promisify(client.sadd).bind(client); - //@ts-ignore - await clientSadd(keyName, value); - } - - if (expire) { - const clientExpire = util.promisify(client.expire).bind(client); - await clientExpire(keyName, ttl); - } - return; - }; - - return new Promise(async (resolve, reject) => { - // TODO: For array and object fields it should not have a "value" field it should - // have a parameter field for a path. Because it is not possible to set - // array, object via parameter directly (should maybe be possible?!?!) - // Should maybe have a parameter which is JSON. - const credentials = await this.getCredentials('redis'); - - const redisOptions: redis.ClientOpts = { - host: credentials.host as string, - port: credentials.port as number, - db: credentials.database as number, - }; - - if (credentials.password) { - redisOptions.password = credentials.password as string; - } - - const client = redis.createClient(redisOptions); - - const operation = this.getNodeParameter('operation', 0); - - client.on('error', (err: Error) => { - client.quit(); - reject(err); - }); - - client.on('ready', async (_err: Error | null) => { - client.select(credentials.database as number); - try { - if (operation === 'info') { - const clientInfo = util.promisify(client.info).bind(client); - const result = await clientInfo(); - - resolve([[{ json: convertInfoToObject(result as string) }]]); - client.quit(); - } else if ( - ['delete', 'get', 'keys', 'set', 'incr', 'publish', 'push', 'pop'].includes(operation) - ) { - const items = this.getInputData(); - const returnItems: INodeExecutionData[] = []; - - let item: INodeExecutionData; - for (let itemIndex = 0; itemIndex < items.length; itemIndex++) { - item = { json: {} }; - - if (operation === 'delete') { - const keyDelete = this.getNodeParameter('key', itemIndex) as string; - - const clientDel = util.promisify(client.del).bind(client); - // @ts-ignore - await clientDel(keyDelete); - returnItems.push(items[itemIndex]); - } else if (operation === 'get') { - const propertyName = this.getNodeParameter('propertyName', itemIndex) as string; - const keyGet = this.getNodeParameter('key', itemIndex) as string; - const keyType = this.getNodeParameter('keyType', itemIndex) as string; - - const value = (await getValue(client, keyGet, keyType)) || null; - - const options = this.getNodeParameter('options', itemIndex, {}); - - if (options.dotNotation === false) { - item.json[propertyName] = value; - } else { - set(item.json, propertyName, value); - } - - returnItems.push(item); - } else if (operation === 'keys') { - const keyPattern = this.getNodeParameter('keyPattern', itemIndex) as string; - const getValues = this.getNodeParameter('getValues', itemIndex, true) as boolean; - - const clientKeys = util.promisify(client.keys).bind(client); - const keys = await clientKeys(keyPattern); - - if (!getValues) { - returnItems.push({ json: { keys } }); - continue; - } - - for (const keyName of keys) { - item.json[keyName] = await getValue(client, keyName); - } - returnItems.push(item); - } else if (operation === 'set') { - const keySet = this.getNodeParameter('key', itemIndex) as string; - const value = this.getNodeParameter('value', itemIndex) as string; - const keyType = this.getNodeParameter('keyType', itemIndex) as string; - const valueIsJSON = this.getNodeParameter( - 'valueIsJSON', - itemIndex, - true, - ) as boolean; - const expire = this.getNodeParameter('expire', itemIndex, false) as boolean; - const ttl = this.getNodeParameter('ttl', itemIndex, -1) as number; - - await setValue(client, keySet, value, expire, ttl, keyType, valueIsJSON); - returnItems.push(items[itemIndex]); - } else if (operation === 'incr') { - const keyIncr = this.getNodeParameter('key', itemIndex) as string; - const expire = this.getNodeParameter('expire', itemIndex, false) as boolean; - const ttl = this.getNodeParameter('ttl', itemIndex, -1) as number; - const clientIncr = util.promisify(client.incr).bind(client); - // @ts-ignore - const incrementVal = await clientIncr(keyIncr); - if (expire && ttl > 0) { - const clientExpire = util.promisify(client.expire).bind(client); - await clientExpire(keyIncr, ttl); - } - returnItems.push({ json: { [keyIncr]: incrementVal } }); - } else if (operation === 'publish') { - const channel = this.getNodeParameter('channel', itemIndex) as string; - const messageData = this.getNodeParameter('messageData', itemIndex) as string; - const clientPublish = util.promisify(client.publish).bind(client); - await clientPublish(channel, messageData); - returnItems.push(items[itemIndex]); - } else if (operation === 'push') { - const redisList = this.getNodeParameter('list', itemIndex) as string; - const messageData = this.getNodeParameter('messageData', itemIndex) as string; - const tail = this.getNodeParameter('tail', itemIndex, false) as boolean; - const action = tail ? client.RPUSH : client.LPUSH; - const clientPush = util.promisify(action).bind(client); - // @ts-ignore: typescript not understanding generic function signatures - await clientPush(redisList, messageData); - returnItems.push(items[itemIndex]); - } else if (operation === 'pop') { - const redisList = this.getNodeParameter('list', itemIndex) as string; - const tail = this.getNodeParameter('tail', itemIndex, false) as boolean; - const propertyName = this.getNodeParameter( - 'propertyName', - itemIndex, - 'propertyName', - ) as string; - - const action = tail ? client.rpop : client.lpop; - const clientPop = util.promisify(action).bind(client); - const value = await clientPop(redisList); - - let outputValue; - try { - outputValue = JSON.parse(value); - } catch { - outputValue = value; - } - const options = this.getNodeParameter('options', itemIndex, {}); - if (options.dotNotation === false) { - item.json[propertyName] = outputValue; - } else { - set(item.json, propertyName, outputValue); - } - returnItems.push(item); - } + const options = this.getNodeParameter('options', itemIndex, {}); + if (options.dotNotation === false) { + item.json[propertyName] = outputValue; + } else { + set(item.json, propertyName, outputValue); } - - client.quit(); - resolve([returnItems]); + returnItems.push(item); } - } catch (error) { - reject(error); } - }); - }); + } + } catch (error) { + throw error; + } finally { + await client.quit(); + } + + return [returnItems]; } } diff --git a/packages/nodes-base/nodes/Redis/RedisTrigger.node.ts b/packages/nodes-base/nodes/Redis/RedisTrigger.node.ts index 39622f0c0c..9616ba486a 100644 --- a/packages/nodes-base/nodes/Redis/RedisTrigger.node.ts +++ b/packages/nodes-base/nodes/Redis/RedisTrigger.node.ts @@ -1,3 +1,4 @@ +/* eslint-disable @typescript-eslint/no-loop-func */ import type { ITriggerFunctions, IDataObject, @@ -7,7 +8,7 @@ import type { } from 'n8n-workflow'; import { NodeOperationError } from 'n8n-workflow'; -import redis from 'redis'; +import { redisConnectionTest, setupRedisClient } from './utils'; export class RedisTrigger implements INodeType { description: INodeTypeDescription = { @@ -26,6 +27,7 @@ export class RedisTrigger implements INodeType { { name: 'redis', required: true, + testedBy: 'redisConnectionTest', }, ], properties: [ @@ -64,36 +66,29 @@ export class RedisTrigger implements INodeType { ], }; + methods = { + credentialTest: { redisConnectionTest }, + }; + async trigger(this: ITriggerFunctions): Promise { const credentials = await this.getCredentials('redis'); - const redisOptions: redis.ClientOpts = { - host: credentials.host as string, - port: credentials.port as number, - db: credentials.database as number, - }; - - if (credentials.password) { - redisOptions.password = credentials.password as string; - } - const channels = (this.getNodeParameter('channels') as string).split(','); - const options = this.getNodeParameter('options') as IDataObject; if (!channels) { throw new NodeOperationError(this.getNode(), 'Channels are mandatory!'); } - const client = redis.createClient(redisOptions); + const client = setupRedisClient(credentials); const manualTriggerFunction = async () => { - await new Promise((resolve, reject) => { - client.on('connect', () => { - for (const channel of channels) { - client.psubscribe(channel); - } - client.on('pmessage', (pattern: string, channel: string, message: string) => { + await client.connect(); + await client.ping(); + + try { + for (const channel of channels) { + await client.pSubscribe(channel, (message) => { if (options.jsonParseBody) { try { message = JSON.parse(message); @@ -102,19 +97,15 @@ export class RedisTrigger implements INodeType { if (options.onlyMessage) { this.emit([this.helpers.returnJsonArray({ message })]); - resolve(true); return; } this.emit([this.helpers.returnJsonArray({ channel, message })]); - resolve(true); }); - }); - - client.on('error', (error) => { - reject(error); - }); - }); + } + } catch (error) { + throw new NodeOperationError(this.getNode(), error); + } }; if (this.getMode() === 'trigger') { @@ -122,7 +113,7 @@ export class RedisTrigger implements INodeType { } async function closeFunction() { - client.quit(); + await client.quit(); } return { diff --git a/packages/nodes-base/nodes/Redis/test/Redis.node.test.ts b/packages/nodes-base/nodes/Redis/test/Redis.node.test.ts new file mode 100644 index 0000000000..892165169a --- /dev/null +++ b/packages/nodes-base/nodes/Redis/test/Redis.node.test.ts @@ -0,0 +1,158 @@ +import { mock } from 'jest-mock-extended'; +import type { RedisClientType } from '@redis/client'; +import type { IExecuteFunctions } from 'n8n-workflow'; +import { Redis } from '../Redis.node'; + +const mockClient = mock(); +jest.mock('redis', () => ({ + createClient: () => mockClient, +})); + +describe('Redis Node', () => { + const mockCredential = { + host: 'redis', + port: 1234, + database: 0, + password: 'random', + }; + + const node = new Redis(); + const thisArg = mock({}); + thisArg.getCredentials.calledWith('redis').mockResolvedValue(mockCredential); + + beforeEach(() => jest.clearAllMocks()); + + afterEach(() => { + expect(mockClient.connect).toHaveBeenCalled(); + expect(mockClient.ping).toHaveBeenCalled(); + expect(mockClient.quit).toHaveBeenCalled(); + }); + + it('info operation', async () => { + thisArg.getNodeParameter.calledWith('operation', 0).mockReturnValue('info'); + mockClient.info.mockResolvedValue(` +# Server +redis_version:6.2.14 +redis_git_sha1:00000000 +redis_git_dirty:0 +redis_mode:standalone +arch_bits:64 +tcp_port:6379 +uptime_in_seconds:429905 +uptime_in_days:4 + +# Clients +connected_clients:1 +cluster_connections:0 +max_clients:10000 + +# Memory +used_memory:876648 + +# Replication +role:master +connected_slaves:0 +master_failover_state:no-failover + `); + + const output = await node.execute.call(thisArg); + + expect(mockClient.info).toHaveBeenCalled(); + expect(output[0][0].json).toEqual({ + redis_version: 6.2, + redis_git_sha1: 0, + redis_git_dirty: 0, + redis_mode: 'standalone', + arch_bits: 64, + tcp_port: 6379, + uptime_in_seconds: 429905, + uptime_in_days: 4, + connected_clients: 1, + cluster_connections: 0, + max_clients: 10000, + used_memory: 876648, + role: 'master', + connected_slaves: 0, + master_failover_state: 'no-failover', + }); + }); + + it('delete operation', async () => { + thisArg.getInputData.mockReturnValue([{ json: { x: 1 } }]); + thisArg.getNodeParameter.calledWith('operation', 0).mockReturnValue('delete'); + thisArg.getNodeParameter.calledWith('key', 0).mockReturnValue('key1'); + mockClient.del.calledWith('key1').mockResolvedValue(1); + + const output = await node.execute.call(thisArg); + expect(mockClient.del).toHaveBeenCalledWith('key1'); + expect(output[0][0].json).toEqual({ x: 1 }); + }); + + describe('get operation', () => { + beforeEach(() => { + thisArg.getInputData.mockReturnValue([{ json: { x: 1 } }]); + thisArg.getNodeParameter.calledWith('operation', 0).mockReturnValue('get'); + thisArg.getNodeParameter.calledWith('options', 0).mockReturnValue({ dotNotation: true }); + thisArg.getNodeParameter.calledWith('key', 0).mockReturnValue('key1'); + thisArg.getNodeParameter.calledWith('propertyName', 0).mockReturnValue('x.y'); + }); + + it('keyType = automatic', async () => { + thisArg.getNodeParameter.calledWith('keyType', 0).mockReturnValue('automatic'); + mockClient.type.calledWith('key1').mockResolvedValue('string'); + mockClient.get.calledWith('key1').mockResolvedValue('value'); + + const output = await node.execute.call(thisArg); + expect(mockClient.type).toHaveBeenCalledWith('key1'); + expect(mockClient.get).toHaveBeenCalledWith('key1'); + expect(output[0][0].json).toEqual({ x: { y: 'value' } }); + }); + + it('keyType = hash', async () => { + thisArg.getNodeParameter.calledWith('keyType', 0).mockReturnValue('hash'); + mockClient.hGetAll.calledWith('key1').mockResolvedValue({ + field1: '1', + field2: '2', + }); + + const output = await node.execute.call(thisArg); + expect(mockClient.hGetAll).toHaveBeenCalledWith('key1'); + expect(output[0][0].json).toEqual({ + x: { + y: { + field1: '1', + field2: '2', + }, + }, + }); + }); + }); + + describe('keys operation', () => { + beforeEach(() => { + thisArg.getInputData.mockReturnValue([{ json: { x: 1 } }]); + thisArg.getNodeParameter.calledWith('operation', 0).mockReturnValue('keys'); + thisArg.getNodeParameter.calledWith('keyPattern', 0).mockReturnValue('key*'); + mockClient.keys.calledWith('key*').mockResolvedValue(['key1', 'key2']); + }); + + it('getValues = false', async () => { + thisArg.getNodeParameter.calledWith('getValues', 0).mockReturnValue(false); + + const output = await node.execute.call(thisArg); + expect(mockClient.keys).toHaveBeenCalledWith('key*'); + expect(output[0][0].json).toEqual({ keys: ['key1', 'key2'] }); + }); + + it('getValues = true', async () => { + thisArg.getNodeParameter.calledWith('getValues', 0).mockReturnValue(true); + mockClient.type.mockResolvedValue('string'); + mockClient.get.calledWith('key1').mockResolvedValue('value1'); + mockClient.get.calledWith('key2').mockResolvedValue('value2'); + + const output = await node.execute.call(thisArg); + expect(mockClient.keys).toHaveBeenCalledWith('key*'); + expect(output[0][0].json).toEqual({ key1: 'value1', key2: 'value2' }); + }); + }); +}); diff --git a/packages/nodes-base/nodes/Redis/utils.ts b/packages/nodes-base/nodes/Redis/utils.ts new file mode 100644 index 0000000000..6aca6b13bf --- /dev/null +++ b/packages/nodes-base/nodes/Redis/utils.ts @@ -0,0 +1,168 @@ +import type { + ICredentialDataDecryptedObject, + ICredentialTestFunctions, + ICredentialsDecrypted, + IDataObject, + IExecuteFunctions, + INodeCredentialTestResult, +} from 'n8n-workflow'; +import { NodeOperationError } from 'n8n-workflow'; + +import { createClient } from 'redis'; +export type RedisClientType = ReturnType; + +export function setupRedisClient(credentials: ICredentialDataDecryptedObject): RedisClientType { + const redisOptions = { + socket: { + host: credentials.host as string, + port: credentials.port as number, + }, + database: credentials.database as number, + password: (credentials.password as string) || undefined, + }; + + return createClient(redisOptions); +} + +export async function redisConnectionTest( + this: ICredentialTestFunctions, + credential: ICredentialsDecrypted, +): Promise { + const credentials = credential.data as ICredentialDataDecryptedObject; + + try { + const client = setupRedisClient(credentials); + await client.connect(); + await client.ping(); + return { + status: 'OK', + message: 'Connection successful!', + }; + } catch (error) { + return { + status: 'Error', + message: error.message, + }; + } +} + +/** Parses the given value in a number if it is one else returns a string */ +function getParsedValue(value: string): string | number { + if (value.match(/^[\d\.]+$/) === null) { + // Is a string + return value; + } else { + // Is a number + return parseFloat(value); + } +} + +/** Converts the Redis Info String into an object */ +export function convertInfoToObject(stringData: string): IDataObject { + const returnData: IDataObject = {}; + + let key: string, value: string; + for (const line of stringData.split('\n')) { + if (['#', ''].includes(line.charAt(0))) { + continue; + } + [key, value] = line.split(':'); + if (key === undefined || value === undefined) { + continue; + } + value = value.trim(); + + if (value.includes('=')) { + returnData[key] = {}; + let key2: string, value2: string; + for (const keyValuePair of value.split(',')) { + [key2, value2] = keyValuePair.split('='); + (returnData[key] as IDataObject)[key2] = getParsedValue(value2); + } + } else { + returnData[key] = getParsedValue(value); + } + } + + return returnData; +} + +export async function getValue(client: RedisClientType, keyName: string, type?: string) { + if (type === undefined || type === 'automatic') { + // Request the type first + type = await client.type(keyName); + } + + if (type === 'string') { + return client.get(keyName); + } else if (type === 'hash') { + return client.hGetAll(keyName); + } else if (type === 'list') { + return client.lRange(keyName, 0, -1); + } else if (type === 'sets') { + return client.sMembers(keyName); + } +} + +export async function setValue( + this: IExecuteFunctions, + client: RedisClientType, + keyName: string, + value: string | number | object | string[] | number[], + expire: boolean, + ttl: number, + type?: string, + valueIsJSON?: boolean, +) { + if (type === undefined || type === 'automatic') { + // Request the type first + if (typeof value === 'string') { + type = 'string'; + } else if (Array.isArray(value)) { + type = 'list'; + } else if (typeof value === 'object') { + type = 'hash'; + } else { + throw new NodeOperationError( + this.getNode(), + 'Could not identify the type to set. Please set it manually!', + ); + } + } + + if (type === 'string') { + await client.set(keyName, value.toString()); + } else if (type === 'hash') { + if (valueIsJSON) { + let values: unknown; + if (typeof value === 'string') { + try { + values = JSON.parse(value); + } catch { + // This is how we originally worked and prevents a breaking change + values = value; + } + } else { + values = value; + } + for (const key of Object.keys(values as object)) { + await client.hSet(keyName, key, (values as IDataObject)[key]!.toString()); + } + } else { + const values = value.toString().split(' '); + await client.hSet(keyName, values); + } + } else if (type === 'list') { + for (let index = 0; index < (value as string[]).length; index++) { + await client.lSet(keyName, index, (value as IDataObject)[index]!.toString()); + } + } else if (type === 'sets') { + //@ts-ignore + await client.sAdd(keyName, value); + } + + if (expire) { + await client.expire(keyName, ttl); + } + return; +} diff --git a/packages/nodes-base/package.json b/packages/nodes-base/package.json index eb4fbbb36c..5770cb7731 100644 --- a/packages/nodes-base/package.json +++ b/packages/nodes-base/package.json @@ -816,7 +816,6 @@ "@types/node-ssh": "^7.0.1", "@types/nodemailer": "^6.4.0", "@types/promise-ftp": "^1.3.4", - "@types/redis": "^2.8.11", "@types/request-promise-native": "~1.0.15", "@types/rfc2047": "^2.0.1", "@types/showdown": "^1.9.4", @@ -877,7 +876,7 @@ "pretty-bytes": "5.6.0", "promise-ftp": "1.3.5", "pyodide": "0.23.4", - "redis": "3.1.2", + "redis": "4.6.12", "rfc2047": "4.0.1", "rhea": "1.0.24", "rss-parser": "3.12.0", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index f418fe6ba0..9f12f9da06 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -231,7 +231,7 @@ importers: version: 1.2.0 langchain: specifier: 0.0.198 - version: 0.0.198(@aws-sdk/client-bedrock-runtime@3.454.0)(@aws-sdk/credential-provider-node@3.451.0)(@getzep/zep-js@0.9.0)(@google-ai/generativelanguage@0.2.1)(@huggingface/inference@2.6.4)(@pinecone-database/pinecone@1.1.2)(@qdrant/js-client-rest@1.7.0)(@supabase/supabase-js@2.38.5)(@xata.io/client@0.25.3)(axios@1.6.2)(cohere-ai@6.2.2)(d3-dsv@2.0.0)(epub2@3.0.1)(html-to-text@9.0.5)(lodash@4.17.21)(mammoth@1.6.0)(pdf-parse@1.1.1)(pg@8.11.3)(redis@4.6.11)(typeorm@0.3.17) + version: 0.0.198(@aws-sdk/client-bedrock-runtime@3.454.0)(@aws-sdk/credential-provider-node@3.451.0)(@getzep/zep-js@0.9.0)(@google-ai/generativelanguage@0.2.1)(@huggingface/inference@2.6.4)(@pinecone-database/pinecone@1.1.2)(@qdrant/js-client-rest@1.7.0)(@supabase/supabase-js@2.38.5)(@xata.io/client@0.25.3)(axios@1.6.2)(cohere-ai@6.2.2)(d3-dsv@2.0.0)(epub2@3.0.1)(html-to-text@9.0.5)(lodash@4.17.21)(mammoth@1.6.0)(pdf-parse@1.1.1)(pg@8.11.3)(redis@4.6.12)(typeorm@0.3.17) lodash: specifier: 4.17.21 version: 4.17.21 @@ -257,8 +257,8 @@ importers: specifier: 8.11.3 version: 8.11.3 redis: - specifier: 4.6.11 - version: 4.6.11 + specifier: 4.6.12 + version: 4.6.12 sqlite3: specifier: 5.1.6 version: 5.1.6 @@ -267,7 +267,7 @@ importers: version: 0.9.4 typeorm: specifier: 0.3.17 - version: 0.3.17(mssql@9.1.1)(pg@8.11.3)(redis@4.6.11)(sqlite3@5.1.6) + version: 0.3.17(mssql@9.1.1)(pg@8.11.3)(redis@4.6.12)(sqlite3@5.1.6) zod: specifier: 3.22.4 version: 3.22.4 @@ -1363,8 +1363,8 @@ importers: specifier: 0.23.4 version: 0.23.4(patch_hash=kzcwsjcayy5m6iezu7r4tdimjq) redis: - specifier: 3.1.2 - version: 3.1.2 + specifier: 4.6.12 + version: 4.6.12 rfc2047: specifier: 4.0.1 version: 4.0.1 @@ -1465,9 +1465,6 @@ importers: '@types/promise-ftp': specifier: ^1.3.4 version: 1.3.4 - '@types/redis': - specifier: ^2.8.11 - version: 2.8.32 '@types/request-promise-native': specifier: ~1.0.15 version: 1.0.18 @@ -7158,16 +7155,16 @@ packages: '@babel/runtime': 7.22.6 dev: true - /@redis/bloom@1.2.0(@redis/client@1.5.12): + /@redis/bloom@1.2.0(@redis/client@1.5.13): resolution: {integrity: sha512-HG2DFjYKbpNmVXsa0keLHp/3leGJz1mjh09f2RLGGLQZzSHpkmZWuwJbAvo3QcRY8p80m5+ZdXZdYOSBLlp7Cg==} peerDependencies: '@redis/client': ^1.0.0 dependencies: - '@redis/client': 1.5.12 + '@redis/client': 1.5.13 dev: false - /@redis/client@1.5.12: - resolution: {integrity: sha512-/ZjE18HRzMd80eXIIUIPcH81UoZpwulbo8FmbElrjPqH0QC0SeIKu1BOU49bO5trM5g895kAjhvalt5h77q+4A==} + /@redis/client@1.5.13: + resolution: {integrity: sha512-epkUM9D0Sdmt93/8Ozk43PNjLi36RZzG+d/T1Gdu5AI8jvghonTeLYV69WVWdilvFo+PYxbP0TZ0saMvr6nscQ==} engines: {node: '>=14'} dependencies: cluster-key-slot: 1.1.2 @@ -7175,36 +7172,36 @@ packages: yallist: 4.0.0 dev: false - /@redis/graph@1.1.1(@redis/client@1.5.12): + /@redis/graph@1.1.1(@redis/client@1.5.13): resolution: {integrity: sha512-FEMTcTHZozZciLRl6GiiIB4zGm5z5F3F6a6FZCyrfxdKOhFlGkiAqlexWMBzCi4DcRoyiOsuLfW+cjlGWyExOw==} peerDependencies: '@redis/client': ^1.0.0 dependencies: - '@redis/client': 1.5.12 + '@redis/client': 1.5.13 dev: false - /@redis/json@1.0.6(@redis/client@1.5.12): + /@redis/json@1.0.6(@redis/client@1.5.13): resolution: {integrity: sha512-rcZO3bfQbm2zPRpqo82XbW8zg4G/w4W3tI7X8Mqleq9goQjAGLL7q/1n1ZX4dXEAmORVZ4s1+uKLaUOg7LrUhw==} peerDependencies: '@redis/client': ^1.0.0 dependencies: - '@redis/client': 1.5.12 + '@redis/client': 1.5.13 dev: false - /@redis/search@1.1.6(@redis/client@1.5.12): + /@redis/search@1.1.6(@redis/client@1.5.13): resolution: {integrity: sha512-mZXCxbTYKBQ3M2lZnEddwEAks0Kc7nauire8q20oA0oA/LoA+E/b5Y5KZn232ztPb1FkIGqo12vh3Lf+Vw5iTw==} peerDependencies: '@redis/client': ^1.0.0 dependencies: - '@redis/client': 1.5.12 + '@redis/client': 1.5.13 dev: false - /@redis/time-series@1.0.5(@redis/client@1.5.12): + /@redis/time-series@1.0.5(@redis/client@1.5.13): resolution: {integrity: sha512-IFjIgTusQym2B5IZJG3XKr5llka7ey84fw/NOYqESP5WUfQs9zz1ww/9+qoz4ka/S6KcGBodzlCeZ5UImKbscg==} peerDependencies: '@redis/client': ^1.0.0 dependencies: - '@redis/client': 1.5.12 + '@redis/client': 1.5.13 dev: false /@rollup/plugin-alias@5.1.0(rollup@3.29.4): @@ -10598,12 +10595,6 @@ packages: csstype: 3.1.1 dev: true - /@types/redis@2.8.32: - resolution: {integrity: sha512-7jkMKxcGq9p242exlbsVzuJb57KqHRhNl4dHoQu2Y5v9bCAbtIXXH0R3HleSQW4CTOqpHIYUW3t6tpUj4BVQ+w==} - dependencies: - '@types/node': 18.16.16 - dev: true - /@types/replacestream@4.0.1: resolution: {integrity: sha512-3ecTmnzB90sgarVpIszCF1cX2cnxwqDovWb31jGrKfxAL0Knui1H7Reaz/zlT9zaE3u0un7L5cNy9fQPy0d2sg==} dev: true @@ -18941,7 +18932,7 @@ packages: resolution: {integrity: sha512-Xq9nH7KlWZmXAtodXDDRE7vs6DU1gTU8zYDHDiWLSip45Egwq3plLHzPn27NgvzL2r1LMPC1vdqh98sQxtqj4A==} dev: false - /langchain@0.0.198(@aws-sdk/client-bedrock-runtime@3.454.0)(@aws-sdk/credential-provider-node@3.451.0)(@getzep/zep-js@0.9.0)(@google-ai/generativelanguage@0.2.1)(@huggingface/inference@2.6.4)(@pinecone-database/pinecone@1.1.2)(@qdrant/js-client-rest@1.7.0)(@supabase/supabase-js@2.38.5)(@xata.io/client@0.25.3)(axios@1.6.2)(cohere-ai@6.2.2)(d3-dsv@2.0.0)(epub2@3.0.1)(html-to-text@9.0.5)(lodash@4.17.21)(mammoth@1.6.0)(pdf-parse@1.1.1)(pg@8.11.3)(redis@4.6.11)(typeorm@0.3.17): + /langchain@0.0.198(@aws-sdk/client-bedrock-runtime@3.454.0)(@aws-sdk/credential-provider-node@3.451.0)(@getzep/zep-js@0.9.0)(@google-ai/generativelanguage@0.2.1)(@huggingface/inference@2.6.4)(@pinecone-database/pinecone@1.1.2)(@qdrant/js-client-rest@1.7.0)(@supabase/supabase-js@2.38.5)(@xata.io/client@0.25.3)(axios@1.6.2)(cohere-ai@6.2.2)(d3-dsv@2.0.0)(epub2@3.0.1)(html-to-text@9.0.5)(lodash@4.17.21)(mammoth@1.6.0)(pdf-parse@1.1.1)(pg@8.11.3)(redis@4.6.12)(typeorm@0.3.17): resolution: {integrity: sha512-YC0O1g8r61InCWyF5NmiQjdghdq6LKcgMrDZtqLbgDxAe4RoSldonm+5oNXS3yjCISG0j3s5Cty+yB7klqvUpg==} engines: {node: '>=18'} peerDependencies: @@ -19279,8 +19270,8 @@ packages: p-retry: 4.6.2 pdf-parse: 1.1.1 pg: 8.11.3 - redis: 4.6.11 - typeorm: 0.3.17(mssql@9.1.1)(pg@8.11.3)(redis@4.6.11)(sqlite3@5.1.6) + redis: 4.6.12 + typeorm: 0.3.17(mssql@9.1.1)(pg@8.11.3)(redis@4.6.12)(sqlite3@5.1.6) uuid: 9.0.0 yaml: 2.3.4 zod: 3.22.4 @@ -23233,25 +23224,15 @@ packages: dependencies: redis-errors: 1.2.0 - /redis@3.1.2: - resolution: {integrity: sha512-grn5KoZLr/qrRQVwoSkmzdbw6pwF+/rwODtrOr6vuBRiR/f3rjSTGupbF90Zpqm2oenix8Do6RV7pYEkGwlKkw==} - engines: {node: '>=10'} + /redis@4.6.12: + resolution: {integrity: sha512-41Xuuko6P4uH4VPe5nE3BqXHB7a9lkFL0J29AlxKaIfD6eWO8VO/5PDF9ad2oS+mswMsfFxaM5DlE3tnXT+P8Q==} dependencies: - denque: 1.5.1 - redis-commands: 1.7.0 - redis-errors: 1.2.0 - redis-parser: 3.0.0 - dev: false - - /redis@4.6.11: - resolution: {integrity: sha512-kg1Lt4NZLYkAjPOj/WcyIGWfZfnyfKo1Wg9YKVSlzhFwxpFIl3LYI8BWy1Ab963LLDsTz2+OwdsesHKljB3WMQ==} - dependencies: - '@redis/bloom': 1.2.0(@redis/client@1.5.12) - '@redis/client': 1.5.12 - '@redis/graph': 1.1.1(@redis/client@1.5.12) - '@redis/json': 1.0.6(@redis/client@1.5.12) - '@redis/search': 1.1.6(@redis/client@1.5.12) - '@redis/time-series': 1.0.5(@redis/client@1.5.12) + '@redis/bloom': 1.2.0(@redis/client@1.5.13) + '@redis/client': 1.5.13 + '@redis/graph': 1.1.1(@redis/client@1.5.13) + '@redis/json': 1.0.6(@redis/client@1.5.13) + '@redis/search': 1.1.6(@redis/client@1.5.13) + '@redis/time-series': 1.0.5(@redis/client@1.5.13) dev: false /reflect-metadata@0.1.13: @@ -25740,7 +25721,7 @@ packages: - supports-color dev: false - /typeorm@0.3.17(mssql@9.1.1)(pg@8.11.3)(redis@4.6.11)(sqlite3@5.1.6): + /typeorm@0.3.17(mssql@9.1.1)(pg@8.11.3)(redis@4.6.12)(sqlite3@5.1.6): resolution: {integrity: sha512-UDjUEwIQalO9tWw9O2A4GU+sT3oyoUXheHJy4ft+RFdnRdQctdQ34L9SqE2p7LdwzafHx1maxT+bqXON+Qnmig==} engines: {node: '>= 12.9.0'} hasBin: true @@ -25810,7 +25791,7 @@ packages: mkdirp: 2.1.3 mssql: 9.1.1 pg: 8.11.3 - redis: 4.6.11 + redis: 4.6.12 reflect-metadata: 0.1.13 sha.js: 2.4.11 sqlite3: 5.1.6 From 7cdbb424e33bcc6dbc664ec5e1bff6eed4deb80f 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, 17 Jan 2024 10:16:13 +0100 Subject: [PATCH 08/69] refactor(core): Move methods from WorkflowHelpers into various workflow services (no-changelog) (#8348) --- packages/cli/src/Server.ts | 14 +- .../cli/src/WorkflowExecuteAdditionalData.ts | 13 +- packages/cli/src/WorkflowHelpers.ts | 363 +----------------- packages/cli/src/WorkflowRunner.ts | 19 +- .../src/executions/executions.service.ee.ts | 6 +- .../cli/src/executions/executions.service.ts | 7 +- .../src/services/userOnboarding.service.ts | 69 ++++ .../cli/src/workflows/workflow.service.ee.ts | 119 +++++- .../cli/src/workflows/workflow.service.ts | 132 +------ .../workflows/workflowExecution.service.ts | 286 ++++++++++++++ .../src/workflows/workflowSharing.service.ts | 36 ++ .../workflows/workflowStaticData.service.ts | 9 + .../cli/src/workflows/workflows.controller.ts | 42 +- .../integration/executions.controller.test.ts | 3 + .../cli/test/integration/shared/workflow.ts | 78 ++++ .../workflows/workflow.service.ee.test.ts | 180 +++++++++ .../{ => workflows}/workflow.service.test.ts | 19 +- .../workflows.controller.ee.test.ts | 50 +-- .../workflows.controller.test.ts | 29 +- .../cli/test/unit/WorkflowHelpers.test.ts | 218 +---------- .../workflowHistory.service.ee.test.ts | 6 +- 21 files changed, 896 insertions(+), 802 deletions(-) create mode 100644 packages/cli/src/services/userOnboarding.service.ts create mode 100644 packages/cli/src/workflows/workflowExecution.service.ts create mode 100644 packages/cli/src/workflows/workflowSharing.service.ts create mode 100644 packages/cli/test/integration/shared/workflow.ts create mode 100644 packages/cli/test/integration/workflows/workflow.service.ee.test.ts rename packages/cli/test/integration/{ => workflows}/workflow.service.test.ts (89%) rename packages/cli/test/integration/{ => workflows}/workflows.controller.ee.test.ts (96%) rename packages/cli/test/integration/{ => workflows}/workflows.controller.test.ts (96%) diff --git a/packages/cli/src/Server.ts b/packages/cli/src/Server.ts index cb11e29c5c..0ea596179f 100644 --- a/packages/cli/src/Server.ts +++ b/packages/cli/src/Server.ts @@ -28,7 +28,6 @@ import history from 'connect-history-api-fallback'; import config from '@/config'; import { Queue } from '@/Queue'; -import { getSharedWorkflowIds } from '@/WorkflowHelpers'; import { WorkflowsController } from '@/workflows/workflows.controller'; import { @@ -103,6 +102,7 @@ import { RoleController } from './controllers/role.controller'; import { BadRequestError } from './errors/response-errors/bad-request.error'; import { NotFoundError } from './errors/response-errors/not-found.error'; import { MultiMainSetup } from './services/orchestration/main/MultiMainSetup.ee'; +import { WorkflowSharingService } from './workflows/workflowSharing.service'; const exec = promisify(callbackExec); @@ -436,7 +436,9 @@ export class Server extends AbstractServer { }, }; - const sharedWorkflowIds = await getSharedWorkflowIds(req.user); + const sharedWorkflowIds = await Container.get( + WorkflowSharingService, + ).getSharedWorkflowIds(req.user); if (!sharedWorkflowIds.length) return []; @@ -484,7 +486,9 @@ export class Server extends AbstractServer { const filter = req.query.filter ? jsonParse(req.query.filter) : {}; - const sharedWorkflowIds = await getSharedWorkflowIds(req.user); + const sharedWorkflowIds = await Container.get( + WorkflowSharingService, + ).getSharedWorkflowIds(req.user); for (const data of executingWorkflows) { if ( @@ -517,7 +521,9 @@ export class Server extends AbstractServer { ResponseHelper.send(async (req: ExecutionRequest.Stop): Promise => { const { id: executionId } = req.params; - const sharedWorkflowIds = await getSharedWorkflowIds(req.user); + const sharedWorkflowIds = await Container.get(WorkflowSharingService).getSharedWorkflowIds( + req.user, + ); if (!sharedWorkflowIds.length) { throw new NotFoundError('Execution not found'); diff --git a/packages/cli/src/WorkflowExecuteAdditionalData.ts b/packages/cli/src/WorkflowExecuteAdditionalData.ts index a170d01f85..0e2d2f313d 100644 --- a/packages/cli/src/WorkflowExecuteAdditionalData.ts +++ b/packages/cli/src/WorkflowExecuteAdditionalData.ts @@ -68,6 +68,7 @@ import { saveExecutionProgress } from './executionLifecycleHooks/saveExecutionPr import { WorkflowStaticDataService } from './workflows/workflowStaticData.service'; import { WorkflowRepository } from './databases/repositories/workflow.repository'; import { UrlService } from './services/url.service'; +import { WorkflowExecutionService } from './workflows/workflowExecution.service'; const ERROR_TRIGGER_TYPE = config.getEnv('nodes.errorTriggerType'); @@ -194,7 +195,11 @@ export function executeErrorWorkflow( Container.get(OwnershipService) .getWorkflowOwnerCached(workflowId) .then((user) => { - void WorkflowHelpers.executeErrorWorkflow(errorWorkflow, workflowErrorData, user); + void Container.get(WorkflowExecutionService).executeErrorWorkflow( + errorWorkflow, + workflowErrorData, + user, + ); }) .catch((error: Error) => { ErrorReporter.error(error); @@ -218,7 +223,11 @@ export function executeErrorWorkflow( void Container.get(OwnershipService) .getWorkflowOwnerCached(workflowId) .then((user) => { - void WorkflowHelpers.executeErrorWorkflow(workflowId, workflowErrorData, user); + void Container.get(WorkflowExecutionService).executeErrorWorkflow( + workflowId, + workflowErrorData, + user, + ); }); } } diff --git a/packages/cli/src/WorkflowHelpers.ts b/packages/cli/src/WorkflowHelpers.ts index 9186ea5e8b..9ea4e8335d 100644 --- a/packages/cli/src/WorkflowHelpers.ts +++ b/packages/cli/src/WorkflowHelpers.ts @@ -1,52 +1,22 @@ -import type { FindOptionsWhere } from 'typeorm'; -import { In } from 'typeorm'; import { Container } from 'typedi'; - +import { v4 as uuid } from 'uuid'; import type { IDataObject, - IExecuteData, INode, INodeCredentialsDetails, IRun, - IRunExecutionData, ITaskData, NodeApiError, WorkflowExecuteMode, WorkflowOperationError, -} from 'n8n-workflow'; -import { - ErrorReporterProxy as ErrorReporter, - NodeOperationError, - SubworkflowOperationError, Workflow, + NodeOperationError, } from 'n8n-workflow'; -import { v4 as uuid } from 'uuid'; -import omit from 'lodash/omit'; -import type { - ExecutionPayload, - IWorkflowErrorData, - IWorkflowExecutionDataProcess, -} from '@/Interfaces'; -import { NodeTypes } from '@/NodeTypes'; -import { WorkflowRunner } from '@/WorkflowRunner'; -import config from '@/config'; -import type { WorkflowEntity } from '@db/entities/WorkflowEntity'; -import type { User } from '@db/entities/User'; -import { PermissionChecker } from './UserManagement/PermissionChecker'; -import { UserService } from './services/user.service'; -import type { CredentialsEntity } from '@db/entities/CredentialsEntity'; -import type { SharedWorkflow } from '@db/entities/SharedWorkflow'; -import type { RoleNames } from '@db/entities/Role'; -import { CredentialsRepository } from '@db/repositories/credentials.repository'; -import { ExecutionRepository } from '@db/repositories/execution.repository'; -import { RoleRepository } from '@db/repositories/role.repository'; -import { SharedWorkflowRepository } from '@db/repositories/sharedWorkflow.repository'; -import { WorkflowRepository } from '@db/repositories/workflow.repository'; -import { RoleService } from './services/role.service'; -import { VariablesService } from './environments/variables/variables.service.ee'; -import { Logger } from './Logger'; -const ERROR_TRIGGER_TYPE = config.getEnv('nodes.errorTriggerType'); +import type { IWorkflowExecutionDataProcess } from '@/Interfaces'; +import type { WorkflowEntity } from '@db/entities/WorkflowEntity'; +import { CredentialsRepository } from '@db/repositories/credentials.repository'; +import { VariablesService } from '@/environments/variables/variables.service.ee'; export function generateFailedExecutionFromError( mode: WorkflowExecuteMode, @@ -97,7 +67,6 @@ export function generateFailedExecutionFromError( /** * Returns the data of the last executed node - * */ export function getDataLastExecutedNodeData(inputData: IRun): ITaskData | undefined { const { runData, pinData = {} } = inputData.data.resultData; @@ -133,168 +102,6 @@ export function getDataLastExecutedNodeData(inputData: IRun): ITaskData | undefi return lastNodeRunData; } -/** - * Executes the error workflow - * - * @param {string} workflowId The id of the error workflow - * @param {IWorkflowErrorData} workflowErrorData The error data - */ -export async function executeErrorWorkflow( - workflowId: string, - workflowErrorData: IWorkflowErrorData, - runningUser: User, -): Promise { - const logger = Container.get(Logger); - // Wrap everything in try/catch to make sure that no errors bubble up and all get caught here - try { - const workflowData = await Container.get(WorkflowRepository).findOneBy({ id: workflowId }); - - if (workflowData === null) { - // The error workflow could not be found - logger.error( - `Calling Error Workflow for "${workflowErrorData.workflow.id}". Could not find error workflow "${workflowId}"`, - { workflowId }, - ); - return; - } - - const executionMode = 'error'; - const nodeTypes = Container.get(NodeTypes); - - const workflowInstance = new Workflow({ - id: workflowId, - name: workflowData.name, - nodeTypes, - nodes: workflowData.nodes, - connections: workflowData.connections, - active: workflowData.active, - staticData: workflowData.staticData, - settings: workflowData.settings, - }); - - try { - const failedNode = workflowErrorData.execution?.lastNodeExecuted - ? workflowInstance.getNode(workflowErrorData.execution?.lastNodeExecuted) - : undefined; - await Container.get(PermissionChecker).checkSubworkflowExecutePolicy( - workflowInstance, - workflowErrorData.workflow.id!, - failedNode ?? undefined, - ); - } catch (error) { - const initialNode = workflowInstance.getStartNode(); - if (initialNode) { - const errorWorkflowPermissionError = new SubworkflowOperationError( - `Another workflow: (ID ${workflowErrorData.workflow.id}) tried to invoke this workflow to handle errors.`, - "Unfortunately current permissions do not allow this. Please check that this workflow's settings allow it to be called by others", - ); - - // Create a fake execution and save it to DB. - const fakeExecution = generateFailedExecutionFromError( - 'error', - errorWorkflowPermissionError, - initialNode, - ); - - const fullExecutionData: ExecutionPayload = { - data: fakeExecution.data, - mode: fakeExecution.mode, - finished: false, - startedAt: new Date(), - stoppedAt: new Date(), - workflowData, - waitTill: null, - status: fakeExecution.status, - workflowId: workflowData.id, - }; - - await Container.get(ExecutionRepository).createNewExecution(fullExecutionData); - } - logger.info('Error workflow execution blocked due to subworkflow settings', { - erroredWorkflowId: workflowErrorData.workflow.id, - errorWorkflowId: workflowId, - }); - return; - } - - let node: INode; - let workflowStartNode: INode | undefined; - for (const nodeName of Object.keys(workflowInstance.nodes)) { - node = workflowInstance.nodes[nodeName]; - if (node.type === ERROR_TRIGGER_TYPE) { - workflowStartNode = node; - } - } - - if (workflowStartNode === undefined) { - logger.error( - `Calling Error Workflow for "${workflowErrorData.workflow.id}". Could not find "${ERROR_TRIGGER_TYPE}" in workflow "${workflowId}"`, - ); - return; - } - - // Can execute without webhook so go on - - // Initialize the data of the webhook node - const nodeExecutionStack: IExecuteData[] = []; - nodeExecutionStack.push({ - node: workflowStartNode, - data: { - main: [ - [ - { - json: workflowErrorData, - }, - ], - ], - }, - source: null, - }); - - const runExecutionData: IRunExecutionData = { - startData: {}, - resultData: { - runData: {}, - }, - executionData: { - contextData: {}, - metadata: {}, - nodeExecutionStack, - waitingExecution: {}, - waitingExecutionSource: {}, - }, - }; - - const runData: IWorkflowExecutionDataProcess = { - executionMode, - executionData: runExecutionData, - workflowData, - userId: runningUser.id, - }; - - const workflowRunner = new WorkflowRunner(); - await workflowRunner.run(runData); - } catch (error) { - ErrorReporter.error(error); - logger.error( - // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access - `Calling Error Workflow for "${workflowErrorData.workflow.id}": "${error.message}"`, - { workflowId: workflowErrorData.workflow.id }, - ); - } -} - -/** - * Returns the static data of workflow - */ -export async function getStaticDataById(workflowId: string) { - const workflowData = await Container.get(WorkflowRepository).findOne({ - select: ['staticData'], - where: { id: workflowId }, - }); - return workflowData?.staticData ?? {}; -} - /** * Set node ids if not already set */ @@ -416,164 +223,6 @@ export async function replaceInvalidCredentials(workflow: WorkflowEntity): Promi return workflow; } -/** - * Get the IDs of the workflows that have been shared with the user. - * Returns all IDs if user has the 'workflow:read' scope. - */ -export async function getSharedWorkflowIds(user: User, roleNames?: RoleNames[]): Promise { - const where: FindOptionsWhere = {}; - if (!user.hasGlobalScope('workflow:read')) { - where.userId = user.id; - } - if (roleNames?.length) { - const roleIds = await Container.get(RoleRepository).getIdsInScopeWorkflowByNames(roleNames); - - where.roleId = In(roleIds); - } - const sharedWorkflows = await Container.get(SharedWorkflowRepository).find({ - where, - select: ['workflowId'], - }); - return sharedWorkflows.map(({ workflowId }) => workflowId); -} -/** - * Check if user owns more than 15 workflows or more than 2 workflows with at least 2 nodes. - * If user does, set flag in its settings. - */ -export async function isBelowOnboardingThreshold(user: User): Promise { - let belowThreshold = true; - const skippedTypes = ['n8n-nodes-base.start', 'n8n-nodes-base.stickyNote']; - - const workflowOwnerRole = await Container.get(RoleService).findWorkflowOwnerRole(); - const ownedWorkflowsIds = await Container.get(SharedWorkflowRepository) - .find({ - where: { - userId: user.id, - roleId: workflowOwnerRole?.id, - }, - select: ['workflowId'], - }) - .then((ownedWorkflows) => ownedWorkflows.map(({ workflowId }) => workflowId)); - - if (ownedWorkflowsIds.length > 15) { - belowThreshold = false; - } else { - // just fetch workflows' nodes to keep memory footprint low - const workflows = await Container.get(WorkflowRepository).find({ - where: { id: In(ownedWorkflowsIds) }, - select: ['nodes'], - }); - - // valid workflow: 2+ nodes without start node - const validWorkflowCount = workflows.reduce((counter, workflow) => { - if (counter <= 2 && workflow.nodes.length > 2) { - const nodes = workflow.nodes.filter((node) => !skippedTypes.includes(node.type)); - if (nodes.length >= 2) { - return counter + 1; - } - } - return counter; - }, 0); - - // more than 2 valid workflows required - belowThreshold = validWorkflowCount <= 2; - } - - // user is above threshold --> set flag in settings - if (!belowThreshold) { - void Container.get(UserService).updateSettings(user.id, { isOnboarded: true }); - } - - return belowThreshold; -} - -/** Get all nodes in a workflow where the node credential is not accessible to the user. */ -export function getNodesWithInaccessibleCreds(workflow: WorkflowEntity, userCredIds: string[]) { - if (!workflow.nodes) { - return []; - } - return workflow.nodes.filter((node) => { - if (!node.credentials) return false; - - const allUsedCredentials = Object.values(node.credentials); - - const allUsedCredentialIds = allUsedCredentials.map((nodeCred) => nodeCred.id?.toString()); - return allUsedCredentialIds.some( - (nodeCredId) => nodeCredId && !userCredIds.includes(nodeCredId), - ); - }); -} - -export function validateWorkflowCredentialUsage( - newWorkflowVersion: WorkflowEntity, - previousWorkflowVersion: WorkflowEntity, - credentialsUserHasAccessTo: CredentialsEntity[], -) { - /** - * We only need to check nodes that use credentials the current user cannot access, - * since these can be 2 possibilities: - * - Same ID already exist: it's a read only node and therefore cannot be changed - * - It's a new node which indicates tampering and therefore must fail saving - */ - - const allowedCredentialIds = credentialsUserHasAccessTo.map((cred) => cred.id); - - const nodesWithCredentialsUserDoesNotHaveAccessTo = getNodesWithInaccessibleCreds( - newWorkflowVersion, - allowedCredentialIds, - ); - - // If there are no nodes with credentials the user does not have access to we can skip the rest - if (nodesWithCredentialsUserDoesNotHaveAccessTo.length === 0) { - return newWorkflowVersion; - } - - const previouslyExistingNodeIds = previousWorkflowVersion.nodes.map((node) => node.id); - - // If it's a new node we can't allow it to be saved - // since it uses creds the node doesn't have access - const isTamperingAttempt = (inaccessibleCredNodeId: string) => - !previouslyExistingNodeIds.includes(inaccessibleCredNodeId); - - const logger = Container.get(Logger); - nodesWithCredentialsUserDoesNotHaveAccessTo.forEach((node) => { - if (isTamperingAttempt(node.id)) { - logger.verbose('Blocked workflow update due to tampering attempt', { - nodeType: node.type, - nodeName: node.name, - nodeId: node.id, - nodeCredentials: node.credentials, - }); - // Node is new, so this is probably a tampering attempt. Throw an error - throw new NodeOperationError( - node, - `You don't have access to the credentials in the '${node.name}' node. Ask the owner to share them with you.`, - ); - } - // Replace the node with the previous version of the node - // Since it cannot be modified (read only node) - const nodeIdx = newWorkflowVersion.nodes.findIndex( - (newWorkflowNode) => newWorkflowNode.id === node.id, - ); - - logger.debug('Replacing node with previous version when saving updated workflow', { - nodeType: node.type, - nodeName: node.name, - nodeId: node.id, - }); - const previousNodeVersion = previousWorkflowVersion.nodes.find( - (previousNode) => previousNode.id === node.id, - ); - // Allow changing only name, position and disabled status for read-only nodes - Object.assign( - newWorkflowVersion.nodes[nodeIdx], - omit(previousNodeVersion, ['name', 'position', 'disabled']), - ); - }); - - return newWorkflowVersion; -} - export function getExecutionStartNode(data: IWorkflowExecutionDataProcess, workflow: Workflow) { let startNode; if ( diff --git a/packages/cli/src/WorkflowRunner.ts b/packages/cli/src/WorkflowRunner.ts index a6534af337..339db979e2 100644 --- a/packages/cli/src/WorkflowRunner.ts +++ b/packages/cli/src/WorkflowRunner.ts @@ -1,10 +1,8 @@ /* eslint-disable @typescript-eslint/no-unsafe-argument */ - /* eslint-disable @typescript-eslint/no-unsafe-member-access */ - /* eslint-disable @typescript-eslint/no-shadow */ /* eslint-disable @typescript-eslint/no-unsafe-assignment */ - +import { Container } from 'typedi'; import type { IProcessMessage } from 'n8n-core'; import { WorkflowExecute } from 'n8n-core'; @@ -29,6 +27,7 @@ import { fork } from 'child_process'; import { ActiveExecutions } from '@/ActiveExecutions'; import config from '@/config'; +import { ExecutionRepository } from '@db/repositories/execution.repository'; import { ExternalHooks } from '@/ExternalHooks'; import type { IExecutionResponse, @@ -38,7 +37,6 @@ import type { } from '@/Interfaces'; import { NodeTypes } from '@/NodeTypes'; import type { Job, JobData, JobResponse } from '@/Queue'; - import { Queue } from '@/Queue'; import { decodeWebhookResponse } from '@/helpers/decodeWebhookResponse'; import * as WorkflowHelpers from '@/WorkflowHelpers'; @@ -47,10 +45,9 @@ import { generateFailedExecutionFromError } from '@/WorkflowHelpers'; import { initErrorHandling } from '@/ErrorReporting'; import { PermissionChecker } from '@/UserManagement/PermissionChecker'; import { Push } from '@/push'; -import { Container } from 'typedi'; -import { InternalHooks } from './InternalHooks'; -import { ExecutionRepository } from '@db/repositories/execution.repository'; -import { Logger } from './Logger'; +import { InternalHooks } from '@/InternalHooks'; +import { Logger } from '@/Logger'; +import { WorkflowStaticDataService } from '@/workflows/workflowStaticData.service'; export class WorkflowRunner { logger: Logger; @@ -269,7 +266,8 @@ export class WorkflowRunner { ): Promise { const workflowId = data.workflowData.id; if (loadStaticData === true && workflowId) { - data.workflowData.staticData = await WorkflowHelpers.getStaticDataById(workflowId); + data.workflowData.staticData = + await Container.get(WorkflowStaticDataService).getStaticDataById(workflowId); } const nodeTypes = Container.get(NodeTypes); @@ -672,7 +670,8 @@ export class WorkflowRunner { const subprocess = fork(pathJoin(__dirname, 'WorkflowRunnerProcess.js')); if (loadStaticData === true && workflowId) { - data.workflowData.staticData = await WorkflowHelpers.getStaticDataById(workflowId); + data.workflowData.staticData = + await Container.get(WorkflowStaticDataService).getStaticDataById(workflowId); } data.restartExecutionId = restartExecutionId; diff --git a/packages/cli/src/executions/executions.service.ee.ts b/packages/cli/src/executions/executions.service.ee.ts index 17e8179bfc..5182f147e8 100644 --- a/packages/cli/src/executions/executions.service.ee.ts +++ b/packages/cli/src/executions/executions.service.ee.ts @@ -1,12 +1,12 @@ +import Container from 'typedi'; import type { User } from '@db/entities/User'; -import { getSharedWorkflowIds } from '@/WorkflowHelpers'; import { ExecutionsService } from './executions.service'; import type { ExecutionRequest } from './execution.request'; import type { IExecutionResponse, IExecutionFlattedResponse } from '@/Interfaces'; import { EnterpriseWorkflowService } from '../workflows/workflow.service.ee'; import type { WorkflowWithSharingsAndCredentials } from '@/workflows/workflows.types'; -import Container from 'typedi'; import { WorkflowRepository } from '@/databases/repositories/workflow.repository'; +import { WorkflowSharingService } from '@/workflows/workflowSharing.service'; export class EnterpriseExecutionsService extends ExecutionsService { /** @@ -14,7 +14,7 @@ export class EnterpriseExecutionsService extends ExecutionsService { */ static async getWorkflowIdsForUser(user: User): Promise { // Get all workflows - return getSharedWorkflowIds(user); + return Container.get(WorkflowSharingService).getSharedWorkflowIds(user); } static async getExecution( diff --git a/packages/cli/src/executions/executions.service.ts b/packages/cli/src/executions/executions.service.ts index a0a836856a..5ae2b26abd 100644 --- a/packages/cli/src/executions/executions.service.ts +++ b/packages/cli/src/executions/executions.service.ts @@ -1,3 +1,4 @@ +import { Container, Service } from 'typedi'; import { validate as jsonSchemaValidate } from 'jsonschema'; import type { IWorkflowBase, @@ -8,6 +9,7 @@ import type { WorkflowExecuteMode, } from 'n8n-workflow'; import { ApplicationError, jsonParse, Workflow, WorkflowOperationError } from 'n8n-workflow'; + import { ActiveExecutions } from '@/ActiveExecutions'; import config from '@/config'; import type { User } from '@db/entities/User'; @@ -22,10 +24,8 @@ import type { import { NodeTypes } from '@/NodeTypes'; import { Queue } from '@/Queue'; import type { ExecutionRequest } from './execution.request'; -import { getSharedWorkflowIds } from '@/WorkflowHelpers'; import { WorkflowRunner } from '@/WorkflowRunner'; import * as GenericHelpers from '@/GenericHelpers'; -import { Container, Service } from 'typedi'; import { getStatusUsingPreviousExecutionStatusMethod } from './executionHelpers'; import type { IGetExecutionsQueryFilter } from '@db/repositories/execution.repository'; import { ExecutionRepository } from '@db/repositories/execution.repository'; @@ -33,6 +33,7 @@ import { WorkflowRepository } from '@db/repositories/workflow.repository'; import { Logger } from '@/Logger'; import { InternalServerError } from '@/errors/response-errors/internal-server.error'; import { NotFoundError } from '@/errors/response-errors/not-found.error'; +import { WorkflowSharingService } from '@/workflows/workflowSharing.service'; const schemaGetExecutionsQueryFilter = { $id: '/IGetExecutionsQueryFilter', @@ -77,7 +78,7 @@ export class ExecutionsService { */ static async getWorkflowIdsForUser(user: User): Promise { // Get all workflows using owner role - return getSharedWorkflowIds(user, ['owner']); + return Container.get(WorkflowSharingService).getSharedWorkflowIds(user, ['owner']); } static async getExecutionsList(req: ExecutionRequest.GetAll): Promise { diff --git a/packages/cli/src/services/userOnboarding.service.ts b/packages/cli/src/services/userOnboarding.service.ts new file mode 100644 index 0000000000..f1d23cf1bc --- /dev/null +++ b/packages/cli/src/services/userOnboarding.service.ts @@ -0,0 +1,69 @@ +import { Service } from 'typedi'; +import { In } from 'typeorm'; + +import type { User } from '@db/entities/User'; +import { SharedWorkflowRepository } from '@db/repositories/sharedWorkflow.repository'; +import { WorkflowRepository } from '@db/repositories/workflow.repository'; +import { RoleService } from '@/services/role.service'; +import { UserService } from '@/services/user.service'; + +@Service() +export class UserOnboardingService { + constructor( + private readonly sharedWorkflowRepository: SharedWorkflowRepository, + private readonly workflowRepository: WorkflowRepository, + private readonly roleService: RoleService, + private readonly userService: UserService, + ) {} + + /** + * Check if user owns more than 15 workflows or more than 2 workflows with at least 2 nodes. + * If user does, set flag in its settings. + */ + async isBelowThreshold(user: User): Promise { + let belowThreshold = true; + const skippedTypes = ['n8n-nodes-base.start', 'n8n-nodes-base.stickyNote']; + + const workflowOwnerRole = await this.roleService.findWorkflowOwnerRole(); + const ownedWorkflowsIds = await this.sharedWorkflowRepository + .find({ + where: { + userId: user.id, + roleId: workflowOwnerRole?.id, + }, + select: ['workflowId'], + }) + .then((ownedWorkflows) => ownedWorkflows.map(({ workflowId }) => workflowId)); + + if (ownedWorkflowsIds.length > 15) { + belowThreshold = false; + } else { + // just fetch workflows' nodes to keep memory footprint low + const workflows = await this.workflowRepository.find({ + where: { id: In(ownedWorkflowsIds) }, + select: ['nodes'], + }); + + // valid workflow: 2+ nodes without start node + const validWorkflowCount = workflows.reduce((counter, workflow) => { + if (counter <= 2 && workflow.nodes.length > 2) { + const nodes = workflow.nodes.filter((node) => !skippedTypes.includes(node.type)); + if (nodes.length >= 2) { + return counter + 1; + } + } + return counter; + }, 0); + + // more than 2 valid workflows required + belowThreshold = validWorkflowCount <= 2; + } + + // user is above threshold --> set flag in settings + if (!belowThreshold) { + void this.userService.updateSettings(user.id, { isOnboarded: true }); + } + + return belowThreshold; + } +} diff --git a/packages/cli/src/workflows/workflow.service.ee.ts b/packages/cli/src/workflows/workflow.service.ee.ts index aba0422491..161e580a83 100644 --- a/packages/cli/src/workflows/workflow.service.ee.ts +++ b/packages/cli/src/workflows/workflow.service.ee.ts @@ -1,30 +1,29 @@ -import * as WorkflowHelpers from '@/WorkflowHelpers'; +import { Service } from 'typedi'; +import omit from 'lodash/omit'; +import { ApplicationError, NodeOperationError } from 'n8n-workflow'; + +import type { CredentialsEntity } from '@db/entities/CredentialsEntity'; import type { User } from '@db/entities/User'; import type { WorkflowEntity } from '@db/entities/WorkflowEntity'; +import { CredentialsRepository } from '@db/repositories/credentials.repository'; +import { WorkflowRepository } from '@db/repositories/workflow.repository'; +import { SharedWorkflowRepository } from '@db/repositories/sharedWorkflow.repository'; +import { CredentialsService } from '@/credentials/credentials.service'; +import { BadRequestError } from '@/errors/response-errors/bad-request.error'; +import { NotFoundError } from '@/errors/response-errors/not-found.error'; +import { Logger } from '@/Logger'; import type { CredentialUsedByWorkflow, WorkflowWithSharingsAndCredentials, } from './workflows.types'; -import { CredentialsService } from '@/credentials/credentials.service'; -import { ApplicationError, NodeOperationError } from 'n8n-workflow'; -import { Service } from 'typedi'; -import type { CredentialsEntity } from '@db/entities/CredentialsEntity'; -import { SharedWorkflowRepository } from '@db/repositories/sharedWorkflow.repository'; -import { BadRequestError } from '@/errors/response-errors/bad-request.error'; -import { NotFoundError } from '@/errors/response-errors/not-found.error'; -import { WorkflowRepository } from '@/databases/repositories/workflow.repository'; -import { CredentialsRepository } from '@/databases/repositories/credentials.repository'; -import { RoleService } from '@/services/role.service'; -import { UserRepository } from '@/databases/repositories/user.repository'; @Service() export class EnterpriseWorkflowService { constructor( + private readonly logger: Logger, private readonly sharedWorkflowRepository: SharedWorkflowRepository, private readonly workflowRepository: WorkflowRepository, private readonly credentialsRepository: CredentialsRepository, - private readonly userRepository: UserRepository, - private readonly roleService: RoleService, ) {} async isOwned( @@ -143,11 +142,7 @@ export class EnterpriseWorkflowService { const allCredentials = await CredentialsService.getMany(user); try { - return WorkflowHelpers.validateWorkflowCredentialUsage( - workflow, - previousVersion, - allCredentials, - ); + return this.validateWorkflowCredentialUsage(workflow, previousVersion, allCredentials); } catch (error) { if (error instanceof NodeOperationError) { throw new BadRequestError(error.message); @@ -157,4 +152,90 @@ export class EnterpriseWorkflowService { ); } } + + validateWorkflowCredentialUsage( + newWorkflowVersion: WorkflowEntity, + previousWorkflowVersion: WorkflowEntity, + credentialsUserHasAccessTo: CredentialsEntity[], + ) { + /** + * We only need to check nodes that use credentials the current user cannot access, + * since these can be 2 possibilities: + * - Same ID already exist: it's a read only node and therefore cannot be changed + * - It's a new node which indicates tampering and therefore must fail saving + */ + + const allowedCredentialIds = credentialsUserHasAccessTo.map((cred) => cred.id); + + const nodesWithCredentialsUserDoesNotHaveAccessTo = this.getNodesWithInaccessibleCreds( + newWorkflowVersion, + allowedCredentialIds, + ); + + // If there are no nodes with credentials the user does not have access to we can skip the rest + if (nodesWithCredentialsUserDoesNotHaveAccessTo.length === 0) { + return newWorkflowVersion; + } + + const previouslyExistingNodeIds = previousWorkflowVersion.nodes.map((node) => node.id); + + // If it's a new node we can't allow it to be saved + // since it uses creds the node doesn't have access + const isTamperingAttempt = (inaccessibleCredNodeId: string) => + !previouslyExistingNodeIds.includes(inaccessibleCredNodeId); + + nodesWithCredentialsUserDoesNotHaveAccessTo.forEach((node) => { + if (isTamperingAttempt(node.id)) { + this.logger.verbose('Blocked workflow update due to tampering attempt', { + nodeType: node.type, + nodeName: node.name, + nodeId: node.id, + nodeCredentials: node.credentials, + }); + // Node is new, so this is probably a tampering attempt. Throw an error + throw new NodeOperationError( + node, + `You don't have access to the credentials in the '${node.name}' node. Ask the owner to share them with you.`, + ); + } + // Replace the node with the previous version of the node + // Since it cannot be modified (read only node) + const nodeIdx = newWorkflowVersion.nodes.findIndex( + (newWorkflowNode) => newWorkflowNode.id === node.id, + ); + + this.logger.debug('Replacing node with previous version when saving updated workflow', { + nodeType: node.type, + nodeName: node.name, + nodeId: node.id, + }); + const previousNodeVersion = previousWorkflowVersion.nodes.find( + (previousNode) => previousNode.id === node.id, + ); + // Allow changing only name, position and disabled status for read-only nodes + Object.assign( + newWorkflowVersion.nodes[nodeIdx], + omit(previousNodeVersion, ['name', 'position', 'disabled']), + ); + }); + + return newWorkflowVersion; + } + + /** Get all nodes in a workflow where the node credential is not accessible to the user. */ + getNodesWithInaccessibleCreds(workflow: WorkflowEntity, userCredIds: string[]) { + if (!workflow.nodes) { + return []; + } + return workflow.nodes.filter((node) => { + if (!node.credentials) return false; + + const allUsedCredentials = Object.values(node.credentials); + + const allUsedCredentialIds = allUsedCredentials.map((nodeCred) => nodeCred.id?.toString()); + return allUsedCredentialIds.some( + (nodeCredId) => nodeCredId && !userCredIds.includes(nodeCredId), + ); + }); + } } diff --git a/packages/cli/src/workflows/workflow.service.ts b/packages/cli/src/workflows/workflow.service.ts index c6546155b0..186cb2d9c6 100644 --- a/packages/cli/src/workflows/workflow.service.ts +++ b/packages/cli/src/workflows/workflow.service.ts @@ -1,34 +1,28 @@ import Container, { Service } from 'typedi'; -import type { INode, IPinData } from 'n8n-workflow'; -import { NodeApiError, Workflow } from 'n8n-workflow'; +import { NodeApiError } from 'n8n-workflow'; import pick from 'lodash/pick'; import omit from 'lodash/omit'; import { v4 as uuid } from 'uuid'; -import { ActiveWorkflowRunner } from '@/ActiveWorkflowRunner'; -import * as WorkflowHelpers from '@/WorkflowHelpers'; +import { BinaryDataService } from 'n8n-core'; + import config from '@/config'; import type { User } from '@db/entities/User'; import type { WorkflowEntity } from '@db/entities/WorkflowEntity'; +import { ExecutionRepository } from '@db/repositories/execution.repository'; +import { SharedWorkflowRepository } from '@db/repositories/sharedWorkflow.repository'; +import { WorkflowTagMappingRepository } from '@db/repositories/workflowTagMapping.repository'; +import { WorkflowRepository } from '@db/repositories/workflow.repository'; +import { ActiveWorkflowRunner } from '@/ActiveWorkflowRunner'; +import * as WorkflowHelpers from '@/WorkflowHelpers'; import { validateEntity } from '@/GenericHelpers'; import { ExternalHooks } from '@/ExternalHooks'; import { hasSharing, type ListQuery } from '@/requests'; -import type { WorkflowRequest } from '@/workflows/workflow.request'; import { TagService } from '@/services/tag.service'; -import type { IWorkflowDb, IWorkflowExecutionDataProcess } from '@/Interfaces'; -import { NodeTypes } from '@/NodeTypes'; -import { WorkflowRunner } from '@/WorkflowRunner'; -import * as WorkflowExecuteAdditionalData from '@/WorkflowExecuteAdditionalData'; -import { TestWebhooks } from '@/TestWebhooks'; import { InternalHooks } from '@/InternalHooks'; -import { WorkflowRepository } from '@db/repositories/workflow.repository'; import { OwnershipService } from '@/services/ownership.service'; import { WorkflowHistoryService } from './workflowHistory/workflowHistory.service.ee'; -import { BinaryDataService } from 'n8n-core'; import { Logger } from '@/Logger'; import { MultiMainSetup } from '@/services/orchestration/main/MultiMainSetup.ee'; -import { SharedWorkflowRepository } from '@db/repositories/sharedWorkflow.repository'; -import { WorkflowTagMappingRepository } from '@db/repositories/workflowTagMapping.repository'; -import { ExecutionRepository } from '@db/repositories/execution.repository'; import { BadRequestError } from '@/errors/response-errors/bad-request.error'; import { NotFoundError } from '@/errors/response-errors/not-found.error'; @@ -45,54 +39,10 @@ export class WorkflowService { private readonly tagService: TagService, private readonly workflowHistoryService: WorkflowHistoryService, private readonly multiMainSetup: MultiMainSetup, - private readonly nodeTypes: NodeTypes, - private readonly testWebhooks: TestWebhooks, private readonly externalHooks: ExternalHooks, private readonly activeWorkflowRunner: ActiveWorkflowRunner, ) {} - /** - * Find the pinned trigger to execute the workflow from, if any. - * - * - In a full execution, select the _first_ pinned trigger. - * - In a partial execution, - * - select the _first_ pinned trigger that leads to the executed node, - * - else select the executed pinned trigger. - */ - findPinnedTrigger(workflow: IWorkflowDb, startNodes?: string[], pinData?: IPinData) { - if (!pinData || !startNodes) return null; - - const isTrigger = (nodeTypeName: string) => - ['trigger', 'webhook'].some((suffix) => nodeTypeName.toLowerCase().includes(suffix)); - - const pinnedTriggers = workflow.nodes.filter( - (node) => !node.disabled && pinData[node.name] && isTrigger(node.type), - ); - - if (pinnedTriggers.length === 0) return null; - - if (startNodes?.length === 0) return pinnedTriggers[0]; // full execution - - const [startNodeName] = startNodes; - - const parentNames = new Workflow({ - nodes: workflow.nodes, - connections: workflow.connections, - active: workflow.active, - nodeTypes: this.nodeTypes, - }).getParentNodes(startNodeName); - - let checkNodeName = ''; - - if (parentNames.length === 0) { - checkNodeName = startNodeName; - } else { - checkNodeName = parentNames.find((pn) => pn === pinnedTriggers[0].name) as string; - } - - return pinnedTriggers.find((pt) => pt.name === checkNodeName) ?? null; // partial execution - } - async getMany(sharedWorkflowIds: string[], options?: ListQuery.Options) { const { workflows, count } = await this.workflowRepository.getMany(sharedWorkflowIds, options); @@ -293,70 +243,6 @@ export class WorkflowService { return updatedWorkflow; } - async runManually( - { - workflowData, - runData, - pinData, - startNodes, - destinationNode, - }: WorkflowRequest.ManualRunPayload, - user: User, - sessionId?: string, - ) { - const pinnedTrigger = this.findPinnedTrigger(workflowData, startNodes, pinData); - - // If webhooks nodes exist and are active we have to wait for till we receive a call - if ( - pinnedTrigger === null && - (runData === undefined || - startNodes === undefined || - startNodes.length === 0 || - destinationNode === undefined) - ) { - const additionalData = await WorkflowExecuteAdditionalData.getBase(user.id); - - const needsWebhook = await this.testWebhooks.needsWebhook( - user.id, - workflowData, - additionalData, - runData, - sessionId, - destinationNode, - ); - - if (needsWebhook) return { waitingForWebhook: true }; - } - - // For manual testing always set to not active - workflowData.active = false; - - // Start the workflow - const data: IWorkflowExecutionDataProcess = { - destinationNode, - executionMode: 'manual', - runData, - pinData, - sessionId, - startNodes, - workflowData, - userId: user.id, - }; - - const hasRunData = (node: INode) => runData !== undefined && !!runData[node.name]; - - if (pinnedTrigger && !hasRunData(pinnedTrigger)) { - data.startNodes = [pinnedTrigger.name]; - } - - const workflowRunner = new WorkflowRunner(); - const executionId = await workflowRunner.run(data); - - return { - executionId, - }; - } - async delete(user: User, workflowId: string): Promise { await this.externalHooks.run('workflow.delete', [workflowId]); diff --git a/packages/cli/src/workflows/workflowExecution.service.ts b/packages/cli/src/workflows/workflowExecution.service.ts new file mode 100644 index 0000000000..d18f02519d --- /dev/null +++ b/packages/cli/src/workflows/workflowExecution.service.ts @@ -0,0 +1,286 @@ +import { Service } from 'typedi'; +import type { IExecuteData, INode, IPinData, IRunExecutionData } from 'n8n-workflow'; +import { + SubworkflowOperationError, + Workflow, + ErrorReporterProxy as ErrorReporter, +} from 'n8n-workflow'; + +import config from '@/config'; +import type { User } from '@db/entities/User'; +import { ExecutionRepository } from '@db/repositories/execution.repository'; +import { WorkflowRepository } from '@db/repositories/workflow.repository'; +import * as WorkflowHelpers from '@/WorkflowHelpers'; +import type { WorkflowRequest } from '@/workflows/workflow.request'; +import type { + ExecutionPayload, + IWorkflowDb, + IWorkflowErrorData, + IWorkflowExecutionDataProcess, +} from '@/Interfaces'; +import { NodeTypes } from '@/NodeTypes'; +import { WorkflowRunner } from '@/WorkflowRunner'; +import * as WorkflowExecuteAdditionalData from '@/WorkflowExecuteAdditionalData'; +import { TestWebhooks } from '@/TestWebhooks'; +import { Logger } from '@/Logger'; +import { PermissionChecker } from '@/UserManagement/PermissionChecker'; + +@Service() +export class WorkflowExecutionService { + constructor( + private readonly logger: Logger, + private readonly executionRepository: ExecutionRepository, + private readonly workflowRepository: WorkflowRepository, + private readonly nodeTypes: NodeTypes, + private readonly testWebhooks: TestWebhooks, + private readonly permissionChecker: PermissionChecker, + ) {} + + async executeManually( + { + workflowData, + runData, + pinData, + startNodes, + destinationNode, + }: WorkflowRequest.ManualRunPayload, + user: User, + sessionId?: string, + ) { + const pinnedTrigger = this.findPinnedTrigger(workflowData, startNodes, pinData); + + // If webhooks nodes exist and are active we have to wait for till we receive a call + if ( + pinnedTrigger === null && + (runData === undefined || + startNodes === undefined || + startNodes.length === 0 || + destinationNode === undefined) + ) { + const additionalData = await WorkflowExecuteAdditionalData.getBase(user.id); + + const needsWebhook = await this.testWebhooks.needsWebhook( + user.id, + workflowData, + additionalData, + runData, + sessionId, + destinationNode, + ); + + if (needsWebhook) return { waitingForWebhook: true }; + } + + // For manual testing always set to not active + workflowData.active = false; + + // Start the workflow + const data: IWorkflowExecutionDataProcess = { + destinationNode, + executionMode: 'manual', + runData, + pinData, + sessionId, + startNodes, + workflowData, + userId: user.id, + }; + + const hasRunData = (node: INode) => runData !== undefined && !!runData[node.name]; + + if (pinnedTrigger && !hasRunData(pinnedTrigger)) { + data.startNodes = [pinnedTrigger.name]; + } + + const workflowRunner = new WorkflowRunner(); + const executionId = await workflowRunner.run(data); + + return { + executionId, + }; + } + + /** Executes an error workflow */ + async executeErrorWorkflow( + workflowId: string, + workflowErrorData: IWorkflowErrorData, + runningUser: User, + ): Promise { + // Wrap everything in try/catch to make sure that no errors bubble up and all get caught here + try { + const workflowData = await this.workflowRepository.findOneBy({ id: workflowId }); + if (workflowData === null) { + // The error workflow could not be found + this.logger.error( + `Calling Error Workflow for "${workflowErrorData.workflow.id}". Could not find error workflow "${workflowId}"`, + { workflowId }, + ); + return; + } + + const executionMode = 'error'; + const workflowInstance = new Workflow({ + id: workflowId, + name: workflowData.name, + nodeTypes: this.nodeTypes, + nodes: workflowData.nodes, + connections: workflowData.connections, + active: workflowData.active, + staticData: workflowData.staticData, + settings: workflowData.settings, + }); + + try { + const failedNode = workflowErrorData.execution?.lastNodeExecuted + ? workflowInstance.getNode(workflowErrorData.execution?.lastNodeExecuted) + : undefined; + await this.permissionChecker.checkSubworkflowExecutePolicy( + workflowInstance, + workflowErrorData.workflow.id!, + failedNode ?? undefined, + ); + } catch (error) { + const initialNode = workflowInstance.getStartNode(); + if (initialNode) { + const errorWorkflowPermissionError = new SubworkflowOperationError( + `Another workflow: (ID ${workflowErrorData.workflow.id}) tried to invoke this workflow to handle errors.`, + "Unfortunately current permissions do not allow this. Please check that this workflow's settings allow it to be called by others", + ); + + // Create a fake execution and save it to DB. + const fakeExecution = WorkflowHelpers.generateFailedExecutionFromError( + 'error', + errorWorkflowPermissionError, + initialNode, + ); + + const fullExecutionData: ExecutionPayload = { + data: fakeExecution.data, + mode: fakeExecution.mode, + finished: false, + startedAt: new Date(), + stoppedAt: new Date(), + workflowData, + waitTill: null, + status: fakeExecution.status, + workflowId: workflowData.id, + }; + + await this.executionRepository.createNewExecution(fullExecutionData); + } + this.logger.info('Error workflow execution blocked due to subworkflow settings', { + erroredWorkflowId: workflowErrorData.workflow.id, + errorWorkflowId: workflowId, + }); + return; + } + + let node: INode; + let workflowStartNode: INode | undefined; + const ERROR_TRIGGER_TYPE = config.getEnv('nodes.errorTriggerType'); + for (const nodeName of Object.keys(workflowInstance.nodes)) { + node = workflowInstance.nodes[nodeName]; + if (node.type === ERROR_TRIGGER_TYPE) { + workflowStartNode = node; + } + } + + if (workflowStartNode === undefined) { + this.logger.error( + `Calling Error Workflow for "${workflowErrorData.workflow.id}". Could not find "${ERROR_TRIGGER_TYPE}" in workflow "${workflowId}"`, + ); + return; + } + + // Can execute without webhook so go on + // Initialize the data of the webhook node + const nodeExecutionStack: IExecuteData[] = []; + nodeExecutionStack.push({ + node: workflowStartNode, + data: { + main: [ + [ + { + json: workflowErrorData, + }, + ], + ], + }, + source: null, + }); + + const runExecutionData: IRunExecutionData = { + startData: {}, + resultData: { + runData: {}, + }, + executionData: { + contextData: {}, + metadata: {}, + nodeExecutionStack, + waitingExecution: {}, + waitingExecutionSource: {}, + }, + }; + + const runData: IWorkflowExecutionDataProcess = { + executionMode, + executionData: runExecutionData, + workflowData, + userId: runningUser.id, + }; + + const workflowRunner = new WorkflowRunner(); + await workflowRunner.run(runData); + } catch (error) { + ErrorReporter.error(error); + this.logger.error( + // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access + `Calling Error Workflow for "${workflowErrorData.workflow.id}": "${error.message}"`, + { workflowId: workflowErrorData.workflow.id }, + ); + } + } + + /** + * Find the pinned trigger to execute the workflow from, if any. + * + * - In a full execution, select the _first_ pinned trigger. + * - In a partial execution, + * - select the _first_ pinned trigger that leads to the executed node, + * - else select the executed pinned trigger. + */ + private findPinnedTrigger(workflow: IWorkflowDb, startNodes?: string[], pinData?: IPinData) { + if (!pinData || !startNodes) return null; + + const isTrigger = (nodeTypeName: string) => + ['trigger', 'webhook'].some((suffix) => nodeTypeName.toLowerCase().includes(suffix)); + + const pinnedTriggers = workflow.nodes.filter( + (node) => !node.disabled && pinData[node.name] && isTrigger(node.type), + ); + + if (pinnedTriggers.length === 0) return null; + + if (startNodes?.length === 0) return pinnedTriggers[0]; // full execution + + const [startNodeName] = startNodes; + + const parentNames = new Workflow({ + nodes: workflow.nodes, + connections: workflow.connections, + active: workflow.active, + nodeTypes: this.nodeTypes, + }).getParentNodes(startNodeName); + + let checkNodeName = ''; + + if (parentNames.length === 0) { + checkNodeName = startNodeName; + } else { + checkNodeName = parentNames.find((pn) => pn === pinnedTriggers[0].name) as string; + } + + return pinnedTriggers.find((pt) => pt.name === checkNodeName) ?? null; // partial execution + } +} diff --git a/packages/cli/src/workflows/workflowSharing.service.ts b/packages/cli/src/workflows/workflowSharing.service.ts new file mode 100644 index 0000000000..a322b2c327 --- /dev/null +++ b/packages/cli/src/workflows/workflowSharing.service.ts @@ -0,0 +1,36 @@ +import { Service } from 'typedi'; +import { In, type FindOptionsWhere } from 'typeorm'; + +import type { RoleNames } from '@db/entities/Role'; +import type { SharedWorkflow } from '@db/entities/SharedWorkflow'; +import type { User } from '@db/entities/User'; +import { RoleRepository } from '@db/repositories/role.repository'; +import { SharedWorkflowRepository } from '@db/repositories/sharedWorkflow.repository'; + +@Service() +export class WorkflowSharingService { + constructor( + private readonly roleRepository: RoleRepository, + private readonly sharedWorkflowRepository: SharedWorkflowRepository, + ) {} + + /** + * Get the IDs of the workflows that have been shared with the user. + * Returns all IDs if user has the 'workflow:read' scope. + */ + async getSharedWorkflowIds(user: User, roleNames?: RoleNames[]): Promise { + const where: FindOptionsWhere = {}; + if (!user.hasGlobalScope('workflow:read')) { + where.userId = user.id; + } + if (roleNames?.length) { + const roleIds = await this.roleRepository.getIdsInScopeWorkflowByNames(roleNames); + where.roleId = In(roleIds); + } + const sharedWorkflows = await this.sharedWorkflowRepository.find({ + where, + select: ['workflowId'], + }); + return sharedWorkflows.map(({ workflowId }) => workflowId); + } +} diff --git a/packages/cli/src/workflows/workflowStaticData.service.ts b/packages/cli/src/workflows/workflowStaticData.service.ts index b569c69a30..b77d1b716c 100644 --- a/packages/cli/src/workflows/workflowStaticData.service.ts +++ b/packages/cli/src/workflows/workflowStaticData.service.ts @@ -11,6 +11,15 @@ export class WorkflowStaticDataService { private readonly workflowRepository: WorkflowRepository, ) {} + /** Returns the static data of workflow */ + async getStaticDataById(workflowId: string) { + const workflowData = await this.workflowRepository.findOne({ + select: ['staticData'], + where: { id: workflowId }, + }); + return workflowData?.staticData ?? {}; + } + /** Saves the static data if it changed */ async saveStaticData(workflow: Workflow): Promise { if (workflow.staticData.__dataChanged === true) { diff --git a/packages/cli/src/workflows/workflows.controller.ts b/packages/cli/src/workflows/workflows.controller.ts index c38fa32fb6..b3fa0307ce 100644 --- a/packages/cli/src/workflows/workflows.controller.ts +++ b/packages/cli/src/workflows/workflows.controller.ts @@ -1,22 +1,27 @@ +import { Service } from 'typedi'; import express from 'express'; import { v4 as uuid } from 'uuid'; - import axios from 'axios'; + import * as Db from '@/Db'; import * as GenericHelpers from '@/GenericHelpers'; import * as ResponseHelper from '@/ResponseHelper'; import * as WorkflowHelpers from '@/WorkflowHelpers'; import type { IWorkflowResponse } from '@/Interfaces'; import config from '@/config'; +import { Authorized, Delete, Get, Patch, Post, Put, RestController } from '@/decorators'; +import type { RoleNames } from '@db/entities/Role'; import { SharedWorkflow } from '@db/entities/SharedWorkflow'; import { WorkflowEntity } from '@db/entities/WorkflowEntity'; +import { SharedWorkflowRepository } from '@db/repositories/sharedWorkflow.repository'; +import { TagRepository } from '@db/repositories/tag.repository'; +import { WorkflowRepository } from '@db/repositories/workflow.repository'; +import { UserRepository } from '@db/repositories/user.repository'; import { validateEntity } from '@/GenericHelpers'; import { ExternalHooks } from '@/ExternalHooks'; import { ListQuery } from '@/requests'; -import { isBelowOnboardingThreshold } from '@/WorkflowHelpers'; import { WorkflowService } from './workflow.service'; import { isSharingEnabled } from '@/UserManagement/UserManagementHelper'; -import Container, { Service } from 'typedi'; import { InternalHooks } from '@/InternalHooks'; import { RoleService } from '@/services/role.service'; import * as utils from '@/utils'; @@ -24,20 +29,17 @@ import { listQueryMiddleware } from '@/middlewares'; import { TagService } from '@/services/tag.service'; import { WorkflowHistoryService } from './workflowHistory/workflowHistory.service.ee'; import { Logger } from '@/Logger'; -import { SharedWorkflowRepository } from '@db/repositories/sharedWorkflow.repository'; import { BadRequestError } from '@/errors/response-errors/bad-request.error'; import { NotFoundError } from '@/errors/response-errors/not-found.error'; import { InternalServerError } from '@/errors/response-errors/internal-server.error'; -import { NamingService } from '@/services/naming.service'; -import { TagRepository } from '@/databases/repositories/tag.repository'; -import { EnterpriseWorkflowService } from './workflow.service.ee'; -import { WorkflowRepository } from '@/databases/repositories/workflow.repository'; -import type { RoleNames } from '@/databases/entities/Role'; import { UnauthorizedError } from '@/errors/response-errors/unauthorized.error'; +import { NamingService } from '@/services/naming.service'; +import { UserOnboardingService } from '@/services/userOnboarding.service'; import { CredentialsService } from '../credentials/credentials.service'; -import { UserRepository } from '@/databases/repositories/user.repository'; -import { Authorized, Delete, Get, Patch, Post, Put, RestController } from '@/decorators'; import { WorkflowRequest } from './workflow.request'; +import { EnterpriseWorkflowService } from './workflow.service.ee'; +import { WorkflowExecutionService } from './workflowExecution.service'; +import { WorkflowSharingService } from './workflowSharing.service'; @Service() @Authorized() @@ -53,8 +55,11 @@ export class WorkflowsController { private readonly workflowHistoryService: WorkflowHistoryService, private readonly tagService: TagService, private readonly namingService: NamingService, + private readonly userOnboardingService: UserOnboardingService, private readonly workflowRepository: WorkflowRepository, private readonly workflowService: WorkflowService, + private readonly workflowExecutionService: WorkflowExecutionService, + private readonly workflowSharingService: WorkflowSharingService, private readonly sharedWorkflowRepository: SharedWorkflowRepository, private readonly userRepository: UserRepository, ) {} @@ -142,9 +147,12 @@ export class WorkflowsController { async getAll(req: ListQuery.Request, res: express.Response) { try { const roles: RoleNames[] = isSharingEnabled() ? [] : ['owner']; - const sharedWorkflowIds = await WorkflowHelpers.getSharedWorkflowIds(req.user, roles); + const sharedWorkflowIds = await this.workflowSharingService.getSharedWorkflowIds( + req.user, + roles, + ); - const { workflows: data, count } = await Container.get(WorkflowService).getMany( + const { workflows: data, count } = await this.workflowService.getMany( sharedWorkflowIds, req.listQueryOptions, ); @@ -166,7 +174,7 @@ export class WorkflowsController { const onboardingFlowEnabled = !config.getEnv('workflows.onboardingFlowDisabled') && !req.user.settings?.isOnboarded && - (await isBelowOnboardingThreshold(req.user)); + (await this.userOnboardingService.isBelowThreshold(req.user)); return { name, onboardingFlowEnabled }; } @@ -321,7 +329,11 @@ export class WorkflowsController { } } - return this.workflowService.runManually(req.body, req.user, GenericHelpers.getSessionId(req)); + return this.workflowExecutionService.executeManually( + req.body, + req.user, + GenericHelpers.getSessionId(req), + ); } @Put('/:workflowId/share') diff --git a/packages/cli/test/integration/executions.controller.test.ts b/packages/cli/test/integration/executions.controller.test.ts index 16f7e8d780..5d4bfe5307 100644 --- a/packages/cli/test/integration/executions.controller.test.ts +++ b/packages/cli/test/integration/executions.controller.test.ts @@ -1,10 +1,13 @@ import type { User } from '@db/entities/User'; +import { Push } from '@/push'; import { createSuccessfulExecution, getAllExecutions } from './shared/db/executions'; import { createOwner } from './shared/db/users'; import { createWorkflow } from './shared/db/workflows'; import * as testDb from './shared/testDb'; import { setupTestServer } from './shared/utils'; +import { mockInstance } from '../shared/mocking'; +mockInstance(Push); let testServer = setupTestServer({ endpointGroups: ['executions'] }); let owner: User; diff --git a/packages/cli/test/integration/shared/workflow.ts b/packages/cli/test/integration/shared/workflow.ts new file mode 100644 index 0000000000..0b2b12f53a --- /dev/null +++ b/packages/cli/test/integration/shared/workflow.ts @@ -0,0 +1,78 @@ +import type { INode } from 'n8n-workflow'; +import { WorkflowEntity } from '@db/entities/WorkflowEntity'; + +export const FIRST_CREDENTIAL_ID = '1'; +export const SECOND_CREDENTIAL_ID = '2'; +export const THIRD_CREDENTIAL_ID = '3'; + +const NODE_WITH_NO_CRED = '0133467b-df4a-473d-9295-fdd9d01fa45a'; +const NODE_WITH_ONE_CRED = '4673f869-f2dc-4a33-b053-ca3193bc5226'; +const NODE_WITH_TWO_CRED = '9b4208bd-8f10-4a6a-ad3b-da47a326f7da'; + +const nodeWithNoCredentials: INode = { + id: NODE_WITH_NO_CRED, + name: 'Node with no Credential', + typeVersion: 1, + type: 'n8n-nodes-base.fakeNode', + position: [0, 0], + credentials: {}, + parameters: {}, +}; + +const nodeWithOneCredential: INode = { + id: NODE_WITH_ONE_CRED, + name: 'Node with a single credential', + typeVersion: 1, + type: '', + position: [0, 0], + credentials: { + test: { + id: FIRST_CREDENTIAL_ID, + name: 'First fake credential', + }, + }, + parameters: {}, +}; + +const nodeWithTwoCredentials: INode = { + id: NODE_WITH_TWO_CRED, + name: 'Node with two credentials', + typeVersion: 1, + type: '', + position: [0, 0], + credentials: { + mcTest: { + id: SECOND_CREDENTIAL_ID, + name: 'Second fake credential', + }, + mcTest2: { + id: THIRD_CREDENTIAL_ID, + name: 'Third fake credential', + }, + }, + parameters: {}, +}; + +export function getWorkflow(options?: { + addNodeWithoutCreds?: boolean; + addNodeWithOneCred?: boolean; + addNodeWithTwoCreds?: boolean; +}) { + const workflow = new WorkflowEntity(); + + workflow.nodes = []; + + if (options?.addNodeWithoutCreds) { + workflow.nodes.push(nodeWithNoCredentials); + } + + if (options?.addNodeWithOneCred) { + workflow.nodes.push(nodeWithOneCredential); + } + + if (options?.addNodeWithTwoCreds) { + workflow.nodes.push(nodeWithTwoCredentials); + } + + return workflow; +} diff --git a/packages/cli/test/integration/workflows/workflow.service.ee.test.ts b/packages/cli/test/integration/workflows/workflow.service.ee.test.ts new file mode 100644 index 0000000000..4cb823b7c0 --- /dev/null +++ b/packages/cli/test/integration/workflows/workflow.service.ee.test.ts @@ -0,0 +1,180 @@ +import Container from 'typedi'; +import { mock } from 'jest-mock-extended'; +import { CredentialsEntity } from '@db/entities/CredentialsEntity'; +import { CredentialsRepository } from '@db/repositories/credentials.repository'; +import { SharedWorkflowRepository } from '@db/repositories/sharedWorkflow.repository'; +import { WorkflowRepository } from '@db/repositories/workflow.repository'; +import { Telemetry } from '@/telemetry'; +import { EnterpriseWorkflowService } from '@/workflows/workflow.service.ee'; + +import * as testDb from '../shared/testDb'; +import { mockInstance } from '../../shared/mocking'; +import { + FIRST_CREDENTIAL_ID, + SECOND_CREDENTIAL_ID, + THIRD_CREDENTIAL_ID, + getWorkflow, +} from '../shared/workflow'; + +describe('EnterpriseWorkflowService', () => { + let service: EnterpriseWorkflowService; + + beforeAll(async () => { + await testDb.init(); + mockInstance(Telemetry); + + service = new EnterpriseWorkflowService( + mock(), + Container.get(SharedWorkflowRepository), + Container.get(WorkflowRepository), + Container.get(CredentialsRepository), + ); + }); + + afterEach(async () => { + await testDb.truncate(['Workflow']); + jest.restoreAllMocks(); + }); + + afterAll(async () => { + await testDb.terminate(); + }); + + describe('validateWorkflowCredentialUsage', () => { + function generateCredentialEntity(credentialId: string) { + const credentialEntity = new CredentialsEntity(); + credentialEntity.id = credentialId; + return credentialEntity; + } + + it('Should throw error saving a workflow using credential without access', () => { + const newWorkflowVersion = getWorkflow({ addNodeWithOneCred: true }); + const previousWorkflowVersion = getWorkflow(); + expect(() => { + service.validateWorkflowCredentialUsage(newWorkflowVersion, previousWorkflowVersion, []); + }).toThrow(); + }); + + it('Should not throw error when saving a workflow using credential with access', () => { + const newWorkflowVersion = getWorkflow({ addNodeWithOneCred: true }); + const previousWorkflowVersion = getWorkflow(); + expect(() => { + service.validateWorkflowCredentialUsage(newWorkflowVersion, previousWorkflowVersion, [ + generateCredentialEntity('1'), + ]); + }).not.toThrow(); + }); + + it('Should not throw error when saving a workflow removing node without credential access', () => { + const newWorkflowVersion = getWorkflow(); + const previousWorkflowVersion = getWorkflow({ addNodeWithOneCred: true }); + expect(() => { + service.validateWorkflowCredentialUsage(newWorkflowVersion, previousWorkflowVersion, [ + generateCredentialEntity('1'), + ]); + }).not.toThrow(); + }); + + it('Should save fine when not making changes to workflow without access', () => { + const workflowWithOneCredential = getWorkflow({ addNodeWithOneCred: true }); + expect(() => { + service.validateWorkflowCredentialUsage( + workflowWithOneCredential, + workflowWithOneCredential, + [], + ); + }).not.toThrow(); + }); + + it('Should throw error saving a workflow adding node without credential access', () => { + const newWorkflowVersion = getWorkflow({ + addNodeWithOneCred: true, + addNodeWithTwoCreds: true, + }); + const previousWorkflowVersion = getWorkflow({ addNodeWithOneCred: true }); + expect(() => { + service.validateWorkflowCredentialUsage(newWorkflowVersion, previousWorkflowVersion, []); + }).toThrow(); + }); + }); + + describe('getNodesWithInaccessibleCreds', () => { + test('Should return an empty list for a workflow without nodes', () => { + const workflow = getWorkflow(); + const nodesWithInaccessibleCreds = service.getNodesWithInaccessibleCreds(workflow, []); + expect(nodesWithInaccessibleCreds).toHaveLength(0); + }); + + test('Should return an empty list for a workflow with nodes without credentials', () => { + const workflow = getWorkflow({ addNodeWithoutCreds: true }); + const nodesWithInaccessibleCreds = service.getNodesWithInaccessibleCreds(workflow, []); + expect(nodesWithInaccessibleCreds).toHaveLength(0); + }); + + test('Should return an element for a node with a credential without access', () => { + const workflow = getWorkflow({ addNodeWithOneCred: true }); + const nodesWithInaccessibleCreds = service.getNodesWithInaccessibleCreds(workflow, []); + expect(nodesWithInaccessibleCreds).toHaveLength(1); + }); + + test('Should return an empty list for a node with a credential with access', () => { + const workflow = getWorkflow({ addNodeWithOneCred: true }); + const nodesWithInaccessibleCreds = service.getNodesWithInaccessibleCreds(workflow, [ + FIRST_CREDENTIAL_ID, + ]); + expect(nodesWithInaccessibleCreds).toHaveLength(0); + }); + + test('Should return an element for a node with two credentials and mixed access', () => { + const workflow = getWorkflow({ addNodeWithTwoCreds: true }); + const nodesWithInaccessibleCreds = service.getNodesWithInaccessibleCreds(workflow, [ + SECOND_CREDENTIAL_ID, + ]); + expect(nodesWithInaccessibleCreds).toHaveLength(1); + }); + + test('Should return one node for a workflow with two nodes and two credentials', () => { + const workflow = getWorkflow({ addNodeWithOneCred: true, addNodeWithTwoCreds: true }); + const nodesWithInaccessibleCreds = service.getNodesWithInaccessibleCreds(workflow, [ + SECOND_CREDENTIAL_ID, + THIRD_CREDENTIAL_ID, + ]); + expect(nodesWithInaccessibleCreds).toHaveLength(1); + }); + + test('Should return one element for a workflows with two nodes and one credential', () => { + const workflow = getWorkflow({ + addNodeWithoutCreds: true, + addNodeWithOneCred: true, + addNodeWithTwoCreds: true, + }); + const nodesWithInaccessibleCreds = service.getNodesWithInaccessibleCreds(workflow, [ + FIRST_CREDENTIAL_ID, + ]); + expect(nodesWithInaccessibleCreds).toHaveLength(1); + }); + + test('Should return one element for a workflows with two nodes and partial credential access', () => { + const workflow = getWorkflow({ addNodeWithOneCred: true, addNodeWithTwoCreds: true }); + const nodesWithInaccessibleCreds = service.getNodesWithInaccessibleCreds(workflow, [ + FIRST_CREDENTIAL_ID, + SECOND_CREDENTIAL_ID, + ]); + expect(nodesWithInaccessibleCreds).toHaveLength(1); + }); + + test('Should return two elements for a workflows with two nodes and partial credential access', () => { + const workflow = getWorkflow({ addNodeWithOneCred: true, addNodeWithTwoCreds: true }); + const nodesWithInaccessibleCreds = service.getNodesWithInaccessibleCreds(workflow, [ + SECOND_CREDENTIAL_ID, + ]); + expect(nodesWithInaccessibleCreds).toHaveLength(2); + }); + + test('Should return two elements for a workflows with two nodes and no credential access', () => { + const workflow = getWorkflow({ addNodeWithOneCred: true, addNodeWithTwoCreds: true }); + const nodesWithInaccessibleCreds = service.getNodesWithInaccessibleCreds(workflow, []); + expect(nodesWithInaccessibleCreds).toHaveLength(2); + }); + }); +}); diff --git a/packages/cli/test/integration/workflow.service.test.ts b/packages/cli/test/integration/workflows/workflow.service.test.ts similarity index 89% rename from packages/cli/test/integration/workflow.service.test.ts rename to packages/cli/test/integration/workflows/workflow.service.test.ts index b98fb8ff74..34f1a970ed 100644 --- a/packages/cli/test/integration/workflow.service.test.ts +++ b/packages/cli/test/integration/workflows/workflow.service.test.ts @@ -1,15 +1,16 @@ import Container from 'typedi'; -import { ActiveWorkflowRunner } from '@/ActiveWorkflowRunner'; -import * as testDb from './shared/testDb'; -import { WorkflowService } from '@/workflows/workflow.service'; -import { mockInstance } from '../shared/mocking'; -import { createOwner } from './shared/db/users'; -import { createWorkflow } from './shared/db/workflows'; -import { SharedWorkflowRepository } from '@/databases/repositories/sharedWorkflow.repository'; import { mock } from 'jest-mock-extended'; -import { WorkflowRepository } from '@/databases/repositories/workflow.repository'; +import { ActiveWorkflowRunner } from '@/ActiveWorkflowRunner'; +import { SharedWorkflowRepository } from '@db/repositories/sharedWorkflow.repository'; +import { WorkflowRepository } from '@db/repositories/workflow.repository'; import { Telemetry } from '@/telemetry'; import { MultiMainSetup } from '@/services/orchestration/main/MultiMainSetup.ee'; +import { WorkflowService } from '@/workflows/workflow.service'; + +import * as testDb from '../shared/testDb'; +import { mockInstance } from '../../shared/mocking'; +import { createOwner } from '../shared/db/users'; +import { createWorkflow } from '../shared/db/workflows'; let workflowService: WorkflowService; let activeWorkflowRunner: ActiveWorkflowRunner; @@ -34,8 +35,6 @@ beforeAll(async () => { mock(), multiMainSetup, mock(), - mock(), - mock(), activeWorkflowRunner, ); }); diff --git a/packages/cli/test/integration/workflows.controller.ee.test.ts b/packages/cli/test/integration/workflows/workflows.controller.ee.test.ts similarity index 96% rename from packages/cli/test/integration/workflows.controller.ee.test.ts rename to packages/cli/test/integration/workflows/workflows.controller.ee.test.ts index 8ca0955442..e49267c0f6 100644 --- a/packages/cli/test/integration/workflows.controller.ee.test.ts +++ b/packages/cli/test/integration/workflows/workflows.controller.ee.test.ts @@ -3,24 +3,28 @@ import type { SuperAgentTest } from 'supertest'; import { v4 as uuid } from 'uuid'; import type { INode } from 'n8n-workflow'; -import * as UserManagementHelpers from '@/UserManagement/UserManagementHelper'; +import type { Role } from '@db/entities/Role'; import type { User } from '@db/entities/User'; -import { getSharedWorkflowIds } from '@/WorkflowHelpers'; import { WorkflowHistoryRepository } from '@db/repositories/workflowHistory.repository'; import { ActiveWorkflowRunner } from '@/ActiveWorkflowRunner'; - -import { mockInstance } from '../shared/mocking'; -import * as utils from './shared/utils/'; -import * as testDb from './shared/testDb'; -import type { SaveCredentialFunction } from './shared/types'; -import { makeWorkflow } from './shared/utils/'; -import { randomCredentialPayload } from './shared/random'; -import { affixRoleToSaveCredential, shareCredentialWithUsers } from './shared/db/credentials'; -import { getCredentialOwnerRole, getGlobalMemberRole, getGlobalOwnerRole } from './shared/db/roles'; -import { createUser } from './shared/db/users'; -import { createWorkflow, getWorkflowSharing, shareWorkflowWithUsers } from './shared/db/workflows'; -import type { Role } from '@/databases/entities/Role'; +import * as UserManagementHelpers from '@/UserManagement/UserManagementHelper'; import { Push } from '@/push'; +import { WorkflowSharingService } from '@/workflows/workflowSharing.service'; + +import { mockInstance } from '../../shared/mocking'; +import * as utils from '../shared/utils/'; +import * as testDb from '../shared/testDb'; +import type { SaveCredentialFunction } from '../shared/types'; +import { makeWorkflow } from '../shared/utils/'; +import { randomCredentialPayload } from '../shared/random'; +import { affixRoleToSaveCredential, shareCredentialWithUsers } from '../shared/db/credentials'; +import { + getCredentialOwnerRole, + getGlobalMemberRole, + getGlobalOwnerRole, +} from '../shared/db/roles'; +import { createUser } from '../shared/db/users'; +import { createWorkflow, getWorkflowSharing, shareWorkflowWithUsers } from '../shared/db/workflows'; let globalMemberRole: Role; let owner: User; @@ -31,7 +35,7 @@ let authMemberAgent: SuperAgentTest; let authAnotherMemberAgent: SuperAgentTest; let saveCredential: SaveCredentialFunction; -const activeWorkflowRunnerLike = mockInstance(ActiveWorkflowRunner); +const activeWorkflowRunner = mockInstance(ActiveWorkflowRunner); mockInstance(Push); const sharingSpy = jest.spyOn(UserManagementHelpers, 'isSharingEnabled').mockReturnValue(true); @@ -60,8 +64,8 @@ beforeAll(async () => { }); beforeEach(async () => { - activeWorkflowRunnerLike.add.mockReset(); - activeWorkflowRunnerLike.remove.mockReset(); + activeWorkflowRunner.add.mockReset(); + activeWorkflowRunner.remove.mockReset(); await testDb.truncate(['Workflow', 'SharedWorkflow', 'WorkflowHistory']); }); @@ -988,7 +992,8 @@ describe('getSharedWorkflowIds', () => { owner.globalRole = await getGlobalOwnerRole(); const workflow1 = await createWorkflow({}, member); const workflow2 = await createWorkflow({}, anotherMember); - const sharedWorkflowIds = await getSharedWorkflowIds(owner); + const sharedWorkflowIds = + await Container.get(WorkflowSharingService).getSharedWorkflowIds(owner); expect(sharedWorkflowIds).toHaveLength(2); expect(sharedWorkflowIds).toContain(workflow1.id); expect(sharedWorkflowIds).toContain(workflow2.id); @@ -1001,7 +1006,8 @@ describe('getSharedWorkflowIds', () => { const workflow3 = await createWorkflow({}, anotherMember); await shareWorkflowWithUsers(workflow1, [member]); await shareWorkflowWithUsers(workflow3, [member]); - const sharedWorkflowIds = await getSharedWorkflowIds(member); + const sharedWorkflowIds = + await Container.get(WorkflowSharingService).getSharedWorkflowIds(member); expect(sharedWorkflowIds).toHaveLength(2); expect(sharedWorkflowIds).toContain(workflow1.id); expect(sharedWorkflowIds).toContain(workflow3.id); @@ -1130,7 +1136,7 @@ describe('PATCH /workflows/:id - activate workflow', () => { const response = await authOwnerAgent.patch(`/workflows/${workflow.id}`).send(payload); expect(response.statusCode).toBe(200); - expect(activeWorkflowRunnerLike.add).toBeCalled(); + expect(activeWorkflowRunner.add).toBeCalled(); const { data: { id, versionId, active }, @@ -1152,8 +1158,8 @@ describe('PATCH /workflows/:id - activate workflow', () => { const response = await authOwnerAgent.patch(`/workflows/${workflow.id}`).send(payload); expect(response.statusCode).toBe(200); - expect(activeWorkflowRunnerLike.add).not.toBeCalled(); - expect(activeWorkflowRunnerLike.remove).toBeCalled(); + expect(activeWorkflowRunner.add).not.toBeCalled(); + expect(activeWorkflowRunner.remove).toBeCalled(); const { data: { id, versionId, active }, diff --git a/packages/cli/test/integration/workflows.controller.test.ts b/packages/cli/test/integration/workflows/workflows.controller.test.ts similarity index 96% rename from packages/cli/test/integration/workflows.controller.test.ts rename to packages/cli/test/integration/workflows/workflows.controller.test.ts index a85dfee776..47c8788889 100644 --- a/packages/cli/test/integration/workflows.controller.test.ts +++ b/packages/cli/test/integration/workflows/workflows.controller.test.ts @@ -1,27 +1,28 @@ +import Container from 'typedi'; import type { SuperAgentTest } from 'supertest'; +import { v4 as uuid } from 'uuid'; import type { INode, IPinData } from 'n8n-workflow'; + import * as UserManagementHelpers from '@/UserManagement/UserManagementHelper'; import type { User } from '@db/entities/User'; -import { v4 as uuid } from 'uuid'; +import { WorkflowRepository } from '@db/repositories/workflow.repository'; +import type { WorkflowEntity } from '@db/entities/WorkflowEntity'; import { RoleService } from '@/services/role.service'; -import Container from 'typedi'; import type { ListQuery } from '@/requests'; import { WorkflowHistoryRepository } from '@db/repositories/workflowHistory.repository'; import { ActiveWorkflowRunner } from '@/ActiveWorkflowRunner'; - -import { mockInstance } from '../shared/mocking'; -import * as utils from './shared/utils/'; -import * as testDb from './shared/testDb'; -import { makeWorkflow, MOCK_PINDATA } from './shared/utils/'; -import { randomCredentialPayload } from './shared/random'; -import { saveCredential } from './shared/db/credentials'; -import { createOwner } from './shared/db/users'; -import { createWorkflow } from './shared/db/workflows'; -import { createTag } from './shared/db/tags'; import { Push } from '@/push'; import { EnterpriseWorkflowService } from '@/workflows/workflow.service.ee'; -import { WorkflowRepository } from '@/databases/repositories/workflow.repository'; -import type { WorkflowEntity } from '@/databases/entities/WorkflowEntity'; + +import { mockInstance } from '../../shared/mocking'; +import * as utils from '../shared/utils/'; +import * as testDb from '../shared/testDb'; +import { makeWorkflow, MOCK_PINDATA } from '../shared/utils/'; +import { randomCredentialPayload } from '../shared/random'; +import { saveCredential } from '../shared/db/credentials'; +import { createOwner } from '../shared/db/users'; +import { createWorkflow } from '../shared/db/workflows'; +import { createTag } from '../shared/db/tags'; let owner: User; let authOwnerAgent: SuperAgentTest; diff --git a/packages/cli/test/unit/WorkflowHelpers.test.ts b/packages/cli/test/unit/WorkflowHelpers.test.ts index 54f8b6e912..998e2b8b42 100644 --- a/packages/cli/test/unit/WorkflowHelpers.test.ts +++ b/packages/cli/test/unit/WorkflowHelpers.test.ts @@ -1,150 +1,8 @@ -import type { INode } from 'n8n-workflow'; import { type Workflow } from 'n8n-workflow'; -import { WorkflowEntity } from '@db/entities/WorkflowEntity'; -import { CredentialsEntity } from '@db/entities/CredentialsEntity'; -import { - getExecutionStartNode, - getNodesWithInaccessibleCreds, - validateWorkflowCredentialUsage, -} from '@/WorkflowHelpers'; +import { getExecutionStartNode } from '@/WorkflowHelpers'; import type { IWorkflowExecutionDataProcess } from '@/Interfaces'; -const FIRST_CREDENTIAL_ID = '1'; -const SECOND_CREDENTIAL_ID = '2'; -const THIRD_CREDENTIAL_ID = '3'; - -const NODE_WITH_NO_CRED = '0133467b-df4a-473d-9295-fdd9d01fa45a'; -const NODE_WITH_ONE_CRED = '4673f869-f2dc-4a33-b053-ca3193bc5226'; -const NODE_WITH_TWO_CRED = '9b4208bd-8f10-4a6a-ad3b-da47a326f7da'; - describe('WorkflowHelpers', () => { - describe('getNodesWithInaccessibleCreds', () => { - test('Should return an empty list for a workflow without nodes', () => { - const workflow = getWorkflow(); - const nodesWithInaccessibleCreds = getNodesWithInaccessibleCreds(workflow, []); - expect(nodesWithInaccessibleCreds).toHaveLength(0); - }); - - test('Should return an empty list for a workflow with nodes without credentials', () => { - const workflow = getWorkflow({ addNodeWithoutCreds: true }); - const nodesWithInaccessibleCreds = getNodesWithInaccessibleCreds(workflow, []); - expect(nodesWithInaccessibleCreds).toHaveLength(0); - }); - - test('Should return an element for a node with a credential without access', () => { - const workflow = getWorkflow({ addNodeWithOneCred: true }); - const nodesWithInaccessibleCreds = getNodesWithInaccessibleCreds(workflow, []); - expect(nodesWithInaccessibleCreds).toHaveLength(1); - }); - - test('Should return an empty list for a node with a credential with access', () => { - const workflow = getWorkflow({ addNodeWithOneCred: true }); - const nodesWithInaccessibleCreds = getNodesWithInaccessibleCreds(workflow, [ - FIRST_CREDENTIAL_ID, - ]); - expect(nodesWithInaccessibleCreds).toHaveLength(0); - }); - - test('Should return an element for a node with two credentials and mixed access', () => { - const workflow = getWorkflow({ addNodeWithTwoCreds: true }); - const nodesWithInaccessibleCreds = getNodesWithInaccessibleCreds(workflow, [ - SECOND_CREDENTIAL_ID, - ]); - expect(nodesWithInaccessibleCreds).toHaveLength(1); - }); - - test('Should return one node for a workflow with two nodes and two credentials', () => { - const workflow = getWorkflow({ addNodeWithOneCred: true, addNodeWithTwoCreds: true }); - const nodesWithInaccessibleCreds = getNodesWithInaccessibleCreds(workflow, [ - SECOND_CREDENTIAL_ID, - THIRD_CREDENTIAL_ID, - ]); - expect(nodesWithInaccessibleCreds).toHaveLength(1); - }); - - test('Should return one element for a workflows with two nodes and one credential', () => { - const workflow = getWorkflow({ - addNodeWithoutCreds: true, - addNodeWithOneCred: true, - addNodeWithTwoCreds: true, - }); - const nodesWithInaccessibleCreds = getNodesWithInaccessibleCreds(workflow, [ - FIRST_CREDENTIAL_ID, - ]); - expect(nodesWithInaccessibleCreds).toHaveLength(1); - }); - - test('Should return one element for a workflows with two nodes and partial credential access', () => { - const workflow = getWorkflow({ addNodeWithOneCred: true, addNodeWithTwoCreds: true }); - const nodesWithInaccessibleCreds = getNodesWithInaccessibleCreds(workflow, [ - FIRST_CREDENTIAL_ID, - SECOND_CREDENTIAL_ID, - ]); - expect(nodesWithInaccessibleCreds).toHaveLength(1); - }); - - test('Should return two elements for a workflows with two nodes and partial credential access', () => { - const workflow = getWorkflow({ addNodeWithOneCred: true, addNodeWithTwoCreds: true }); - const nodesWithInaccessibleCreds = getNodesWithInaccessibleCreds(workflow, [ - SECOND_CREDENTIAL_ID, - ]); - expect(nodesWithInaccessibleCreds).toHaveLength(2); - }); - - test('Should return two elements for a workflows with two nodes and no credential access', () => { - const workflow = getWorkflow({ addNodeWithOneCred: true, addNodeWithTwoCreds: true }); - const nodesWithInaccessibleCreds = getNodesWithInaccessibleCreds(workflow, []); - expect(nodesWithInaccessibleCreds).toHaveLength(2); - }); - }); - - describe('validateWorkflowCredentialUsage', () => { - it('Should throw error saving a workflow using credential without access', () => { - const newWorkflowVersion = getWorkflow({ addNodeWithOneCred: true }); - const previousWorkflowVersion = getWorkflow(); - expect(() => { - validateWorkflowCredentialUsage(newWorkflowVersion, previousWorkflowVersion, []); - }).toThrow(); - }); - - it('Should not throw error when saving a workflow using credential with access', () => { - const newWorkflowVersion = getWorkflow({ addNodeWithOneCred: true }); - const previousWorkflowVersion = getWorkflow(); - expect(() => { - validateWorkflowCredentialUsage(newWorkflowVersion, previousWorkflowVersion, [ - generateCredentialEntity(FIRST_CREDENTIAL_ID), - ]); - }).not.toThrow(); - }); - - it('Should not throw error when saving a workflow removing node without credential access', () => { - const newWorkflowVersion = getWorkflow(); - const previousWorkflowVersion = getWorkflow({ addNodeWithOneCred: true }); - expect(() => { - validateWorkflowCredentialUsage(newWorkflowVersion, previousWorkflowVersion, [ - generateCredentialEntity(FIRST_CREDENTIAL_ID), - ]); - }).not.toThrow(); - }); - - it('Should save fine when not making changes to workflow without access', () => { - const workflowWithOneCredential = getWorkflow({ addNodeWithOneCred: true }); - expect(() => { - validateWorkflowCredentialUsage(workflowWithOneCredential, workflowWithOneCredential, []); - }).not.toThrow(); - }); - - it('Should throw error saving a workflow adding node without credential access', () => { - const newWorkflowVersion = getWorkflow({ - addNodeWithOneCred: true, - addNodeWithTwoCreds: true, - }); - const previousWorkflowVersion = getWorkflow({ addNodeWithOneCred: true }); - expect(() => { - validateWorkflowCredentialUsage(newWorkflowVersion, previousWorkflowVersion, []); - }).toThrow(); - }); - }); describe('getExecutionStartNode', () => { it('Should return undefined', () => { const data = { @@ -186,77 +44,3 @@ describe('WorkflowHelpers', () => { }); }); }); - -function generateCredentialEntity(credentialId: string) { - const credentialEntity = new CredentialsEntity(); - credentialEntity.id = credentialId; - return credentialEntity; -} - -export function getWorkflow(options?: { - addNodeWithoutCreds?: boolean; - addNodeWithOneCred?: boolean; - addNodeWithTwoCreds?: boolean; -}) { - const workflow = new WorkflowEntity(); - - workflow.nodes = []; - - if (options?.addNodeWithoutCreds) { - workflow.nodes.push(nodeWithNoCredentials); - } - - if (options?.addNodeWithOneCred) { - workflow.nodes.push(nodeWithOneCredential); - } - - if (options?.addNodeWithTwoCreds) { - workflow.nodes.push(nodeWithTwoCredentials); - } - - return workflow; -} - -const nodeWithNoCredentials: INode = { - id: NODE_WITH_NO_CRED, - name: 'Node with no Credential', - typeVersion: 1, - type: 'n8n-nodes-base.fakeNode', - position: [0, 0], - credentials: {}, - parameters: {}, -}; - -const nodeWithOneCredential: INode = { - id: NODE_WITH_ONE_CRED, - name: 'Node with a single credential', - typeVersion: 1, - type: '', - position: [0, 0], - credentials: { - test: { - id: FIRST_CREDENTIAL_ID, - name: 'First fake credential', - }, - }, - parameters: {}, -}; - -const nodeWithTwoCredentials: INode = { - id: NODE_WITH_TWO_CRED, - name: 'Node with two credentials', - typeVersion: 1, - type: '', - position: [0, 0], - credentials: { - mcTest: { - id: SECOND_CREDENTIAL_ID, - name: 'Second fake credential', - }, - mcTest2: { - id: THIRD_CREDENTIAL_ID, - name: 'Third fake credential', - }, - }, - parameters: {}, -}; diff --git a/packages/cli/test/unit/services/workflowHistory.service.ee.test.ts b/packages/cli/test/unit/services/workflowHistory.service.ee.test.ts index 7a623188e6..05ccae7005 100644 --- a/packages/cli/test/unit/services/workflowHistory.service.ee.test.ts +++ b/packages/cli/test/unit/services/workflowHistory.service.ee.test.ts @@ -1,11 +1,11 @@ +import { mockClear } from 'jest-mock-extended'; import { User } from '@db/entities/User'; import { WorkflowHistoryRepository } from '@db/repositories/workflowHistory.repository'; import { SharedWorkflowRepository } from '@db/repositories/sharedWorkflow.repository'; import { WorkflowHistoryService } from '@/workflows/workflowHistory/workflowHistory.service.ee'; -import { mockInstance } from '../../shared/mocking'; import { Logger } from '@/Logger'; -import { getWorkflow } from '../WorkflowHelpers.test'; -import { mockClear } from 'jest-mock-extended'; +import { mockInstance } from '../../shared/mocking'; +import { getWorkflow } from '../../integration/shared/workflow'; const workflowHistoryRepository = mockInstance(WorkflowHistoryRepository); const logger = mockInstance(Logger); From 159b328587f3c57c73ae77c2a0c5d5c6ecc330aa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Iv=C3=A1n=20Ovejero?= Date: Wed, 17 Jan 2024 11:02:40 +0100 Subject: [PATCH 09/69] fix(editor): Capture indexed access expressions when building completions (#8331) --- .../completions/__tests__/completions.test.ts | 11 +++++++++++ .../codemirror/completions/datatype.completions.ts | 1 + .../src/plugins/codemirror/completions/utils.ts | 3 +-- 3 files changed, 13 insertions(+), 2 deletions(-) diff --git a/packages/editor-ui/src/plugins/codemirror/completions/__tests__/completions.test.ts b/packages/editor-ui/src/plugins/codemirror/completions/__tests__/completions.test.ts index 1e17c4f2a3..2ca9c0bf9c 100644 --- a/packages/editor-ui/src/plugins/codemirror/completions/__tests__/completions.test.ts +++ b/packages/editor-ui/src/plugins/codemirror/completions/__tests__/completions.test.ts @@ -197,6 +197,17 @@ describe('Resolution-based completions', () => { }); }); + describe('indexed access completions', () => { + test('should return string completions for indexed access that resolves to string literal: {{ "abc"[0].| }}', () => { + // @ts-expect-error Spied function is mistyped + resolveParameterSpy.mockReturnValueOnce('a'); + + expect(completions('{{ "abc"[0].| }}')).toHaveLength( + natives('string').length + extensions('string').length, + ); + }); + }); + describe('complex expression completions', () => { const resolveParameterSpy = vi.spyOn(workflowHelpers, 'resolveParameter'); const { $input } = mockProxy; diff --git a/packages/editor-ui/src/plugins/codemirror/completions/datatype.completions.ts b/packages/editor-ui/src/plugins/codemirror/completions/datatype.completions.ts index 2a1728a97f..fc19938242 100644 --- a/packages/editor-ui/src/plugins/codemirror/completions/datatype.completions.ts +++ b/packages/editor-ui/src/plugins/codemirror/completions/datatype.completions.ts @@ -503,6 +503,7 @@ const regexes = { doubleQuoteStringLiteral: /(".+")\.([^"{\s])*/, // "abc". dateLiteral: /\(?new Date\(\(?.*?\)\)?\.([^{\s])*/, // new Date(). or (new Date()). arrayLiteral: /(\[.+\])\.([^{\s])*/, // [1, 2, 3]. + indexedAccess: /([^{\s]+\[.+\])\.([^{\s])*/, // 'abc'[0]. or 'abc'.split('')[0] or similar ones objectLiteral: /\(\{.*\}\)\.([^{\s])*/, // ({}). mathGlobal: /Math\.([^{\s])*/, // Math. diff --git a/packages/editor-ui/src/plugins/codemirror/completions/utils.ts b/packages/editor-ui/src/plugins/codemirror/completions/utils.ts index 169f4d7296..abec2b58be 100644 --- a/packages/editor-ui/src/plugins/codemirror/completions/utils.ts +++ b/packages/editor-ui/src/plugins/codemirror/completions/utils.ts @@ -43,9 +43,8 @@ export function longestCommonPrefix(...strings: string[]) { // suggestions can be matched based on it. function extractSubExpression(userInput: string): string { const dollarSignIndex = userInput.indexOf('$'); - // If it's not a dollar sign expression just strip parentheses if (dollarSignIndex === -1) { - userInput = userInput.replace(/^.+(\(|\[|{)/, ''); + return userInput; } else if (!stringLiteralRegex.test(userInput)) { // If there is a dollar sign in the input and input is not a string literal, // extract part of following the last $ From eb1320fd7a4a67cd16de10c4174c7bcf2c177b06 Mon Sep 17 00:00:00 2001 From: Elias Meire Date: Wed, 17 Jan 2024 12:31:20 +0100 Subject: [PATCH 10/69] fix(core): Parse any readable stream response instead of only IncomingMessage (#8359) --- packages/core/src/NodeExecuteFunctions.ts | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/packages/core/src/NodeExecuteFunctions.ts b/packages/core/src/NodeExecuteFunctions.ts index 746d30eede..73a9621248 100644 --- a/packages/core/src/NodeExecuteFunctions.ts +++ b/packages/core/src/NodeExecuteFunctions.ts @@ -2855,10 +2855,7 @@ const getRequestHelperFunctions = ( let contentBody: Exclude; - if ( - newResponse.body?.constructor.name === 'IncomingMessage' && - paginationOptions.binaryResult !== true - ) { + if (newResponse.body instanceof Readable && paginationOptions.binaryResult !== true) { const data = await this.helpers .binaryToBuffer(newResponse.body as Buffer | Readable) .then((body) => body.toString()); @@ -2954,10 +2951,7 @@ const getRequestHelperFunctions = ( // configured to stop on 404 response codes. For that reason we have to throw here // now an error manually if the response code is not a success one. let data = tempResponseData.body; - if ( - data?.constructor.name === 'IncomingMessage' && - paginationOptions.binaryResult !== true - ) { + if (data instanceof Readable && paginationOptions.binaryResult !== true) { data = await this.helpers .binaryToBuffer(tempResponseData.body as Buffer | Readable) .then((body) => body.toString()); From b51cbb325e03fd42be6dca99819d4cc7c4c1574b Mon Sep 17 00:00:00 2001 From: Elias Meire Date: Wed, 17 Jan 2024 12:55:12 +0100 Subject: [PATCH 11/69] fix(Salesforce Node): Upgrade to API version 59 (#8346) --- .../nodes/Salesforce/GenericFunctions.ts | 2 +- test-results.json | 2387 ----------------- 2 files changed, 1 insertion(+), 2388 deletions(-) delete mode 100644 test-results.json diff --git a/packages/nodes-base/nodes/Salesforce/GenericFunctions.ts b/packages/nodes-base/nodes/Salesforce/GenericFunctions.ts index fa59d770ee..cdaa585a8f 100644 --- a/packages/nodes-base/nodes/Salesforce/GenericFunctions.ts +++ b/packages/nodes-base/nodes/Salesforce/GenericFunctions.ts @@ -29,7 +29,7 @@ function getOptions( method, body, qs, - uri: `${instanceUrl}/services/data/v39.0${endpoint}`, + uri: `${instanceUrl}/services/data/v59.0${endpoint}`, json: true, }; diff --git a/test-results.json b/test-results.json deleted file mode 100644 index a58e83e37b..0000000000 --- a/test-results.json +++ /dev/null @@ -1,2387 +0,0 @@ -{ - "totalWorkflows": 124, - "slackMessage": "", - "summary": { - "failedExecutions": 103, - "warningExecutions": 20, - "successfulExecutions": 1, - "errors": [ - { - "workflowId": "113", - "error": "Workflow failed to execute: There is no active execution with id \"7\"." - }, - { - "workflowId": "115", - "error": "Workflow failed to execute: There is no active execution with id \"8\"." - }, - { - "workflowId": "117", - "error": "Workflow failed to execute: There is no active execution with id \"10\"." - }, - { - "workflowId": "116", - "error": "Workflow failed to execute: There is no active execution with id \"9\"." - }, - { - "workflowId": "106", - "error": "Workflow failed to execute: There is no active execution with id \"14\"." - }, - { - "workflowId": "10", - "error": "Workflow failed to execute: There is no active execution with id \"15\"." - }, - { - "workflowId": "114", - "error": "Workflow failed to execute: There is no active execution with id \"13\"." - }, - { - "workflowId": "112", - "error": "Workflow failed to execute: There is no active execution with id \"5\"." - }, - { - "workflowId": "11", - "error": "Workflow failed to execute: There is no active execution with id \"3\"." - }, - { - "workflowId": "130", - "error": "Workflow failed to execute: There is no active execution with id \"26\"." - }, - { - "workflowId": "131", - "error": "Workflow failed to execute: There is no active execution with id \"27\"." - }, - { - "workflowId": "132", - "error": "Workflow failed to execute: There is no active execution with id \"28\"." - }, - { - "workflowId": "133", - "error": "Workflow failed to execute: There is no active execution with id \"29\"." - }, - { - "workflowId": "123", - "error": "Workflow failed to execute: There is no active execution with id \"19\"." - }, - { - "workflowId": "121", - "error": "Workflow failed to execute: There is no active execution with id \"18\"." - }, - { - "workflowId": "120", - "error": "Workflow failed to execute: There is no active execution with id \"17\"." - }, - { - "workflowId": "126", - "error": "Workflow failed to execute: There is no active execution with id \"21\"." - }, - { - "workflowId": "118", - "error": "Workflow failed to execute: There is no active execution with id \"22\"." - }, - { - "workflowId": "127", - "error": "Workflow failed to execute: There is no active execution with id \"23\"." - }, - { - "workflowId": "128", - "error": "Workflow failed to execute: There is no active execution with id \"24\"." - }, - { - "workflowId": "129", - "error": "Workflow failed to execute: There is no active execution with id \"25\"." - }, - { - "workflowId": "137", - "error": "Workflow failed to execute: There is no active execution with id \"30\"." - }, - { - "workflowId": "140", - "error": "Workflow failed to execute: There is no active execution with id \"32\"." - }, - { - "workflowId": "138", - "error": "Workflow failed to execute: There is no active execution with id \"31\"." - }, - { - "workflowId": "142", - "error": "Workflow failed to execute: There is no active execution with id \"34\"." - }, - { - "workflowId": "154", - "error": "Workflow failed to execute: There is no active execution with id \"43\"." - }, - { - "workflowId": "153", - "error": "Workflow failed to execute: There is no active execution with id \"42\"." - }, - { - "workflowId": "151", - "error": "Workflow failed to execute: There is no active execution with id \"40\"." - }, - { - "workflowId": "145", - "error": "Workflow failed to execute: There is no active execution with id \"36\"." - }, - { - "workflowId": "146", - "error": "Workflow failed to execute: There is no active execution with id \"37\"." - }, - { - "workflowId": "141", - "error": "Workflow failed to execute: There is no active execution with id \"33\"." - }, - { - "workflowId": "143", - "error": "Workflow failed to execute: There is no active execution with id \"35\"." - }, - { - "workflowId": "15", - "error": "Workflow failed to execute: There is no active execution with id \"38\"." - }, - { - "workflowId": "150", - "error": "Workflow failed to execute: There is no active execution with id \"39\"." - }, - { - "workflowId": "152", - "error": "Workflow failed to execute: There is no active execution with id \"41\"." - }, - { - "workflowId": "156", - "error": "Workflow failed to execute: There is no active execution with id \"45\"." - }, - { - "workflowId": "161", - "error": "Workflow failed to execute: There is no active execution with id \"46\"." - }, - { - "workflowId": "16", - "error": "Workflow failed to execute: There is no active execution with id \"47\"." - }, - { - "workflowId": "182", - "error": "Workflow failed to execute: There is no active execution with id \"54\"." - }, - { - "workflowId": "163", - "error": "Workflow failed to execute: There is no active execution with id \"55\"." - }, - { - "workflowId": "162", - "error": "Workflow failed to execute: There is no active execution with id \"56\"." - }, - { - "workflowId": "187", - "error": "Workflow failed to execute: There is no active execution with id \"57\"." - }, - { - "workflowId": "190", - "error": "Workflow failed to execute: There is no active execution with id \"58\"." - }, - { - "workflowId": "170", - "error": "Workflow failed to execute: There is no active execution with id \"50\"." - }, - { - "workflowId": "166", - "error": "Workflow failed to execute: There is no active execution with id \"48\"." - }, - { - "workflowId": "172", - "error": "Workflow failed to execute: There is no active execution with id \"51\"." - }, - { - "workflowId": "174", - "error": "Workflow failed to execute: There is no active execution with id \"52\"." - }, - { - "workflowId": "177", - "error": "Workflow failed to execute: There is no active execution with id \"53\"." - }, - { - "workflowId": "191", - "error": "Workflow failed to execute: There is no active execution with id \"59\"." - }, - { - "workflowId": "194", - "error": "Workflow failed to execute: There is no active execution with id \"60\"." - }, - { - "workflowId": "195", - "error": "Workflow failed to execute: There is no active execution with id \"62\"." - }, - { - "workflowId": "196", - "error": "Workflow failed to execute: There is no active execution with id \"61\"." - }, - { - "workflowId": "208", - "error": "Workflow failed to execute: There is no active execution with id \"72\"." - }, - { - "workflowId": "206", - "error": "Workflow failed to execute: There is no active execution with id \"70\"." - }, - { - "workflowId": "202", - "error": "Workflow failed to execute: There is no active execution with id \"66\"." - }, - { - "workflowId": "199", - "error": "Workflow failed to execute: There is no active execution with id \"65\"." - }, - { - "workflowId": "198", - "error": "Workflow failed to execute: There is no active execution with id \"64\"." - }, - { - "workflowId": "204", - "error": "Workflow failed to execute: There is no active execution with id \"68\"." - }, - { - "workflowId": "197", - "error": "Workflow failed to execute: There is no active execution with id \"63\"." - }, - { - "workflowId": "203", - "error": "Workflow failed to execute: There is no active execution with id \"67\"." - }, - { - "workflowId": "205", - "error": "Workflow failed to execute: There is no active execution with id \"69\"." - }, - { - "workflowId": "207", - "error": "Workflow failed to execute: There is no active execution with id \"71\"." - }, - { - "workflowId": "209", - "error": "Workflow failed to execute: There is no active execution with id \"73\"." - }, - { - "workflowId": "223", - "error": "Workflow failed to execute: There is no active execution with id \"75\"." - }, - { - "workflowId": "35", - "error": "Workflow failed to execute: There is no active execution with id \"81\"." - }, - { - "workflowId": "36", - "error": "Workflow failed to execute: There is no active execution with id \"82\"." - }, - { - "workflowId": "37", - "error": "Workflow failed to execute: There is no active execution with id \"83\"." - }, - { - "workflowId": "40", - "error": "Workflow failed to execute: There is no active execution with id \"84\"." - }, - { - "workflowId": "29", - "error": "Workflow failed to execute: There is no active execution with id \"79\"." - }, - { - "workflowId": "41", - "error": "Workflow failed to execute: There is no active execution with id \"85\"." - }, - { - "workflowId": "32", - "error": "Workflow failed to execute: There is no active execution with id \"80\"." - }, - { - "workflowId": "26", - "error": "Workflow failed to execute: There is no active execution with id \"78\"." - }, - { - "workflowId": "42", - "error": "Workflow failed to execute: There is no active execution with id \"87\"." - }, - { - "workflowId": "43", - "error": "Workflow failed to execute: There is no active execution with id \"88\"." - }, - { - "workflowId": "44", - "error": "Workflow failed to execute: There is no active execution with id \"89\"." - }, - { - "workflowId": "45", - "error": "Workflow failed to execute: There is no active execution with id \"90\"." - }, - { - "workflowId": "55", - "error": "Workflow failed to execute: There is no active execution with id \"96\"." - }, - { - "workflowId": "46", - "error": "Workflow failed to execute: There is no active execution with id \"97\"." - }, - { - "workflowId": "50", - "error": "Workflow failed to execute: There is no active execution with id \"92\"." - }, - { - "workflowId": "51", - "error": "Workflow failed to execute: There is no active execution with id \"93\"." - }, - { - "workflowId": "58", - "error": "Workflow failed to execute: There is no active execution with id \"98\"." - }, - { - "workflowId": "53", - "error": "Workflow failed to execute: There is no active execution with id \"95\"." - }, - { - "workflowId": "48", - "error": "Workflow failed to execute: There is no active execution with id \"91\"." - }, - { - "workflowId": "52", - "error": "Workflow failed to execute: There is no active execution with id \"94\"." - }, - { - "workflowId": "60", - "error": "Workflow failed to execute: There is no active execution with id \"100\"." - }, - { - "workflowId": "61", - "error": "Workflow failed to execute: There is no active execution with id \"101\"." - }, - { - "workflowId": "62", - "error": "Workflow failed to execute: There is no active execution with id \"102\"." - }, - { - "workflowId": "67", - "error": "Workflow failed to execute: There is no active execution with id \"104\"." - }, - { - "workflowId": "73", - "error": "Workflow failed to execute: There is no active execution with id \"107\"." - }, - { - "workflowId": "71", - "error": "Workflow failed to execute: There is no active execution with id \"106\"." - }, - { - "workflowId": "70", - "error": "Workflow failed to execute: There is no active execution with id \"105\"." - }, - { - "workflowId": "75", - "error": "Workflow failed to execute: There is no active execution with id \"108\"." - }, - { - "workflowId": "77", - "error": "Workflow failed to execute: There is no active execution with id \"109\"." - }, - { - "workflowId": "63", - "error": "Workflow failed to execute: There is no active execution with id \"103\"." - }, - { - "workflowId": "76", - "error": "Workflow failed to execute: There is no active execution with id \"110\"." - }, - { - "workflowId": "81", - "error": "Workflow failed to execute: There is no active execution with id \"111\"." - }, - { - "workflowId": "82", - "error": "Workflow failed to execute: There is no active execution with id \"112\"." - }, - { - "workflowId": "84", - "error": "Workflow failed to execute: There is no active execution with id \"114\"." - }, - { - "workflowId": "88", - "error": "Workflow failed to execute: There is no active execution with id \"115\"." - }, - { - "workflowId": "87", - "error": "Workflow failed to execute: There is no active execution with id \"117\"." - }, - { - "workflowId": "86", - "error": "Workflow failed to execute: There is no active execution with id \"118\"." - }, - { - "workflowId": "96", - "error": "Workflow failed to execute: There is no active execution with id \"122\"." - }, - { - "workflowId": "98", - "error": "Workflow failed to execute: There is no active execution with id \"124\"." - } - ], - "warnings": [ - { - "workflowId": "101", - "error": "Workflow contains new data that previously did not exist." - }, - { - "workflowId": "111", - "error": "Workflow contains new data that previously did not exist." - }, - { - "workflowId": "108", - "error": "Workflow contains new data that previously did not exist." - }, - { - "workflowId": "107", - "error": "Workflow contains new data that previously did not exist." - }, - { - "workflowId": "104", - "error": "Workflow contains new data that previously did not exist." - }, - { - "workflowId": "105", - "error": "Workflow contains new data that previously did not exist." - }, - { - "workflowId": "103", - "error": "Workflow contains new data that previously did not exist." - }, - { - "workflowId": "125", - "error": "connect ECONNREFUSED 157.90.159.163:5432 on node Postgres" - }, - { - "workflowId": "216", - "error": "Workflow contains new data that previously did not exist." - }, - { - "workflowId": "17", - "error": "Workflow contains new data that previously did not exist." - }, - { - "workflowId": "6", - "error": "Workflow contains new data that previously did not exist." - }, - { - "workflowId": "91", - "error": "Workflow contains new data that previously did not exist." - }, - { - "workflowId": "90", - "error": "Workflow contains new data that previously did not exist." - }, - { - "workflowId": "95", - "error": "Workflow contains new data that previously did not exist." - }, - { - "workflowId": "93", - "error": "Workflow contains new data that previously did not exist." - }, - { - "workflowId": "97", - "error": "Workflow contains new data that previously did not exist." - }, - { - "workflowId": "83", - "error": "Workflow contains new data that previously did not exist." - }, - { - "workflowId": "212", - "error": "Workflow contains new data that previously did not exist." - }, - { - "workflowId": "155", - "error": "Workflow contains new data that previously did not exist." - }, - { - "workflowId": "229", - "error": "Workflow contains new data that previously did not exist." - } - ] - }, - "coveredNodes": { - "n8n-nodes-base.manualTrigger": 1, - "n8n-nodes-base.httpRequest": 1 - }, - "executions": [ - { - "workflowId": "101", - "workflowName": "RenameKeys", - "executionTime": 0, - "finished": true, - "executionStatus": "warning", - "coveredNodes": { - "n8n-nodes-base.start": 1, - "n8n-nodes-base.renameKeys": 1, - "n8n-nodes-base.set": 1, - "n8n-nodes-base.function": 1 - }, - "error": "Workflow contains new data that previously did not exist.", - "changes": { - "data": { - "executionData": { - "metadata__added": {} - } - } - } - }, - { - "workflowId": "113", - "workflowName": "AMQP", - "executionTime": 0, - "finished": false, - "executionStatus": "error", - "coveredNodes": { - "n8n-nodes-base.start": 1, - "n8n-nodes-base.amqp": 1, - "n8n-nodes-base.set": 1 - }, - "error": "Workflow failed to execute: There is no active execution with id \"7\"." - }, - { - "workflowId": "111", - "workflowName": "Spreadsheet File", - "executionTime": 0, - "finished": true, - "executionStatus": "warning", - "coveredNodes": { - "n8n-nodes-base.start": 1, - "n8n-nodes-base.spreadsheetFile": 2, - "n8n-nodes-base.function": 3 - }, - "error": "Workflow contains new data that previously did not exist.", - "changes": { - "data": { - "executionData": { - "metadata__added": {} - } - } - } - }, - { - "workflowId": "115", - "workflowName": "Cockpit:Collection:createEntry updateEntry getAllEntries:Singleton:get:Form:submit", - "executionTime": 0, - "finished": false, - "executionStatus": "error", - "coveredNodes": { - "n8n-nodes-base.start": 1, - "n8n-nodes-base.cockpit": 5 - }, - "error": "Workflow failed to execute: There is no active execution with id \"8\"." - }, - { - "workflowId": "117", - "workflowName": "Gotify:Message:create getAll delete", - "executionTime": 0, - "finished": false, - "executionStatus": "error", - "coveredNodes": { - "n8n-nodes-base.start": 1, - "n8n-nodes-base.gotify": 3 - }, - "error": "Workflow failed to execute: There is no active execution with id \"10\"." - }, - { - "workflowId": "116", - "workflowName": "Ghost:Post(Admin API):create update get getAll delete:Post(Content API):getAll get", - "executionTime": 0, - "finished": false, - "executionStatus": "error", - "coveredNodes": { - "n8n-nodes-base.start": 1, - "n8n-nodes-base.ghost": 7 - }, - "error": "Workflow failed to execute: There is no active execution with id \"9\"." - }, - { - "workflowId": "106", - "workflowName": "Box:Folder:create search get delete:File:upload get download copy search delete", - "executionTime": 0, - "finished": false, - "executionStatus": "error", - "coveredNodes": { - "n8n-nodes-base.start": 1, - "n8n-nodes-base.box": 11, - "n8n-nodes-base.readBinaryFile": 1 - }, - "error": "Workflow failed to execute: There is no active execution with id \"14\"." - }, - { - "workflowId": "10", - "workflowName": "PagerDuty:incident:create get update getAll:incidentNote:create getAll:User:get:LogEntry:getAll get", - "executionTime": 0, - "finished": false, - "executionStatus": "error", - "coveredNodes": { - "n8n-nodes-base.start": 1, - "n8n-nodes-base.pagerDuty": 9, - "n8n-nodes-base.function": 1 - }, - "error": "Workflow failed to execute: There is no active execution with id \"15\"." - }, - { - "workflowId": "114", - "workflowName": "Monday:Board:create get getAll archive:BoardColumn:create getAll:BoardGroup:create delete getAll:BoardItem:create addUpdate changeColumnValue get getAll getByColumnValue move delete", - "executionTime": 0, - "finished": false, - "executionStatus": "error", - "coveredNodes": { - "n8n-nodes-base.start": 1, - "n8n-nodes-base.mondayCom": 17, - "n8n-nodes-base.merge": 1 - }, - "error": "Workflow failed to execute: There is no active execution with id \"13\"." - }, - { - "workflowId": "108", - "workflowName": "Merge:append keepKeyMatches mergeByIndex(leftjoin,innerjoin,outerjoin) mergeByKey(ifBlank,always,ifMissing) Multiplex passThrough removeKeyMatches", - "executionTime": 0, - "finished": true, - "executionStatus": "warning", - "coveredNodes": { - "n8n-nodes-base.start": 1, - "n8n-nodes-base.merge": 11, - "n8n-nodes-base.set": 12, - "n8n-nodes-base.function": 13 - }, - "error": "Workflow contains new data that previously did not exist.", - "changes": { - "data": { - "executionData": { - "metadata__added": {} - } - } - } - }, - { - "workflowId": "107", - "workflowName": "ReadBinaryFiles", - "executionTime": 0, - "finished": true, - "executionStatus": "warning", - "coveredNodes": { - "n8n-nodes-base.start": 1, - "n8n-nodes-base.function": 1, - "n8n-nodes-base.readBinaryFiles": 1 - }, - "error": "Workflow contains new data that previously did not exist.", - "changes": { - "data": { - "executionData": { - "metadata__added": {} - } - } - } - }, - { - "workflowId": "112", - "workflowName": "Rabbitmq:queue:exchange", - "executionTime": 0, - "finished": false, - "executionStatus": "error", - "coveredNodes": { - "n8n-nodes-base.start": 1, - "n8n-nodes-base.rabbitmq": 5 - }, - "error": "Workflow failed to execute: There is no active execution with id \"5\"." - }, - { - "workflowId": "11", - "workflowName": "Mailchimp:Member:getall get create update delete:Member Tag:create delete:ListGroup:getAll:Campaign:getAll get replicate delete", - "executionTime": 0, - "finished": false, - "executionStatus": "error", - "coveredNodes": { - "n8n-nodes-base.start": 1, - "n8n-nodes-base.mailchimp": 12, - "n8n-nodes-base.function": 1 - }, - "error": "Workflow failed to execute: There is no active execution with id \"3\"." - }, - { - "workflowId": "104", - "workflowName": "ReadPDF", - "executionTime": 1, - "finished": true, - "executionStatus": "warning", - "coveredNodes": { - "n8n-nodes-base.start": 1, - "n8n-nodes-base.readPDF": 1, - "n8n-nodes-base.readBinaryFile": 1 - }, - "error": "Workflow contains new data that previously did not exist.", - "changes": { - "data": { - "resultData": { - "runData": { - "Read Binary File": [ - [ - "~", - { - "data": { - "main": [ - [ - "~", - [ - [ - "~", - { - "binary": { - "data": { - "fileType__added": "pdf" - } - } - } - ] - ] - ] - ] - } - } - ] - ], - "Read PDF": [ - [ - "~", - { - "data": { - "main": [ - [ - "~", - [ - [ - "~", - { - "binary": { - "data": { - "fileType__added": "pdf" - } - } - } - ] - ] - ] - ] - } - } - ] - ] - } - }, - "executionData": { - "metadata__added": {} - } - } - } - }, - { - "workflowId": "105", - "workflowName": "WriteBinaryFile", - "executionTime": 1, - "finished": true, - "executionStatus": "warning", - "coveredNodes": { - "n8n-nodes-base.start": 1, - "n8n-nodes-base.writeBinaryFile": 1, - "n8n-nodes-base.readBinaryFile": 1, - "n8n-nodes-base.function": 2 - }, - "error": "Workflow contains new data that previously did not exist.", - "changes": { - "data": { - "executionData": { - "metadata__added": {} - } - } - } - }, - { - "workflowId": "103", - "workflowName": "ExecuteCommand", - "executionTime": 1, - "finished": true, - "executionStatus": "warning", - "coveredNodes": { - "n8n-nodes-base.start": 1, - "n8n-nodes-base.executeCommand": 2, - "n8n-nodes-base.readBinaryFile": 1, - "n8n-nodes-base.set": 1, - "n8n-nodes-base.function": 1 - }, - "error": "Workflow contains new data that previously did not exist.", - "changes": { - "data": { - "executionData": { - "metadata__added": {} - } - } - } - }, - { - "workflowId": "130", - "workflowName": "Wekan:Board:create get getAll delete:List:create get getAll delete:Card:create update get getAll delete:CardComment:create get getAll delete:CheckList:create get getAll delete:CheckListItem:get update delete", - "executionTime": 0, - "finished": false, - "executionStatus": "error", - "coveredNodes": { - "n8n-nodes-base.start": 1, - "n8n-nodes-base.wekan": 24, - "n8n-nodes-base.merge": 1 - }, - "error": "Workflow failed to execute: There is no active execution with id \"26\"." - }, - { - "workflowId": "131", - "workflowName": "Line:Notification:send", - "executionTime": 0, - "finished": false, - "executionStatus": "error", - "coveredNodes": { - "n8n-nodes-base.start": 1, - "n8n-nodes-base.line": 1 - }, - "error": "Workflow failed to execute: There is no active execution with id \"27\"." - }, - { - "workflowId": "132", - "workflowName": "Mandrill:Message:sendTemplate sendHTML", - "executionTime": 0, - "finished": false, - "executionStatus": "error", - "coveredNodes": { - "n8n-nodes-base.start": 1, - "n8n-nodes-base.mandrill": 2 - }, - "error": "Workflow failed to execute: There is no active execution with id \"28\"." - }, - { - "workflowId": "133", - "workflowName": "Twilio:SMS:send", - "executionTime": 0, - "finished": false, - "executionStatus": "error", - "coveredNodes": { - "n8n-nodes-base.start": 1, - "n8n-nodes-base.twilio": 1 - }, - "error": "Workflow failed to execute: There is no active execution with id \"29\"." - }, - { - "workflowId": "123", - "workflowName": "MongoDB:insert find update delete", - "executionTime": 0, - "finished": false, - "executionStatus": "error", - "coveredNodes": { - "n8n-nodes-base.start": 1, - "n8n-nodes-base.mongoDb": 4, - "n8n-nodes-base.set": 2 - }, - "error": "Workflow failed to execute: There is no active execution with id \"19\"." - }, - { - "workflowId": "121", - "workflowName": "Mautic:Company:create update get getAll delete:Contact:create update get getAll delete:ContactCompany:add remove", - "executionTime": 0, - "finished": false, - "executionStatus": "error", - "coveredNodes": { - "n8n-nodes-base.start": 1, - "n8n-nodes-base.mautic": 12, - "n8n-nodes-base.merge": 1 - }, - "error": "Workflow failed to execute: There is no active execution with id \"18\"." - }, - { - "workflowId": "120", - "workflowName": "MySQL:insert update executeQuery", - "executionTime": 0, - "finished": false, - "executionStatus": "error", - "coveredNodes": { - "n8n-nodes-base.start": 1, - "n8n-nodes-base.set": 2, - "n8n-nodes-base.mySql": 4 - }, - "error": "Workflow failed to execute: There is no active execution with id \"17\"." - }, - { - "workflowId": "126", - "workflowName": "Kafka", - "executionTime": 0, - "finished": false, - "executionStatus": "error", - "coveredNodes": { - "n8n-nodes-base.start": 1, - "n8n-nodes-base.kafka": 1, - "n8n-nodes-base.set": 1 - }, - "error": "Workflow failed to execute: There is no active execution with id \"21\"." - }, - { - "workflowId": "118", - "workflowName": "Redis:info set keys get delete", - "executionTime": 0, - "finished": false, - "executionStatus": "error", - "coveredNodes": { - "n8n-nodes-base.start": 1, - "n8n-nodes-base.redis": 5, - "n8n-nodes-base.set": 1, - "n8n-nodes-base.function": 1 - }, - "error": "Workflow failed to execute: There is no active execution with id \"22\"." - }, - { - "workflowId": "127", - "workflowName": "Rundeck:Job:execute getMetadata", - "executionTime": 0, - "finished": false, - "executionStatus": "error", - "coveredNodes": { - "n8n-nodes-base.start": 1, - "n8n-nodes-base.rundeck": 2 - }, - "error": "Workflow failed to execute: There is no active execution with id \"23\"." - }, - { - "workflowId": "128", - "workflowName": "Yourls:Url:shorten stats expand", - "executionTime": 0, - "finished": false, - "executionStatus": "error", - "coveredNodes": { - "n8n-nodes-base.start": 1, - "n8n-nodes-base.yourls": 3 - }, - "error": "Workflow failed to execute: There is no active execution with id \"24\"." - }, - { - "workflowId": "129", - "workflowName": "NextCloud:NextCloud:Folder:create move copy delete list:File:upload move copy download delete", - "executionTime": 0, - "finished": false, - "executionStatus": "error", - "coveredNodes": { - "n8n-nodes-base.start": 1, - "n8n-nodes-base.nextCloud": 16, - "n8n-nodes-base.set": 2, - "n8n-nodes-base.readBinaryFile": 1, - "n8n-nodes-base.function": 8 - }, - "error": "Workflow failed to execute: There is no active execution with id \"25\"." - }, - { - "workflowId": "137", - "workflowName": "TheHive[v3]:Alert:create update get getAll promote merge:Case:create update get getAll:Observable:create update get search getAll:Task:create update get search getAll:Log:create get getAll", - "executionTime": 0, - "finished": false, - "executionStatus": "error", - "coveredNodes": { - "n8n-nodes-base.start": 1, - "n8n-nodes-base.theHive": 31, - "n8n-nodes-base.function": 9 - }, - "error": "Workflow failed to execute: There is no active execution with id \"30\"." - }, - { - "workflowId": "140", - "workflowName": "Mailgun", - "executionTime": 0, - "finished": false, - "executionStatus": "error", - "coveredNodes": { - "n8n-nodes-base.start": 1, - "n8n-nodes-base.mailgun": 1 - }, - "error": "Workflow failed to execute: There is no active execution with id \"32\"." - }, - { - "workflowId": "138", - "workflowName": "TheHive[v4]:Alert:create update get getAll promote merge:Case:create update get getAll:Observable:create update get search getAll:Task:create update get search getAll:Log:create get getAll", - "executionTime": 0, - "finished": false, - "executionStatus": "error", - "coveredNodes": { - "n8n-nodes-base.start": 1, - "n8n-nodes-base.theHive": 35, - "n8n-nodes-base.function": 10 - }, - "error": "Workflow failed to execute: There is no active execution with id \"31\"." - }, - { - "workflowId": "142", - "workflowName": "MicrosoftOutlook:Folder:create get getAll getChildren delete:Message send getAll get getMime update delete:FolderMessage:getAll:Draft:create update get delete send:MessageAttachment:add getAll get download", - "executionTime": 0, - "finished": false, - "executionStatus": "error", - "coveredNodes": { - "n8n-nodes-base.start": 1, - "n8n-nodes-base.microsoftOutlook": 24, - "n8n-nodes-base.moveBinaryData": 1, - "n8n-nodes-base.set": 1, - "n8n-nodes-base.function": 11 - }, - "error": "Workflow failed to execute: There is no active execution with id \"34\"." - }, - { - "workflowId": "154", - "workflowName": "AWSRekognition:Image:analyze", - "executionTime": 0, - "finished": false, - "executionStatus": "error", - "coveredNodes": { - "n8n-nodes-base.start": 1, - "n8n-nodes-base.awsRekognition": 1, - "n8n-nodes-base.function": 1 - }, - "error": "Workflow failed to execute: There is no active execution with id \"43\"." - }, - { - "workflowId": "153", - "workflowName": "AWSLambda:invoke", - "executionTime": 0, - "finished": false, - "executionStatus": "error", - "coveredNodes": { - "n8n-nodes-base.start": 1, - "n8n-nodes-base.awsLambda": 2 - }, - "error": "Workflow failed to execute: There is no active execution with id \"42\"." - }, - { - "workflowId": "151", - "workflowName": "Wise:Account:getStatement getBalances getCurrencies:ExchangeRate:get:Profile:get getAll:Recipient:getAll:Quote:create get:Transfer:create get getAll execute delete", - "executionTime": 0, - "finished": false, - "executionStatus": "error", - "coveredNodes": { - "n8n-nodes-base.start": 1, - "n8n-nodes-base.wise": 16 - }, - "error": "Workflow failed to execute: There is no active execution with id \"40\"." - }, - { - "workflowId": "145", - "workflowName": "Peekalink:isAvailable preview", - "executionTime": 0, - "finished": false, - "executionStatus": "error", - "coveredNodes": { - "n8n-nodes-base.start": 1, - "n8n-nodes-base.peekalink": 2 - }, - "error": "Workflow failed to execute: There is no active execution with id \"36\"." - }, - { - "workflowId": "146", - "workflowName": "TimescaleDB:insert update executeQuery", - "executionTime": 0, - "finished": false, - "executionStatus": "error", - "coveredNodes": { - "n8n-nodes-base.start": 1, - "n8n-nodes-base.timescaleDb": 4, - "n8n-nodes-base.set": 2 - }, - "error": "Workflow failed to execute: There is no active execution with id \"37\"." - }, - { - "workflowId": "141", - "workflowName": "MicrosoftExcel:Table:lookup getRows getColumns addRow:Workbook:addWorksheet getAll:Worksheet:getAll getContent", - "executionTime": 0, - "finished": false, - "executionStatus": "error", - "coveredNodes": { - "n8n-nodes-base.start": 1, - "n8n-nodes-base.microsoftExcel": 8, - "n8n-nodes-base.set": 1, - "n8n-nodes-base.function": 4 - }, - "error": "Workflow failed to execute: There is no active execution with id \"33\"." - }, - { - "workflowId": "143", - "workflowName": "Clearbit:Company:enrich autocomplete:Person:enrich", - "executionTime": 0, - "finished": false, - "executionStatus": "error", - "coveredNodes": { - "n8n-nodes-base.start": 1, - "n8n-nodes-base.clearbit": 3 - }, - "error": "Workflow failed to execute: There is no active execution with id \"35\"." - }, - { - "workflowId": "15", - "workflowName": "DropBox:File: upload move copy download delete", - "executionTime": 0, - "finished": false, - "executionStatus": "error", - "coveredNodes": { - "n8n-nodes-base.start": 1, - "n8n-nodes-base.dropbox": 10, - "n8n-nodes-base.readBinaryFile": 1, - "n8n-nodes-base.function": 7 - }, - "error": "Workflow failed to execute: There is no active execution with id \"38\"." - }, - { - "workflowId": "150", - "workflowName": "PostHog:Event:create:Identity:create:Alias:create:Track:page screen", - "executionTime": 0, - "finished": false, - "executionStatus": "error", - "coveredNodes": { - "n8n-nodes-base.start": 1, - "n8n-nodes-base.postHog": 5 - }, - "error": "Workflow failed to execute: There is no active execution with id \"39\"." - }, - { - "workflowId": "152", - "workflowName": "AWSComprehend:Text:detectDominantLanguage detectSentiment detectEntities", - "executionTime": 0, - "finished": false, - "executionStatus": "error", - "coveredNodes": { - "n8n-nodes-base.start": 1, - "n8n-nodes-base.awsComprehend": 3 - }, - "error": "Workflow failed to execute: There is no active execution with id \"41\"." - }, - { - "workflowId": "125", - "workflowName": "Postgres: insert update executeQuery", - "executionTime": 1, - "finished": false, - "executionStatus": "warning", - "coveredNodes": { - "n8n-nodes-base.start": 1, - "n8n-nodes-base.postgres": 4, - "n8n-nodes-base.set": 2 - }, - "error": "connect ECONNREFUSED 157.90.159.163:5432 on node Postgres" - }, - { - "workflowId": "156", - "workflowName": "AWSSNS:publish", - "executionTime": 0, - "finished": false, - "executionStatus": "error", - "coveredNodes": { - "n8n-nodes-base.start": 1, - "n8n-nodes-base.awsSns": 1 - }, - "error": "Workflow failed to execute: There is no active execution with id \"45\"." - }, - { - "workflowId": "161", - "workflowName": "Vonage:SMS:send", - "executionTime": 0, - "finished": false, - "executionStatus": "error", - "coveredNodes": { - "n8n-nodes-base.start": 1, - "n8n-nodes-base.vonage": 1 - }, - "error": "Workflow failed to execute: There is no active execution with id \"46\"." - }, - { - "workflowId": "16", - "workflowName": "OpenWeatherMap:: current-weather 5-day-forecast", - "executionTime": 0, - "finished": false, - "executionStatus": "error", - "coveredNodes": { - "n8n-nodes-base.start": 1, - "n8n-nodes-base.openWeatherMap": 2 - }, - "error": "Workflow failed to execute: There is no active execution with id \"47\"." - }, - { - "workflowId": "182", - "workflowName": "Webflow:Item:create get update delete getAll", - "executionTime": 0, - "finished": false, - "executionStatus": "error", - "coveredNodes": { - "n8n-nodes-base.start": 1, - "n8n-nodes-base.webflow": 5 - }, - "error": "Workflow failed to execute: There is no active execution with id \"54\"." - }, - { - "workflowId": "163", - "workflowName": "GoogleSlides:Presentation:create get getSlides replaceText:Page:get getThumbnail", - "executionTime": 0, - "finished": false, - "executionStatus": "error", - "coveredNodes": { - "n8n-nodes-base.start": 1, - "n8n-nodes-base.googleSlides": 7 - }, - "error": "Workflow failed to execute: There is no active execution with id \"55\"." - }, - { - "workflowId": "162", - "workflowName": "AWSSQS:sendMessage", - "executionTime": 0, - "finished": false, - "executionStatus": "error", - "coveredNodes": { - "n8n-nodes-base.start": 1, - "n8n-nodes-base.awsSqs": 2 - }, - "error": "Workflow failed to execute: There is no active execution with id \"56\"." - }, - { - "workflowId": "187", - "workflowName": "Strapi:Entry:create get getAll update delete", - "executionTime": 0, - "finished": false, - "executionStatus": "error", - "coveredNodes": { - "n8n-nodes-base.start": 1, - "n8n-nodes-base.strapi": 5, - "n8n-nodes-base.set": 2 - }, - "error": "Workflow failed to execute: There is no active execution with id \"57\"." - }, - { - "workflowId": "190", - "workflowName": "Discourse:User:create get getAll:Category:create update getAll:Group:create update get getAll:UserGroup:add remove:Post:create update get getAll", - "executionTime": 0, - "finished": false, - "executionStatus": "error", - "coveredNodes": { - "n8n-nodes-base.start": 1, - "n8n-nodes-base.discourse": 16, - "n8n-nodes-base.set": 1 - }, - "error": "Workflow failed to execute: There is no active execution with id \"58\"." - }, - { - "workflowId": "170", - "workflowName": "Xero:Contact:create get getAll update:Invoice:create get getAll update", - "executionTime": 0, - "finished": false, - "executionStatus": "error", - "coveredNodes": { - "n8n-nodes-base.start": 1, - "n8n-nodes-base.xero": 8 - }, - "error": "Workflow failed to execute: There is no active execution with id \"50\"." - }, - { - "workflowId": "166", - "workflowName": "Intercom:Company:create get getAll update users:Lead:create get getAll update delete:User:create get getAll update delete", - "executionTime": 0, - "finished": false, - "executionStatus": "error", - "coveredNodes": { - "n8n-nodes-base.start": 1, - "n8n-nodes-base.intercom": 15 - }, - "error": "Workflow failed to execute: There is no active execution with id \"48\"." - }, - { - "workflowId": "172", - "workflowName": "Paypal:Payout:create get:PayoutItem:get", - "executionTime": 0, - "finished": false, - "executionStatus": "error", - "coveredNodes": { - "n8n-nodes-base.start": 1, - "n8n-nodes-base.payPal": 4 - }, - "error": "Workflow failed to execute: There is no active execution with id \"51\"." - }, - { - "workflowId": "174", - "workflowName": "Vero:User:create alias addTags removeTags unsubscribe resubscribe delete:Event:track", - "executionTime": 0, - "finished": false, - "executionStatus": "error", - "coveredNodes": { - "n8n-nodes-base.start": 1, - "n8n-nodes-base.vero": 8, - "n8n-nodes-base.set": 1 - }, - "error": "Workflow failed to execute: There is no active execution with id \"52\"." - }, - { - "workflowId": "177", - "workflowName": "uProc:Finance:*;", - "executionTime": 0, - "finished": false, - "executionStatus": "error", - "coveredNodes": { - "n8n-nodes-base.start": 1, - "n8n-nodes-base.uproc": 24 - }, - "error": "Workflow failed to execute: There is no active execution with id \"53\"." - }, - { - "workflowId": "191", - "workflowName": "Mailerlite:Subscriber:create get update getAll", - "executionTime": 0, - "finished": false, - "executionStatus": "error", - "coveredNodes": { - "n8n-nodes-base.start": 1, - "n8n-nodes-base.mailerLite": 4 - }, - "error": "Workflow failed to execute: There is no active execution with id \"59\"." - }, - { - "workflowId": "194", - "workflowName": "Mailjet:Email:send sendTemplate", - "executionTime": 0, - "finished": false, - "executionStatus": "error", - "coveredNodes": { - "n8n-nodes-base.start": 1, - "n8n-nodes-base.mailjet": 3 - }, - "error": "Workflow failed to execute: There is no active execution with id \"60\"." - }, - { - "workflowId": "195", - "workflowName": "Kitemaker:Organization:get:Space:getAll:User:getAll:WorkItem:create get getAll update", - "executionTime": 0, - "finished": false, - "executionStatus": "error", - "coveredNodes": { - "n8n-nodes-base.start": 1, - "n8n-nodes-base.kitemaker": 7 - }, - "error": "Workflow failed to execute: There is no active execution with id \"62\"." - }, - { - "workflowId": "196", - "workflowName": "QuickBooks:Bill:create get update getAll delete:Item:getAll:get:Vendor:create get getAll update:Employee:create get update getAll:Customer:create get update getAll:Estimate:create get update getAll send delete:Invoice:create get update getAll send delete:Payment:create get update getAll send delete", - "executionTime": 0, - "finished": false, - "executionStatus": "error", - "coveredNodes": { - "n8n-nodes-base.start": 1, - "n8n-nodes-base.quickbooks": 37 - }, - "error": "Workflow failed to execute: There is no active execution with id \"61\"." - }, - { - "workflowId": "208", - "workflowName": "RocketChat:Chat:postMessage", - "executionTime": 0, - "finished": false, - "executionStatus": "error", - "coveredNodes": { - "n8n-nodes-base.start": 1, - "n8n-nodes-base.rocketchat": 1 - }, - "error": "Workflow failed to execute: There is no active execution with id \"72\"." - }, - { - "workflowId": "206", - "workflowName": "Keap:Company:create getAll:Contact:upsert get getAll delete:ContactNote:create get update getAll delete:ContactTag:create getAll delete", - "executionTime": 0, - "finished": false, - "executionStatus": "error", - "coveredNodes": { - "n8n-nodes-base.start": 1, - "n8n-nodes-base.keap": 14 - }, - "error": "Workflow failed to execute: There is no active execution with id \"70\"." - }, - { - "workflowId": "202", - "workflowName": "Beeminder:Datapoint:create getAll update delete", - "executionTime": 0, - "finished": false, - "executionStatus": "error", - "coveredNodes": { - "n8n-nodes-base.start": 1, - "n8n-nodes-base.beeminder": 5 - }, - "error": "Workflow failed to execute: There is no active execution with id \"66\"." - }, - { - "workflowId": "199", - "workflowName": "MailCheck:Email:check", - "executionTime": 0, - "finished": false, - "executionStatus": "error", - "coveredNodes": { - "n8n-nodes-base.start": 1, - "n8n-nodes-base.mailcheck": 1 - }, - "error": "Workflow failed to execute: There is no active execution with id \"65\"." - }, - { - "workflowId": "198", - "workflowName": "GoogleCloudFirestore:Document:create get upset getAll query delete:Collection:getAll", - "executionTime": 0, - "finished": false, - "executionStatus": "error", - "coveredNodes": { - "n8n-nodes-base.start": 1, - "n8n-nodes-base.googleFirebaseCloudFirestore": 7, - "n8n-nodes-base.set": 2 - }, - "error": "Workflow failed to execute: There is no active execution with id \"64\"." - }, - { - "workflowId": "204", - "workflowName": "HelpScout:Mailbox:getAll get:Customer:create get update getAll properties:Conversation:create get getAll delete:Thread:create getAll", - "executionTime": 0, - "finished": false, - "executionStatus": "error", - "coveredNodes": { - "n8n-nodes-base.start": 1, - "n8n-nodes-base.helpScout": 13 - }, - "error": "Workflow failed to execute: There is no active execution with id \"68\"." - }, - { - "workflowId": "197", - "workflowName": "MQTT:sendMessage", - "executionTime": 0, - "finished": false, - "executionStatus": "error", - "coveredNodes": { - "n8n-nodes-base.start": 1, - "n8n-nodes-base.mqtt": 3, - "n8n-nodes-base.function": 1 - }, - "error": "Workflow failed to execute: There is no active execution with id \"63\"." - }, - { - "workflowId": "203", - "workflowName": "Paddle:Payment:getAll reschedule:Plan:getAll get:Product:getAll:Coupon:create update getAll:User:getAll", - "executionTime": 0, - "finished": false, - "executionStatus": "error", - "coveredNodes": { - "n8n-nodes-base.start": 1, - "n8n-nodes-base.paddle": 9, - "n8n-nodes-base.dateTime": 1 - }, - "error": "Workflow failed to execute: There is no active execution with id \"67\"." - }, - { - "workflowId": "205", - "workflowName": "Pushover:Message:push", - "executionTime": 0, - "finished": false, - "executionStatus": "error", - "coveredNodes": { - "n8n-nodes-base.start": 1, - "n8n-nodes-base.pushover": 1 - }, - "error": "Workflow failed to execute: There is no active execution with id \"69\"." - }, - { - "workflowId": "207", - "workflowName": "Keap:EcommerceOrder:create get getAll delete:EcommerceProduct:create get getAll delete:Email:createRecord getAll:File:upload getAll delete", - "executionTime": 0, - "finished": false, - "executionStatus": "error", - "coveredNodes": { - "n8n-nodes-base.start": 1, - "n8n-nodes-base.keap": 15 - }, - "error": "Workflow failed to execute: There is no active execution with id \"71\"." - }, - { - "workflowId": "209", - "workflowName": "AWS Transcribe:TranscriptionJob:create get getAll delete", - "executionTime": 0, - "finished": false, - "executionStatus": "error", - "coveredNodes": { - "n8n-nodes-base.start": 1, - "n8n-nodes-base.awsTranscribe": 4, - "n8n-nodes-base.set": 1, - "n8n-nodes-base.function": 1 - }, - "error": "Workflow failed to execute: There is no active execution with id \"73\"." - }, - { - "workflowId": "223", - "workflowName": "LingvaNex:Translate", - "executionTime": 0, - "finished": false, - "executionStatus": "error", - "coveredNodes": { - "n8n-nodes-base.start": 1, - "n8n-nodes-base.lingvaNex": 1 - }, - "error": "Workflow failed to execute: There is no active execution with id \"75\"." - }, - { - "workflowId": "216", - "workflowName": "FunctionItem:JSON,Binary and Static data", - "executionTime": 0, - "finished": true, - "executionStatus": "warning", - "coveredNodes": { - "n8n-nodes-base.start": 1, - "n8n-nodes-base.functionItem": 3 - }, - "error": "Workflow contains new data that previously did not exist.", - "changes": { - "data": { - "executionData": { - "metadata__added": {} - } - } - } - }, - { - "workflowId": "17", - "workflowName": "OpenThesaurus:: Get Synonyms", - "executionTime": 1, - "finished": true, - "executionStatus": "warning", - "coveredNodes": { - "n8n-nodes-base.start": 1, - "n8n-nodes-base.openThesaurus": 1 - }, - "error": "Workflow contains new data that previously did not exist.", - "changes": { - "data": { - "executionData": { - "metadata__added": {} - } - } - } - }, - { - "workflowId": "35", - "workflowName": "Slack:User:getPresence info:UserProfile:get update:Message:post getPermalink update postEphermera l delete:Reaction:add get remove", - "executionTime": 0, - "finished": false, - "executionStatus": "error", - "coveredNodes": { - "n8n-nodes-base.start": 1, - "n8n-nodes-base.slack": 12 - }, - "error": "Workflow failed to execute: There is no active execution with id \"81\"." - }, - { - "workflowId": "36", - "workflowName": "Slack:File:upload getAll get:Star:add getAll delete:Channgel:create update setPurpose setTopic ge t invite leave join getAll history replies member archive unarchive", - "executionTime": 0, - "finished": false, - "executionStatus": "error", - "coveredNodes": { - "n8n-nodes-base.start": 1, - "n8n-nodes-base.slack": 22 - }, - "error": "Workflow failed to execute: There is no active execution with id \"82\"." - }, - { - "workflowId": "37", - "workflowName": "GitLab:Repository:get getIssues:Issue:create createComment edit get lock:Release:create get getAll update delete:User:getRepositories", - "executionTime": 0, - "finished": false, - "executionStatus": "error", - "coveredNodes": { - "n8n-nodes-base.start": 1, - "n8n-nodes-base.gitlab": 13 - }, - "error": "Workflow failed to execute: There is no active execution with id \"83\"." - }, - { - "workflowId": "40", - "workflowName": "Gmail:Draft:create getAll get delete:Label:create getAll get delete:Message:send getAll get reply delete:MessageLabel:add remove", - "executionTime": 0, - "finished": false, - "executionStatus": "error", - "coveredNodes": { - "n8n-nodes-base.start": 1, - "n8n-nodes-base.gmail": 16, - "n8n-nodes-base.function": 2 - }, - "error": "Workflow failed to execute: There is no active execution with id \"84\"." - }, - { - "workflowId": "29", - "workflowName": "ClickUp:Comment:create update getAll delete", - "executionTime": 0, - "finished": false, - "executionStatus": "error", - "coveredNodes": { - "n8n-nodes-base.start": 1, - "n8n-nodes-base.clickUp": 8, - "n8n-nodes-base.function": 7 - }, - "error": "Workflow failed to execute: There is no active execution with id \"79\"." - }, - { - "workflowId": "41", - "workflowName": "GoogleCalendar:Event:create getAll get update delete:Calendar:availability", - "executionTime": 0, - "finished": false, - "executionStatus": "error", - "coveredNodes": { - "n8n-nodes-base.start": 1, - "n8n-nodes-base.googleCalendar": 6 - }, - "error": "Workflow failed to execute: There is no active execution with id \"85\"." - }, - { - "workflowId": "32", - "workflowName": "Drif:Contact:create update get delete", - "executionTime": 0, - "finished": false, - "executionStatus": "error", - "coveredNodes": { - "n8n-nodes-base.start": 1, - "n8n-nodes-base.drift": 4 - }, - "error": "Workflow failed to execute: There is no active execution with id \"80\"." - }, - { - "workflowId": "26", - "workflowName": "ClickUp:Folder:create update getAll get delete:Goal:create update getAll get delete:GoalKeyResult:create update delete", - "executionTime": 0, - "finished": false, - "executionStatus": "error", - "coveredNodes": { - "n8n-nodes-base.start": 1, - "n8n-nodes-base.clickUp": 13, - "n8n-nodes-base.function": 11 - }, - "error": "Workflow failed to execute: There is no active execution with id \"78\"." - }, - { - "workflowId": "42", - "workflowName": "GoogleTask:create update getAll get delete", - "executionTime": 0, - "finished": false, - "executionStatus": "error", - "coveredNodes": { - "n8n-nodes-base.start": 1, - "n8n-nodes-base.googleTasks": 5 - }, - "error": "Workflow failed to execute: There is no active execution with id \"87\"." - }, - { - "workflowId": "43", - "workflowName": "GoogleContacts:create update getAll get delete", - "executionTime": 0, - "finished": false, - "executionStatus": "error", - "coveredNodes": { - "n8n-nodes-base.start": 1, - "n8n-nodes-base.googleContacts": 5, - "n8n-nodes-base.function": 1 - }, - "error": "Workflow failed to execute: There is no active execution with id \"88\"." - }, - { - "workflowId": "44", - "workflowName": "GoogleDrive:Folder:create share delete:File:upload share list download copy delete", - "executionTime": 0, - "finished": false, - "executionStatus": "error", - "coveredNodes": { - "n8n-nodes-base.start": 1, - "n8n-nodes-base.googleDrive": 11 - }, - "error": "Workflow failed to execute: There is no active execution with id \"89\"." - }, - { - "workflowId": "45", - "workflowName": "GoogleSheets:SpreadSheet:create:Sheet:create append read lookup update delete clean remove", - "executionTime": 0, - "finished": false, - "executionStatus": "error", - "coveredNodes": { - "n8n-nodes-base.start": 1, - "n8n-nodes-base.googleSheets": 9, - "n8n-nodes-base.set": 1, - "n8n-nodes-base.function": 1 - }, - "error": "Workflow failed to execute: There is no active execution with id \"90\"." - }, - { - "workflowId": "55", - "workflowName": "Hunter: domainSearch emailFinder emailVerifier", - "executionTime": 0, - "finished": false, - "executionStatus": "error", - "coveredNodes": { - "n8n-nodes-base.start": 1, - "n8n-nodes-base.hunter": 3 - }, - "error": "Workflow failed to execute: There is no active execution with id \"96\"." - }, - { - "workflowId": "46", - "workflowName": "GoogleBooks:Volume:getAll get:BookShelf:getAll get:BookShelfVolume:add move getAll remove clear", - "executionTime": 0, - "finished": false, - "executionStatus": "error", - "coveredNodes": { - "n8n-nodes-base.start": 1, - "n8n-nodes-base.googleBooks": 9 - }, - "error": "Workflow failed to execute: There is no active execution with id \"97\"." - }, - { - "workflowId": "50", - "workflowName": "Brandfetch:color company font logo industry", - "executionTime": 0, - "finished": false, - "executionStatus": "error", - "coveredNodes": { - "n8n-nodes-base.start": 1, - "n8n-nodes-base.Brandfetch": 5 - }, - "error": "Workflow failed to execute: There is no active execution with id \"92\"." - }, - { - "workflowId": "51", - "workflowName": "Clockify:Project:create update get getAll:Tag:create update getAll delete:TimeEntry:create update get delete", - "executionTime": 0, - "finished": false, - "executionStatus": "error", - "coveredNodes": { - "n8n-nodes-base.start": 1, - "n8n-nodes-base.clockify": 13, - "n8n-nodes-base.function": 1 - }, - "error": "Workflow failed to execute: There is no active execution with id \"93\"." - }, - { - "workflowId": "58", - "workflowName": "Todoist:Task:create get close reopen getAll delete", - "executionTime": 0, - "finished": false, - "executionStatus": "error", - "coveredNodes": { - "n8n-nodes-base.start": 1, - "n8n-nodes-base.todoist": 6 - }, - "error": "Workflow failed to execute: There is no active execution with id \"98\"." - }, - { - "workflowId": "53", - "workflowName": "ConvertKit:CustomField:create getAll update delete:Form:getAll addSubscriber getSubscriptions:Tag:create getAll:TagSubscriber:add getAll delete", - "executionTime": 0, - "finished": false, - "executionStatus": "error", - "coveredNodes": { - "n8n-nodes-base.start": 1, - "n8n-nodes-base.convertKit": 12 - }, - "error": "Workflow failed to execute: There is no active execution with id \"95\"." - }, - { - "workflowId": "48", - "workflowName": "Asana:Project:getAll get:Task:create update move getAll get delete:TaskComment:add remove:TaskTag:add remove:TaskProject:add remov e:SubTask:create getAll:User:get getAll", - "executionTime": 0, - "finished": false, - "executionStatus": "error", - "coveredNodes": { - "n8n-nodes-base.start": 1, - "n8n-nodes-base.asana": 18 - }, - "error": "Workflow failed to execute: There is no active execution with id \"91\"." - }, - { - "workflowId": "52", - "workflowName": "Coda:Table:getAllColumns getColumn getAllRows getRow createRow pushButton deleteRow:View:getAll get getAllViewColumns getAllViewRows updateViewRow pushViewButto n:Formula:get getAll:Control:get getAll", - "executionTime": 0, - "finished": false, - "executionStatus": "error", - "coveredNodes": { - "n8n-nodes-base.start": 1, - "n8n-nodes-base.coda": 17, - "n8n-nodes-base.set": 2 - }, - "error": "Workflow failed to execute: There is no active execution with id \"94\"." - }, - { - "workflowId": "60", - "workflowName": "Taiga:Issue:create update get getAll delete", - "executionTime": 0, - "finished": false, - "executionStatus": "error", - "coveredNodes": { - "n8n-nodes-base.start": 1, - "n8n-nodes-base.taiga": 5 - }, - "error": "Workflow failed to execute: There is no active execution with id \"100\"." - }, - { - "workflowId": "61", - "workflowName": "Spotify:Artist:get getAlbums getRelatedArtists getTopTracks:Album:get getNewReleases getTracks:Track:get getAudioFeatures:Playlist:create getUserPlaylists get add getTracks delete:Player:recentylPlayed currentlyPlaying:Library:getLikedTracks", - "executionTime": 0, - "finished": false, - "executionStatus": "error", - "coveredNodes": { - "n8n-nodes-base.start": 1, - "n8n-nodes-base.spotify": 18 - }, - "error": "Workflow failed to execute: There is no active execution with id \"101\"." - }, - { - "workflowId": "62", - "workflowName": "ProfitWell:Company:getSetting:Metric:getMontly getDaily", - "executionTime": 0, - "finished": false, - "executionStatus": "error", - "coveredNodes": { - "n8n-nodes-base.start": 1, - "n8n-nodes-base.profitWell": 3 - }, - "error": "Workflow failed to execute: There is no active execution with id \"102\"." - }, - { - "workflowId": "67", - "workflowName": " Twist:Channel:create update get getAll archive unarchive delete:MessageConversation:create get getAll update delete:Thread:create get update getAll delete:Comment:create get update getAll delete", - "executionTime": 0, - "finished": false, - "executionStatus": "error", - "coveredNodes": { - "n8n-nodes-base.start": 1, - "n8n-nodes-base.twist": 23 - }, - "error": "Workflow failed to execute: There is no active execution with id \"104\"." - }, - { - "workflowId": "73", - "workflowName": "Youtube:Channel:get getAll update:Playlist:create update getAll get delete:PlaylistItem:add getAll get delete:videoCategory:getAll:Video:rate get upload update delete", - "executionTime": 0, - "finished": false, - "executionStatus": "error", - "coveredNodes": { - "n8n-nodes-base.start": 1, - "n8n-nodes-base.youTube": 18, - "n8n-nodes-base.httpRequest": 1, - "n8n-nodes-base.readBinaryFile": 1, - "n8n-nodes-base.function": 7 - }, - "error": "Workflow failed to execute: There is no active execution with id \"107\"." - }, - { - "workflowId": "71", - "workflowName": "Contentful-preview-api:locale:getAll:entry:getAll:ContentType:get:Asset:getAll get:Space:get", - "executionTime": 0, - "finished": false, - "executionStatus": "error", - "coveredNodes": { - "n8n-nodes-base.start": 1, - "n8n-nodes-base.contentful": 7 - }, - "error": "Workflow failed to execute: There is no active execution with id \"106\"." - }, - { - "workflowId": "70", - "workflowName": "Contentful-delivery-api:locale:getAll:entry:getAll:ContentType:get:Asset:getAll get:Space:get", - "executionTime": 0, - "finished": false, - "executionStatus": "error", - "coveredNodes": { - "n8n-nodes-base.start": 1, - "n8n-nodes-base.contentful": 7 - }, - "error": "Workflow failed to execute: There is no active execution with id \"105\"." - }, - { - "workflowId": "75", - "workflowName": "Harvest:Client:create update getAll get delete:Invoice:create update getAll get delete:Contact:create update get getAll delete:Company:get", - "executionTime": 0, - "finished": false, - "executionStatus": "error", - "coveredNodes": { - "n8n-nodes-base.start": 1, - "n8n-nodes-base.harvest": 16 - }, - "error": "Workflow failed to execute: There is no active execution with id \"108\"." - }, - { - "workflowId": "77", - "workflowName": "Harvest:Expense:create update get getAll delete:Estimate:create update get getAll delete", - "executionTime": 0, - "finished": false, - "executionStatus": "error", - "coveredNodes": { - "n8n-nodes-base.start": 1, - "n8n-nodes-base.harvest": 12 - }, - "error": "Workflow failed to execute: There is no active execution with id \"109\"." - }, - { - "workflowId": "63", - "workflowName": "Segment:Identify:create:Track:event page", - "executionTime": 0, - "finished": false, - "executionStatus": "error", - "coveredNodes": { - "n8n-nodes-base.start": 1, - "n8n-nodes-base.segment": 4 - }, - "error": "Workflow failed to execute: There is no active execution with id \"103\"." - }, - { - "workflowId": "76", - "workflowName": "Harvest:Project:create update getAll get delete:Task:getAll get:TimeEntry:createByDuration createByStartEnd update getAll restartTime stopTime delete:User:me get getAll update", - "executionTime": 0, - "finished": false, - "executionStatus": "error", - "coveredNodes": { - "n8n-nodes-base.start": 1, - "n8n-nodes-base.harvest": 24 - }, - "error": "Workflow failed to execute: There is no active execution with id \"110\"." - }, - { - "workflowId": "6", - "workflowName": "Static files workflow", - "executionTime": 0, - "finished": true, - "executionStatus": "warning", - "coveredNodes": { - "n8n-nodes-base.start": 1, - "n8n-nodes-base.writeBinaryFile": 1, - "n8n-nodes-base.function": 1 - }, - "error": "Workflow contains new data that previously did not exist.", - "changes": { - "data": { - "executionData": { - "metadata__added": {} - } - } - } - }, - { - "workflowId": "81", - "workflowName": "PhantomBuster:getAll get launch getOutput", - "executionTime": 0, - "finished": false, - "executionStatus": "error", - "coveredNodes": { - "n8n-nodes-base.start": 1, - "n8n-nodes-base.phantombuster": 4 - }, - "error": "Workflow failed to execute: There is no active execution with id \"111\"." - }, - { - "workflowId": "82", - "workflowName": "Microsoft OneDrive:Folder:create getChildren share search delete:File:upload get share download copy delete search", - "executionTime": 0, - "finished": false, - "executionStatus": "error", - "coveredNodes": { - "n8n-nodes-base.start": 1, - "n8n-nodes-base.microsoftOneDrive": 12, - "n8n-nodes-base.function": 6 - }, - "error": "Workflow failed to execute: There is no active execution with id \"112\"." - }, - { - "workflowId": "84", - "workflowName": "Matrix:Room:create invite kick leave:RoomMember:getAll:Message:create getAll:Account:me:Media:upload:Event:get", - "executionTime": 0, - "finished": false, - "executionStatus": "error", - "coveredNodes": { - "n8n-nodes-base.start": 1, - "n8n-nodes-base.matrix": 10, - "n8n-nodes-base.readBinaryFile": 1, - "n8n-nodes-base.merge": 1 - }, - "error": "Workflow failed to execute: There is no active execution with id \"114\"." - }, - { - "workflowId": "91", - "workflowName": "HTML Extract:json binary", - "executionTime": 0, - "finished": true, - "executionStatus": "warning", - "coveredNodes": { - "n8n-nodes-base.start": 1, - "n8n-nodes-base.functionItem": 1, - "n8n-nodes-base.htmlExtract": 2, - "n8n-nodes-base.function": 2, - "n8n-nodes-base.moveBinaryData": 1 - }, - "error": "Workflow contains new data that previously did not exist.", - "changes": { - "data": { - "resultData": { - "runData": { - "Move Binary Data": [ - [ - "~", - { - "data": { - "main": [ - [ - "~", - [ - [ - "~", - { - "binary": { - "data": { - "fileExtension__added": "json" - } - } - } - ] - ] - ] - ] - } - } - ] - ] - } - }, - "executionData": { - "metadata__added": {} - } - } - } - }, - { - "workflowId": "88", - "workflowName": "ActiveCampaign:EcommerceCustomer:create update get getAll delete:EcommerceOrder:create update get getAll delete:EcommerceOrderProduct:getByProductId getByOrderId getAll", - "executionTime": 0, - "finished": false, - "executionStatus": "error", - "coveredNodes": { - "n8n-nodes-base.start": 1, - "n8n-nodes-base.activeCampaign": 17 - }, - "error": "Workflow failed to execute: There is no active execution with id \"115\"." - }, - { - "workflowId": "87", - "workflowName": "ActiveCampaign:Deal:create update get createNote updateNote getAll delete:Tag:create update get getAll delete:Connection:create update get getAll delete:List:getAll", - "executionTime": 0, - "finished": false, - "executionStatus": "error", - "coveredNodes": { - "n8n-nodes-base.start": 1, - "n8n-nodes-base.activeCampaign": 20 - }, - "error": "Workflow failed to execute: There is no active execution with id \"117\"." - }, - { - "workflowId": "86", - "workflowName": "ActiveCampaign:Contact:create update get getAll delete:Account:create update get getAll delete:AccountContact:create update delete:ContactTag:add remove:ContactList:add remove", - "executionTime": 0, - "finished": false, - "executionStatus": "error", - "coveredNodes": { - "n8n-nodes-base.start": 1, - "n8n-nodes-base.activeCampaign": 17, - "n8n-nodes-base.merge": 1 - }, - "error": "Workflow failed to execute: There is no active execution with id \"118\"." - }, - { - "workflowId": "90", - "workflowName": "Date&Time:formatDate", - "executionTime": 0, - "finished": true, - "executionStatus": "warning", - "coveredNodes": { - "n8n-nodes-base.start": 1, - "n8n-nodes-base.dateTime": 6, - "n8n-nodes-base.function": 1 - }, - "error": "Workflow contains new data that previously did not exist.", - "changes": { - "data": { - "executionData": { - "metadata__added": {} - } - } - } - }, - { - "workflowId": "95", - "workflowName": "Set", - "executionTime": 0, - "finished": true, - "executionStatus": "warning", - "coveredNodes": { - "n8n-nodes-base.start": 1, - "n8n-nodes-base.set": 1, - "n8n-nodes-base.function": 1 - }, - "error": "Workflow contains new data that previously did not exist.", - "changes": { - "data": { - "executionData": { - "metadata__added": {} - } - } - } - }, - { - "workflowId": "93", - "workflowName": "Move Binary Data:toJSON:toBinary", - "executionTime": 0, - "finished": true, - "executionStatus": "warning", - "coveredNodes": { - "n8n-nodes-base.start": 1, - "n8n-nodes-base.moveBinaryData": 2, - "n8n-nodes-base.functionItem": 1, - "n8n-nodes-base.function": 1 - }, - "error": "Workflow contains new data that previously did not exist.", - "changes": { - "data": { - "resultData": { - "runData": { - "Move Binary Data": [ - [ - "~", - { - "data": { - "main": [ - [ - "~", - [ - [ - "~", - { - "binary": { - "data": { - "fileExtension__added": "json" - } - } - } - ] - ] - ] - ] - } - } - ] - ] - } - }, - "executionData": { - "metadata__added": {} - } - } - } - }, - { - "workflowId": "96", - "workflowName": "MessageBird:Sms:send:Balance:get", - "executionTime": 0, - "finished": false, - "executionStatus": "error", - "coveredNodes": { - "n8n-nodes-base.start": 1, - "n8n-nodes-base.messageBird": 2 - }, - "error": "Workflow failed to execute: There is no active execution with id \"122\"." - }, - { - "workflowId": "97", - "workflowName": "Crypto", - "executionTime": 0, - "finished": true, - "executionStatus": "warning", - "coveredNodes": { - "n8n-nodes-base.start": 1, - "n8n-nodes-base.crypto": 15, - "n8n-nodes-base.function": 3 - }, - "error": "Workflow contains new data that previously did not exist.", - "changes": { - "data": { - "executionData": { - "metadata__added": {} - } - } - } - }, - { - "workflowId": "230", - "workflowName": "CredentialOnlyNodes", - "executionTime": 1, - "finished": true, - "executionStatus": "success", - "coveredNodes": { - "n8n-nodes-base.manualTrigger": 1, - "n8n-nodes-base.httpRequest": 1 - } - }, - { - "workflowId": "98", - "workflowName": "Disqus:Forum:get getCategories getThreads getPosts", - "executionTime": 0, - "finished": false, - "executionStatus": "error", - "coveredNodes": { - "n8n-nodes-base.start": 1, - "n8n-nodes-base.disqus": 4 - }, - "error": "Workflow failed to execute: There is no active execution with id \"124\"." - }, - { - "workflowId": "83", - "workflowName": "RSS Feed", - "executionTime": 1, - "finished": true, - "executionStatus": "warning", - "coveredNodes": { - "n8n-nodes-base.start": 1, - "n8n-nodes-base.rssFeedRead": 1, - "n8n-nodes-base.function": 1 - }, - "error": "Workflow contains new data that previously did not exist.", - "changes": { - "data": { - "executionData": { - "metadata__added": {} - } - } - } - }, - { - "workflowId": "212", - "workflowName": "Git:*", - "executionTime": 3, - "finished": true, - "executionStatus": "warning", - "coveredNodes": { - "n8n-nodes-base.start": 1, - "n8n-nodes-base.git": 14, - "n8n-nodes-base.executeCommand": 2 - }, - "error": "Workflow contains new data that previously did not exist.", - "changes": { - "data": { - "resultData": { - "runData": { - "Git2": [ - [ - "~", - { - "data": { - "main": [ - [ - "~", - [ - [ - " " - ], - [ - "-", - { - "json": { - "success": true - }, - "pairedItem": { - "item": 1 - } - } - ] - ] - ] - ] - } - } - ] - ], - "Git7": [ - [ - "~", - { - "data": { - "main": [ - [ - "~", - [ - [ - " " - ], - [ - "-", - { - "json": { - "success": true - }, - "pairedItem": { - "item": 1 - } - } - ] - ] - ] - ] - } - } - ] - ] - } - }, - "executionData": { - "metadata__added": {} - } - } - } - }, - { - "workflowId": "155", - "workflowName": "AWSS3:Bucket:create getAll search:File:upload getAll download delete:Folder:create getAll delete", - "executionTime": 16, - "finished": true, - "executionStatus": "warning", - "coveredNodes": { - "n8n-nodes-base.start": 1, - "n8n-nodes-base.awsS3": 12, - "n8n-nodes-base.set": 2, - "n8n-nodes-base.function": 1 - }, - "error": "Workflow contains new data that previously did not exist.", - "changes": { - "data": { - "executionData": { - "metadata__added": {} - } - } - } - }, - { - "workflowId": "229", - "workflowName": "GoogleDrive:Folder:create share delete:File:upload share list download copy delete copy (RL)", - "executionTime": 23, - "finished": true, - "executionStatus": "warning", - "coveredNodes": { - "n8n-nodes-base.start": 1, - "n8n-nodes-base.googleDrive": 20, - "n8n-nodes-base.stickyNote": 2 - }, - "error": "Workflow contains new data that previously did not exist.", - "changes": { - "data": { - "executionData": { - "metadata__added": {} - } - } - } - } - ] -} \ No newline at end of file From 48a0f91c72c6bb18f0a93c621abd470cbaa9c8ab Mon Sep 17 00:00:00 2001 From: Giulio Andreini Date: Wed, 17 Jan 2024 13:01:19 +0100 Subject: [PATCH 12/69] docs(Discord Node): Better OAuth description (no-changelog) (#8350) --- .../nodes-base/nodes/Discord/v2/actions/versionDescription.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/nodes-base/nodes/Discord/v2/actions/versionDescription.ts b/packages/nodes-base/nodes/Discord/v2/actions/versionDescription.ts index 23c7e3337f..beb0572f84 100644 --- a/packages/nodes-base/nodes/Discord/v2/actions/versionDescription.ts +++ b/packages/nodes-base/nodes/Discord/v2/actions/versionDescription.ts @@ -61,7 +61,7 @@ export const versionDescription: INodeTypeDescription = { { name: 'OAuth2', value: 'oAuth2', - description: 'Manage messages, channels, and members on a server', + description: "Same features as 'Bot Token' with easier Bot installation", }, { name: 'Webhook', From d5455d7accb193078b05a0f52386cf9303b6a00f Mon Sep 17 00:00:00 2001 From: Omar Ajoue Date: Wed, 17 Jan 2024 12:08:20 +0000 Subject: [PATCH 13/69] fix(core): Report when waitTill is invalid and handle it (#8356) --- packages/cli/src/WaitTracker.ts | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/packages/cli/src/WaitTracker.ts b/packages/cli/src/WaitTracker.ts index 435b6f5b94..b39222bc63 100644 --- a/packages/cli/src/WaitTracker.ts +++ b/packages/cli/src/WaitTracker.ts @@ -53,6 +53,21 @@ export class WaitTracker { for (const execution of executions) { const executionId = execution.id; if (this.waitingExecutions[executionId] === undefined) { + if (!(execution.waitTill instanceof Date)) { + // n8n expects waitTill to be a date object + // but for some reason it's not being converted + // we are handling this like this since it seems to address the issue + // for some users, as reported by Jon when using a custom image. + // Once we figure out why this it not a Date object, we can remove this. + ErrorReporter.error('Wait Till is not a date object', { + extra: { + variableType: typeof execution.waitTill, + }, + }); + if (typeof execution.waitTill === 'string') { + execution.waitTill = new Date(execution.waitTill); + } + } const triggerTime = execution.waitTill!.getTime() - new Date().getTime(); this.waitingExecutions[executionId] = { executionId, From 771d2fa34161915285b1ac38e94cd4d76dfc04c4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Iv=C3=A1n=20Ovejero?= Date: Wed, 17 Jan 2024 14:01:56 +0100 Subject: [PATCH 14/69] test(core): Fix cleanup in test teardown script (no-changelog) (#8361) --- packages/cli/src/config/index.ts | 1 - packages/cli/test/setup-test-folder.ts | 2 ++ 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/packages/cli/src/config/index.ts b/packages/cli/src/config/index.ts index 59962ff774..5fa96e75ab 100644 --- a/packages/cli/src/config/index.ts +++ b/packages/cli/src/config/index.ts @@ -14,7 +14,6 @@ if (inE2ETests) { process.env.N8N_AI_ENABLED = 'true'; } else if (inTest) { process.env.N8N_LOG_LEVEL = 'silent'; - process.env.N8N_ENCRYPTION_KEY = 'test_key'; process.env.N8N_PUBLIC_API_DISABLED = 'true'; process.env.SKIP_STATISTICS_EVENTS = 'true'; } else { diff --git a/packages/cli/test/setup-test-folder.ts b/packages/cli/test/setup-test-folder.ts index 3709ed250b..4435df5ece 100644 --- a/packages/cli/test/setup-test-folder.ts +++ b/packages/cli/test/setup-test-folder.ts @@ -2,6 +2,8 @@ import { tmpdir } from 'os'; import { join } from 'path'; import { mkdirSync, mkdtempSync, writeFileSync } from 'fs'; +process.env.N8N_ENCRYPTION_KEY = 'test_key'; + const baseDir = join(tmpdir(), 'n8n-tests/'); mkdirSync(baseDir, { recursive: true }); From 7268d2425936e8bcb7c60f0b31f58d9540cecb82 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Wed, 17 Jan 2024 14:20:55 +0100 Subject: [PATCH 15/69] :rocket: Release 1.25.0 (#8362) Co-authored-by: netroy --- CHANGELOG.md | 41 ++++++++++++++++++++++ package.json | 2 +- packages/@n8n/nodes-langchain/package.json | 2 +- packages/cli/package.json | 2 +- packages/core/package.json | 2 +- packages/design-system/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 +- 10 files changed, 50 insertions(+), 9 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index bc07f55eb2..beb70c8ae4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,44 @@ +# [1.25.0](https://github.com/n8n-io/n8n/compare/n8n@1.24.0...n8n@1.25.0) (2024-01-17) + + +### Bug Fixes + +* Add fallback resolver for langchain modules ([#8308](https://github.com/n8n-io/n8n/issues/8308)) ([851060d](https://github.com/n8n-io/n8n/commit/851060dd3f38245da6e09c04ec0b12b24b63dca4)) +* **API:** Fix manual chat trigger execution ([#8300](https://github.com/n8n-io/n8n/issues/8300)) ([884396e](https://github.com/n8n-io/n8n/commit/884396ea0d9f4a8d7987daf2b674f080056dd1d1)) +* **AwsS3 Node:** Return confirmation of success after upload ([#8312](https://github.com/n8n-io/n8n/issues/8312)) ([c921665](https://github.com/n8n-io/n8n/commit/c921665f9abe19d9e8831062c1e7673d4d1ea694)) +* **core:** Account for immediate confirmation request during test webhook creation ([#8329](https://github.com/n8n-io/n8n/issues/8329)) ([5fbd797](https://github.com/n8n-io/n8n/commit/5fbd7971e04640be3f877b3aa22d4aee61c1d40a)) +* **core:** Ensure waiting executions account for workflow timezone ([#8340](https://github.com/n8n-io/n8n/issues/8340)) ([3734c89](https://github.com/n8n-io/n8n/commit/3734c89cf64514489831b5339d722c89b300cc54)) +* **core:** Parse any readable stream response instead of only IncomingMessage ([#8359](https://github.com/n8n-io/n8n/issues/8359)) ([eb1320f](https://github.com/n8n-io/n8n/commit/eb1320fd7a4a67cd16de10c4174c7bcf2c177b06)) +* **core:** Prevent invalid compressed responses from making executions stuck forever ([#8315](https://github.com/n8n-io/n8n/issues/8315)) ([0776814](https://github.com/n8n-io/n8n/commit/0776814ed8c520326a6447dcd7b6c53fda933054)) +* **core:** Prevent issues with missing or mismatching encryption key ([#8332](https://github.com/n8n-io/n8n/issues/8332)) ([d4c93b1](https://github.com/n8n-io/n8n/commit/d4c93b16071081002b4bd316be0921bc7867dd82)) +* **core:** Prevent NodeErrors from being wrapped multiple times ([#8301](https://github.com/n8n-io/n8n/issues/8301)) ([b267bf0](https://github.com/n8n-io/n8n/commit/b267bf07e365d8bb82a9847fb3c490437dc1010e)) +* **core:** Replace all `moment` imports with `moment-timezone` ([#8337](https://github.com/n8n-io/n8n/issues/8337)) ([52a2e25](https://github.com/n8n-io/n8n/commit/52a2e25a25e9a009a536d8a371d9404e75d756f4)) +* **core:** Report when waitTill is invalid and handle it ([#8356](https://github.com/n8n-io/n8n/issues/8356)) ([d5455d7](https://github.com/n8n-io/n8n/commit/d5455d7accb193078b05a0f52386cf9303b6a00f)) +* **editor:** Add read only mode to filter component ([#8285](https://github.com/n8n-io/n8n/issues/8285)) ([dcc76f3](https://github.com/n8n-io/n8n/commit/dcc76f348075b6e05e3f38bb9694d25ac9a5646b)) +* **editor:** Capture indexed access expressions when building completions ([#8331](https://github.com/n8n-io/n8n/issues/8331)) ([159b328](https://github.com/n8n-io/n8n/commit/159b328587f3c57c73ae77c2a0c5d5c6ecc330aa)) +* **editor:** Fix issue with synchronization table on LDAP not loading data ([#8327](https://github.com/n8n-io/n8n/issues/8327)) ([6b92d49](https://github.com/n8n-io/n8n/commit/6b92d49ea58b8e5797e4e938444b161a63137638)) +* **editor:** Properly set colors for connections and labels on nodes with pinned data ([#8209](https://github.com/n8n-io/n8n/issues/8209)) ([3b8ccb9](https://github.com/n8n-io/n8n/commit/3b8ccb9fb903036a7d6e4b33f6b5a8933576e9e6)) +* Fix node graph telemetry with default values ([#8297](https://github.com/n8n-io/n8n/issues/8297)) ([93b969a](https://github.com/n8n-io/n8n/commit/93b969a327e0770d9a0e81a95a5185b0fc12ebc6)) +* **Google Drive Node:** Fix issue preventing service account from downloading files ([#7642](https://github.com/n8n-io/n8n/issues/7642)) ([cf7131d](https://github.com/n8n-io/n8n/commit/cf7131d766dfc7aec2c973525653ffec1ced03c1)) +* **HTTP Request Node:** Delete `response.request` only when it's a valid circular references ([#8293](https://github.com/n8n-io/n8n/issues/8293)) ([05c43fa](https://github.com/n8n-io/n8n/commit/05c43faa2d7582a8ce58b9bb3338c00253ad3281)) +* **Microsoft SQL Node:** Fix "Maximum call stack size exceeded" error on too many rows ([#8334](https://github.com/n8n-io/n8n/issues/8334)) ([bb2be8d](https://github.com/n8n-io/n8n/commit/bb2be8d70580896321641a49a3044165763eb9e1)) +* **Ollama Model Node:** Use a simpler credentials test ([#8318](https://github.com/n8n-io/n8n/issues/8318)) ([63b738a](https://github.com/n8n-io/n8n/commit/63b738a542429934b3838bfc814ea2a4c51675c7)) +* **OpenAI Node:** Load correct models for operation ([#8313](https://github.com/n8n-io/n8n/issues/8313)) ([a6a5372](https://github.com/n8n-io/n8n/commit/a6a5372b5f8e48e98788c4e3750ac4b63e91a96f)) +* Properly output saml validation errors ([#8284](https://github.com/n8n-io/n8n/issues/8284)) ([8c7f399](https://github.com/n8n-io/n8n/commit/8c7f39907fa82fa37af4436511d4a2daaff13015)) +* **Salesforce Node:** Upgrade to API version 59 ([#8346](https://github.com/n8n-io/n8n/issues/8346)) ([b51cbb3](https://github.com/n8n-io/n8n/commit/b51cbb325e03fd42be6dca99819d4cc7c4c1574b)) +* **Supabase Node:** Pagination for get all rows ([#8311](https://github.com/n8n-io/n8n/issues/8311)) ([e080476](https://github.com/n8n-io/n8n/commit/e0804768e84aefe9d66ab683080f67bb15a1cb58)) +* **Venafi TLS Protect Cloud Node:** Remove parameter `Application Server Type` ([#8325](https://github.com/n8n-io/n8n/issues/8325)) ([e3cedf7](https://github.com/n8n-io/n8n/commit/e3cedf7db038a70c9d48bb7c665b1be4beb872a9)) +* **Venafi TLS Protect Cloud Trigger Node:** Handle new webhook payload format ([#8326](https://github.com/n8n-io/n8n/issues/8326)) ([057d7d0](https://github.com/n8n-io/n8n/commit/057d7d031828ea8b6e779ca535ccd50d91bfa0cc)) + + +### Features + +* **core:** Implement inter-main communication for test webhooks in multi-main setup ([#8267](https://github.com/n8n-io/n8n/issues/8267)) ([1a0e285](https://github.com/n8n-io/n8n/commit/1a0e28555385f682aa335115c4d72e671c0bdc85)) +* **editor:** Add new `/templates/search` endpoint ([#8227](https://github.com/n8n-io/n8n/issues/8227)) ([4277e92](https://github.com/n8n-io/n8n/commit/4277e92ec07671a679b0d9ab6e691ef9208585bd)) +* Implement Chat Memory Manager node ([#8127](https://github.com/n8n-io/n8n/issues/8127)) ([464be93](https://github.com/n8n-io/n8n/commit/464be9332354620b2f1890136abf95dfdb71fd2e)) + + + # [1.24.0](https://github.com/n8n-io/n8n/compare/n8n@1.23.0...n8n@1.24.0) (2024-01-10) diff --git a/package.json b/package.json index 2e794cfbf2..cf8c92fe15 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "n8n", - "version": "1.24.0", + "version": "1.25.0", "private": true, "homepage": "https://n8n.io", "engines": { diff --git a/packages/@n8n/nodes-langchain/package.json b/packages/@n8n/nodes-langchain/package.json index 6dfa670242..09ba700cf0 100644 --- a/packages/@n8n/nodes-langchain/package.json +++ b/packages/@n8n/nodes-langchain/package.json @@ -1,6 +1,6 @@ { "name": "@n8n/n8n-nodes-langchain", - "version": "0.9.0", + "version": "0.10.0", "description": "", "license": "SEE LICENSE IN LICENSE.md", "homepage": "https://n8n.io", diff --git a/packages/cli/package.json b/packages/cli/package.json index 0a8b3ba08b..e49bc000e4 100644 --- a/packages/cli/package.json +++ b/packages/cli/package.json @@ -1,6 +1,6 @@ { "name": "n8n", - "version": "1.24.0", + "version": "1.25.0", "description": "n8n Workflow Automation Tool", "license": "SEE LICENSE IN LICENSE.md", "homepage": "https://n8n.io", diff --git a/packages/core/package.json b/packages/core/package.json index 5039d4cf79..6a788736a5 100644 --- a/packages/core/package.json +++ b/packages/core/package.json @@ -1,6 +1,6 @@ { "name": "n8n-core", - "version": "1.24.0", + "version": "1.25.0", "description": "Core functionality of n8n", "license": "SEE LICENSE IN LICENSE.md", "homepage": "https://n8n.io", diff --git a/packages/design-system/package.json b/packages/design-system/package.json index 9addb27a53..691d2c047f 100644 --- a/packages/design-system/package.json +++ b/packages/design-system/package.json @@ -1,6 +1,6 @@ { "name": "n8n-design-system", - "version": "1.18.0", + "version": "1.19.0", "license": "SEE LICENSE IN LICENSE.md", "homepage": "https://n8n.io", "author": { diff --git a/packages/editor-ui/package.json b/packages/editor-ui/package.json index 7ea5b2b8c4..bfcb6c4682 100644 --- a/packages/editor-ui/package.json +++ b/packages/editor-ui/package.json @@ -1,6 +1,6 @@ { "name": "n8n-editor-ui", - "version": "1.24.0", + "version": "1.25.0", "description": "Workflow Editor UI for n8n", "license": "SEE LICENSE IN LICENSE.md", "homepage": "https://n8n.io", diff --git a/packages/node-dev/package.json b/packages/node-dev/package.json index bf2a302b43..33b0ceb9c4 100644 --- a/packages/node-dev/package.json +++ b/packages/node-dev/package.json @@ -1,6 +1,6 @@ { "name": "n8n-node-dev", - "version": "1.24.0", + "version": "1.25.0", "description": "CLI to simplify n8n credentials/node development", "license": "SEE LICENSE IN LICENSE.md", "homepage": "https://n8n.io", diff --git a/packages/nodes-base/package.json b/packages/nodes-base/package.json index 5770cb7731..63ecf0f735 100644 --- a/packages/nodes-base/package.json +++ b/packages/nodes-base/package.json @@ -1,6 +1,6 @@ { "name": "n8n-nodes-base", - "version": "1.24.0", + "version": "1.25.0", "description": "Base nodes of n8n", "license": "SEE LICENSE IN LICENSE.md", "homepage": "https://n8n.io", diff --git a/packages/workflow/package.json b/packages/workflow/package.json index 0e310ca48f..458274fb4d 100644 --- a/packages/workflow/package.json +++ b/packages/workflow/package.json @@ -1,6 +1,6 @@ { "name": "n8n-workflow", - "version": "1.24.0", + "version": "1.25.0", "description": "Workflow base code of n8n", "license": "SEE LICENSE IN LICENSE.md", "homepage": "https://n8n.io", From 01280815c950413188905f5d17a13157685d0a27 Mon Sep 17 00:00:00 2001 From: Michael Kret <88898367+michael-radency@users.noreply.github.com> Date: Wed, 17 Jan 2024 16:18:49 +0200 Subject: [PATCH 16/69] fix(Microsoft Outlook Node): Message -> Send with attachments (#8238) --- .../v2/actions/message/reply.operation.ts | 26 ++++++++++++----- .../v2/actions/message/send.operation.ts | 29 ++++++++++++++----- 2 files changed, 40 insertions(+), 15 deletions(-) diff --git a/packages/nodes-base/nodes/Microsoft/Outlook/v2/actions/message/reply.operation.ts b/packages/nodes-base/nodes/Microsoft/Outlook/v2/actions/message/reply.operation.ts index d9f102b0b5..c55f89c965 100644 --- a/packages/nodes-base/nodes/Microsoft/Outlook/v2/actions/message/reply.operation.ts +++ b/packages/nodes-base/nodes/Microsoft/Outlook/v2/actions/message/reply.operation.ts @@ -1,5 +1,4 @@ import type { - IBinaryKeyData, IDataObject, IExecuteFunctions, INodeExecutionData, @@ -254,7 +253,9 @@ export async function execute(this: IExecuteFunctions, index: number, items: INo if (additionalFields.attachments) { const attachments = (additionalFields.attachments as IDataObject).attachments as IDataObject[]; // // Handle attachments - const data = attachments.map((attachment) => { + const data: IDataObject[] = []; + + for (const attachment of attachments) { const binaryPropertyName = attachment.binaryPropertyName as string; if (items[index].binary === undefined) { @@ -274,13 +275,24 @@ export async function execute(this: IExecuteFunctions, index: number, items: INo ); } - const binaryData = (items[index].binary as IBinaryKeyData)[binaryPropertyName]; - return { + const binaryData = this.helpers.assertBinaryData(index, binaryPropertyName); + + let fileBase64; + if (binaryData.id) { + const chunkSize = 256 * 1024; + const stream = await this.helpers.getBinaryStream(binaryData.id, chunkSize); + const buffer = await this.helpers.binaryToBuffer(stream); + fileBase64 = buffer.toString('base64'); + } else { + fileBase64 = binaryData.data; + } + + data.push({ '@odata.type': '#microsoft.graph.fileAttachment', name: binaryData.fileName, - contentBytes: binaryData.data, - }; - }); + contentBytes: fileBase64, + }); + } for (const attachment of data) { await microsoftApiRequest.call( diff --git a/packages/nodes-base/nodes/Microsoft/Outlook/v2/actions/message/send.operation.ts b/packages/nodes-base/nodes/Microsoft/Outlook/v2/actions/message/send.operation.ts index f8bc763317..0e45dbb8cf 100644 --- a/packages/nodes-base/nodes/Microsoft/Outlook/v2/actions/message/send.operation.ts +++ b/packages/nodes-base/nodes/Microsoft/Outlook/v2/actions/message/send.operation.ts @@ -1,5 +1,4 @@ import type { - IBinaryKeyData, IDataObject, IExecuteFunctions, INodeExecutionData, @@ -226,8 +225,9 @@ export async function execute(this: IExecuteFunctions, index: number, items: INo if (additionalFields.attachments) { const attachments = (additionalFields.attachments as IDataObject).attachments as IDataObject[]; - // // Handle attachments - message.attachments = attachments.map((attachment) => { + const messageAttachments: IDataObject[] = []; + + for (const attachment of attachments) { const binaryPropertyName = attachment.binaryPropertyName as string; if (items[index].binary === undefined) { @@ -247,13 +247,26 @@ export async function execute(this: IExecuteFunctions, index: number, items: INo ); } - const binaryData = (items[index].binary as IBinaryKeyData)[binaryPropertyName]; - return { + const binaryData = this.helpers.assertBinaryData(index, binaryPropertyName); + + let fileBase64; + if (binaryData.id) { + const chunkSize = 256 * 1024; + const stream = await this.helpers.getBinaryStream(binaryData.id, chunkSize); + const buffer = await this.helpers.binaryToBuffer(stream); + fileBase64 = buffer.toString('base64'); + } else { + fileBase64 = binaryData.data; + } + + messageAttachments.push({ '@odata.type': '#microsoft.graph.fileAttachment', name: binaryData.fileName, - contentBytes: binaryData.data, - }; - }); + contentBytes: fileBase64, + }); + } + + message.attachments = messageAttachments; } const body: IDataObject = { From 2eb829a6b48ec5963552534b5b6c9d15ce6e9dfc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Iv=C3=A1n=20Ovejero?= Date: Wed, 17 Jan 2024 15:42:19 +0100 Subject: [PATCH 17/69] refactor(core): Use DI in execution services (no-changelog) (#8358) --- packages/cli/src/ActiveWorkflowRunner.ts | 4 +- ....service.ee.ts => execution.service.ee.ts} | 35 +++--- ...utions.service.ts => execution.service.ts} | 113 +++++++----------- .../src/executions/executions.controller.ts | 36 ++++-- .../integration/ActiveWorkflowRunner.test.ts | 4 +- .../integration/executions.controller.test.ts | 3 + .../integration/publicApi/workflows.test.ts | 4 +- .../test/integration/shared/utils/index.ts | 4 +- .../cli/test/integration/users.api.test.ts | 4 +- 9 files changed, 101 insertions(+), 106 deletions(-) rename packages/cli/src/executions/{executions.service.ee.ts => execution.service.ee.ts} (51%) rename packages/cli/src/executions/{executions.service.ts => execution.service.ts} (77%) diff --git a/packages/cli/src/ActiveWorkflowRunner.ts b/packages/cli/src/ActiveWorkflowRunner.ts index e0e03ad119..1e36b4e65d 100644 --- a/packages/cli/src/ActiveWorkflowRunner.ts +++ b/packages/cli/src/ActiveWorkflowRunner.ts @@ -35,7 +35,7 @@ import * as WorkflowExecuteAdditionalData from '@/WorkflowExecuteAdditionalData' import type { WorkflowEntity } from '@db/entities/WorkflowEntity'; import { ActiveExecutions } from '@/ActiveExecutions'; -import { ExecutionsService } from './executions/executions.service'; +import { ExecutionService } from './executions/execution.service'; import { STARTING_NODES, WORKFLOW_REACTIVATE_INITIAL_TIMEOUT, @@ -74,7 +74,7 @@ export class ActiveWorkflowRunner { private readonly workflowRepository: WorkflowRepository, private readonly multiMainSetup: MultiMainSetup, private readonly activationErrorsService: ActivationErrorsService, - private readonly executionService: ExecutionsService, + private readonly executionService: ExecutionService, private readonly workflowStaticDataService: WorkflowStaticDataService, private readonly activeWorkflowsService: ActiveWorkflowsService, ) {} diff --git a/packages/cli/src/executions/executions.service.ee.ts b/packages/cli/src/executions/execution.service.ee.ts similarity index 51% rename from packages/cli/src/executions/executions.service.ee.ts rename to packages/cli/src/executions/execution.service.ee.ts index 5182f147e8..4b5ab89892 100644 --- a/packages/cli/src/executions/executions.service.ee.ts +++ b/packages/cli/src/executions/execution.service.ee.ts @@ -1,41 +1,38 @@ -import Container from 'typedi'; -import type { User } from '@db/entities/User'; -import { ExecutionsService } from './executions.service'; +import { ExecutionService } from './execution.service'; import type { ExecutionRequest } from './execution.request'; import type { IExecutionResponse, IExecutionFlattedResponse } from '@/Interfaces'; import { EnterpriseWorkflowService } from '../workflows/workflow.service.ee'; import type { WorkflowWithSharingsAndCredentials } from '@/workflows/workflows.types'; import { WorkflowRepository } from '@/databases/repositories/workflow.repository'; -import { WorkflowSharingService } from '@/workflows/workflowSharing.service'; +import { Service } from 'typedi'; -export class EnterpriseExecutionsService extends ExecutionsService { - /** - * Function to get the workflow Ids for a User regardless of role - */ - static async getWorkflowIdsForUser(user: User): Promise { - // Get all workflows - return Container.get(WorkflowSharingService).getSharedWorkflowIds(user); - } +@Service() +export class EnterpriseExecutionsService { + constructor( + private readonly executionService: ExecutionService, + private readonly workflowRepository: WorkflowRepository, + private readonly enterpriseWorkflowService: EnterpriseWorkflowService, + ) {} - static async getExecution( + async getExecution( req: ExecutionRequest.Get, + sharedWorkflowIds: string[], ): Promise { - const execution = await super.getExecution(req); + const execution = await this.executionService.getExecution(req, sharedWorkflowIds); if (!execution) return; const relations = ['shared', 'shared.user', 'shared.role']; - const workflow = (await Container.get(WorkflowRepository).get( + const workflow = (await this.workflowRepository.get( { id: execution.workflowId }, { relations }, )) as WorkflowWithSharingsAndCredentials; + if (!workflow) return; - const enterpriseWorkflowService = Container.get(EnterpriseWorkflowService); - - enterpriseWorkflowService.addOwnerAndSharings(workflow); - await enterpriseWorkflowService.addCredentialsToWorkflow(workflow, req.user); + this.enterpriseWorkflowService.addOwnerAndSharings(workflow); + await this.enterpriseWorkflowService.addCredentialsToWorkflow(workflow, req.user); execution.workflowData = { ...execution.workflowData, diff --git a/packages/cli/src/executions/executions.service.ts b/packages/cli/src/executions/execution.service.ts similarity index 77% rename from packages/cli/src/executions/executions.service.ts rename to packages/cli/src/executions/execution.service.ts index 5ae2b26abd..c818cb660e 100644 --- a/packages/cli/src/executions/executions.service.ts +++ b/packages/cli/src/executions/execution.service.ts @@ -1,4 +1,4 @@ -import { Container, Service } from 'typedi'; +import { Service } from 'typedi'; import { validate as jsonSchemaValidate } from 'jsonschema'; import type { IWorkflowBase, @@ -12,12 +12,10 @@ import { ApplicationError, jsonParse, Workflow, WorkflowOperationError } from 'n import { ActiveExecutions } from '@/ActiveExecutions'; import config from '@/config'; -import type { User } from '@db/entities/User'; import type { ExecutionPayload, IExecutionFlattedResponse, IExecutionResponse, - IExecutionsListResponse, IWorkflowDb, IWorkflowExecutionDataProcess, } from '@/Interfaces'; @@ -33,7 +31,6 @@ import { WorkflowRepository } from '@db/repositories/workflow.repository'; import { Logger } from '@/Logger'; import { InternalServerError } from '@/errors/response-errors/internal-server.error'; import { NotFoundError } from '@/errors/response-errors/not-found.error'; -import { WorkflowSharingService } from '@/workflows/workflowSharing.service'; const schemaGetExecutionsQueryFilter = { $id: '/IGetExecutionsQueryFilter', @@ -71,21 +68,18 @@ const schemaGetExecutionsQueryFilter = { const allowedExecutionsQueryFilterFields = Object.keys(schemaGetExecutionsQueryFilter.properties); @Service() -export class ExecutionsService { - /** - * Function to get the workflow Ids for a User - * Overridden in EE version to ignore roles - */ - static async getWorkflowIdsForUser(user: User): Promise { - // Get all workflows using owner role - return Container.get(WorkflowSharingService).getSharedWorkflowIds(user, ['owner']); - } +export class ExecutionService { + constructor( + private readonly logger: Logger, + private readonly queue: Queue, + private readonly activeExecutions: ActiveExecutions, + private readonly executionRepository: ExecutionRepository, + private readonly workflowRepository: WorkflowRepository, + private readonly nodeTypes: NodeTypes, + ) {} - static async getExecutionsList(req: ExecutionRequest.GetAll): Promise { - const sharedWorkflowIds = await this.getWorkflowIdsForUser(req.user); + async getExecutionsList(req: ExecutionRequest.GetAll, sharedWorkflowIds: string[]) { if (sharedWorkflowIds.length === 0) { - // return early since without shared workflows there can be no hits - // (note: getSharedWorkflowIds() returns _all_ workflow ids for global owners) return { count: 0, estimated: false, @@ -107,7 +101,7 @@ export class ExecutionsService { } } } catch (error) { - Container.get(Logger).error('Failed to parse filter', { + this.logger.error('Failed to parse filter', { userId: req.user.id, filter: req.query.filter, }); @@ -118,7 +112,7 @@ export class ExecutionsService { // safeguard against querying workflowIds not shared with the user const workflowId = filter?.workflowId?.toString(); if (workflowId !== undefined && !sharedWorkflowIds.includes(workflowId)) { - Container.get(Logger).verbose( + this.logger.verbose( `User ${req.user.id} attempted to query non-shared workflow ${workflowId}`, ); return { @@ -135,26 +129,21 @@ export class ExecutionsService { const executingWorkflowIds: string[] = []; if (config.getEnv('executions.mode') === 'queue') { - const queue = Container.get(Queue); - const currentJobs = await queue.getJobs(['active', 'waiting']); + const currentJobs = await this.queue.getJobs(['active', 'waiting']); executingWorkflowIds.push(...currentJobs.map(({ data }) => data.executionId)); } // We may have manual executions even with queue so we must account for these. - executingWorkflowIds.push( - ...Container.get(ActiveExecutions) - .getActiveExecutions() - .map(({ id }) => id), - ); + executingWorkflowIds.push(...this.activeExecutions.getActiveExecutions().map(({ id }) => id)); - const { count, estimated } = await Container.get(ExecutionRepository).countExecutions( + const { count, estimated } = await this.executionRepository.countExecutions( filter, sharedWorkflowIds, executingWorkflowIds, req.user.hasGlobalScope('workflow:list'), ); - const formattedExecutions = await Container.get(ExecutionRepository).searchExecutions( + const formattedExecutions = await this.executionRepository.searchExecutions( filter, limit, executingWorkflowIds, @@ -171,26 +160,20 @@ export class ExecutionsService { }; } - static async getExecution( + async getExecution( req: ExecutionRequest.Get, + sharedWorkflowIds: string[], ): Promise { - const sharedWorkflowIds = await this.getWorkflowIdsForUser(req.user); if (!sharedWorkflowIds.length) return undefined; const { id: executionId } = req.params; - const execution = await Container.get(ExecutionRepository).findIfShared( - executionId, - sharedWorkflowIds, - ); + const execution = await this.executionRepository.findIfShared(executionId, sharedWorkflowIds); if (!execution) { - Container.get(Logger).info( - 'Attempt to read execution was blocked due to insufficient permissions', - { - userId: req.user.id, - executionId, - }, - ); + this.logger.info('Attempt to read execution was blocked due to insufficient permissions', { + userId: req.user.id, + executionId, + }); return undefined; } @@ -201,18 +184,17 @@ export class ExecutionsService { return execution; } - static async retryExecution(req: ExecutionRequest.Retry): Promise { - const sharedWorkflowIds = await this.getWorkflowIdsForUser(req.user); + async retryExecution(req: ExecutionRequest.Retry, sharedWorkflowIds: string[]) { if (!sharedWorkflowIds.length) return false; const { id: executionId } = req.params; - const execution = (await Container.get(ExecutionRepository).findIfShared( + const execution = (await this.executionRepository.findIfShared( executionId, sharedWorkflowIds, )) as unknown as IExecutionResponse; if (!execution) { - Container.get(Logger).info( + this.logger.info( 'Attempt to retry an execution was blocked due to insufficient permissions', { userId: req.user.id, @@ -260,7 +242,7 @@ export class ExecutionsService { // Loads the currently saved workflow to execute instead of the // one saved at the time of the execution. const workflowId = execution.workflowData.id; - const workflowData = (await Container.get(WorkflowRepository).findOneBy({ + const workflowData = (await this.workflowRepository.findOneBy({ id: workflowId, })) as IWorkflowBase; @@ -272,14 +254,14 @@ export class ExecutionsService { } data.workflowData = workflowData; - const nodeTypes = Container.get(NodeTypes); + const workflowInstance = new Workflow({ id: workflowData.id, name: workflowData.name, nodes: workflowData.nodes, connections: workflowData.connections, active: false, - nodeTypes, + nodeTypes: this.nodeTypes, staticData: undefined, settings: workflowData.settings, }); @@ -289,14 +271,11 @@ export class ExecutionsService { // Find the data of the last executed node in the new workflow const node = workflowInstance.getNode(stack.node.name); if (node === null) { - Container.get(Logger).error( - 'Failed to retry an execution because a node could not be found', - { - userId: req.user.id, - executionId, - nodeName: stack.node.name, - }, - ); + this.logger.error('Failed to retry an execution because a node could not be found', { + userId: req.user.id, + executionId, + nodeName: stack.node.name, + }); throw new WorkflowOperationError( `Could not find the node "${stack.node.name}" in workflow. It probably got deleted or renamed. Without it the workflow can sadly not be retried.`, ); @@ -310,8 +289,7 @@ export class ExecutionsService { const workflowRunner = new WorkflowRunner(); const retriedExecutionId = await workflowRunner.run(data); - const executionData = - await Container.get(ActiveExecutions).getPostExecutePromise(retriedExecutionId); + const executionData = await this.activeExecutions.getPostExecutePromise(retriedExecutionId); if (!executionData) { throw new ApplicationError('The retry did not start for an unknown reason.'); @@ -320,8 +298,7 @@ export class ExecutionsService { return !!executionData.finished; } - static async deleteExecutions(req: ExecutionRequest.Delete): Promise { - const sharedWorkflowIds = await this.getWorkflowIdsForUser(req.user); + async deleteExecutions(req: ExecutionRequest.Delete, sharedWorkflowIds: string[]) { if (sharedWorkflowIds.length === 0) { // return early since without shared workflows there can be no hits // (note: getSharedWorkflowIds() returns _all_ workflow ids for global owners) @@ -342,14 +319,10 @@ export class ExecutionsService { } } - return Container.get(ExecutionRepository).deleteExecutionsByFilter( - requestFilters, - sharedWorkflowIds, - { - deleteBefore, - ids, - }, - ); + return this.executionRepository.deleteExecutionsByFilter(requestFilters, sharedWorkflowIds, { + deleteBefore, + ids, + }); } async createErrorExecution( @@ -358,7 +331,7 @@ export class ExecutionsService { workflowData: IWorkflowDb, workflow: Workflow, mode: WorkflowExecuteMode, - ): Promise { + ) { const saveDataErrorExecutionDisabled = workflowData?.settings?.saveDataErrorExecution === 'none'; @@ -420,6 +393,6 @@ export class ExecutionsService { status: 'error', }; - await Container.get(ExecutionRepository).createNewExecution(fullExecutionData); + await this.executionRepository.createNewExecution(fullExecutionData); } } diff --git a/packages/cli/src/executions/executions.controller.ts b/packages/cli/src/executions/executions.controller.ts index d068c75ec0..b4163cad04 100644 --- a/packages/cli/src/executions/executions.controller.ts +++ b/packages/cli/src/executions/executions.controller.ts @@ -1,31 +1,53 @@ import { ExecutionRequest } from './execution.request'; -import { ExecutionsService } from './executions.service'; +import { ExecutionService } from './execution.service'; import { Authorized, Get, Post, RestController } from '@/decorators'; -import { EnterpriseExecutionsService } from './executions.service.ee'; +import { EnterpriseExecutionsService } from './execution.service.ee'; import { isSharingEnabled } from '@/UserManagement/UserManagementHelper'; +import { WorkflowSharingService } from '@/workflows/workflowSharing.service'; +import type { User } from '@/databases/entities/User'; @Authorized() @RestController('/executions') export class ExecutionsController { + constructor( + private readonly executionService: ExecutionService, + private readonly enterpriseExecutionService: EnterpriseExecutionsService, + private readonly workflowSharingService: WorkflowSharingService, + ) {} + + private async getAccessibleWorkflowIds(user: User) { + return isSharingEnabled() + ? this.workflowSharingService.getSharedWorkflowIds(user) + : this.workflowSharingService.getSharedWorkflowIds(user, ['owner']); + } + @Get('/') async getExecutionsList(req: ExecutionRequest.GetAll) { - return ExecutionsService.getExecutionsList(req); + const workflowIds = await this.getAccessibleWorkflowIds(req.user); + + return this.executionService.getExecutionsList(req, workflowIds); } @Get('/:id') async getExecution(req: ExecutionRequest.Get) { + const workflowIds = await this.getAccessibleWorkflowIds(req.user); + return isSharingEnabled() - ? EnterpriseExecutionsService.getExecution(req) - : ExecutionsService.getExecution(req); + ? this.enterpriseExecutionService.getExecution(req, workflowIds) + : this.executionService.getExecution(req, workflowIds); } @Post('/:id/retry') async retryExecution(req: ExecutionRequest.Retry) { - return ExecutionsService.retryExecution(req); + const workflowIds = await this.getAccessibleWorkflowIds(req.user); + + return this.executionService.retryExecution(req, workflowIds); } @Post('/delete') async deleteExecutions(req: ExecutionRequest.Delete) { - return ExecutionsService.deleteExecutions(req); + const workflowIds = await this.getAccessibleWorkflowIds(req.user); + + return this.executionService.deleteExecutions(req, workflowIds); } } diff --git a/packages/cli/test/integration/ActiveWorkflowRunner.test.ts b/packages/cli/test/integration/ActiveWorkflowRunner.test.ts index d4f2baa5e2..c524aa082a 100644 --- a/packages/cli/test/integration/ActiveWorkflowRunner.test.ts +++ b/packages/cli/test/integration/ActiveWorkflowRunner.test.ts @@ -23,14 +23,14 @@ import { setSchedulerAsLoadedNode } from './shared/utils'; import * as testDb from './shared/testDb'; import { createOwner } from './shared/db/users'; import { createWorkflow } from './shared/db/workflows'; -import { ExecutionsService } from '@/executions/executions.service'; +import { ExecutionService } from '@/executions/execution.service'; import { WorkflowService } from '@/workflows/workflow.service'; import { ActiveWorkflowsService } from '@/services/activeWorkflows.service'; mockInstance(ActiveExecutions); mockInstance(Push); mockInstance(SecretsHelper); -mockInstance(ExecutionsService); +mockInstance(ExecutionService); mockInstance(WorkflowService); const webhookService = mockInstance(WebhookService); diff --git a/packages/cli/test/integration/executions.controller.test.ts b/packages/cli/test/integration/executions.controller.test.ts index 5d4bfe5307..35c5dd12f0 100644 --- a/packages/cli/test/integration/executions.controller.test.ts +++ b/packages/cli/test/integration/executions.controller.test.ts @@ -6,6 +6,9 @@ import { createWorkflow } from './shared/db/workflows'; import * as testDb from './shared/testDb'; import { setupTestServer } from './shared/utils'; import { mockInstance } from '../shared/mocking'; +import { EnterpriseExecutionsService } from '@/executions/execution.service.ee'; + +mockInstance(EnterpriseExecutionsService); mockInstance(Push); let testServer = setupTestServer({ endpointGroups: ['executions'] }); diff --git a/packages/cli/test/integration/publicApi/workflows.test.ts b/packages/cli/test/integration/publicApi/workflows.test.ts index 47b0c2c15f..7912a1fd14 100644 --- a/packages/cli/test/integration/publicApi/workflows.test.ts +++ b/packages/cli/test/integration/publicApi/workflows.test.ts @@ -18,7 +18,7 @@ import { createWorkflow, createWorkflowWithTrigger } from '../shared/db/workflow import { createTag } from '../shared/db/tags'; import { mockInstance } from '../../shared/mocking'; import { Push } from '@/push'; -import { ExecutionsService } from '@/executions/executions.service'; +import { ExecutionService } from '@/executions/execution.service'; let workflowOwnerRole: Role; let owner: User; @@ -31,7 +31,7 @@ const testServer = utils.setupTestServer({ endpointGroups: ['publicApi'] }); const license = testServer.license; mockInstance(Push); -mockInstance(ExecutionsService); +mockInstance(ExecutionService); beforeAll(async () => { const [globalOwnerRole, globalMemberRole, fetchedWorkflowOwnerRole] = await getAllRoles(); diff --git a/packages/cli/test/integration/shared/utils/index.ts b/packages/cli/test/integration/shared/utils/index.ts index 63fcd48621..1959a4deec 100644 --- a/packages/cli/test/integration/shared/utils/index.ts +++ b/packages/cli/test/integration/shared/utils/index.ts @@ -18,7 +18,7 @@ import { SettingsRepository } from '@db/repositories/settings.repository'; import { mockNodeTypesData } from '../../../unit/Helpers'; import { MultiMainSetup } from '@/services/orchestration/main/MultiMainSetup.ee'; import { mockInstance } from '../../../shared/mocking'; -import { ExecutionsService } from '@/executions/executions.service'; +import { ExecutionService } from '@/executions/execution.service'; export { setupTestServer } from './testServer'; @@ -32,7 +32,7 @@ export { setupTestServer } from './testServer'; export async function initActiveWorkflowRunner() { mockInstance(MultiMainSetup); - mockInstance(ExecutionsService); + mockInstance(ExecutionService); const { ActiveWorkflowRunner } = await import('@/ActiveWorkflowRunner'); const workflowRunner = Container.get(ActiveWorkflowRunner); await workflowRunner.init(); diff --git a/packages/cli/test/integration/users.api.test.ts b/packages/cli/test/integration/users.api.test.ts index 94818e034e..4b108e6753 100644 --- a/packages/cli/test/integration/users.api.test.ts +++ b/packages/cli/test/integration/users.api.test.ts @@ -18,10 +18,10 @@ import * as testDb from './shared/testDb'; import type { SuperAgentTest } from 'supertest'; import type { Role } from '@db/entities/Role'; import type { User } from '@db/entities/User'; -import { ExecutionsService } from '@/executions/executions.service'; +import { ExecutionService } from '@/executions/execution.service'; import { mockInstance } from '../shared/mocking'; -mockInstance(ExecutionsService); +mockInstance(ExecutionService); const testServer = utils.setupTestServer({ endpointGroups: ['users'], From 9a1cc568068dfcb95a9e77e5a93543a89ba69588 Mon Sep 17 00:00:00 2001 From: Tomi Turtiainen <10324676+tomi@users.noreply.github.com> Date: Wed, 17 Jan 2024 17:08:50 +0200 Subject: [PATCH 18/69] fix: Set '@typescript-eslint/return-await' rule to 'always' for node code (no-changelog) (#8363) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: कारतोफ्फेलस्क्रिप्ट™ --- .../nodes/agents/Agent/Agent.node.ts | 10 +- .../agents/ConversationalAgent/execute.ts | 2 +- .../agents/OpenAiFunctionsAgent/execute.ts | 2 +- .../agents/PlanAndExecuteAgent/execute.ts | 2 +- .../agents/Agent/agents/ReActAgent/execute.ts | 2 +- .../agents/Agent/agents/SqlAgent/execute.ts | 2 +- .../OpenAiAssistant/OpenAiAssistant.node.ts | 2 +- .../nodes/chains/ChainLLM/ChainLlm.node.ts | 2 +- .../ChainRetrievalQA/ChainRetrievalQa.node.ts | 2 +- .../V1/ChainSummarizationV1.node.ts | 2 +- .../V2/ChainSummarizationV2.node.ts | 2 +- .../MemoryChatRetriever.node.ts | 4 +- .../MemoryManager/MemoryManager.node.ts | 2 +- .../nodes/tools/ToolCode/ToolCode.node.ts | 2 +- .../VectorStoreInMemory.node.ts | 2 +- .../VectorStoreInMemoryInsert.node.ts | 2 +- .../VectorStorePinecone.node.ts | 2 +- .../VectorStorePineconeInsert.node.ts | 2 +- .../VectorStoreQdrant.node.ts | 2 +- .../VectorStoreSupabase.node.ts | 2 +- .../VectorStoreSupabaseInsert.node.ts | 2 +- .../VectorStoreZepInsert.node.ts | 2 +- .../shared/createVectorStoreNode.ts | 4 +- packages/@n8n_io/eslint-config/node.js | 7 ++ packages/cli/src/AbstractServer.ts | 2 +- packages/cli/src/ActiveExecutions.ts | 4 +- packages/cli/src/ActiveWebhooks.ts | 4 +- packages/cli/src/ActiveWorkflowRunner.ts | 4 +- packages/cli/src/CredentialsHelper.ts | 5 +- packages/cli/src/Db.ts | 2 +- .../ExternalSecrets.controller.ee.ts | 2 +- .../ExternalSecrets.service.ee.ts | 4 +- .../ExternalSecretsManager.ee.ts | 11 ++- .../src/ExternalSecrets/providers/vault.ts | 2 +- packages/cli/src/InternalHooks.ts | 68 +++++++------- packages/cli/src/Ldap/helpers.ts | 16 ++-- packages/cli/src/Ldap/ldap.controller.ts | 4 +- packages/cli/src/License.ts | 8 +- packages/cli/src/LoadNodesAndCredentials.ts | 2 +- packages/cli/src/Mfa/helpers.ts | 2 +- packages/cli/src/Mfa/mfa.service.ts | 2 +- packages/cli/src/PublicApi/index.ts | 2 +- .../credentials/credentials.service.ts | 8 +- .../v1/handlers/users/users.service.ee.ts | 2 +- .../handlers/workflows/workflows.service.ts | 12 +-- packages/cli/src/Queue.ts | 12 +-- packages/cli/src/Server.ts | 5 +- packages/cli/src/TestWebhooks.ts | 7 +- .../email/UserManagementMailer.ts | 2 +- packages/cli/src/WaitingWebhooks.ts | 2 +- packages/cli/src/WorkflowRunner.ts | 17 ++-- packages/cli/src/WorkflowRunnerProcess.ts | 8 +- packages/cli/src/auth/jwt.ts | 2 +- packages/cli/src/commands/BaseCommand.ts | 9 +- packages/cli/src/commands/executeBatch.ts | 6 +- packages/cli/src/commands/start.ts | 2 +- .../cli/src/commands/user-management/reset.ts | 2 +- packages/cli/src/commands/worker.ts | 5 +- .../controllers/activeWorkflows.controller.ts | 4 +- .../cli/src/controllers/auth.controller.ts | 4 +- .../dynamicNodeParameters.controller.ts | 8 +- .../src/controllers/invitation.controller.ts | 5 +- .../src/controllers/nodeTypes.controller.ts | 4 +- .../oauth/abstractOAuth.controller.ts | 6 +- .../controllers/orchestration.controller.ts | 6 +- .../cli/src/controllers/owner.controller.ts | 2 +- .../cli/src/controllers/tags.controller.ts | 6 +- .../cli/src/controllers/users.controller.ts | 5 +- .../workflowStatistics.controller.ts | 4 +- .../credentials/credentials.controller.ee.ts | 2 +- .../src/credentials/credentials.controller.ts | 4 +- .../src/credentials/credentials.service.ee.ts | 4 +- .../src/credentials/credentials.service.ts | 8 +- packages/cli/src/databases/dsl/Indices.ts | 7 +- packages/cli/src/databases/dsl/Table.ts | 10 +- .../1620821879465-UniqueWorkflowNames.ts | 2 +- .../common/1659888469333-AddJsonKeyPinData.ts | 11 ++- ...1726148419-RemoveWorkflowDataLoadedFlag.ts | 19 ++-- ...0580449-PurgeInvalidWorkflowConnections.ts | 11 ++- .../1681134145996-AddUserActivatedProperty.ts | 10 +- .../1681134145996-AddUserActivatedProperty.ts | 10 +- .../repositories/credentials.repository.ts | 8 +- .../repositories/execution.repository.ts | 19 ++-- .../repositories/executionData.repository.ts | 2 +- .../installedPackages.repository.ts | 2 +- .../databases/repositories/role.repository.ts | 4 +- .../sharedCredentials.repository.ts | 8 +- .../repositories/sharedWorkflow.repository.ts | 18 ++-- .../databases/repositories/tag.repository.ts | 2 +- .../databases/repositories/user.repository.ts | 10 +- .../repositories/workflow.repository.ts | 26 +++--- .../workflowHistory.repository.ts | 2 +- .../src/databases/utils/migrationHelpers.ts | 4 +- .../cli/src/decorators/registerController.ts | 3 +- .../sourceControl/sourceControl.service.ee.ts | 4 +- .../sourceControlExport.service.ee.ts | 4 +- .../sourceControlGit.service.ee.ts | 22 ++--- .../sourceControlImport.service.ee.ts | 2 +- .../variables/variables.controller.ee.ts | 2 +- .../variables/variables.service.ee.ts | 4 +- .../MessageEventBusDestination.ee.ts | 2 +- .../src/eventbus/eventBus.controller.ee.ts | 8 +- .../cli/src/eventbus/eventBus.controller.ts | 16 ++-- .../cli/src/executions/execution.service.ts | 12 ++- .../src/executions/executions.controller.ts | 14 +-- .../cli/src/license/license.controller.ts | 6 +- .../listQuery/dtos/credentials.filter.dto.ts | 2 +- .../listQuery/dtos/user.filter.dto.ts | 2 +- .../listQuery/dtos/workflow.filter.dto.ts | 2 +- packages/cli/src/posthog/index.ts | 2 +- .../security-audit/SecurityAudit.service.ts | 2 +- .../risk-reporters/CredentialsRiskReporter.ts | 2 +- .../src/services/activeWorkflows.service.ts | 2 +- .../cli/src/services/cache/cache.service.ts | 2 +- .../src/services/cache/redis.cache-manager.ts | 12 +-- .../src/services/communityPackages.service.ts | 12 +-- packages/cli/src/services/events.service.ts | 10 +- .../src/services/executionMetadata.service.ts | 2 +- packages/cli/src/services/frontend.service.ts | 2 +- packages/cli/src/services/naming.service.ts | 4 +- .../orchestration/main/MultiMainSetup.ee.ts | 2 +- .../webhook/handleCommandMessageWebhook.ts | 2 +- .../cli/src/services/ownership.service.ts | 2 +- packages/cli/src/services/password.utility.ts | 4 +- packages/cli/src/services/pruning.service.ts | 2 +- .../redis/RedisServicePubSubPublisher.ts | 2 +- packages/cli/src/services/role.service.ts | 16 ++-- packages/cli/src/services/tag.service.ts | 8 +- .../test-webhook-registrations.service.ts | 2 +- packages/cli/src/services/user.service.ts | 33 +++---- packages/cli/src/services/webhook.service.ts | 12 +-- packages/cli/src/shutdown/Shutdown.service.ts | 2 +- .../src/sso/saml/routes/saml.controller.ee.ts | 8 +- packages/cli/src/telemetry/index.ts | 10 +- .../workflowHistory.service.ee.ts | 4 +- .../workflowHistoryManager.ee.ts | 2 +- .../cli/src/workflows/workflows.controller.ts | 2 +- .../externalSecrets.api.test.ts | 4 +- .../integration/executions.controller.test.ts | 2 +- .../test/integration/ldap/ldap.api.test.ts | 2 +- .../test/integration/pruning.service.test.ts | 2 +- .../DatabaseRiskReporter.test.ts | 6 +- .../FilesystemRiskReporter.test.ts | 2 +- .../InstanceRiskReporter.test.ts | 4 +- .../security-audit/NodesRiskReporter.test.ts | 2 +- .../test/integration/security-audit/utils.ts | 2 +- .../test/integration/shared/db/credentials.ts | 8 +- .../test/integration/shared/db/executions.ts | 18 ++-- .../cli/test/integration/shared/db/roles.ts | 14 +-- .../cli/test/integration/shared/db/users.ts | 20 ++-- .../integration/shared/db/workflowHistory.ts | 15 +-- .../test/integration/shared/db/workflows.ts | 14 +-- .../integration/shared/utils/testServer.ts | 2 +- .../cli/test/integration/variables.test.ts | 4 +- .../integration/workflowHistory.api.test.ts | 22 +++-- .../workflowHistoryManager.test.ts | 2 +- packages/cli/test/teardown.ts | 4 +- .../ExternalSecretsManager.test.ts | 2 +- packages/cli/test/unit/Helpers.ts | 2 +- .../cli/test/unit/PermissionChecker.test.ts | 6 +- .../test/unit/UserManagementMailer.test.ts | 4 +- .../communityPackages.service.test.ts | 2 +- .../unit/shutdown/Shutdown.service.test.ts | 2 +- packages/core/src/ActiveWorkflows.ts | 8 +- .../core/src/BinaryData/BinaryData.service.ts | 22 +++-- .../core/src/BinaryData/FileSystem.manager.ts | 4 +- .../src/BinaryData/ObjectStore.manager.ts | 6 +- packages/core/src/BinaryData/utils.ts | 2 +- packages/core/src/NodeExecuteFunctions.ts | 91 ++++++++++--------- .../src/ObjectStore/ObjectStore.service.ee.ts | 10 +- packages/core/src/ObjectStore/utils.ts | 4 +- packages/core/src/WorkflowExecute.ts | 13 ++- .../nodes/ActionNetwork/GenericFunctions.ts | 4 +- .../nodes/Airtable/v1/GenericFunctions.ts | 2 +- .../nodes/Airtable/v2/AirtableV2.node.ts | 2 +- .../nodes/Airtable/v2/transport/index.ts | 2 +- .../nodes/ApiTemplateIo/ApiTemplateIo.node.ts | 4 +- .../nodes/ApiTemplateIo/GenericFunctions.ts | 2 +- .../nodes/Asana/GenericFunctions.ts | 2 +- .../nodes/Aws/Comprehend/GenericFunctions.ts | 2 +- .../nodes/Aws/Rekognition/GenericFunctions.ts | 2 +- .../nodes/Aws/S3/V1/GenericFunctions.ts | 2 +- .../nodes/Aws/S3/V2/GenericFunctions.ts | 2 +- .../nodes/Aws/Textract/GenericFunctions.ts | 2 +- .../BambooHr/v1/methods/credentialTest.ts | 2 +- .../nodes/Bannerbear/Bannerbear.node.ts | 2 +- .../nodes/Baserow/GenericFunctions.ts | 2 +- .../Beeminder/Beeminder.node.functions.ts | 10 +- .../nodes/Beeminder/GenericFunctions.ts | 2 +- .../nodes/Bitwarden/Bitwarden.node.ts | 4 +- .../nodes/Brevo/GenericFunctions.ts | 6 +- .../nodes/Calendly/GenericFunctions.ts | 4 +- .../nodes/Clockify/GenericFunctions.ts | 2 +- .../nodes/Cockpit/CollectionFunctions.ts | 6 +- .../nodes-base/nodes/Cockpit/FormFunctions.ts | 2 +- .../nodes/Cockpit/SingletonFunctions.ts | 4 +- .../nodes-base/nodes/Code/PythonSandbox.ts | 2 +- .../nodes/Copper/GenericFunctions.ts | 2 +- .../nodes/Cortex/GenericFunctions.ts | 2 +- .../nodes/CustomerIo/GenericFunctions.ts | 2 +- .../nodes-base/nodes/Dhl/GenericFunctions.ts | 2 +- .../test/v2/node/channel/create.test.ts | 2 +- .../v2/node/channel/deleteChannel.test.ts | 2 +- .../Discord/test/v2/node/channel/get.test.ts | 2 +- .../test/v2/node/channel/getAll.test.ts | 2 +- .../test/v2/node/channel/update.test.ts | 2 +- .../test/v2/node/member/getAll.test.ts | 2 +- .../test/v2/node/member/roleAdd.test.ts | 2 +- .../test/v2/node/member/roleRemove.test.ts | 2 +- .../v2/node/message/deleteMessage.test.ts | 2 +- .../Discord/test/v2/node/message/get.test.ts | 2 +- .../test/v2/node/message/getAll.test.ts | 2 +- .../test/v2/node/message/react.test.ts | 2 +- .../Discord/test/v2/node/message/send.test.ts | 2 +- .../test/v2/node/webhook/sendLegacy.test.ts | 2 +- .../nodes/Discord/v2/DiscordV2.node.ts | 2 +- .../nodes/Dropbox/GenericFunctions.ts | 2 +- .../nodes/Dropcontact/Dropcontact.node.ts | 2 +- .../nodes/Dropcontact/GenericFunction.ts | 2 +- .../ElasticSecurity/GenericFunctions.ts | 2 +- .../EmailReadImap/v1/EmailReadImapV1.node.ts | 6 +- .../EmailReadImap/v2/EmailReadImapV2.node.ts | 6 +- .../nodes-base/nodes/Emelia/Emelia.node.ts | 4 +- .../ExecuteCommand/ExecuteCommand.node.ts | 2 +- .../nodes/FacebookLeadAds/GenericFunctions.ts | 8 +- .../nodes/FileMaker/FileMaker.node.ts | 2 +- .../nodes/Form/v1/FormTriggerV1.node.ts | 2 +- .../nodes/Form/v2/FormTriggerV2.node.ts | 2 +- .../nodes/Freshservice/GenericFunctions.ts | 2 +- .../nodes/FreshworksCrm/FreshworksCrm.node.ts | 28 +++--- .../nodes/FreshworksCrm/GenericFunctions.ts | 2 +- .../nodes/Ghost/GenericFunctions.ts | 2 +- .../nodes/GoToWebinar/GenericFunctions.ts | 2 +- .../nodes/GoToWebinar/GoToWebinar.node.ts | 10 +- .../Analytics/v2/GoogleAnalyticsV2.node.ts | 2 +- .../test/v2/node/executeQuery.test.ts | 2 +- .../test/v2/node/insert.autoMapMode.test.ts | 2 +- .../test/v2/node/insert.manualMode.test.ts | 2 +- .../BigQuery/v2/GoogleBigQueryV2.node.ts | 2 +- .../Google/Drive/v2/GoogleDriveV2.node.ts | 2 +- .../nodes/Google/GenericFunctions.ts | 2 +- .../nodes/Google/Gmail/GenericFunctions.ts | 2 +- .../nodes/Google/Sheet/v1/GoogleSheet.ts | 4 +- .../Google/Sheet/v2/GoogleSheetsV2.node.ts | 2 +- .../Google/Sheet/v2/helpers/GoogleSheet.ts | 2 +- .../nodes/HighLevel/GenericFunctions.ts | 2 +- .../nodes/HomeAssistant/HomeAssistant.node.ts | 8 +- .../nodes/HttpRequest/GenericFunctions.ts | 8 +- .../test/binaryData/HttpRequest.test.ts | 2 +- .../HttpRequest/test/node/HttpRequest.test.ts | 2 +- .../nodes/Hubspot/V1/GenericFunctions.ts | 2 +- .../nodes/Hubspot/V2/GenericFunctions.ts | 2 +- .../nodes/ItemLists/V1/ItemListsV1.node.ts | 2 +- .../nodes/ItemLists/V2/ItemListsV2.node.ts | 2 +- .../nodes/ItemLists/V3/ItemListsV3.node.ts | 2 +- packages/nodes-base/nodes/Jira/Jira.node.ts | 2 +- .../nodes/KoBoToolbox/GenericFunctions.ts | 2 +- .../nodes/Lemlist/GenericFunctions.ts | 2 +- .../nodes/Linear/GenericFunctions.ts | 2 +- .../LocalFileTrigger/LocalFileTrigger.node.ts | 2 +- .../nodes-base/nodes/Magento/Magento2.node.ts | 11 ++- .../nodes/Mailchimp/GenericFunctions.ts | 2 +- .../nodes/Mailjet/GenericFunctions.ts | 2 +- .../nodes/Matrix/GenericFunctions.ts | 18 ++-- .../nodes/Mattermost/v1/MattermostV1.node.ts | 2 +- .../nodes/Mattermost/v1/transport/index.ts | 2 +- .../Dynamics/MicrosoftDynamicsCrm.node.ts | 26 +++--- .../Excel/test/v2/node/table/addTable.test.ts | 2 +- .../Excel/test/v2/node/table/append.test.ts | 2 +- .../test/v2/node/table/convertToRange.test.ts | 2 +- .../test/v2/node/table/deleteTable.test.ts | 2 +- .../test/v2/node/table/getColumns.test.ts | 2 +- .../Excel/test/v2/node/table/getRows.test.ts | 2 +- .../Excel/test/v2/node/table/lookup.test.ts | 2 +- .../v2/node/workbook/addWorksheet.test.ts | 2 +- .../v2/node/workbook/deleteWorkbook.test.ts | 2 +- .../test/v2/node/workbook/getAll.test.ts | 2 +- .../test/v2/node/worksheet/append.test.ts | 2 +- .../test/v2/node/worksheet/clear.test.ts | 2 +- .../v2/node/worksheet/deleteWorksheet.test.ts | 2 +- .../test/v2/node/worksheet/getAll.test.ts | 2 +- .../test/v2/node/worksheet/readRows.test.ts | 2 +- .../test/v2/node/worksheet/update.test.ts | 2 +- .../test/v2/node/worksheet/upsert.test.ts | 2 +- .../Excel/v2/MicrosoftExcelV2.node.ts | 2 +- .../test/v2/node/calendar/create.test.ts | 2 +- .../test/v2/node/calendar/delete.test.ts | 2 +- .../Outlook/test/v2/node/calendar/get.test.ts | 2 +- .../test/v2/node/calendar/getAll.test.ts | 2 +- .../test/v2/node/calendar/update.test.ts | 2 +- .../test/v2/node/contact/create.test.ts | 2 +- .../test/v2/node/contact/update.test.ts | 2 +- .../Outlook/test/v2/node/draft/create.test.ts | 2 +- .../Outlook/test/v2/node/draft/send.test.ts | 2 +- .../Outlook/test/v2/node/event/create.test.ts | 2 +- .../test/v2/node/folder/create.test.ts | 2 +- .../test/v2/node/folderMessage/getAll.test.ts | 2 +- .../Outlook/test/v2/node/message/move.test.ts | 2 +- .../test/v2/node/message/reply.test.ts | 2 +- .../Outlook/test/v2/node/message/send.test.ts | 2 +- .../Microsoft/Outlook/v1/GenericFunctions.ts | 2 +- .../Outlook/v2/MicrosoftOutlookV2.node.ts | 2 +- .../Outlook/v2/methods/listSearch.ts | 4 +- .../nodes/Microsoft/Sql/GenericFunctions.ts | 20 ++-- .../nodes/MySql/test/v1/executeQuery.test.ts | 2 +- .../nodes/MySql/v1/GenericFunctions.ts | 2 +- .../nodes-base/nodes/MySql/v1/MySqlV1.node.ts | 7 +- .../nodes-base/nodes/MySql/v2/MySqlV2.node.ts | 2 +- .../nodes/MySql/v2/transport/index.ts | 2 +- .../nodes/N8n/test/node/N8n.test.ts | 2 +- .../nodes/NocoDB/GenericFunctions.ts | 2 +- .../nodes/Postgres/v1/genericFunctions.ts | 28 +++--- .../nodes/Postgres/v2/PostgresV2.node.ts | 2 +- .../actions/database/deleteTable.operation.ts | 2 +- .../database/executeQuery.operation.ts | 2 +- .../v2/actions/database/insert.operation.ts | 2 +- .../v2/actions/database/select.operation.ts | 2 +- .../v2/actions/database/upsert.operation.ts | 2 +- .../nodes/QuickBooks/GenericFunctions.ts | 2 +- .../nodes/QuickBooks/QuickBooks.node.ts | 18 ++-- .../nodes/RabbitMQ/GenericFunctions.ts | 6 +- packages/nodes-base/nodes/Redis/utils.ts | 8 +- .../nodes/Rocketchat/GenericFunctions.ts | 2 +- .../RssFeedRead/test/node/RssFeedRead.test.ts | 2 +- .../nodes-base/nodes/Rundeck/RundeckApi.ts | 4 +- .../nodes/Salesforce/GenericFunctions.ts | 2 +- .../nodes/Schedule/ScheduleTrigger.node.ts | 22 ++--- .../nodes/Segment/GenericFunctions.ts | 2 +- .../nodes/SendGrid/GenericFunctions.ts | 2 +- .../nodes/Shopify/GenericFunctions.ts | 2 +- .../nodes/Snowflake/GenericFunctions.ts | 6 +- .../nodes/Splunk/GenericFunctions.ts | 2 +- .../nodes/Strapi/GenericFunctions.ts | 2 +- .../nodes-base/nodes/Stripe/Stripe.node.ts | 2 +- packages/nodes-base/nodes/Stripe/helpers.ts | 2 +- .../nodes/Supabase/GenericFunctions.ts | 2 +- .../SurveyMonkey/SurveyMonkeyTrigger.node.ts | 2 +- .../nodes/SyncroMSP/v1/SyncroMspV1.node.ts | 2 +- .../nodes/SyncroMSP/v1/transport/index.ts | 2 +- .../nodes/Taiga/GenericFunctions.ts | 4 +- .../nodes/TheHive/GenericFunctions.ts | 2 +- .../TheHiveProject/TheHiveProject.node.ts | 2 +- .../nodes/TheHiveProject/actions/router.ts | 2 +- .../TheHiveProject/methods/listSearch.ts | 34 ++++++- .../TheHiveProject/transport/requestApi.ts | 2 +- .../nodes-base/nodes/Todoist/v1/Service.ts | 2 +- .../nodes-base/nodes/Todoist/v2/Service.ts | 2 +- .../nodes/Trello/GenericFunctions.ts | 2 +- .../nodes/Twake/GenericFunctions.ts | 2 +- .../nodes/Twilio/GenericFunctions.ts | 2 +- .../nodes/UrlScanIo/GenericFunctions.ts | 2 +- .../Venafi/ProtectCloud/GenericFunctions.ts | 4 +- packages/nodes-base/nodes/Wait/Wait.node.ts | 12 +-- .../nodes/Webflow/GenericFunctions.ts | 2 +- .../nodes-base/nodes/Webhook/Webhook.node.ts | 4 +- .../nodes/Wekan/GenericFunctions.ts | 2 +- .../nodes/WooCommerce/GenericFunctions.ts | 2 +- .../nodes/Wufoo/GenericFunctions.ts | 2 +- .../nodes/Zendesk/GenericFunctions.ts | 2 +- .../nodes-base/nodes/Zoho/GenericFunctions.ts | 2 +- .../nodes-base/nodes/Zoho/ZohoCrm.node.ts | 54 +++++------ .../nodes-base/nodes/Zoom/GenericFunctions.ts | 2 +- packages/nodes-base/test/nodes/Helpers.ts | 4 +- packages/workflow/.eslintrc.js | 4 + packages/workflow/src/DeferredPromise.ts | 4 +- packages/workflow/src/RoutingNode.ts | 55 +++++------ packages/workflow/src/Workflow.ts | 12 ++- packages/workflow/src/utils.ts | 2 +- packages/workflow/test/Helpers.ts | 2 +- 369 files changed, 1041 insertions(+), 928 deletions(-) diff --git a/packages/@n8n/nodes-langchain/nodes/agents/Agent/Agent.node.ts b/packages/@n8n/nodes-langchain/nodes/agents/Agent/Agent.node.ts index 49fa322f5d..378fa51c55 100644 --- a/packages/@n8n/nodes-langchain/nodes/agents/Agent/Agent.node.ts +++ b/packages/@n8n/nodes-langchain/nodes/agents/Agent/Agent.node.ts @@ -252,15 +252,15 @@ export class Agent implements INodeType { const agentType = this.getNodeParameter('agent', 0, '') as string; if (agentType === 'conversationalAgent') { - return conversationalAgentExecute.call(this); + return await conversationalAgentExecute.call(this); } else if (agentType === 'openAiFunctionsAgent') { - return openAiFunctionsAgentExecute.call(this); + return await openAiFunctionsAgentExecute.call(this); } else if (agentType === 'reActAgent') { - return reActAgentAgentExecute.call(this); + return await reActAgentAgentExecute.call(this); } else if (agentType === 'sqlAgent') { - return sqlAgentAgentExecute.call(this); + return await sqlAgentAgentExecute.call(this); } else if (agentType === 'planAndExecuteAgent') { - return planAndExecuteAgentExecute.call(this); + return await planAndExecuteAgentExecute.call(this); } throw new NodeOperationError(this.getNode(), `The agent type "${agentType}" is not supported`); diff --git a/packages/@n8n/nodes-langchain/nodes/agents/Agent/agents/ConversationalAgent/execute.ts b/packages/@n8n/nodes-langchain/nodes/agents/Agent/agents/ConversationalAgent/execute.ts index abc820b325..dd0ff5c07b 100644 --- a/packages/@n8n/nodes-langchain/nodes/agents/Agent/agents/ConversationalAgent/execute.ts +++ b/packages/@n8n/nodes-langchain/nodes/agents/Agent/agents/ConversationalAgent/execute.ts @@ -102,5 +102,5 @@ export async function conversationalAgentExecute( returnData.push({ json: response }); } - return this.prepareOutputData(returnData); + return await this.prepareOutputData(returnData); } diff --git a/packages/@n8n/nodes-langchain/nodes/agents/Agent/agents/OpenAiFunctionsAgent/execute.ts b/packages/@n8n/nodes-langchain/nodes/agents/Agent/agents/OpenAiFunctionsAgent/execute.ts index e21b5bdf42..285a95f0cf 100644 --- a/packages/@n8n/nodes-langchain/nodes/agents/Agent/agents/OpenAiFunctionsAgent/execute.ts +++ b/packages/@n8n/nodes-langchain/nodes/agents/Agent/agents/OpenAiFunctionsAgent/execute.ts @@ -101,5 +101,5 @@ export async function openAiFunctionsAgentExecute( returnData.push({ json: response }); } - return this.prepareOutputData(returnData); + return await this.prepareOutputData(returnData); } diff --git a/packages/@n8n/nodes-langchain/nodes/agents/Agent/agents/PlanAndExecuteAgent/execute.ts b/packages/@n8n/nodes-langchain/nodes/agents/Agent/agents/PlanAndExecuteAgent/execute.ts index e1e031b85a..bfa5f533d2 100644 --- a/packages/@n8n/nodes-langchain/nodes/agents/Agent/agents/PlanAndExecuteAgent/execute.ts +++ b/packages/@n8n/nodes-langchain/nodes/agents/Agent/agents/PlanAndExecuteAgent/execute.ts @@ -76,5 +76,5 @@ export async function planAndExecuteAgentExecute( returnData.push({ json: response }); } - return this.prepareOutputData(returnData); + return await this.prepareOutputData(returnData); } diff --git a/packages/@n8n/nodes-langchain/nodes/agents/Agent/agents/ReActAgent/execute.ts b/packages/@n8n/nodes-langchain/nodes/agents/Agent/agents/ReActAgent/execute.ts index 0366fedf73..492272f5af 100644 --- a/packages/@n8n/nodes-langchain/nodes/agents/Agent/agents/ReActAgent/execute.ts +++ b/packages/@n8n/nodes-langchain/nodes/agents/Agent/agents/ReActAgent/execute.ts @@ -94,5 +94,5 @@ export async function reActAgentAgentExecute( returnData.push({ json: response }); } - return this.prepareOutputData(returnData); + return await this.prepareOutputData(returnData); } diff --git a/packages/@n8n/nodes-langchain/nodes/agents/Agent/agents/SqlAgent/execute.ts b/packages/@n8n/nodes-langchain/nodes/agents/Agent/agents/SqlAgent/execute.ts index faa3c4b609..b52c0d1634 100644 --- a/packages/@n8n/nodes-langchain/nodes/agents/Agent/agents/SqlAgent/execute.ts +++ b/packages/@n8n/nodes-langchain/nodes/agents/Agent/agents/SqlAgent/execute.ts @@ -101,5 +101,5 @@ export async function sqlAgentAgentExecute( returnData.push({ json: response }); } - return this.prepareOutputData(returnData); + return await this.prepareOutputData(returnData); } diff --git a/packages/@n8n/nodes-langchain/nodes/agents/OpenAiAssistant/OpenAiAssistant.node.ts b/packages/@n8n/nodes-langchain/nodes/agents/OpenAiAssistant/OpenAiAssistant.node.ts index 22234ae584..67ea020d14 100644 --- a/packages/@n8n/nodes-langchain/nodes/agents/OpenAiAssistant/OpenAiAssistant.node.ts +++ b/packages/@n8n/nodes-langchain/nodes/agents/OpenAiAssistant/OpenAiAssistant.node.ts @@ -380,6 +380,6 @@ export class OpenAiAssistant implements INodeType { returnData.push({ json: response }); } - return this.prepareOutputData(returnData); + return await this.prepareOutputData(returnData); } } diff --git a/packages/@n8n/nodes-langchain/nodes/chains/ChainLLM/ChainLlm.node.ts b/packages/@n8n/nodes-langchain/nodes/chains/ChainLLM/ChainLlm.node.ts index d181dfa1f8..d1a5363636 100644 --- a/packages/@n8n/nodes-langchain/nodes/chains/ChainLLM/ChainLlm.node.ts +++ b/packages/@n8n/nodes-langchain/nodes/chains/ChainLLM/ChainLlm.node.ts @@ -166,7 +166,7 @@ async function getChain( // If there are no output parsers, create a simple LLM chain and execute the query if (!outputParsers.length) { - return createSimpleLLMChain(context, llm, query, chatTemplate); + return await createSimpleLLMChain(context, llm, query, chatTemplate); } // If there's only one output parser, use it; otherwise, create a combined output parser diff --git a/packages/@n8n/nodes-langchain/nodes/chains/ChainRetrievalQA/ChainRetrievalQa.node.ts b/packages/@n8n/nodes-langchain/nodes/chains/ChainRetrievalQA/ChainRetrievalQa.node.ts index 4c4637fcd6..bac0a9bbf1 100644 --- a/packages/@n8n/nodes-langchain/nodes/chains/ChainRetrievalQA/ChainRetrievalQa.node.ts +++ b/packages/@n8n/nodes-langchain/nodes/chains/ChainRetrievalQA/ChainRetrievalQa.node.ts @@ -126,6 +126,6 @@ export class ChainRetrievalQa implements INodeType { const response = await chain.call({ query }); returnData.push({ json: { response } }); } - return this.prepareOutputData(returnData); + return await this.prepareOutputData(returnData); } } diff --git a/packages/@n8n/nodes-langchain/nodes/chains/ChainSummarization/V1/ChainSummarizationV1.node.ts b/packages/@n8n/nodes-langchain/nodes/chains/ChainSummarization/V1/ChainSummarizationV1.node.ts index 79347087af..f97fb4d1ec 100644 --- a/packages/@n8n/nodes-langchain/nodes/chains/ChainSummarization/V1/ChainSummarizationV1.node.ts +++ b/packages/@n8n/nodes-langchain/nodes/chains/ChainSummarization/V1/ChainSummarizationV1.node.ts @@ -258,6 +258,6 @@ export class ChainSummarizationV1 implements INodeType { returnData.push({ json: { response } }); } - return this.prepareOutputData(returnData); + return await this.prepareOutputData(returnData); } } diff --git a/packages/@n8n/nodes-langchain/nodes/chains/ChainSummarization/V2/ChainSummarizationV2.node.ts b/packages/@n8n/nodes-langchain/nodes/chains/ChainSummarization/V2/ChainSummarizationV2.node.ts index 784406b156..d8aaa992a1 100644 --- a/packages/@n8n/nodes-langchain/nodes/chains/ChainSummarization/V2/ChainSummarizationV2.node.ts +++ b/packages/@n8n/nodes-langchain/nodes/chains/ChainSummarization/V2/ChainSummarizationV2.node.ts @@ -415,6 +415,6 @@ export class ChainSummarizationV2 implements INodeType { } } - return this.prepareOutputData(returnData); + return await this.prepareOutputData(returnData); } } diff --git a/packages/@n8n/nodes-langchain/nodes/memory/MemoryChatRetriever/MemoryChatRetriever.node.ts b/packages/@n8n/nodes-langchain/nodes/memory/MemoryChatRetriever/MemoryChatRetriever.node.ts index f3adf3e92b..77d3609027 100644 --- a/packages/@n8n/nodes-langchain/nodes/memory/MemoryChatRetriever/MemoryChatRetriever.node.ts +++ b/packages/@n8n/nodes-langchain/nodes/memory/MemoryChatRetriever/MemoryChatRetriever.node.ts @@ -98,7 +98,7 @@ export class MemoryChatRetriever implements INodeType { const messages = await memory?.chatHistory.getMessages(); if (simplifyOutput && messages) { - return this.prepareOutputData(simplifyMessages(messages)); + return await this.prepareOutputData(simplifyMessages(messages)); } const serializedMessages = @@ -107,6 +107,6 @@ export class MemoryChatRetriever implements INodeType { return { json: serializedMessage as unknown as IDataObject }; }) ?? []; - return this.prepareOutputData(serializedMessages); + return await this.prepareOutputData(serializedMessages); } } diff --git a/packages/@n8n/nodes-langchain/nodes/memory/MemoryManager/MemoryManager.node.ts b/packages/@n8n/nodes-langchain/nodes/memory/MemoryManager/MemoryManager.node.ts index 8c196673b1..de84f62f2f 100644 --- a/packages/@n8n/nodes-langchain/nodes/memory/MemoryManager/MemoryManager.node.ts +++ b/packages/@n8n/nodes-langchain/nodes/memory/MemoryManager/MemoryManager.node.ts @@ -324,6 +324,6 @@ export class MemoryManager implements INodeType { result.push(...executionData); } - return this.prepareOutputData(result); + return await this.prepareOutputData(result); } } diff --git a/packages/@n8n/nodes-langchain/nodes/tools/ToolCode/ToolCode.node.ts b/packages/@n8n/nodes-langchain/nodes/tools/ToolCode/ToolCode.node.ts index b3087ad12c..945416c3c5 100644 --- a/packages/@n8n/nodes-langchain/nodes/tools/ToolCode/ToolCode.node.ts +++ b/packages/@n8n/nodes-langchain/nodes/tools/ToolCode/ToolCode.node.ts @@ -163,7 +163,7 @@ export class ToolCode implements INodeType { const runFunction = async (query: string): Promise => { const sandbox = getSandbox(query, itemIndex); - return sandbox.runCode() as Promise; + return await (sandbox.runCode() as Promise); }; return { diff --git a/packages/@n8n/nodes-langchain/nodes/vector_store/VectorStoreInMemory/VectorStoreInMemory.node.ts b/packages/@n8n/nodes-langchain/nodes/vector_store/VectorStoreInMemory/VectorStoreInMemory.node.ts index b7d58b8411..fecf8d163a 100644 --- a/packages/@n8n/nodes-langchain/nodes/vector_store/VectorStoreInMemory/VectorStoreInMemory.node.ts +++ b/packages/@n8n/nodes-langchain/nodes/vector_store/VectorStoreInMemory/VectorStoreInMemory.node.ts @@ -46,7 +46,7 @@ export const VectorStoreInMemory = createVectorStoreNode({ const memoryKey = context.getNodeParameter('memoryKey', itemIndex) as string; const vectorStoreSingleton = MemoryVectorStoreManager.getInstance(embeddings); - return vectorStoreSingleton.getVectorStore(`${workflowId}__${memoryKey}`); + return await vectorStoreSingleton.getVectorStore(`${workflowId}__${memoryKey}`); }, async populateVectorStore(context, embeddings, documents, itemIndex) { const memoryKey = context.getNodeParameter('memoryKey', itemIndex) as string; diff --git a/packages/@n8n/nodes-langchain/nodes/vector_store/VectorStoreInMemoryInsert/VectorStoreInMemoryInsert.node.ts b/packages/@n8n/nodes-langchain/nodes/vector_store/VectorStoreInMemoryInsert/VectorStoreInMemoryInsert.node.ts index 51f0a2f7a9..39f89bfce5 100644 --- a/packages/@n8n/nodes-langchain/nodes/vector_store/VectorStoreInMemoryInsert/VectorStoreInMemoryInsert.node.ts +++ b/packages/@n8n/nodes-langchain/nodes/vector_store/VectorStoreInMemoryInsert/VectorStoreInMemoryInsert.node.ts @@ -108,6 +108,6 @@ export class VectorStoreInMemoryInsert implements INodeType { clearStore, ); - return this.prepareOutputData(serializedDocuments); + return await this.prepareOutputData(serializedDocuments); } } diff --git a/packages/@n8n/nodes-langchain/nodes/vector_store/VectorStorePinecone/VectorStorePinecone.node.ts b/packages/@n8n/nodes-langchain/nodes/vector_store/VectorStorePinecone/VectorStorePinecone.node.ts index a348ffbc40..9c0c9ccbba 100644 --- a/packages/@n8n/nodes-langchain/nodes/vector_store/VectorStorePinecone/VectorStorePinecone.node.ts +++ b/packages/@n8n/nodes-langchain/nodes/vector_store/VectorStorePinecone/VectorStorePinecone.node.ts @@ -97,7 +97,7 @@ export const VectorStorePinecone = createVectorStoreNode({ filter, }; - return PineconeStore.fromExistingIndex(embeddings, config); + return await PineconeStore.fromExistingIndex(embeddings, config); }, async populateVectorStore(context, embeddings, documents, itemIndex) { const index = context.getNodeParameter('pineconeIndex', itemIndex, '', { diff --git a/packages/@n8n/nodes-langchain/nodes/vector_store/VectorStorePineconeInsert/VectorStorePineconeInsert.node.ts b/packages/@n8n/nodes-langchain/nodes/vector_store/VectorStorePineconeInsert/VectorStorePineconeInsert.node.ts index cb764c5c7b..3f89de79e1 100644 --- a/packages/@n8n/nodes-langchain/nodes/vector_store/VectorStorePineconeInsert/VectorStorePineconeInsert.node.ts +++ b/packages/@n8n/nodes-langchain/nodes/vector_store/VectorStorePineconeInsert/VectorStorePineconeInsert.node.ts @@ -134,6 +134,6 @@ export class VectorStorePineconeInsert implements INodeType { pineconeIndex, }); - return this.prepareOutputData(serializedDocuments); + return await this.prepareOutputData(serializedDocuments); } } diff --git a/packages/@n8n/nodes-langchain/nodes/vector_store/VectorStoreQdrant/VectorStoreQdrant.node.ts b/packages/@n8n/nodes-langchain/nodes/vector_store/VectorStoreQdrant/VectorStoreQdrant.node.ts index 759330539e..3a08dfdc42 100644 --- a/packages/@n8n/nodes-langchain/nodes/vector_store/VectorStoreQdrant/VectorStoreQdrant.node.ts +++ b/packages/@n8n/nodes-langchain/nodes/vector_store/VectorStoreQdrant/VectorStoreQdrant.node.ts @@ -59,7 +59,7 @@ export const VectorStoreQdrant = createVectorStoreNode({ collectionName: collection, }; - return QdrantVectorStore.fromExistingCollection(embeddings, config); + return await QdrantVectorStore.fromExistingCollection(embeddings, config); }, async populateVectorStore(context, embeddings, documents, itemIndex) { const collectionName = context.getNodeParameter('qdrantCollection', itemIndex, '', { diff --git a/packages/@n8n/nodes-langchain/nodes/vector_store/VectorStoreSupabase/VectorStoreSupabase.node.ts b/packages/@n8n/nodes-langchain/nodes/vector_store/VectorStoreSupabase/VectorStoreSupabase.node.ts index 931fc22f82..b4ceae0548 100644 --- a/packages/@n8n/nodes-langchain/nodes/vector_store/VectorStoreSupabase/VectorStoreSupabase.node.ts +++ b/packages/@n8n/nodes-langchain/nodes/vector_store/VectorStoreSupabase/VectorStoreSupabase.node.ts @@ -76,7 +76,7 @@ export const VectorStoreSupabase = createVectorStoreNode({ const credentials = await context.getCredentials('supabaseApi'); const client = createClient(credentials.host as string, credentials.serviceRole as string); - return SupabaseVectorStore.fromExistingIndex(embeddings, { + return await SupabaseVectorStore.fromExistingIndex(embeddings, { client, tableName, queryName: options.queryName ?? 'match_documents', diff --git a/packages/@n8n/nodes-langchain/nodes/vector_store/VectorStoreSupabaseInsert/VectorStoreSupabaseInsert.node.ts b/packages/@n8n/nodes-langchain/nodes/vector_store/VectorStoreSupabaseInsert/VectorStoreSupabaseInsert.node.ts index f199ecd6af..c374b083f9 100644 --- a/packages/@n8n/nodes-langchain/nodes/vector_store/VectorStoreSupabaseInsert/VectorStoreSupabaseInsert.node.ts +++ b/packages/@n8n/nodes-langchain/nodes/vector_store/VectorStoreSupabaseInsert/VectorStoreSupabaseInsert.node.ts @@ -122,6 +122,6 @@ export class VectorStoreSupabaseInsert implements INodeType { queryName, }); - return this.prepareOutputData(serializedDocuments); + return await this.prepareOutputData(serializedDocuments); } } diff --git a/packages/@n8n/nodes-langchain/nodes/vector_store/VectorStoreZepInsert/VectorStoreZepInsert.node.ts b/packages/@n8n/nodes-langchain/nodes/vector_store/VectorStoreZepInsert/VectorStoreZepInsert.node.ts index fad1692d37..36cfecf576 100644 --- a/packages/@n8n/nodes-langchain/nodes/vector_store/VectorStoreZepInsert/VectorStoreZepInsert.node.ts +++ b/packages/@n8n/nodes-langchain/nodes/vector_store/VectorStoreZepInsert/VectorStoreZepInsert.node.ts @@ -139,6 +139,6 @@ export class VectorStoreZepInsert implements INodeType { await ZepVectorStore.fromDocuments(processedDocuments, embeddings, zepConfig); - return this.prepareOutputData(serializedDocuments); + return await this.prepareOutputData(serializedDocuments); } } diff --git a/packages/@n8n/nodes-langchain/nodes/vector_store/shared/createVectorStoreNode.ts b/packages/@n8n/nodes-langchain/nodes/vector_store/shared/createVectorStoreNode.ts index 1bc49c868f..277254204d 100644 --- a/packages/@n8n/nodes-langchain/nodes/vector_store/shared/createVectorStoreNode.ts +++ b/packages/@n8n/nodes-langchain/nodes/vector_store/shared/createVectorStoreNode.ts @@ -239,7 +239,7 @@ export const createVectorStoreNode = (args: VectorStoreNodeConstructorArgs) => resultData.push(...serializedDocs); } - return this.prepareOutputData(resultData); + return await this.prepareOutputData(resultData); } if (mode === 'insert') { @@ -267,7 +267,7 @@ export const createVectorStoreNode = (args: VectorStoreNodeConstructorArgs) => } } - return this.prepareOutputData(resultData); + return await this.prepareOutputData(resultData); } throw new NodeOperationError( diff --git a/packages/@n8n_io/eslint-config/node.js b/packages/@n8n_io/eslint-config/node.js index ff4ce11ed1..6acc1e4a3e 100644 --- a/packages/@n8n_io/eslint-config/node.js +++ b/packages/@n8n_io/eslint-config/node.js @@ -8,4 +8,11 @@ module.exports = { es6: true, node: true, }, + + rules: { + /** + * https://github.com/typescript-eslint/typescript-eslint/blob/main/packages/eslint-plugin/docs/rules/return-await.md + */ + '@typescript-eslint/return-await': ['error', 'always'], + }, }; diff --git a/packages/cli/src/AbstractServer.ts b/packages/cli/src/AbstractServer.ts index 71b4c75ea9..b2773c45ea 100644 --- a/packages/cli/src/AbstractServer.ts +++ b/packages/cli/src/AbstractServer.ts @@ -208,7 +208,7 @@ export abstract class AbstractServer { // TODO UM: check if this needs validation with user management. this.app.delete( `/${this.restEndpoint}/test-webhook/:id`, - send(async (req) => testWebhooks.cancelWebhook(req.params.id)), + send(async (req) => await testWebhooks.cancelWebhook(req.params.id)), ); } diff --git a/packages/cli/src/ActiveExecutions.ts b/packages/cli/src/ActiveExecutions.ts index d9526b84c5..94d0aa80b6 100644 --- a/packages/cli/src/ActiveExecutions.ts +++ b/packages/cli/src/ActiveExecutions.ts @@ -178,7 +178,7 @@ export class ActiveExecutions { this.activeExecutions[executionId].workflowExecution!.cancel(); } - return this.getPostExecutePromise(executionId); + return await this.getPostExecutePromise(executionId); } /** @@ -197,7 +197,7 @@ export class ActiveExecutions { this.activeExecutions[executionId].postExecutePromises.push(waitPromise); - return waitPromise.promise(); + return await waitPromise.promise(); } /** diff --git a/packages/cli/src/ActiveWebhooks.ts b/packages/cli/src/ActiveWebhooks.ts index 77102eb247..d7ded74a6c 100644 --- a/packages/cli/src/ActiveWebhooks.ts +++ b/packages/cli/src/ActiveWebhooks.ts @@ -30,7 +30,7 @@ export class ActiveWebhooks implements IWebhookManager { ) {} async getWebhookMethods(path: string) { - return this.webhookService.getWebhookMethods(path); + return await this.webhookService.getWebhookMethods(path); } async findAccessControlOptions(path: string, httpMethod: IHttpRequestMethods) { @@ -120,7 +120,7 @@ export class ActiveWebhooks implements IWebhookManager { throw new NotFoundError('Could not find node to process webhook.'); } - return new Promise((resolve, reject) => { + return await new Promise((resolve, reject) => { const executionMode = 'webhook'; void WebhookHelpers.executeWebhook( workflow, diff --git a/packages/cli/src/ActiveWorkflowRunner.ts b/packages/cli/src/ActiveWorkflowRunner.ts index 1e36b4e65d..a2e082d4b3 100644 --- a/packages/cli/src/ActiveWorkflowRunner.ts +++ b/packages/cli/src/ActiveWorkflowRunner.ts @@ -89,7 +89,7 @@ export class ActiveWorkflowRunner { } async getAllWorkflowActivationErrors() { - return this.activationErrorsService.getAll(); + return await this.activationErrorsService.getAll(); } /** @@ -305,7 +305,7 @@ export class ActiveWorkflowRunner { }; const workflowRunner = new WorkflowRunner(); - return workflowRunner.run(runData, true, undefined, undefined, responsePromise); + return await workflowRunner.run(runData, true, undefined, undefined, responsePromise); } /** diff --git a/packages/cli/src/CredentialsHelper.ts b/packages/cli/src/CredentialsHelper.ts index dad6f9c3ef..df945ef4fc 100644 --- a/packages/cli/src/CredentialsHelper.ts +++ b/packages/cli/src/CredentialsHelper.ts @@ -121,7 +121,10 @@ export class CredentialsHelper extends ICredentialsHelper { if (typeof credentialType.authenticate === 'function') { // Special authentication function is defined - return credentialType.authenticate(credentials, requestOptions as IHttpRequestOptions); + return await credentialType.authenticate( + credentials, + requestOptions as IHttpRequestOptions, + ); } if (typeof credentialType.authenticate === 'object') { diff --git a/packages/cli/src/Db.ts b/packages/cli/src/Db.ts index c53419ac76..b39a8d1e6a 100644 --- a/packages/cli/src/Db.ts +++ b/packages/cli/src/Db.ts @@ -54,7 +54,7 @@ if (!inTest) { } export async function transaction(fn: (entityManager: EntityManager) => Promise): Promise { - return connection.transaction(fn); + return await connection.transaction(fn); } export function getConnectionOptions(dbType: DatabaseType): ConnectionOptions { diff --git a/packages/cli/src/ExternalSecrets/ExternalSecrets.controller.ee.ts b/packages/cli/src/ExternalSecrets/ExternalSecrets.controller.ee.ts index 0cc8a6c086..03967cf3ee 100644 --- a/packages/cli/src/ExternalSecrets/ExternalSecrets.controller.ee.ts +++ b/packages/cli/src/ExternalSecrets/ExternalSecrets.controller.ee.ts @@ -13,7 +13,7 @@ export class ExternalSecretsController { @Get('/providers') @RequireGlobalScope('externalSecretsProvider:list') async getProviders() { - return this.secretsService.getProviders(); + return await this.secretsService.getProviders(); } @Get('/providers/:provider') diff --git a/packages/cli/src/ExternalSecrets/ExternalSecrets.service.ee.ts b/packages/cli/src/ExternalSecrets/ExternalSecrets.service.ee.ts index 1087443ddb..0c378540ac 100644 --- a/packages/cli/src/ExternalSecrets/ExternalSecrets.service.ee.ts +++ b/packages/cli/src/ExternalSecrets/ExternalSecrets.service.ee.ts @@ -134,7 +134,7 @@ export class ExternalSecretsService { } const { settings } = providerAndSettings; const newData = this.unredact(data, settings.settings); - return Container.get(ExternalSecretsManager).testProviderSettings(providerName, newData); + return await Container.get(ExternalSecretsManager).testProviderSettings(providerName, newData); } async updateProvider(providerName: string) { @@ -143,6 +143,6 @@ export class ExternalSecretsService { if (!providerAndSettings) { throw new ExternalSecretsProviderNotFoundError(providerName); } - return Container.get(ExternalSecretsManager).updateProvider(providerName); + return await Container.get(ExternalSecretsManager).updateProvider(providerName); } } diff --git a/packages/cli/src/ExternalSecrets/ExternalSecretsManager.ee.ts b/packages/cli/src/ExternalSecrets/ExternalSecretsManager.ee.ts index 90f8145f1c..8688230234 100644 --- a/packages/cli/src/ExternalSecrets/ExternalSecretsManager.ee.ts +++ b/packages/cli/src/ExternalSecrets/ExternalSecretsManager.ee.ts @@ -48,10 +48,13 @@ export class ExternalSecretsManager { this.initialized = true; resolve(); this.initializingPromise = undefined; - this.updateInterval = setInterval(async () => this.updateSecrets(), updateIntervalTime()); + this.updateInterval = setInterval( + async () => await this.updateSecrets(), + updateIntervalTime(), + ); }); } - return this.initializingPromise; + return await this.initializingPromise; } } @@ -107,8 +110,8 @@ export class ExternalSecretsManager { } const providers: Array = ( await Promise.allSettled( - Object.entries(settings).map(async ([name, providerSettings]) => - this.initProvider(name, providerSettings), + Object.entries(settings).map( + async ([name, providerSettings]) => await this.initProvider(name, providerSettings), ), ) ).map((i) => (i.status === 'rejected' ? null : i.value)); diff --git a/packages/cli/src/ExternalSecrets/providers/vault.ts b/packages/cli/src/ExternalSecrets/providers/vault.ts index 6b1046402a..0bd68f7fb1 100644 --- a/packages/cli/src/ExternalSecrets/providers/vault.ts +++ b/packages/cli/src/ExternalSecrets/providers/vault.ts @@ -436,7 +436,7 @@ export class VaultProvider extends SecretsProvider { await Promise.allSettled( listResp.data.data.keys.map(async (key): Promise<[string, IDataObject] | null> => { if (key.endsWith('/')) { - return this.getKVSecrets(mountPath, kvVersion, path + key); + return await this.getKVSecrets(mountPath, kvVersion, path + key); } let secretPath = mountPath; if (kvVersion === '2') { diff --git a/packages/cli/src/InternalHooks.ts b/packages/cli/src/InternalHooks.ts index c2829fb061..a7f69d19ab 100644 --- a/packages/cli/src/InternalHooks.ts +++ b/packages/cli/src/InternalHooks.ts @@ -56,11 +56,13 @@ export class InternalHooks { eventsService: EventsService, private readonly instanceSettings: InstanceSettings, ) { - eventsService.on('telemetry.onFirstProductionWorkflowSuccess', async (metrics) => - this.onFirstProductionWorkflowSuccess(metrics), + eventsService.on( + 'telemetry.onFirstProductionWorkflowSuccess', + async (metrics) => await this.onFirstProductionWorkflowSuccess(metrics), ); - eventsService.on('telemetry.onFirstWorkflowDataLoad', async (metrics) => - this.onFirstWorkflowDataLoad(metrics), + eventsService.on( + 'telemetry.onFirstWorkflowDataLoad', + async (metrics) => await this.onFirstWorkflowDataLoad(metrics), ); } @@ -88,7 +90,7 @@ export class InternalHooks { license_tenant_id: diagnosticInfo.licenseTenantId, }; - return Promise.all([ + return await Promise.all([ this.telemetry.identify(info), this.telemetry.track('Instance started', { ...info, @@ -98,7 +100,7 @@ export class InternalHooks { } async onFrontendSettingsAPI(sessionId?: string): Promise { - return this.telemetry.track('Session started', { session_id: sessionId }); + return await this.telemetry.track('Session started', { session_id: sessionId }); } async onPersonalizationSurveySubmitted( @@ -111,7 +113,7 @@ export class InternalHooks { personalizationSurveyData[snakeCase(camelCaseKey)] = answers[camelCaseKey]; }); - return this.telemetry.track( + return await this.telemetry.track( 'User responded to personalization questions', personalizationSurveyData, ); @@ -459,7 +461,7 @@ export class InternalHooks { user_id_list: userList, }; - return this.telemetry.track('User updated workflow sharing', properties); + return await this.telemetry.track('User updated workflow sharing', properties); } async onN8nStop(): Promise { @@ -469,7 +471,7 @@ export class InternalHooks { }, 3000); }); - return Promise.race([timeoutPromise, this.telemetry.trackN8nStop()]); + return await Promise.race([timeoutPromise, this.telemetry.trackN8nStop()]); } async onUserDeletion(userDeletionData: { @@ -554,42 +556,42 @@ export class InternalHooks { user_id: string; public_api: boolean; }): Promise { - return this.telemetry.track('User retrieved user', userRetrievedData); + return await this.telemetry.track('User retrieved user', userRetrievedData); } async onUserRetrievedAllUsers(userRetrievedData: { user_id: string; public_api: boolean; }): Promise { - return this.telemetry.track('User retrieved all users', userRetrievedData); + return await this.telemetry.track('User retrieved all users', userRetrievedData); } async onUserRetrievedExecution(userRetrievedData: { user_id: string; public_api: boolean; }): Promise { - return this.telemetry.track('User retrieved execution', userRetrievedData); + return await this.telemetry.track('User retrieved execution', userRetrievedData); } async onUserRetrievedAllExecutions(userRetrievedData: { user_id: string; public_api: boolean; }): Promise { - return this.telemetry.track('User retrieved all executions', userRetrievedData); + return await this.telemetry.track('User retrieved all executions', userRetrievedData); } async onUserRetrievedWorkflow(userRetrievedData: { user_id: string; public_api: boolean; }): Promise { - return this.telemetry.track('User retrieved workflow', userRetrievedData); + return await this.telemetry.track('User retrieved workflow', userRetrievedData); } async onUserRetrievedAllWorkflows(userRetrievedData: { user_id: string; public_api: boolean; }): Promise { - return this.telemetry.track('User retrieved all workflows', userRetrievedData); + return await this.telemetry.track('User retrieved all workflows', userRetrievedData); } async onUserUpdate(userUpdateData: { user: User; fields_changed: string[] }): Promise { @@ -649,7 +651,7 @@ export class InternalHooks { message_type: 'Reset password' | 'New user invite' | 'Resend invite'; public_api: boolean; }): Promise { - return this.telemetry.track( + return await this.telemetry.track( 'Instance sent transactional email to user', userTransactionalEmailData, ); @@ -661,7 +663,7 @@ export class InternalHooks { method: string; api_version: string; }): Promise { - return this.telemetry.track('User invoked API', userInvokedApiData); + return await this.telemetry.track('User invoked API', userInvokedApiData); } async onApiKeyDeleted(apiKeyDeletedData: { user: User; public_api: boolean }): Promise { @@ -709,7 +711,7 @@ export class InternalHooks { } async onInstanceOwnerSetup(instanceOwnerSetupData: { user_id: string }): Promise { - return this.telemetry.track('Owner finished instance setup', instanceOwnerSetupData); + return await this.telemetry.track('Owner finished instance setup', instanceOwnerSetupData); } async onUserSignup( @@ -963,7 +965,7 @@ export class InternalHooks { users_synced: number; error: string; }): Promise { - return this.telemetry.track('Ldap general sync finished', data); + return await this.telemetry.track('Ldap general sync finished', data); } async onUserUpdatedLdapSettings(data: { @@ -980,15 +982,15 @@ export class InternalHooks { loginLabel: string; loginEnabled: boolean; }): Promise { - return this.telemetry.track('Ldap general sync finished', data); + return await this.telemetry.track('Ldap general sync finished', data); } async onLdapLoginSyncFailed(data: { error: string }): Promise { - return this.telemetry.track('Ldap login sync failed', data); + return await this.telemetry.track('Ldap login sync failed', data); } async userLoginFailedDueToLdapDisabled(data: { user_id: string }): Promise { - return this.telemetry.track('User login failed since ldap disabled', data); + return await this.telemetry.track('User login failed since ldap disabled', data); } /* @@ -998,7 +1000,7 @@ export class InternalHooks { user_id: string; workflow_id: string; }): Promise { - return this.telemetry.track('Workflow first prod success', data); + return await this.telemetry.track('Workflow first prod success', data); } async onFirstWorkflowDataLoad(data: { @@ -1009,7 +1011,7 @@ export class InternalHooks { credential_type?: string; credential_id?: string; }): Promise { - return this.telemetry.track('Workflow first data fetched', data); + return await this.telemetry.track('Workflow first data fetched', data); } /** @@ -1023,11 +1025,11 @@ export class InternalHooks { * Audit */ async onAuditGeneratedViaCli() { - return this.telemetry.track('Instance generated security audit via CLI command'); + return await this.telemetry.track('Instance generated security audit via CLI command'); } async onVariableCreated(createData: { variable_type: string }): Promise { - return this.telemetry.track('User created variable', createData); + return await this.telemetry.track('User created variable', createData); } async onSourceControlSettingsUpdated(data: { @@ -1036,7 +1038,7 @@ export class InternalHooks { repo_type: 'github' | 'gitlab' | 'other'; connected: boolean; }): Promise { - return this.telemetry.track('User updated source control settings', data); + return await this.telemetry.track('User updated source control settings', data); } async onSourceControlUserStartedPullUI(data: { @@ -1044,11 +1046,11 @@ export class InternalHooks { workflow_conflicts: number; cred_conflicts: number; }): Promise { - return this.telemetry.track('User started pull via UI', data); + return await this.telemetry.track('User started pull via UI', data); } async onSourceControlUserFinishedPullUI(data: { workflow_updates: number }): Promise { - return this.telemetry.track('User finished pull via UI', { + return await this.telemetry.track('User finished pull via UI', { workflow_updates: data.workflow_updates, }); } @@ -1057,7 +1059,7 @@ export class InternalHooks { workflow_updates: number; forced: boolean; }): Promise { - return this.telemetry.track('User pulled via API', data); + return await this.telemetry.track('User pulled via API', data); } async onSourceControlUserStartedPushUI(data: { @@ -1067,7 +1069,7 @@ export class InternalHooks { creds_eligible_with_conflicts: number; variables_eligible: number; }): Promise { - return this.telemetry.track('User started push via UI', data); + return await this.telemetry.track('User started push via UI', data); } async onSourceControlUserFinishedPushUI(data: { @@ -1076,7 +1078,7 @@ export class InternalHooks { creds_pushed: number; variables_pushed: number; }): Promise { - return this.telemetry.track('User finished push via UI', data); + return await this.telemetry.track('User finished push via UI', data); } async onExternalSecretsProviderSettingsSaved(saveData: { @@ -1086,6 +1088,6 @@ export class InternalHooks { is_new: boolean; error_message?: string | undefined; }): Promise { - return this.telemetry.track('User updated external secrets settings', saveData); + return await this.telemetry.track('User updated external secrets settings', saveData); } } diff --git a/packages/cli/src/Ldap/helpers.ts b/packages/cli/src/Ldap/helpers.ts index cd618600ed..17c3350895 100644 --- a/packages/cli/src/Ldap/helpers.ts +++ b/packages/cli/src/Ldap/helpers.ts @@ -51,7 +51,7 @@ export const randomPassword = (): string => { * Return the user role to be assigned to LDAP users */ export const getLdapUserRole = async (): Promise => { - return Container.get(RoleService).findGlobalMemberRole(); + return await Container.get(RoleService).findGlobalMemberRole(); }; /** @@ -101,7 +101,7 @@ export const escapeFilter = (filter: string): string => { export const getAuthIdentityByLdapId = async ( idAttributeValue: string, ): Promise => { - return Container.get(AuthIdentityRepository).findOne({ + return await Container.get(AuthIdentityRepository).findOne({ relations: ['user', 'user.globalRole'], where: { providerId: idAttributeValue, @@ -111,7 +111,7 @@ export const getAuthIdentityByLdapId = async ( }; export const getUserByEmail = async (email: string): Promise => { - return Container.get(UserRepository).findOne({ + return await Container.get(UserRepository).findOne({ where: { email }, relations: ['globalRole'], }); @@ -190,10 +190,10 @@ export const processUsers = async ( toDisableUsers: string[], ): Promise => { await Db.transaction(async (transactionManager) => { - return Promise.all([ + return await Promise.all([ ...toCreateUsers.map(async ([ldapId, user]) => { const authIdentity = AuthIdentity.create(await transactionManager.save(user), ldapId); - return transactionManager.save(authIdentity); + return await transactionManager.save(authIdentity); }), ...toUpdateUsers.map(async ([ldapId, user]) => { const authIdentity = await transactionManager.findOneBy(AuthIdentity, { @@ -240,7 +240,7 @@ export const getLdapSynchronizations = async ( perPage: number, ): Promise => { const _page = Math.abs(page); - return Container.get(AuthProviderSyncHistoryRepository).find({ + return await Container.get(AuthProviderSyncHistoryRepository).find({ where: { providerType: 'ldap' }, order: { id: 'DESC' }, take: perPage, @@ -267,7 +267,7 @@ export const getMappingAttributes = (ldapConfig: LdapConfig): string[] => { }; export const createLdapAuthIdentity = async (user: User, ldapId: string) => { - return Container.get(AuthIdentityRepository).save(AuthIdentity.create(user, ldapId)); + return await Container.get(AuthIdentityRepository).save(AuthIdentity.create(user, ldapId)); }; export const createLdapUserOnLocalDb = async (role: Role, data: Partial, ldapId: string) => { @@ -288,5 +288,5 @@ export const updateLdapUserOnLocalDb = async (identity: AuthIdentity, data: Part }; export const deleteAllLdapIdentities = async () => { - return Container.get(AuthIdentityRepository).delete({ providerType: 'ldap' }); + return await Container.get(AuthIdentityRepository).delete({ providerType: 'ldap' }); }; diff --git a/packages/cli/src/Ldap/ldap.controller.ts b/packages/cli/src/Ldap/ldap.controller.ts index 441c5c993b..a158d81d49 100644 --- a/packages/cli/src/Ldap/ldap.controller.ts +++ b/packages/cli/src/Ldap/ldap.controller.ts @@ -19,7 +19,7 @@ export class LdapController { @Get('/config') @RequireGlobalScope('ldap:manage') async getConfig() { - return this.ldapService.loadConfig(); + return await this.ldapService.loadConfig(); } @Post('/test-connection') @@ -55,7 +55,7 @@ export class LdapController { @RequireGlobalScope('ldap:sync') async getLdapSync(req: LdapConfiguration.GetSync) { const { page = '0', perPage = '20' } = req.query; - return getLdapSynchronizations(parseInt(page, 10), parseInt(perPage, 10)); + return await getLdapSynchronizations(parseInt(page, 10), parseInt(perPage, 10)); } @Post('/sync') diff --git a/packages/cli/src/License.ts b/packages/cli/src/License.ts index e10bacd77e..541be2789d 100644 --- a/packages/cli/src/License.ts +++ b/packages/cli/src/License.ts @@ -59,13 +59,13 @@ export class License { const offlineMode = !isMainInstance; const autoRenewOffset = config.getEnv('license.autoRenewOffset'); const saveCertStr = isMainInstance - ? async (value: TLicenseBlock) => this.saveCertStr(value) + ? async (value: TLicenseBlock) => await this.saveCertStr(value) : async () => {}; const onFeatureChange = isMainInstance - ? async (features: TFeatures) => this.onFeatureChange(features) + ? async (features: TFeatures) => await this.onFeatureChange(features) : async () => {}; const collectUsageMetrics = isMainInstance - ? async () => this.collectUsageMetrics() + ? async () => await this.collectUsageMetrics() : async () => []; try { @@ -78,7 +78,7 @@ export class License { autoRenewOffset, offlineMode, logger: this.logger, - loadCertStr: async () => this.loadCertStr(), + loadCertStr: async () => await this.loadCertStr(), saveCertStr, deviceFingerprint: () => this.instanceSettings.instanceId, collectUsageMetrics, diff --git a/packages/cli/src/LoadNodesAndCredentials.ts b/packages/cli/src/LoadNodesAndCredentials.ts index 7912679e1c..5af72e4765 100644 --- a/packages/cli/src/LoadNodesAndCredentials.ts +++ b/packages/cli/src/LoadNodesAndCredentials.ts @@ -182,7 +182,7 @@ export class LoadNodesAndCredentials { 'node_modules', packageName, ); - return this.runDirectoryLoader(PackageDirectoryLoader, finalNodeUnpackedPath); + return await this.runDirectoryLoader(PackageDirectoryLoader, finalNodeUnpackedPath); } async unloadPackage(packageName: string) { diff --git a/packages/cli/src/Mfa/helpers.ts b/packages/cli/src/Mfa/helpers.ts index 34d117ed5c..54ebad078b 100644 --- a/packages/cli/src/Mfa/helpers.ts +++ b/packages/cli/src/Mfa/helpers.ts @@ -8,7 +8,7 @@ export const isMfaFeatureEnabled = () => config.get(MFA_FEATURE_ENABLED); const isMfaFeatureDisabled = () => !isMfaFeatureEnabled(); const getUsersWithMfaEnabled = async () => - Container.get(UserRepository).count({ where: { mfaEnabled: true } }); + await Container.get(UserRepository).count({ where: { mfaEnabled: true } }); export const handleMfaDisable = async () => { if (isMfaFeatureDisabled()) { diff --git a/packages/cli/src/Mfa/mfa.service.ts b/packages/cli/src/Mfa/mfa.service.ts index 452a3d7071..019005054d 100644 --- a/packages/cli/src/Mfa/mfa.service.ts +++ b/packages/cli/src/Mfa/mfa.service.ts @@ -25,7 +25,7 @@ export class MfaService { secret, recoveryCodes, ); - return this.userRepository.update(userId, { + return await this.userRepository.update(userId, { mfaSecret: encryptedSecret, mfaRecoveryCodes: encryptedRecoveryCodes, }); diff --git a/packages/cli/src/PublicApi/index.ts b/packages/cli/src/PublicApi/index.ts index d4cb53b26a..212132b77e 100644 --- a/packages/cli/src/PublicApi/index.ts +++ b/packages/cli/src/PublicApi/index.ts @@ -144,7 +144,7 @@ export const loadPublicApiVersions = async ( const apiRouters = await Promise.all( versions.map(async (version) => { const openApiPath = path.join(__dirname, version, 'openapi.yml'); - return createApiRouter(version, openApiPath, __dirname, publicApiEndpoint); + return await createApiRouter(version, openApiPath, __dirname, publicApiEndpoint); }), ); diff --git a/packages/cli/src/PublicApi/v1/handlers/credentials/credentials.service.ts b/packages/cli/src/PublicApi/v1/handlers/credentials/credentials.service.ts index 398254be59..721ea6c8f1 100644 --- a/packages/cli/src/PublicApi/v1/handlers/credentials/credentials.service.ts +++ b/packages/cli/src/PublicApi/v1/handlers/credentials/credentials.service.ts @@ -14,7 +14,7 @@ import { CredentialsRepository } from '@db/repositories/credentials.repository'; import { SharedCredentialsRepository } from '@db/repositories/sharedCredentials.repository'; export async function getCredentials(credentialId: string): Promise { - return Container.get(CredentialsRepository).findOneBy({ id: credentialId }); + return await Container.get(CredentialsRepository).findOneBy({ id: credentialId }); } export async function getSharedCredentials( @@ -22,7 +22,7 @@ export async function getSharedCredentials( credentialId: string, relations?: string[], ): Promise { - return Container.get(SharedCredentialsRepository).findOne({ + return await Container.get(SharedCredentialsRepository).findOne({ where: { userId, credentialsId: credentialId, @@ -64,7 +64,7 @@ export async function saveCredential( await Container.get(ExternalHooks).run('credentials.create', [encryptedData]); - return Db.transaction(async (transactionManager) => { + return await Db.transaction(async (transactionManager) => { const savedCredential = await transactionManager.save(credential); savedCredential.data = credential.data; @@ -85,7 +85,7 @@ export async function saveCredential( export async function removeCredential(credentials: CredentialsEntity): Promise { await Container.get(ExternalHooks).run('credentials.delete', [credentials.id]); - return Container.get(CredentialsRepository).remove(credentials); + return await Container.get(CredentialsRepository).remove(credentials); } export async function encryptCredential(credential: CredentialsEntity): Promise { diff --git a/packages/cli/src/PublicApi/v1/handlers/users/users.service.ee.ts b/packages/cli/src/PublicApi/v1/handlers/users/users.service.ee.ts index 678727e693..94c3880f37 100644 --- a/packages/cli/src/PublicApi/v1/handlers/users/users.service.ee.ts +++ b/packages/cli/src/PublicApi/v1/handlers/users/users.service.ee.ts @@ -15,7 +15,7 @@ export async function getUser(data: { withIdentifier: string; includeRole?: boolean; }): Promise { - return Container.get(UserRepository).findOne({ + return await Container.get(UserRepository).findOne({ where: { ...(uuidValidate(data.withIdentifier) && { id: data.withIdentifier }), ...(!uuidValidate(data.withIdentifier) && { email: data.withIdentifier }), diff --git a/packages/cli/src/PublicApi/v1/handlers/workflows/workflows.service.ts b/packages/cli/src/PublicApi/v1/handlers/workflows/workflows.service.ts index 607aeb31d6..423c0f4651 100644 --- a/packages/cli/src/PublicApi/v1/handlers/workflows/workflows.service.ts +++ b/packages/cli/src/PublicApi/v1/handlers/workflows/workflows.service.ts @@ -25,7 +25,7 @@ export async function getSharedWorkflow( user: User, workflowId?: string | undefined, ): Promise { - return Container.get(SharedWorkflowRepository).findOne({ + return await Container.get(SharedWorkflowRepository).findOne({ where: { ...(!['owner', 'admin'].includes(user.globalRole.name) && { userId: user.id }), ...(workflowId && { workflowId }), @@ -35,7 +35,7 @@ export async function getSharedWorkflow( } export async function getWorkflowById(id: string): Promise { - return Container.get(WorkflowRepository).findOne({ + return await Container.get(WorkflowRepository).findOne({ where: { id }, }); } @@ -45,7 +45,7 @@ export async function createWorkflow( user: User, role: Role, ): Promise { - return Db.transaction(async (transactionManager) => { + return await Db.transaction(async (transactionManager) => { const newWorkflow = new WorkflowEntity(); Object.assign(newWorkflow, workflow); const savedWorkflow = await transactionManager.save(newWorkflow); @@ -70,18 +70,18 @@ export async function setWorkflowAsActive(workflow: WorkflowEntity) { } export async function setWorkflowAsInactive(workflow: WorkflowEntity) { - return Container.get(WorkflowRepository).update(workflow.id, { + return await Container.get(WorkflowRepository).update(workflow.id, { active: false, updatedAt: new Date(), }); } export async function deleteWorkflow(workflow: WorkflowEntity): Promise { - return Container.get(WorkflowRepository).remove(workflow); + return await Container.get(WorkflowRepository).remove(workflow); } export async function updateWorkflow(workflowId: string, updateData: WorkflowEntity) { - return Container.get(WorkflowRepository).update(workflowId, updateData); + return await Container.get(WorkflowRepository).update(workflowId, updateData); } export function parseTagNames(tags: string): string[] { diff --git a/packages/cli/src/Queue.ts b/packages/cli/src/Queue.ts index 88cad41488..e524aed049 100644 --- a/packages/cli/src/Queue.ts +++ b/packages/cli/src/Queue.ts @@ -74,27 +74,27 @@ export class Queue { } async add(jobData: JobData, jobOptions: object): Promise { - return this.jobQueue.add(jobData, jobOptions); + return await this.jobQueue.add(jobData, jobOptions); } async getJob(jobId: JobId): Promise { - return this.jobQueue.getJob(jobId); + return await this.jobQueue.getJob(jobId); } async getJobs(jobTypes: Bull.JobStatus[]): Promise { - return this.jobQueue.getJobs(jobTypes); + return await this.jobQueue.getJobs(jobTypes); } async process(concurrency: number, fn: Bull.ProcessCallbackFunction): Promise { - return this.jobQueue.process(concurrency, fn); + return await this.jobQueue.process(concurrency, fn); } async ping(): Promise { - return this.jobQueue.client.ping(); + return await this.jobQueue.client.ping(); } async pause(isLocal?: boolean): Promise { - return this.jobQueue.pause(isLocal); + return await this.jobQueue.pause(isLocal); } getBullObjectInstance(): JobQueue { diff --git a/packages/cli/src/Server.ts b/packages/cli/src/Server.ts index 0ea596179f..9ddaab99e6 100644 --- a/packages/cli/src/Server.ts +++ b/packages/cli/src/Server.ts @@ -209,8 +209,9 @@ export class Server extends AbstractServer { order: { createdAt: 'ASC' }, where: {}, }) - .then(async (workflow) => - Container.get(InternalHooks).onServerStarted(diagnosticInfo, workflow?.createdAt), + .then( + async (workflow) => + await Container.get(InternalHooks).onServerStarted(diagnosticInfo, workflow?.createdAt), ); Container.get(CollaborationService); diff --git a/packages/cli/src/TestWebhooks.ts b/packages/cli/src/TestWebhooks.ts index e46be34dde..bf3efaea13 100644 --- a/packages/cli/src/TestWebhooks.ts +++ b/packages/cli/src/TestWebhooks.ts @@ -101,7 +101,7 @@ export class TestWebhooks implements IWebhookManager { throw new NotFoundError('Could not find node to process webhook.'); } - return new Promise(async (resolve, reject) => { + return await new Promise(async (resolve, reject) => { try { const executionMode = 'manual'; const executionId = await WebhookHelpers.executeWebhook( @@ -229,7 +229,10 @@ export class TestWebhooks implements IWebhookManager { return false; // no webhooks found to start a workflow } - const timeout = setTimeout(async () => this.cancelWebhook(workflow.id), TEST_WEBHOOK_TIMEOUT); + const timeout = setTimeout( + async () => await this.cancelWebhook(workflow.id), + TEST_WEBHOOK_TIMEOUT, + ); for (const webhook of webhooks) { const key = this.registrations.toKey(webhook); diff --git a/packages/cli/src/UserManagement/email/UserManagementMailer.ts b/packages/cli/src/UserManagement/email/UserManagementMailer.ts index 9aa8efa314..6963f80983 100644 --- a/packages/cli/src/UserManagement/email/UserManagementMailer.ts +++ b/packages/cli/src/UserManagement/email/UserManagementMailer.ts @@ -53,7 +53,7 @@ export class UserManagementMailer { async verifyConnection(): Promise { if (!this.mailer) throw new ApplicationError('No mailer configured.'); - return this.mailer.verifyConnection(); + return await this.mailer.verifyConnection(); } async invite(inviteEmailData: InviteEmailData): Promise { diff --git a/packages/cli/src/WaitingWebhooks.ts b/packages/cli/src/WaitingWebhooks.ts index 223fc92820..c745644dbe 100644 --- a/packages/cli/src/WaitingWebhooks.ts +++ b/packages/cli/src/WaitingWebhooks.ts @@ -122,7 +122,7 @@ export class WaitingWebhooks implements IWebhookManager { const runExecutionData = execution.data; - return new Promise((resolve, reject) => { + return await new Promise((resolve, reject) => { const executionMode = 'webhook'; void WebhookHelpers.executeWebhook( workflow, diff --git a/packages/cli/src/WorkflowRunner.ts b/packages/cli/src/WorkflowRunner.ts index 339db979e2..00d15cbad9 100644 --- a/packages/cli/src/WorkflowRunner.ts +++ b/packages/cli/src/WorkflowRunner.ts @@ -417,14 +417,15 @@ export class WorkflowRunner { fullRunData.status = this.activeExecutions.getStatus(executionId); this.activeExecutions.remove(executionId, fullRunData); }) - .catch(async (error) => - this.processError( - error, - new Date(), - data.executionMode, - executionId, - additionalData.hooks, - ), + .catch( + async (error) => + await this.processError( + error, + new Date(), + data.executionMode, + executionId, + additionalData.hooks, + ), ); } catch (error) { await this.processError( diff --git a/packages/cli/src/WorkflowRunnerProcess.ts b/packages/cli/src/WorkflowRunnerProcess.ts index 199a418416..7ea03f5d7f 100644 --- a/packages/cli/src/WorkflowRunnerProcess.ts +++ b/packages/cli/src/WorkflowRunnerProcess.ts @@ -276,7 +276,7 @@ class WorkflowRunnerProcess { this.data.executionMode, this.data.executionData, ); - return this.workflowExecute.processRunExecutionData(this.workflow); + return await this.workflowExecute.processRunExecutionData(this.workflow); } if ( this.data.runData === undefined || @@ -289,7 +289,7 @@ class WorkflowRunnerProcess { // Can execute without webhook so go on this.workflowExecute = new WorkflowExecute(additionalData, this.data.executionMode); - return this.workflowExecute.run( + return await this.workflowExecute.run( this.workflow, startNode, this.data.destinationNode, @@ -298,7 +298,7 @@ class WorkflowRunnerProcess { } // Execute only the nodes between start and destination nodes this.workflowExecute = new WorkflowExecute(additionalData, this.data.executionMode); - return this.workflowExecute.runPartialWorkflow( + return await this.workflowExecute.runPartialWorkflow( this.workflow, this.data.runData, this.data.startNodes, @@ -383,7 +383,7 @@ class WorkflowRunnerProcess { * @param {*} data The data */ async function sendToParentProcess(type: string, data: any): Promise { - return new Promise((resolve, reject) => { + return await new Promise((resolve, reject) => { process.send!( { type, diff --git a/packages/cli/src/auth/jwt.ts b/packages/cli/src/auth/jwt.ts index bbb6386742..c79479008d 100644 --- a/packages/cli/src/auth/jwt.ts +++ b/packages/cli/src/auth/jwt.ts @@ -80,7 +80,7 @@ export async function resolveJwt(token: string): Promise { const jwtPayload: JwtPayload = Container.get(JwtService).verify(token, { algorithms: ['HS256'], }); - return resolveJwtContent(jwtPayload); + return await resolveJwtContent(jwtPayload); } export async function issueCookie(res: Response, user: User): Promise { diff --git a/packages/cli/src/commands/BaseCommand.ts b/packages/cli/src/commands/BaseCommand.ts index ff664505fd..b7456a69b5 100644 --- a/packages/cli/src/commands/BaseCommand.ts +++ b/packages/cli/src/commands/BaseCommand.ts @@ -59,8 +59,8 @@ export abstract class BaseCommand extends Command { this.nodeTypes = Container.get(NodeTypes); await Container.get(LoadNodesAndCredentials).init(); - await Db.init().catch(async (error: Error) => - this.exitWithCrash('There was an error initializing DB', error), + await Db.init().catch( + async (error: Error) => await this.exitWithCrash('There was an error initializing DB', error), ); // This needs to happen after DB.init() or otherwise DB Connection is not @@ -71,8 +71,9 @@ export abstract class BaseCommand extends Command { await this.server?.init(); - await Db.migrate().catch(async (error: Error) => - this.exitWithCrash('There was an error running database migrations', error), + await Db.migrate().catch( + async (error: Error) => + await this.exitWithCrash('There was an error running database migrations', error), ); const dbType = config.getEnv('database.type'); diff --git a/packages/cli/src/commands/executeBatch.ts b/packages/cli/src/commands/executeBatch.ts index e931ce8e99..921fd49623 100644 --- a/packages/cli/src/commands/executeBatch.ts +++ b/packages/cli/src/commands/executeBatch.ts @@ -120,7 +120,7 @@ export class ExecuteBatch extends BaseCommand { const activeExecutionsInstance = Container.get(ActiveExecutions); const stopPromises = activeExecutionsInstance .getActiveExecutions() - .map(async (execution) => activeExecutionsInstance.stopExecution(execution.id)); + .map(async (execution) => await activeExecutionsInstance.stopExecution(execution.id)); await Promise.allSettled(stopPromises); @@ -410,7 +410,7 @@ export class ExecuteBatch extends BaseCommand { this.initializeLogs(); } - return new Promise(async (res) => { + return await new Promise(async (res) => { const promisesArray = []; for (let i = 0; i < ExecuteBatch.concurrency; i++) { const promise = new Promise(async (resolve) => { @@ -623,7 +623,7 @@ export class ExecuteBatch extends BaseCommand { } }); - return new Promise(async (resolve) => { + return await new Promise(async (resolve) => { let gotCancel = false; // Timeouts execution after 5 minutes. diff --git a/packages/cli/src/commands/start.ts b/packages/cli/src/commands/start.ts index b9c43a9a69..f32d3dabbd 100644 --- a/packages/cli/src/commands/start.ts +++ b/packages/cli/src/commands/start.ts @@ -174,7 +174,7 @@ export class Start extends BaseCommand { ); } streams.push(createWriteStream(destFile, 'utf-8')); - return pipeline(streams); + return await pipeline(streams); } }; diff --git a/packages/cli/src/commands/user-management/reset.ts b/packages/cli/src/commands/user-management/reset.ts index caa46ecc90..ef4904c28d 100644 --- a/packages/cli/src/commands/user-management/reset.ts +++ b/packages/cli/src/commands/user-management/reset.ts @@ -71,7 +71,7 @@ export class Reset extends BaseCommand { await Container.get(UserRepository).save(user); - return Container.get(UserRepository).findOneByOrFail({ globalRoleId: globalRole.id }); + return await Container.get(UserRepository).findOneByOrFail({ globalRoleId: globalRole.id }); } async catch(error: Error): Promise { diff --git a/packages/cli/src/commands/worker.ts b/packages/cli/src/commands/worker.ts index 2988108855..46c20c6bbc 100644 --- a/packages/cli/src/commands/worker.ts +++ b/packages/cli/src/commands/worker.ts @@ -345,8 +345,9 @@ export class Worker extends BaseCommand { Worker.jobQueue = Container.get(Queue); await Worker.jobQueue.init(); this.logger.debug('Queue singleton ready'); - void Worker.jobQueue.process(flags.concurrency, async (job) => - this.runJob(job, this.nodeTypes), + void Worker.jobQueue.process( + flags.concurrency, + async (job) => await this.runJob(job, this.nodeTypes), ); Worker.jobQueue.getBullObjectInstance().on('global:progress', (jobId: JobId, progress) => { diff --git a/packages/cli/src/controllers/activeWorkflows.controller.ts b/packages/cli/src/controllers/activeWorkflows.controller.ts index 6e37b40b86..b86c2c4bad 100644 --- a/packages/cli/src/controllers/activeWorkflows.controller.ts +++ b/packages/cli/src/controllers/activeWorkflows.controller.ts @@ -9,7 +9,7 @@ export class ActiveWorkflowsController { @Get('/') async getActiveWorkflows(req: ActiveWorkflowRequest.GetAllActive) { - return this.activeWorkflowsService.getAllActiveIdsFor(req.user); + return await this.activeWorkflowsService.getAllActiveIdsFor(req.user); } @Get('/error/:id') @@ -18,6 +18,6 @@ export class ActiveWorkflowsController { user, params: { id: workflowId }, } = req; - return this.activeWorkflowsService.getActivationError(workflowId, user); + return await this.activeWorkflowsService.getActivationError(workflowId, user); } } diff --git a/packages/cli/src/controllers/auth.controller.ts b/packages/cli/src/controllers/auth.controller.ts index 35bdacd16f..48d0e777e9 100644 --- a/packages/cli/src/controllers/auth.controller.ts +++ b/packages/cli/src/controllers/auth.controller.ts @@ -102,7 +102,7 @@ export class AuthController { authenticationMethod: usedAuthenticationMethod, }); - return this.userService.toPublic(user, { posthog: this.postHog, withScopes: true }); + return await this.userService.toPublic(user, { posthog: this.postHog, withScopes: true }); } void this.internalHooks.onUserLoginFailed({ user: email, @@ -150,7 +150,7 @@ export class AuthController { } await issueCookie(res, user); - return this.userService.toPublic(user, { posthog: this.postHog, withScopes: true }); + return await this.userService.toPublic(user, { posthog: this.postHog, withScopes: true }); } /** diff --git a/packages/cli/src/controllers/dynamicNodeParameters.controller.ts b/packages/cli/src/controllers/dynamicNodeParameters.controller.ts index d18799341b..8a3fba26bb 100644 --- a/packages/cli/src/controllers/dynamicNodeParameters.controller.ts +++ b/packages/cli/src/controllers/dynamicNodeParameters.controller.ts @@ -57,7 +57,7 @@ export class DynamicNodeParametersController { const additionalData = await getBase(req.user.id, currentNodeParameters); if (methodName) { - return this.service.getOptionsViaMethodName( + return await this.service.getOptionsViaMethodName( methodName, path, additionalData, @@ -68,7 +68,7 @@ export class DynamicNodeParametersController { } if (loadOptions) { - return this.service.getOptionsViaLoadOptions( + return await this.service.getOptionsViaLoadOptions( jsonParse(loadOptions), additionalData, nodeTypeAndVersion, @@ -87,7 +87,7 @@ export class DynamicNodeParametersController { const { path, methodName, filter, paginationToken } = req.query; const { credentials, currentNodeParameters, nodeTypeAndVersion } = req.params; const additionalData = await getBase(req.user.id, currentNodeParameters); - return this.service.getResourceLocatorResults( + return await this.service.getResourceLocatorResults( methodName, path, additionalData, @@ -106,7 +106,7 @@ export class DynamicNodeParametersController { const { path, methodName } = req.query; const { credentials, currentNodeParameters, nodeTypeAndVersion } = req.params; const additionalData = await getBase(req.user.id, currentNodeParameters); - return this.service.getResourceMappingFields( + return await this.service.getResourceMappingFields( methodName, path, additionalData, diff --git a/packages/cli/src/controllers/invitation.controller.ts b/packages/cli/src/controllers/invitation.controller.ts index 379a993021..192de142f1 100644 --- a/packages/cli/src/controllers/invitation.controller.ts +++ b/packages/cli/src/controllers/invitation.controller.ts @@ -177,6 +177,9 @@ export class InvitationController { await this.externalHooks.run('user.profile.update', [invitee.email, publicInvitee]); await this.externalHooks.run('user.password.update', [invitee.email, invitee.password]); - return this.userService.toPublic(updatedUser, { posthog: this.postHog, withScopes: true }); + return await this.userService.toPublic(updatedUser, { + posthog: this.postHog, + withScopes: true, + }); } } diff --git a/packages/cli/src/controllers/nodeTypes.controller.ts b/packages/cli/src/controllers/nodeTypes.controller.ts index 507c7a3c20..ad0b20efbc 100644 --- a/packages/cli/src/controllers/nodeTypes.controller.ts +++ b/packages/cli/src/controllers/nodeTypes.controller.ts @@ -50,8 +50,8 @@ export class NodeTypesController { const nodeTypes: INodeTypeDescription[] = []; - const promises = nodeInfos.map(async ({ name, version }) => - populateTranslation(name, version, nodeTypes), + const promises = nodeInfos.map( + async ({ name, version }) => await populateTranslation(name, version, nodeTypes), ); await Promise.all(promises); diff --git a/packages/cli/src/controllers/oauth/abstractOAuth.controller.ts b/packages/cli/src/controllers/oauth/abstractOAuth.controller.ts index c2e9ded77e..518238db71 100644 --- a/packages/cli/src/controllers/oauth/abstractOAuth.controller.ts +++ b/packages/cli/src/controllers/oauth/abstractOAuth.controller.ts @@ -61,14 +61,14 @@ export abstract class AbstractOAuthController { } protected async getAdditionalData(user: User) { - return WorkflowExecuteAdditionalData.getBase(user.id); + return await WorkflowExecuteAdditionalData.getBase(user.id); } protected async getDecryptedData( credential: ICredentialsDb, additionalData: IWorkflowExecuteAdditionalData, ) { - return this.credentialsHelper.getDecrypted( + return await this.credentialsHelper.getDecrypted( additionalData, credential, credential.type, @@ -105,6 +105,6 @@ export abstract class AbstractOAuthController { /** Get a credential without user check */ protected async getCredentialWithoutUser(credentialId: string): Promise { - return this.credentialsRepository.findOneBy({ id: credentialId }); + return await this.credentialsRepository.findOneBy({ id: credentialId }); } } diff --git a/packages/cli/src/controllers/orchestration.controller.ts b/packages/cli/src/controllers/orchestration.controller.ts index c1c03696fc..77187fe84b 100644 --- a/packages/cli/src/controllers/orchestration.controller.ts +++ b/packages/cli/src/controllers/orchestration.controller.ts @@ -20,20 +20,20 @@ export class OrchestrationController { async getWorkersStatus(req: OrchestrationRequest.Get) { if (!this.licenseService.isWorkerViewLicensed()) return; const id = req.params.id; - return this.singleMainSetup.getWorkerStatus(id); + return await this.singleMainSetup.getWorkerStatus(id); } @RequireGlobalScope('orchestration:read') @Post('/worker/status') async getWorkersStatusAll() { if (!this.licenseService.isWorkerViewLicensed()) return; - return this.singleMainSetup.getWorkerStatus(); + return await this.singleMainSetup.getWorkerStatus(); } @RequireGlobalScope('orchestration:list') @Post('/worker/ids') async getWorkerIdsAll() { if (!this.licenseService.isWorkerViewLicensed()) return; - return this.singleMainSetup.getWorkerIds(); + return await this.singleMainSetup.getWorkerIds(); } } diff --git a/packages/cli/src/controllers/owner.controller.ts b/packages/cli/src/controllers/owner.controller.ts index 2caa1e0616..24e3874138 100644 --- a/packages/cli/src/controllers/owner.controller.ts +++ b/packages/cli/src/controllers/owner.controller.ts @@ -104,7 +104,7 @@ export class OwnerController { void this.internalHooks.onInstanceOwnerSetup({ user_id: userId }); - return this.userService.toPublic(owner, { posthog: this.postHog, withScopes: true }); + return await this.userService.toPublic(owner, { posthog: this.postHog, withScopes: true }); } @Post('/dismiss-banner') diff --git a/packages/cli/src/controllers/tags.controller.ts b/packages/cli/src/controllers/tags.controller.ts index 43c06c83e2..a50cb25dcd 100644 --- a/packages/cli/src/controllers/tags.controller.ts +++ b/packages/cli/src/controllers/tags.controller.ts @@ -32,7 +32,7 @@ export class TagsController { @Get('/') @RequireGlobalScope('tag:list') async getAll(req: TagsRequest.GetAll) { - return this.tagService.getAll({ withUsageCount: req.query.withUsageCount === 'true' }); + return await this.tagService.getAll({ withUsageCount: req.query.withUsageCount === 'true' }); } @Post('/') @@ -40,7 +40,7 @@ export class TagsController { async createTag(req: TagsRequest.Create) { const tag = this.tagService.toEntity({ name: req.body.name }); - return this.tagService.save(tag, 'create'); + return await this.tagService.save(tag, 'create'); } @Patch('/:id(\\w+)') @@ -48,7 +48,7 @@ export class TagsController { async updateTag(req: TagsRequest.Update) { const newTag = this.tagService.toEntity({ id: req.params.id, name: req.body.name.trim() }); - return this.tagService.save(newTag, 'update'); + return await this.tagService.save(newTag, 'update'); } @Delete('/:id(\\w+)') diff --git a/packages/cli/src/controllers/users.controller.ts b/packages/cli/src/controllers/users.controller.ts index cd47a7fde6..0918c5a362 100644 --- a/packages/cli/src/controllers/users.controller.ts +++ b/packages/cli/src/controllers/users.controller.ts @@ -102,8 +102,9 @@ export class UsersController { const users = await this.userRepository.find(findManyOptions); const publicUsers: Array> = await Promise.all( - users.map(async (u) => - this.userService.toPublic(u, { withInviteUrl: true, inviterId: req.user.id }), + users.map( + async (u) => + await this.userService.toPublic(u, { withInviteUrl: true, inviterId: req.user.id }), ), ); diff --git a/packages/cli/src/controllers/workflowStatistics.controller.ts b/packages/cli/src/controllers/workflowStatistics.controller.ts index 1fa97db2d5..b36661f222 100644 --- a/packages/cli/src/controllers/workflowStatistics.controller.ts +++ b/packages/cli/src/controllers/workflowStatistics.controller.ts @@ -49,12 +49,12 @@ export class WorkflowStatisticsController { @Get('/:id/counts/') async getCounts(req: ExecutionRequest.Get): Promise> { - return this.getData(req.params.id, 'count', 0); + return await this.getData(req.params.id, 'count', 0); } @Get('/:id/times/') async getTimes(req: ExecutionRequest.Get): Promise> { - return this.getData(req.params.id, 'latestEvent', null); + return await this.getData(req.params.id, 'latestEvent', null); } @Get('/:id/data-loaded/') diff --git a/packages/cli/src/credentials/credentials.controller.ee.ts b/packages/cli/src/credentials/credentials.controller.ee.ts index eb4fd7ee4a..25045c9aa5 100644 --- a/packages/cli/src/credentials/credentials.controller.ee.ts +++ b/packages/cli/src/credentials/credentials.controller.ee.ts @@ -102,7 +102,7 @@ EECredentialsController.post( mergedCredentials.data = EECredentials.unredact(mergedCredentials.data, decryptedData); } - return EECredentials.test(req.user, mergedCredentials); + return await EECredentials.test(req.user, mergedCredentials); }), ); diff --git a/packages/cli/src/credentials/credentials.controller.ts b/packages/cli/src/credentials/credentials.controller.ts index 82f6bad527..1e9814b29a 100644 --- a/packages/cli/src/credentials/credentials.controller.ts +++ b/packages/cli/src/credentials/credentials.controller.ts @@ -27,7 +27,7 @@ credentialsController.get( '/', listQueryMiddleware, ResponseHelper.send(async (req: ListQuery.Request) => { - return CredentialsService.getMany(req.user, { listQueryOptions: req.listQueryOptions }); + return await CredentialsService.getMany(req.user, { listQueryOptions: req.listQueryOptions }); }), ); @@ -105,7 +105,7 @@ credentialsController.post( mergedCredentials.data = CredentialsService.unredact(mergedCredentials.data, decryptedData); } - return CredentialsService.test(req.user, mergedCredentials); + return await CredentialsService.test(req.user, mergedCredentials); }), ); diff --git a/packages/cli/src/credentials/credentials.service.ee.ts b/packages/cli/src/credentials/credentials.service.ee.ts index 88f824b147..56ca50953a 100644 --- a/packages/cli/src/credentials/credentials.service.ee.ts +++ b/packages/cli/src/credentials/credentials.service.ee.ts @@ -43,7 +43,7 @@ export class EECredentialsService extends CredentialsService { where.userId = user.id; } - return Container.get(SharedCredentialsRepository).findOne({ + return await Container.get(SharedCredentialsRepository).findOne({ where, relations, }); @@ -79,6 +79,6 @@ export class EECredentialsService extends CredentialsService { }), ); - return transaction.save(newSharedCredentials); + return await transaction.save(newSharedCredentials); } } diff --git a/packages/cli/src/credentials/credentials.service.ts b/packages/cli/src/credentials/credentials.service.ts index 1d720c6cd9..f3f80e5a2b 100644 --- a/packages/cli/src/credentials/credentials.service.ts +++ b/packages/cli/src/credentials/credentials.service.ts @@ -35,7 +35,7 @@ export type CredentialsGetSharedOptions = export class CredentialsService { static async get(where: FindOptionsWhere, options?: { relations: string[] }) { - return Container.get(CredentialsRepository).findOne({ + return await Container.get(CredentialsRepository).findOne({ relations: options?.relations, where, }); @@ -94,7 +94,7 @@ export class CredentialsService { } } - return Container.get(SharedCredentialsRepository).findOne({ where, relations }); + return await Container.get(SharedCredentialsRepository).findOne({ where, relations }); } static async prepareCreateData( @@ -180,7 +180,7 @@ export class CredentialsService { // We sadly get nothing back from "update". Neither if it updated a record // nor the new value. So query now the updated entry. - return Container.get(CredentialsRepository).findOneBy({ id: credentialId }); + return await Container.get(CredentialsRepository).findOneBy({ id: credentialId }); } static async save( @@ -231,7 +231,7 @@ export class CredentialsService { credentials: ICredentialsDecrypted, ): Promise { const helper = Container.get(CredentialsHelper); - return helper.testCredentials(user, credentials.type, credentials); + return await helper.testCredentials(user, credentials.type, credentials); } // Take data and replace all sensitive values with a sentinel value. diff --git a/packages/cli/src/databases/dsl/Indices.ts b/packages/cli/src/databases/dsl/Indices.ts index 384cbb0b8a..5e5a3346b8 100644 --- a/packages/cli/src/databases/dsl/Indices.ts +++ b/packages/cli/src/databases/dsl/Indices.ts @@ -40,7 +40,7 @@ export class CreateIndex extends IndexOperation { async execute(queryRunner: QueryRunner) { const { columnNames, isUnique } = this; - return queryRunner.createIndex( + return await queryRunner.createIndex( this.fullTableName, new TableIndex({ name: this.customIndexName ?? this.fullIndexName, columnNames, isUnique }), ); @@ -49,6 +49,9 @@ export class CreateIndex extends IndexOperation { export class DropIndex extends IndexOperation { async execute(queryRunner: QueryRunner) { - return queryRunner.dropIndex(this.fullTableName, this.customIndexName ?? this.fullIndexName); + return await queryRunner.dropIndex( + this.fullTableName, + this.customIndexName ?? this.fullIndexName, + ); } } diff --git a/packages/cli/src/databases/dsl/Table.ts b/packages/cli/src/databases/dsl/Table.ts index e132042749..5e5fa41f03 100644 --- a/packages/cli/src/databases/dsl/Table.ts +++ b/packages/cli/src/databases/dsl/Table.ts @@ -62,7 +62,7 @@ export class CreateTable extends TableOperation { async execute(queryRunner: QueryRunner) { const { driver } = queryRunner.connection; const { columns, tableName: name, prefix, indices, foreignKeys } = this; - return queryRunner.createTable( + return await queryRunner.createTable( new Table({ name: `${prefix}${name}`, columns: columns.map((c) => c.toOptions(driver)), @@ -78,7 +78,7 @@ export class CreateTable extends TableOperation { export class DropTable extends TableOperation { async execute(queryRunner: QueryRunner) { const { tableName: name, prefix } = this; - return queryRunner.dropTable(`${prefix}${name}`, true); + return await queryRunner.dropTable(`${prefix}${name}`, true); } } @@ -95,7 +95,7 @@ export class AddColumns extends TableOperation { async execute(queryRunner: QueryRunner) { const { driver } = queryRunner.connection; const { tableName, prefix, columns } = this; - return queryRunner.addColumns( + return await queryRunner.addColumns( `${prefix}${tableName}`, columns.map((c) => new TableColumn(c.toOptions(driver))), ); @@ -114,7 +114,7 @@ export class DropColumns extends TableOperation { async execute(queryRunner: QueryRunner) { const { tableName, prefix, columnNames } = this; - return queryRunner.dropColumns(`${prefix}${tableName}`, columnNames); + return await queryRunner.dropColumns(`${prefix}${tableName}`, columnNames); } } @@ -136,7 +136,7 @@ class ModifyNotNull extends TableOperation { const oldColumn = table.findColumnByName(columnName)!; const newColumn = oldColumn.clone(); newColumn.isNullable = isNullable; - return queryRunner.changeColumn(table, oldColumn, newColumn); + return await queryRunner.changeColumn(table, oldColumn, newColumn); } } diff --git a/packages/cli/src/databases/migrations/common/1620821879465-UniqueWorkflowNames.ts b/packages/cli/src/databases/migrations/common/1620821879465-UniqueWorkflowNames.ts index 15a8063ef4..d867dcd51c 100644 --- a/packages/cli/src/databases/migrations/common/1620821879465-UniqueWorkflowNames.ts +++ b/packages/cli/src/databases/migrations/common/1620821879465-UniqueWorkflowNames.ts @@ -20,7 +20,7 @@ export class UniqueWorkflowNames1620821879465 implements ReversibleMigration { await Promise.all( duplicates.map(async (workflow, index) => { if (index === 0) return; - return runQuery(`UPDATE ${tableName} SET name = :name WHERE id = :id`, { + return await runQuery(`UPDATE ${tableName} SET name = :name WHERE id = :id`, { name: `${workflow.name} ${index + 1}`, id: workflow.id, }); diff --git a/packages/cli/src/databases/migrations/common/1659888469333-AddJsonKeyPinData.ts b/packages/cli/src/databases/migrations/common/1659888469333-AddJsonKeyPinData.ts index bb21e758fa..dfdaee213f 100644 --- a/packages/cli/src/databases/migrations/common/1659888469333-AddJsonKeyPinData.ts +++ b/packages/cli/src/databases/migrations/common/1659888469333-AddJsonKeyPinData.ts @@ -26,11 +26,12 @@ export class AddJsonKeyPinData1659888469333 implements IrreversibleMigration { const selectQuery = `SELECT id, ${columnName} FROM ${tableName} WHERE ${columnName} IS NOT NULL`; await runInBatches(selectQuery, async (workflows) => { await Promise.all( - this.makeUpdateParams(workflows).map(async (workflow) => - runQuery(`UPDATE ${tableName} SET ${columnName} = :pinData WHERE id = :id;`, { - pinData: workflow.pinData, - id: workflow.id, - }), + this.makeUpdateParams(workflows).map( + async (workflow) => + await runQuery(`UPDATE ${tableName} SET ${columnName} = :pinData WHERE id = :id;`, { + pinData: workflow.pinData, + id: workflow.id, + }), ), ); }); diff --git a/packages/cli/src/databases/migrations/common/1671726148419-RemoveWorkflowDataLoadedFlag.ts b/packages/cli/src/databases/migrations/common/1671726148419-RemoveWorkflowDataLoadedFlag.ts index d1ba1ea95e..a310f8b30b 100644 --- a/packages/cli/src/databases/migrations/common/1671726148419-RemoveWorkflowDataLoadedFlag.ts +++ b/packages/cli/src/databases/migrations/common/1671726148419-RemoveWorkflowDataLoadedFlag.ts @@ -18,13 +18,13 @@ export class RemoveWorkflowDataLoadedFlag1671726148419 implements ReversibleMigr await Promise.all( workflowIds.map( async ({ id, dataLoaded }) => - dataLoaded && - runQuery( - `INSERT INTO ${statisticsTableName} + await (dataLoaded && + runQuery( + `INSERT INTO ${statisticsTableName} (${escape.columnName('workflowId')}, name, count, ${escape.columnName('latestEvent')}) VALUES (:id, :name, 1, ${now})`, - { id, name: StatisticsNames.dataLoaded }, - ), + { id, name: StatisticsNames.dataLoaded }, + )), ), ); @@ -47,10 +47,11 @@ export class RemoveWorkflowDataLoadedFlag1671726148419 implements ReversibleMigr ); await Promise.all( - workflowsIds.map(async ({ workflowId }) => - runQuery(`UPDATE ${workflowTableName} SET ${columnName} = true WHERE id = :id`, { - id: workflowId, - }), + workflowsIds.map( + async ({ workflowId }) => + await runQuery(`UPDATE ${workflowTableName} SET ${columnName} = true WHERE id = :id`, { + id: workflowId, + }), ), ); diff --git a/packages/cli/src/databases/migrations/common/1675940580449-PurgeInvalidWorkflowConnections.ts b/packages/cli/src/databases/migrations/common/1675940580449-PurgeInvalidWorkflowConnections.ts index a15c873d36..51526aaead 100644 --- a/packages/cli/src/databases/migrations/common/1675940580449-PurgeInvalidWorkflowConnections.ts +++ b/packages/cli/src/databases/migrations/common/1675940580449-PurgeInvalidWorkflowConnections.ts @@ -47,10 +47,13 @@ export class PurgeInvalidWorkflowConnections1675940580449 implements Irreversibl }); // Update database with new connections - return runQuery(`UPDATE ${workflowsTable} SET connections = :connections WHERE id = :id`, { - connections: JSON.stringify(connections), - id: workflow.id, - }); + return await runQuery( + `UPDATE ${workflowsTable} SET connections = :connections WHERE id = :id`, + { + connections: JSON.stringify(connections), + id: workflow.id, + }, + ); }), ); } diff --git a/packages/cli/src/databases/migrations/postgresdb/1681134145996-AddUserActivatedProperty.ts b/packages/cli/src/databases/migrations/postgresdb/1681134145996-AddUserActivatedProperty.ts index 50040ea9ca..38c1b22da4 100644 --- a/packages/cli/src/databases/migrations/postgresdb/1681134145996-AddUserActivatedProperty.ts +++ b/packages/cli/src/databases/migrations/postgresdb/1681134145996-AddUserActivatedProperty.ts @@ -18,15 +18,15 @@ export class AddUserActivatedProperty1681134145996 implements ReversibleMigratio AND r.scope = 'workflow'`, )) as UserSettings[]; - const updatedUsers = activatedUsers.map(async (user) => - queryRunner.query( + const updatedUserPromises = activatedUsers.map(async (user) => { + await queryRunner.query( `UPDATE "${tablePrefix}user" SET settings = '${JSON.stringify( user.settings, )}' WHERE id = '${user.id}' `, - ), - ); + ); + }); - await Promise.all(updatedUsers); + await Promise.all(updatedUserPromises); if (!activatedUsers.length) { await queryRunner.query( diff --git a/packages/cli/src/databases/migrations/sqlite/1681134145996-AddUserActivatedProperty.ts b/packages/cli/src/databases/migrations/sqlite/1681134145996-AddUserActivatedProperty.ts index 3648a55cd5..d4e73014a7 100644 --- a/packages/cli/src/databases/migrations/sqlite/1681134145996-AddUserActivatedProperty.ts +++ b/packages/cli/src/databases/migrations/sqlite/1681134145996-AddUserActivatedProperty.ts @@ -18,14 +18,14 @@ export class AddUserActivatedProperty1681134145996 implements ReversibleMigratio AND r.scope = "workflow"`, )) as UserSettings[]; - const updatedUsers = activatedUsers.map(async (user) => - queryRunner.query( + const updatedUserPromises = activatedUsers.map(async (user) => { + await queryRunner.query( // eslint-disable-next-line @typescript-eslint/restrict-template-expressions `UPDATE ${tablePrefix}user SET settings = '${user.settings}' WHERE id = '${user.id}' `, - ), - ); + ); + }); - await Promise.all(updatedUsers); + await Promise.all(updatedUserPromises); if (!activatedUsers.length) { await queryRunner.query( diff --git a/packages/cli/src/databases/repositories/credentials.repository.ts b/packages/cli/src/databases/repositories/credentials.repository.ts index f596f79ae5..c593410d46 100644 --- a/packages/cli/src/databases/repositories/credentials.repository.ts +++ b/packages/cli/src/databases/repositories/credentials.repository.ts @@ -20,11 +20,11 @@ export class CredentialsRepository extends Repository { credentialsId: credentialId, userId: Not(In(userIds)), }; - return transaction.delete(SharedCredentials, conditions); + return await transaction.delete(SharedCredentials, conditions); } async findStartingWith(credentialName: string) { - return this.find({ + return await this.find({ select: ['name'], where: { name: Like(`${credentialName}%`) }, }); @@ -37,7 +37,7 @@ export class CredentialsRepository extends Repository { findManyOptions.where = { ...findManyOptions.where, id: In(credentialIds) }; } - return this.find(findManyOptions); + return await this.find(findManyOptions); } private toFindManyOptions(listQueryOptions?: ListQuery.Options) { @@ -84,6 +84,6 @@ export class CredentialsRepository extends Repository { findManyOptions.relations = ['shared', 'shared.user', 'shared.role']; } - return this.find(findManyOptions); + return await this.find(findManyOptions); } } diff --git a/packages/cli/src/databases/repositories/execution.repository.ts b/packages/cli/src/databases/repositories/execution.repository.ts index 9266604d7f..4aa94548f1 100644 --- a/packages/cli/src/databases/repositories/execution.repository.ts +++ b/packages/cli/src/databases/repositories/execution.repository.ts @@ -251,7 +251,10 @@ export class ExecutionRepository extends Repository { * Permanently remove a single execution and its binary data. */ async hardDelete(ids: { workflowId: string; executionId: string }) { - return Promise.all([this.delete(ids.executionId), this.binaryDataService.deleteMany([ids])]); + return await Promise.all([ + this.delete(ids.executionId), + this.binaryDataService.deleteMany([ids]), + ]); } async updateStatus(executionId: string, status: ExecutionStatus) { @@ -438,7 +441,7 @@ export class ExecutionRepository extends Repository { } async getIdsSince(date: Date) { - return this.find({ + return await this.find({ select: ['id'], where: { startedAt: MoreThanOrEqual(DateUtils.mixedDateToUtcDatetimeString(date)), @@ -474,7 +477,7 @@ export class ExecutionRepository extends Repository { const [timeBasedWhere, countBasedWhere] = toPrune; - return this.createQueryBuilder() + return await this.createQueryBuilder() .update(ExecutionEntity) .set({ deletedAt: new Date() }) .where({ @@ -516,7 +519,7 @@ export class ExecutionRepository extends Repository { } async deleteByIds(executionIds: string[]) { - return this.delete({ id: In(executionIds) }); + return await this.delete({ id: In(executionIds) }); } async getWaitingExecutions() { @@ -534,7 +537,7 @@ export class ExecutionRepository extends Repository { where.waitTill = LessThanOrEqual(DateUtils.mixedDateToUtcDatetimeString(waitTill)); } - return this.findMultipleExecutions({ + return await this.findMultipleExecutions({ select: ['id', 'waitTill'], where, order: { @@ -606,7 +609,7 @@ export class ExecutionRepository extends Repository { where = { ...where, workflowId: In(params.workflowIds) }; } - return this.findMultipleExecutions( + return await this.findMultipleExecutions( { select: [ 'id', @@ -636,7 +639,7 @@ export class ExecutionRepository extends Repository { workflowIds: string[], includeData?: boolean, ): Promise { - return this.findSingleExecution(id, { + return await this.findSingleExecution(id, { where: { workflowId: In(workflowIds), }, @@ -646,7 +649,7 @@ export class ExecutionRepository extends Repository { } async findIfShared(executionId: string, sharedWorkflowIds: string[]) { - return this.findSingleExecution(executionId, { + return await this.findSingleExecution(executionId, { where: { workflowId: In(sharedWorkflowIds), }, diff --git a/packages/cli/src/databases/repositories/executionData.repository.ts b/packages/cli/src/databases/repositories/executionData.repository.ts index e0b49de7ef..3eebab0122 100644 --- a/packages/cli/src/databases/repositories/executionData.repository.ts +++ b/packages/cli/src/databases/repositories/executionData.repository.ts @@ -9,7 +9,7 @@ export class ExecutionDataRepository extends Repository { } async findByExecutionIds(executionIds: string[]) { - return this.find({ + return await this.find({ select: ['workflowData'], where: { executionId: In(executionIds), diff --git a/packages/cli/src/databases/repositories/installedPackages.repository.ts b/packages/cli/src/databases/repositories/installedPackages.repository.ts index 06ef523c07..743528e35f 100644 --- a/packages/cli/src/databases/repositories/installedPackages.repository.ts +++ b/packages/cli/src/databases/repositories/installedPackages.repository.ts @@ -41,7 +41,7 @@ export class InstalledPackagesRepository extends Repository { installedPackage.installedNodes.push(installedNode); - return manager.save(installedNode); + return await manager.save(installedNode); }); }); diff --git a/packages/cli/src/databases/repositories/role.repository.ts b/packages/cli/src/databases/repositories/role.repository.ts index 9694ec1033..1785ace18c 100644 --- a/packages/cli/src/databases/repositories/role.repository.ts +++ b/packages/cli/src/databases/repositories/role.repository.ts @@ -11,7 +11,7 @@ export class RoleRepository extends Repository { } async findRole(scope: RoleScopes, name: RoleNames) { - return this.findOne({ where: { scope, name } }); + return await this.findOne({ where: { scope, name } }); } /** @@ -34,7 +34,7 @@ export class RoleRepository extends Repository { } async getIdsInScopeWorkflowByNames(roleNames: RoleNames[]) { - return this.find({ + return await this.find({ select: ['id'], where: { name: In(roleNames), scope: 'workflow' }, }).then((role) => role.map(({ id }) => id)); diff --git a/packages/cli/src/databases/repositories/sharedCredentials.repository.ts b/packages/cli/src/databases/repositories/sharedCredentials.repository.ts index 8250a61357..98b00561ff 100644 --- a/packages/cli/src/databases/repositories/sharedCredentials.repository.ts +++ b/packages/cli/src/databases/repositories/sharedCredentials.repository.ts @@ -25,7 +25,7 @@ export class SharedCredentialsRepository extends Repository { } async findByCredentialIds(credentialIds: string[]) { - return this.find({ + return await this.find({ relations: ['credentials', 'role', 'user'], where: { credentialsId: In(credentialIds), @@ -34,7 +34,7 @@ export class SharedCredentialsRepository extends Repository { } async makeOwnerOfAllCredentials(user: User, role: Role) { - return this.update({ userId: Not(user.id), roleId: role.id }, { user }); + return await this.update({ userId: Not(user.id), roleId: role.id }, { user }); } /** @@ -58,11 +58,11 @@ export class SharedCredentialsRepository extends Repository { // If credential sharing is not enabled, get only credentials owned by this user if (roleId) where.roleId = roleId; - return this.find({ where }); + return await this.find({ where }); } async deleteByIds(transaction: EntityManager, sharedCredentialsIds: string[], user?: User) { - return transaction.delete(SharedCredentials, { + return await transaction.delete(SharedCredentials, { user, credentialsId: In(sharedCredentialsIds), }); diff --git a/packages/cli/src/databases/repositories/sharedWorkflow.repository.ts b/packages/cli/src/databases/repositories/sharedWorkflow.repository.ts index 8e026cfa00..8ebcc043a5 100644 --- a/packages/cli/src/databases/repositories/sharedWorkflow.repository.ts +++ b/packages/cli/src/databases/repositories/sharedWorkflow.repository.ts @@ -20,7 +20,7 @@ export class SharedWorkflowRepository extends Repository { if (!user.hasGlobalScope('workflow:read')) { where.userId = user.id; } - return this.exist({ where }); + return await this.exist({ where }); } async getSharedWorkflowIds(workflowIds: string[]) { @@ -34,7 +34,7 @@ export class SharedWorkflowRepository extends Repository { } async findByWorkflowIds(workflowIds: string[]) { - return this.find({ + return await this.find({ relations: ['role', 'user'], where: { role: { @@ -68,11 +68,11 @@ export class SharedWorkflowRepository extends Repository { if (extraRelations) relations.push(...extraRelations); - return this.findOne({ relations, where }); + return await this.findOne({ relations, where }); } async makeOwnerOfAllWorkflows(user: User, role: Role) { - return this.update({ userId: Not(user.id), roleId: role.id }, { user }); + return await this.update({ userId: Not(user.id), roleId: role.id }, { user }); } async getSharing( @@ -90,7 +90,7 @@ export class SharedWorkflowRepository extends Repository { where.userId = user.id; } - return this.findOne({ where, relations }); + return await this.findOne({ where, relations }); } async getSharedWorkflows( @@ -100,7 +100,7 @@ export class SharedWorkflowRepository extends Repository { workflowIds?: string[]; }, ): Promise { - return this.find({ + return await this.find({ where: { ...(!['owner', 'admin'].includes(user.globalRole.name) && { userId: user.id }), ...(options.workflowIds && { workflowId: In(options.workflowIds) }), @@ -123,11 +123,11 @@ export class SharedWorkflowRepository extends Repository { return acc; }, []); - return transaction.save(newSharedWorkflows); + return await transaction.save(newSharedWorkflows); } async findWithFields(workflowIds: string[], { fields }: { fields: string[] }) { - return this.find({ + return await this.find({ where: { workflowId: In(workflowIds), }, @@ -136,7 +136,7 @@ export class SharedWorkflowRepository extends Repository { } async deleteByIds(transaction: EntityManager, sharedWorkflowIds: string[], user?: User) { - return transaction.delete(SharedWorkflow, { + return await transaction.delete(SharedWorkflow, { user, workflowId: In(sharedWorkflowIds), }); diff --git a/packages/cli/src/databases/repositories/tag.repository.ts b/packages/cli/src/databases/repositories/tag.repository.ts index 285a4de096..ff41a28b03 100644 --- a/packages/cli/src/databases/repositories/tag.repository.ts +++ b/packages/cli/src/databases/repositories/tag.repository.ts @@ -12,7 +12,7 @@ export class TagRepository extends Repository { } async findMany(tagIds: string[]) { - return this.find({ + return await this.find({ select: ['id', 'name'], where: { id: In(tagIds) }, }); diff --git a/packages/cli/src/databases/repositories/user.repository.ts b/packages/cli/src/databases/repositories/user.repository.ts index e6be912579..c21684d513 100644 --- a/packages/cli/src/databases/repositories/user.repository.ts +++ b/packages/cli/src/databases/repositories/user.repository.ts @@ -11,7 +11,7 @@ export class UserRepository extends Repository { } async findManybyIds(userIds: string[]) { - return this.find({ + return await this.find({ where: { id: In(userIds) }, relations: ['globalRole'], }); @@ -22,11 +22,11 @@ export class UserRepository extends Repository { } async getByIds(transaction: EntityManager, ids: string[]) { - return transaction.find(User, { where: { id: In(ids) } }); + return await transaction.find(User, { where: { id: In(ids) } }); } async findManyByEmail(emails: string[]) { - return this.find({ + return await this.find({ where: { email: In(emails) }, relations: ['globalRole'], select: ['email', 'password', 'id'], @@ -34,11 +34,11 @@ export class UserRepository extends Repository { } async deleteMany(userIds: string[]) { - return this.delete({ id: In(userIds) }); + return await this.delete({ id: In(userIds) }); } async findNonShellUser(email: string) { - return this.findOne({ + return await this.findOne({ where: { email, password: Not(IsNull()), diff --git a/packages/cli/src/databases/repositories/workflow.repository.ts b/packages/cli/src/databases/repositories/workflow.repository.ts index 79cd6461c0..71559353ee 100644 --- a/packages/cli/src/databases/repositories/workflow.repository.ts +++ b/packages/cli/src/databases/repositories/workflow.repository.ts @@ -26,14 +26,14 @@ export class WorkflowRepository extends Repository { } async get(where: FindOptionsWhere, options?: { relations: string[] }) { - return this.findOne({ + return await this.findOne({ where, relations: options?.relations, }); } async getAllActive() { - return this.find({ + return await this.find({ where: { active: true }, relations: ['shared', 'shared.user', 'shared.user.globalRole', 'shared.role'], }); @@ -48,7 +48,7 @@ export class WorkflowRepository extends Repository { } async findById(workflowId: string) { - return this.findOne({ + return await this.findOne({ where: { id: workflowId }, relations: ['shared', 'shared.user', 'shared.user.globalRole', 'shared.role'], }); @@ -61,7 +61,7 @@ export class WorkflowRepository extends Repository { if (fields?.length) options.select = fields as FindOptionsSelect; - return this.find(options); + return await this.find(options); } async getActiveTriggerCount() { @@ -88,7 +88,7 @@ export class WorkflowRepository extends Repository { workflowId: string, userIds: string[], ): Promise { - return transaction.delete(SharedWorkflow, { + return await transaction.delete(SharedWorkflow, { workflowId, userId: Not(In(userIds)), }); @@ -96,7 +96,7 @@ export class WorkflowRepository extends Repository { async updateWorkflowTriggerCount(id: string, triggerCount: number): Promise { const qb = this.createQueryBuilder('workflow'); - return qb + return await qb .update() .set({ triggerCount, @@ -185,35 +185,35 @@ export class WorkflowRepository extends Repository { } async findStartingWith(workflowName: string): Promise> { - return this.find({ + return await this.find({ select: ['name'], where: { name: Like(`${workflowName}%`) }, }); } async findIn(workflowIds: string[]) { - return this.find({ + return await this.find({ select: ['id', 'name'], where: { id: In(workflowIds) }, }); } async findWebhookBasedActiveWorkflows() { - return this.createQueryBuilder('workflow') + return await (this.createQueryBuilder('workflow') .select('DISTINCT workflow.id, workflow.name') .innerJoin(WebhookEntity, 'webhook_entity', 'workflow.id = webhook_entity.workflowId') - .execute() as Promise>; + .execute() as Promise>); } async updateActiveState(workflowId: string, newState: boolean) { - return this.update({ id: workflowId }, { active: newState }); + return await this.update({ id: workflowId }, { active: newState }); } async deactivateAll() { - return this.update({ active: true }, { active: false }); + return await this.update({ active: true }, { active: false }); } async findByActiveState(activeState: boolean) { - return this.findBy({ active: activeState }); + return await this.findBy({ active: activeState }); } } diff --git a/packages/cli/src/databases/repositories/workflowHistory.repository.ts b/packages/cli/src/databases/repositories/workflowHistory.repository.ts index eda4a18e31..bc21b3dca7 100644 --- a/packages/cli/src/databases/repositories/workflowHistory.repository.ts +++ b/packages/cli/src/databases/repositories/workflowHistory.repository.ts @@ -9,6 +9,6 @@ export class WorkflowHistoryRepository extends Repository { } async deleteEarlierThan(date: Date) { - return this.delete({ createdAt: LessThan(date) }); + return await this.delete({ createdAt: LessThan(date) }); } } diff --git a/packages/cli/src/databases/utils/migrationHelpers.ts b/packages/cli/src/databases/utils/migrationHelpers.ts index c254d8c44d..24fc80d86f 100644 --- a/packages/cli/src/databases/utils/migrationHelpers.ts +++ b/packages/cli/src/databases/utils/migrationHelpers.ts @@ -118,9 +118,9 @@ const createContext = (queryRunner: QueryRunner, migration: Migration): Migratio namedParameters, {}, ); - return queryRunner.query(query, parameters) as Promise; + return await (queryRunner.query(query, parameters) as Promise); } else { - return queryRunner.query(sql) as Promise; + return await (queryRunner.query(sql) as Promise); } }, runInBatches: async ( diff --git a/packages/cli/src/decorators/registerController.ts b/packages/cli/src/decorators/registerController.ts index c3b38c8183..bb13781445 100644 --- a/packages/cli/src/decorators/registerController.ts +++ b/packages/cli/src/decorators/registerController.ts @@ -121,7 +121,8 @@ export const registerController = (app: Application, controllerClass: Class controller[handlerName](req, res); + const handler = async (req: Request, res: Response) => + await controller[handlerName](req, res); router[method]( path, ...(authRole ? [createAuthMiddleware(authRole)] : []), diff --git a/packages/cli/src/environments/sourceControl/sourceControl.service.ee.ts b/packages/cli/src/environments/sourceControl/sourceControl.service.ee.ts index a590050525..189d3b9e77 100644 --- a/packages/cli/src/environments/sourceControl/sourceControl.service.ee.ts +++ b/packages/cli/src/environments/sourceControl/sourceControl.service.ee.ts @@ -171,7 +171,7 @@ export class SourceControlService { await this.initGitService(); } await this.gitService.fetch(); - return this.gitService.getBranches(); + return await this.gitService.getBranches(); } async setBranch(branch: string): Promise<{ branches: string[]; currentBranch: string }> { @@ -182,7 +182,7 @@ export class SourceControlService { branchName: branch, connected: branch?.length > 0, }); - return this.gitService.setBranch(branch); + return await this.gitService.setBranch(branch); } // will reset the branch to the remote branch and pull diff --git a/packages/cli/src/environments/sourceControl/sourceControlExport.service.ee.ts b/packages/cli/src/environments/sourceControl/sourceControlExport.service.ee.ts index 2c56b84ecd..771d4fdee4 100644 --- a/packages/cli/src/environments/sourceControl/sourceControlExport.service.ee.ts +++ b/packages/cli/src/environments/sourceControl/sourceControlExport.service.ee.ts @@ -95,7 +95,7 @@ export class SourceControlExportService { owner: owners[e.id], }; this.logger.debug(`Writing workflow ${e.id} to ${fileName}`); - return fsWriteFile(fileName, JSON.stringify(sanitizedWorkflow, null, 2)); + return await fsWriteFile(fileName, JSON.stringify(sanitizedWorkflow, null, 2)); }), ); } @@ -253,7 +253,7 @@ export class SourceControlExportService { nodesAccess: sharedCredential.credentials.nodesAccess, }; this.logger.debug(`Writing credential ${sharedCredential.credentials.id} to ${fileName}`); - return fsWriteFile(fileName, JSON.stringify(sanitizedCredential, null, 2)); + return await fsWriteFile(fileName, JSON.stringify(sanitizedCredential, null, 2)); }), ); return { diff --git a/packages/cli/src/environments/sourceControl/sourceControlGit.service.ee.ts b/packages/cli/src/environments/sourceControl/sourceControlGit.service.ee.ts index 4a440f14e6..64690a632d 100644 --- a/packages/cli/src/environments/sourceControl/sourceControlGit.service.ee.ts +++ b/packages/cli/src/environments/sourceControl/sourceControlGit.service.ee.ts @@ -232,7 +232,7 @@ export class SourceControlGitService { } await this.git.checkout(branch); await this.git.branch([`--set-upstream-to=${SOURCE_CONTROL_ORIGIN}/${branch}`, branch]); - return this.getBranches(); + return await this.getBranches(); } async getCurrentBranch(): Promise<{ current: string; remote: string }> { @@ -253,7 +253,7 @@ export class SourceControlGitService { const currentBranch = await this.getCurrentBranch(); if (currentBranch.remote) { const target = currentBranch.remote; - return this.git.diffSummary(['...' + target, '--ignore-all-space']); + return await this.git.diffSummary(['...' + target, '--ignore-all-space']); } return; } @@ -265,7 +265,7 @@ export class SourceControlGitService { const currentBranch = await this.getCurrentBranch(); if (currentBranch.remote) { const target = currentBranch.current; - return this.git.diffSummary([target, '--ignore-all-space']); + return await this.git.diffSummary([target, '--ignore-all-space']); } return; } @@ -274,7 +274,7 @@ export class SourceControlGitService { if (!this.git) { throw new ApplicationError('Git is not initialized (fetch)'); } - return this.git.fetch(); + return await this.git.fetch(); } async pull(options: { ffOnly: boolean } = { ffOnly: true }): Promise { @@ -286,7 +286,7 @@ export class SourceControlGitService { // eslint-disable-next-line @typescript-eslint/naming-convention Object.assign(params, { '--ff-only': true }); } - return this.git.pull(params); + return await this.git.pull(params); } async push( @@ -300,9 +300,9 @@ export class SourceControlGitService { throw new ApplicationError('Git is not initialized ({)'); } if (force) { - return this.git.push(SOURCE_CONTROL_ORIGIN, branch, ['-f']); + return await this.git.push(SOURCE_CONTROL_ORIGIN, branch, ['-f']); } - return this.git.push(SOURCE_CONTROL_ORIGIN, branch); + return await this.git.push(SOURCE_CONTROL_ORIGIN, branch); } async stage(files: Set, deletedFiles?: Set): Promise { @@ -316,7 +316,7 @@ export class SourceControlGitService { this.logger.debug(`Git rm: ${(error as Error).message}`); } } - return this.git.add(Array.from(files)); + return await this.git.add(Array.from(files)); } async resetBranch( @@ -326,9 +326,9 @@ export class SourceControlGitService { throw new ApplicationError('Git is not initialized (Promise)'); } if (options?.hard) { - return this.git.raw(['reset', '--hard', options.target]); + return await this.git.raw(['reset', '--hard', options.target]); } - return this.git.raw(['reset', options.target]); + return await this.git.raw(['reset', options.target]); // built-in reset method does not work // return this.git.reset(); } @@ -337,7 +337,7 @@ export class SourceControlGitService { if (!this.git) { throw new ApplicationError('Git is not initialized (commit)'); } - return this.git.commit(message); + return await this.git.commit(message); } async status(): Promise { diff --git a/packages/cli/src/environments/sourceControl/sourceControlImport.service.ee.ts b/packages/cli/src/environments/sourceControl/sourceControlImport.service.ee.ts index f4743a3d67..82b6a41c69 100644 --- a/packages/cli/src/environments/sourceControl/sourceControlImport.service.ee.ts +++ b/packages/cli/src/environments/sourceControl/sourceControlImport.service.ee.ts @@ -186,7 +186,7 @@ export class SourceControlImportService { } public async getLocalVariablesFromDb(): Promise { - return this.variablesService.getAllCached(); + return await this.variablesService.getAllCached(); } public async getRemoteTagsAndMappingsFromFile(): Promise<{ diff --git a/packages/cli/src/environments/variables/variables.controller.ee.ts b/packages/cli/src/environments/variables/variables.controller.ee.ts index 69a0b03d2d..5e3eb6452f 100644 --- a/packages/cli/src/environments/variables/variables.controller.ee.ts +++ b/packages/cli/src/environments/variables/variables.controller.ee.ts @@ -23,7 +23,7 @@ export class VariablesController { @Get('/') @RequireGlobalScope('variable:list') async getVariables() { - return this.variablesService.getAllCached(); + return await this.variablesService.getAllCached(); } @Post('/') diff --git a/packages/cli/src/environments/variables/variables.service.ee.ts b/packages/cli/src/environments/variables/variables.service.ee.ts index c249b31f60..0ea2cc6e57 100644 --- a/packages/cli/src/environments/variables/variables.service.ee.ts +++ b/packages/cli/src/environments/variables/variables.service.ee.ts @@ -18,7 +18,7 @@ export class VariablesService { async getAllCached(): Promise { const variables = await this.cacheService.get('variables', { async refreshFn() { - return Container.get(VariablesService).findAll(); + return await Container.get(VariablesService).findAll(); }, }); return (variables as Array>).map((v) => this.variablesRepository.create(v)); @@ -49,7 +49,7 @@ export class VariablesService { } async findAll(): Promise { - return this.variablesRepository.find(); + return await this.variablesRepository.find(); } validateVariable(variable: Omit): void { diff --git a/packages/cli/src/eventbus/MessageEventBusDestination/MessageEventBusDestination.ee.ts b/packages/cli/src/eventbus/MessageEventBusDestination/MessageEventBusDestination.ee.ts index 28736873cc..bf68bb859c 100644 --- a/packages/cli/src/eventbus/MessageEventBusDestination/MessageEventBusDestination.ee.ts +++ b/packages/cli/src/eventbus/MessageEventBusDestination/MessageEventBusDestination.ee.ts @@ -99,7 +99,7 @@ export abstract class MessageEventBusDestination implements MessageEventBusDesti } async deleteFromDb() { - return MessageEventBusDestination.deleteFromDb(this.getId()); + return await MessageEventBusDestination.deleteFromDb(this.getId()); } static async deleteFromDb(id: string) { diff --git a/packages/cli/src/eventbus/eventBus.controller.ee.ts b/packages/cli/src/eventbus/eventBus.controller.ee.ts index 32346dd27f..f88628bfec 100644 --- a/packages/cli/src/eventbus/eventBus.controller.ee.ts +++ b/packages/cli/src/eventbus/eventBus.controller.ee.ts @@ -61,9 +61,9 @@ export class EventBusControllerEE { @RequireGlobalScope('eventBusDestination:list') async getDestination(req: express.Request): Promise { if (isWithIdString(req.query)) { - return eventBus.findDestination(req.query.id); + return await eventBus.findDestination(req.query.id); } else { - return eventBus.findDestination(); + return await eventBus.findDestination(); } } @@ -115,7 +115,7 @@ export class EventBusControllerEE { @RequireGlobalScope('eventBusDestination:test') async sendTestMessage(req: express.Request): Promise { if (isWithIdString(req.query)) { - return eventBus.testDestination(req.query.id); + return await eventBus.testDestination(req.query.id); } return false; } @@ -124,7 +124,7 @@ export class EventBusControllerEE { @RequireGlobalScope('eventBusDestination:delete') async deleteDestination(req: AuthenticatedRequest) { if (isWithIdString(req.query)) { - return eventBus.removeDestination(req.query.id); + return await eventBus.removeDestination(req.query.id); } else { throw new BadRequestError('Query is missing id'); } diff --git a/packages/cli/src/eventbus/eventBus.controller.ts b/packages/cli/src/eventbus/eventBus.controller.ts index 6e46c75d55..f10b4e5674 100644 --- a/packages/cli/src/eventbus/eventBus.controller.ts +++ b/packages/cli/src/eventbus/eventBus.controller.ts @@ -45,17 +45,17 @@ export class EventBusController { if (isWithQueryString(req.query)) { switch (req.query.query as EventMessageReturnMode) { case 'sent': - return eventBus.getEventsSent(); + return await eventBus.getEventsSent(); case 'unsent': - return eventBus.getEventsUnsent(); + return await eventBus.getEventsUnsent(); case 'unfinished': - return eventBus.getUnfinishedExecutions(); + return await eventBus.getUnfinishedExecutions(); case 'all': default: - return eventBus.getEventsAll(); + return await eventBus.getEventsAll(); } } else { - return eventBus.getEventsAll(); + return await eventBus.getEventsAll(); } } @@ -63,7 +63,7 @@ export class EventBusController { @RequireGlobalScope('eventBusEvent:list') async getFailedEvents(req: express.Request): Promise { const amount = parseInt(req.query?.amount as string) ?? 5; - return eventBus.getEventsFailed(amount); + return await eventBus.getEventsFailed(amount); } @Get('/execution/:id') @@ -74,7 +74,7 @@ export class EventBusController { if (req.query?.logHistory) { logHistory = parseInt(req.query.logHistory as string, 10); } - return eventBus.getEventsByExecutionId(req.params.id, logHistory); + return await eventBus.getEventsByExecutionId(req.params.id, logHistory); } return; } @@ -88,7 +88,7 @@ export class EventBusController { const applyToDb = req.query.applyToDb !== undefined ? !!req.query.applyToDb : true; const messages = await eventBus.getEventsByExecutionId(id, logHistory); if (messages.length > 0) { - return recoverExecutionDataFromEventLogMessages(id, messages, applyToDb); + return await recoverExecutionDataFromEventLogMessages(id, messages, applyToDb); } } return; diff --git a/packages/cli/src/executions/execution.service.ts b/packages/cli/src/executions/execution.service.ts index c818cb660e..c64b3953d0 100644 --- a/packages/cli/src/executions/execution.service.ts +++ b/packages/cli/src/executions/execution.service.ts @@ -319,10 +319,14 @@ export class ExecutionService { } } - return this.executionRepository.deleteExecutionsByFilter(requestFilters, sharedWorkflowIds, { - deleteBefore, - ids, - }); + return await this.executionRepository.deleteExecutionsByFilter( + requestFilters, + sharedWorkflowIds, + { + deleteBefore, + ids, + }, + ); } async createErrorExecution( diff --git a/packages/cli/src/executions/executions.controller.ts b/packages/cli/src/executions/executions.controller.ts index b4163cad04..5a0d4c40af 100644 --- a/packages/cli/src/executions/executions.controller.ts +++ b/packages/cli/src/executions/executions.controller.ts @@ -17,15 +17,15 @@ export class ExecutionsController { private async getAccessibleWorkflowIds(user: User) { return isSharingEnabled() - ? this.workflowSharingService.getSharedWorkflowIds(user) - : this.workflowSharingService.getSharedWorkflowIds(user, ['owner']); + ? await this.workflowSharingService.getSharedWorkflowIds(user) + : await this.workflowSharingService.getSharedWorkflowIds(user, ['owner']); } @Get('/') async getExecutionsList(req: ExecutionRequest.GetAll) { const workflowIds = await this.getAccessibleWorkflowIds(req.user); - return this.executionService.getExecutionsList(req, workflowIds); + return await this.executionService.getExecutionsList(req, workflowIds); } @Get('/:id') @@ -33,21 +33,21 @@ export class ExecutionsController { const workflowIds = await this.getAccessibleWorkflowIds(req.user); return isSharingEnabled() - ? this.enterpriseExecutionService.getExecution(req, workflowIds) - : this.executionService.getExecution(req, workflowIds); + ? await this.enterpriseExecutionService.getExecution(req, workflowIds) + : await this.executionService.getExecution(req, workflowIds); } @Post('/:id/retry') async retryExecution(req: ExecutionRequest.Retry) { const workflowIds = await this.getAccessibleWorkflowIds(req.user); - return this.executionService.retryExecution(req, workflowIds); + return await this.executionService.retryExecution(req, workflowIds); } @Post('/delete') async deleteExecutions(req: ExecutionRequest.Delete) { const workflowIds = await this.getAccessibleWorkflowIds(req.user); - return this.executionService.deleteExecutions(req, workflowIds); + return await this.executionService.deleteExecutions(req, workflowIds); } } diff --git a/packages/cli/src/license/license.controller.ts b/packages/cli/src/license/license.controller.ts index ea0df696c9..9690036628 100644 --- a/packages/cli/src/license/license.controller.ts +++ b/packages/cli/src/license/license.controller.ts @@ -9,7 +9,7 @@ export class LicenseController { @Get('/') async getLicenseData() { - return this.licenseService.getLicenseData(); + return await this.licenseService.getLicenseData(); } @Post('/activate') @@ -17,14 +17,14 @@ export class LicenseController { async activateLicense(req: LicenseRequest.Activate) { const { activationKey } = req.body; await this.licenseService.activateLicense(activationKey); - return this.getTokenAndData(); + return await this.getTokenAndData(); } @Post('/renew') @RequireGlobalScope('license:manage') async renewLicense() { await this.licenseService.renewLicense(); - return this.getTokenAndData(); + return await this.getTokenAndData(); } private async getTokenAndData() { diff --git a/packages/cli/src/middlewares/listQuery/dtos/credentials.filter.dto.ts b/packages/cli/src/middlewares/listQuery/dtos/credentials.filter.dto.ts index de7d5cf230..5b5cdb1a63 100644 --- a/packages/cli/src/middlewares/listQuery/dtos/credentials.filter.dto.ts +++ b/packages/cli/src/middlewares/listQuery/dtos/credentials.filter.dto.ts @@ -14,6 +14,6 @@ export class CredentialsFilter extends BaseFilter { type?: string; static async fromString(rawFilter: string) { - return this.toFilter(rawFilter, CredentialsFilter); + return await this.toFilter(rawFilter, CredentialsFilter); } } diff --git a/packages/cli/src/middlewares/listQuery/dtos/user.filter.dto.ts b/packages/cli/src/middlewares/listQuery/dtos/user.filter.dto.ts index 6c7e8a0131..82f124c8e9 100644 --- a/packages/cli/src/middlewares/listQuery/dtos/user.filter.dto.ts +++ b/packages/cli/src/middlewares/listQuery/dtos/user.filter.dto.ts @@ -24,6 +24,6 @@ export class UserFilter extends BaseFilter { isOwner?: boolean; static async fromString(rawFilter: string) { - return this.toFilter(rawFilter, UserFilter); + return await this.toFilter(rawFilter, UserFilter); } } diff --git a/packages/cli/src/middlewares/listQuery/dtos/workflow.filter.dto.ts b/packages/cli/src/middlewares/listQuery/dtos/workflow.filter.dto.ts index 389246cf6c..cadb945a60 100644 --- a/packages/cli/src/middlewares/listQuery/dtos/workflow.filter.dto.ts +++ b/packages/cli/src/middlewares/listQuery/dtos/workflow.filter.dto.ts @@ -21,6 +21,6 @@ export class WorkflowFilter extends BaseFilter { tags?: string[]; static async fromString(rawFilter: string) { - return this.toFilter(rawFilter, WorkflowFilter); + return await this.toFilter(rawFilter, WorkflowFilter); } } diff --git a/packages/cli/src/posthog/index.ts b/packages/cli/src/posthog/index.ts index b62a6eba8d..1fb688c6d7 100644 --- a/packages/cli/src/posthog/index.ts +++ b/packages/cli/src/posthog/index.ts @@ -49,7 +49,7 @@ export class PostHogClient { // cannot use local evaluation because that requires PostHog personal api key with org-wide // https://github.com/PostHog/posthog/issues/4849 - return this.postHog.getAllFlags(fullId, { + return await this.postHog.getAllFlags(fullId, { personProperties: { created_at_timestamp: user.createdAt.getTime().toString(), }, diff --git a/packages/cli/src/security-audit/SecurityAudit.service.ts b/packages/cli/src/security-audit/SecurityAudit.service.ts index 3c41df5e39..afec146065 100644 --- a/packages/cli/src/security-audit/SecurityAudit.service.ts +++ b/packages/cli/src/security-audit/SecurityAudit.service.ts @@ -30,7 +30,7 @@ export class SecurityAuditService { select: ['id', 'name', 'active', 'nodes', 'connections'], }); - const promises = categories.map(async (c) => this.reporters[c].report(workflows)); + const promises = categories.map(async (c) => await this.reporters[c].report(workflows)); const reports = (await Promise.all(promises)).filter((r): r is Risk.Report => r !== null); diff --git a/packages/cli/src/security-audit/risk-reporters/CredentialsRiskReporter.ts b/packages/cli/src/security-audit/risk-reporters/CredentialsRiskReporter.ts index 869aa7c7e3..04d20199b6 100644 --- a/packages/cli/src/security-audit/risk-reporters/CredentialsRiskReporter.ts +++ b/packages/cli/src/security-audit/risk-reporters/CredentialsRiskReporter.ts @@ -119,7 +119,7 @@ export class CredentialsRiskReporter implements RiskReporter { const executionIds = await this.executionRepository.getIdsSince(date); - return this.executionDataRepository.findByExecutionIds(executionIds); + return await this.executionDataRepository.findByExecutionIds(executionIds); } /** diff --git a/packages/cli/src/services/activeWorkflows.service.ts b/packages/cli/src/services/activeWorkflows.service.ts index 7684cb3275..25de43fd1c 100644 --- a/packages/cli/src/services/activeWorkflows.service.ts +++ b/packages/cli/src/services/activeWorkflows.service.ts @@ -47,6 +47,6 @@ export class ActiveWorkflowsService { throw new BadRequestError(`Workflow with ID "${workflowId}" could not be found.`); } - return this.activationErrorsService.get(workflowId); + return await this.activationErrorsService.get(workflowId); } } diff --git a/packages/cli/src/services/cache/cache.service.ts b/packages/cli/src/services/cache/cache.service.ts index 421597a172..09499a4da4 100644 --- a/packages/cli/src/services/cache/cache.service.ts +++ b/packages/cli/src/services/cache/cache.service.ts @@ -332,7 +332,7 @@ export class CacheService extends EventEmitter { if (keys.length === 0) return; - return this.cache.store.mdel(...keys); + return await this.cache.store.mdel(...keys); } /** diff --git a/packages/cli/src/services/cache/redis.cache-manager.ts b/packages/cli/src/services/cache/redis.cache-manager.ts index d556dacdc7..429e0d93c0 100644 --- a/packages/cli/src/services/cache/redis.cache-manager.ts +++ b/packages/cli/src/services/cache/redis.cache-manager.ts @@ -88,7 +88,7 @@ function builder( ); }, mget: async (...args) => - redisCache + await redisCache .mget(args) .then((results) => results.map((result) => @@ -101,8 +101,8 @@ function builder( async del(key) { await redisCache.del(key); }, - ttl: async (key) => redisCache.pttl(key), - keys: async (pattern = '*') => keys(pattern), + ttl: async (key) => await redisCache.pttl(key), + keys: async (pattern = '*') => await keys(pattern), reset, isCacheable, get client() { @@ -137,7 +137,7 @@ function builder( await redisCache.hset(key, fieldValueRecord); }, async hkeys(key: string) { - return redisCache.hkeys(key); + return await redisCache.hkeys(key); }, async hvals(key: string): Promise { const values = await redisCache.hvals(key); @@ -147,7 +147,7 @@ function builder( return (await redisCache.hexists(key, field)) === 1; }, async hdel(key: string, field: string) { - return redisCache.hdel(key, field); + return await redisCache.hdel(key, field); }, } as RedisStore; } @@ -156,7 +156,7 @@ export function redisStoreUsingClient(redisCache: Redis | Cluster, options?: Con const reset = async () => { await redisCache.flushdb(); }; - const keys = async (pattern: string) => redisCache.keys(pattern); + const keys = async (pattern: string) => await redisCache.keys(pattern); return builder(redisCache, reset, keys, options); } diff --git a/packages/cli/src/services/communityPackages.service.ts b/packages/cli/src/services/communityPackages.service.ts index 53b37b99ea..9ee89f61a0 100644 --- a/packages/cli/src/services/communityPackages.service.ts +++ b/packages/cli/src/services/communityPackages.service.ts @@ -59,22 +59,22 @@ export class CommunityPackagesService { } async findInstalledPackage(packageName: string) { - return this.installedPackageRepository.findOne({ + return await this.installedPackageRepository.findOne({ where: { packageName }, relations: ['installedNodes'], }); } async isPackageInstalled(packageName: string) { - return this.installedPackageRepository.exist({ where: { packageName } }); + return await this.installedPackageRepository.exist({ where: { packageName } }); } async getAllInstalledPackages() { - return this.installedPackageRepository.find({ relations: ['installedNodes'] }); + return await this.installedPackageRepository.find({ relations: ['installedNodes'] }); } async removePackageFromDatabase(packageName: InstalledPackages) { - return this.installedPackageRepository.remove(packageName); + return await this.installedPackageRepository.remove(packageName); } async persistInstalledPackage(packageLoader: PackageDirectoryLoader) { @@ -297,14 +297,14 @@ export class CommunityPackagesService { } async installNpmModule(packageName: string, version?: string): Promise { - return this.installOrUpdateNpmModule(packageName, { version }); + return await this.installOrUpdateNpmModule(packageName, { version }); } async updateNpmModule( packageName: string, installedPackage: InstalledPackages, ): Promise { - return this.installOrUpdateNpmModule(packageName, { installedPackage }); + return await this.installOrUpdateNpmModule(packageName, { installedPackage }); } async removeNpmModule(packageName: string, installedPackage: InstalledPackages): Promise { diff --git a/packages/cli/src/services/events.service.ts b/packages/cli/src/services/events.service.ts index 64c8a0e499..10a0e7dc6c 100644 --- a/packages/cli/src/services/events.service.ts +++ b/packages/cli/src/services/events.service.ts @@ -17,9 +17,13 @@ export class EventsService extends EventEmitter { super({ captureRejections: true }); if ('SKIP_STATISTICS_EVENTS' in process.env) return; - this.on('nodeFetchedData', async (workflowId, node) => this.nodeFetchedData(workflowId, node)); - this.on('workflowExecutionCompleted', async (workflowData, runData) => - this.workflowExecutionCompleted(workflowData, runData), + this.on( + 'nodeFetchedData', + async (workflowId, node) => await this.nodeFetchedData(workflowId, node), + ); + this.on( + 'workflowExecutionCompleted', + async (workflowData, runData) => await this.workflowExecutionCompleted(workflowData, runData), ); } diff --git a/packages/cli/src/services/executionMetadata.service.ts b/packages/cli/src/services/executionMetadata.service.ts index 6b62ee2365..26db3dcb4c 100644 --- a/packages/cli/src/services/executionMetadata.service.ts +++ b/packages/cli/src/services/executionMetadata.service.ts @@ -19,6 +19,6 @@ export class ExecutionMetadataService { }); } - return this.executionMetadataRepository.save(metadataRows); + return await this.executionMetadataRepository.save(metadataRows); } } diff --git a/packages/cli/src/services/frontend.service.ts b/packages/cli/src/services/frontend.service.ts index 9c0828c912..7849884d1a 100644 --- a/packages/cli/src/services/frontend.service.ts +++ b/packages/cli/src/services/frontend.service.ts @@ -49,7 +49,7 @@ export class FrontendService { private readonly urlService: UrlService, private readonly internalHooks: InternalHooks, ) { - loadNodesAndCredentials.addPostProcessor(async () => this.generateTypes()); + loadNodesAndCredentials.addPostProcessor(async () => await this.generateTypes()); void this.generateTypes(); this.initSettings(); diff --git a/packages/cli/src/services/naming.service.ts b/packages/cli/src/services/naming.service.ts index 3b49e48803..4244f114de 100644 --- a/packages/cli/src/services/naming.service.ts +++ b/packages/cli/src/services/naming.service.ts @@ -10,11 +10,11 @@ export class NamingService { ) {} async getUniqueWorkflowName(requestedName: string) { - return this.getUniqueName(requestedName, 'workflow'); + return await this.getUniqueName(requestedName, 'workflow'); } async getUniqueCredentialName(requestedName: string) { - return this.getUniqueName(requestedName, 'credential'); + return await this.getUniqueName(requestedName, 'credential'); } private async getUniqueName(requestedName: string, entity: 'workflow' | 'credential') { diff --git a/packages/cli/src/services/orchestration/main/MultiMainSetup.ee.ts b/packages/cli/src/services/orchestration/main/MultiMainSetup.ee.ts index 55822c9ac8..b5eeb69074 100644 --- a/packages/cli/src/services/orchestration/main/MultiMainSetup.ee.ts +++ b/packages/cli/src/services/orchestration/main/MultiMainSetup.ee.ts @@ -137,6 +137,6 @@ export class MultiMainSetup extends SingleMainSetup { } async fetchLeaderKey() { - return this.redisPublisher.get(this.leaderKey); + return await this.redisPublisher.get(this.leaderKey); } } diff --git a/packages/cli/src/services/orchestration/webhook/handleCommandMessageWebhook.ts b/packages/cli/src/services/orchestration/webhook/handleCommandMessageWebhook.ts index f93474e043..3be43ae835 100644 --- a/packages/cli/src/services/orchestration/webhook/handleCommandMessageWebhook.ts +++ b/packages/cli/src/services/orchestration/webhook/handleCommandMessageWebhook.ts @@ -2,5 +2,5 @@ import { handleCommandMessageMain } from '../main/handleCommandMessageMain'; export async function handleCommandMessageWebhook(messageString: string) { // currently webhooks handle commands the same way as the main instance - return handleCommandMessageMain(messageString); + return await handleCommandMessageMain(messageString); } diff --git a/packages/cli/src/services/ownership.service.ts b/packages/cli/src/services/ownership.service.ts index 669356250f..2dfb4e29c3 100644 --- a/packages/cli/src/services/ownership.service.ts +++ b/packages/cli/src/services/ownership.service.ts @@ -74,7 +74,7 @@ export class OwnershipService { async getInstanceOwner() { const globalOwnerRole = await this.roleService.findGlobalOwnerRole(); - return this.userRepository.findOneOrFail({ + return await this.userRepository.findOneOrFail({ where: { globalRoleId: globalOwnerRole.id }, relations: ['globalRole'], }); diff --git a/packages/cli/src/services/password.utility.ts b/packages/cli/src/services/password.utility.ts index d600371e55..314a3fef71 100644 --- a/packages/cli/src/services/password.utility.ts +++ b/packages/cli/src/services/password.utility.ts @@ -12,11 +12,11 @@ export class PasswordUtility { const SALT_ROUNDS = 10; const salt = genSaltSync(SALT_ROUNDS); - return hash(plaintext, salt); + return await hash(plaintext, salt); } async compare(plaintext: string, hashed: string) { - return compare(plaintext, hashed); + return await compare(plaintext, hashed); } validate(plaintext?: string) { diff --git a/packages/cli/src/services/pruning.service.ts b/packages/cli/src/services/pruning.service.ts index f2b142bf1f..3f8d57d6cb 100644 --- a/packages/cli/src/services/pruning.service.ts +++ b/packages/cli/src/services/pruning.service.ts @@ -74,7 +74,7 @@ export class PruningService { const when = [rateMs / TIME.MINUTE, 'min'].join(' '); this.softDeletionInterval = setInterval( - async () => this.softDeleteOnPruningCycle(), + async () => await this.softDeleteOnPruningCycle(), this.rates.softDeletion, ); diff --git a/packages/cli/src/services/redis/RedisServicePubSubPublisher.ts b/packages/cli/src/services/redis/RedisServicePubSubPublisher.ts index 3e23208138..fe080d8e0f 100644 --- a/packages/cli/src/services/redis/RedisServicePubSubPublisher.ts +++ b/packages/cli/src/services/redis/RedisServicePubSubPublisher.ts @@ -57,7 +57,7 @@ export class RedisServicePubSubPublisher extends RedisServiceBaseSender { async get(key: string) { if (!this.redisClient) await this.init(); - return this.redisClient?.get(key); + return await this.redisClient?.get(key); } async clear(key: string) { diff --git a/packages/cli/src/services/role.service.ts b/packages/cli/src/services/role.service.ts index ea880bb9eb..b7c432632f 100644 --- a/packages/cli/src/services/role.service.ts +++ b/packages/cli/src/services/role.service.ts @@ -66,35 +66,35 @@ export class RoleService { } async findGlobalOwnerRole() { - return this.findCached('global', 'owner'); + return await this.findCached('global', 'owner'); } async findGlobalMemberRole() { - return this.findCached('global', 'member'); + return await this.findCached('global', 'member'); } async findGlobalAdminRole() { - return this.findCached('global', 'admin'); + return await this.findCached('global', 'admin'); } async findWorkflowOwnerRole() { - return this.findCached('workflow', 'owner'); + return await this.findCached('workflow', 'owner'); } async findWorkflowEditorRole() { - return this.findCached('workflow', 'editor'); + return await this.findCached('workflow', 'editor'); } async findCredentialOwnerRole() { - return this.findCached('credential', 'owner'); + return await this.findCached('credential', 'owner'); } async findCredentialUserRole() { - return this.findCached('credential', 'user'); + return await this.findCached('credential', 'user'); } async findRoleByUserAndWorkflow(userId: string, workflowId: string) { - return this.sharedWorkflowRepository + return await this.sharedWorkflowRepository .findOne({ where: { workflowId, userId }, relations: ['role'], diff --git a/packages/cli/src/services/tag.service.ts b/packages/cli/src/services/tag.service.ts index 0de35883af..f4f888efb4 100644 --- a/packages/cli/src/services/tag.service.ts +++ b/packages/cli/src/services/tag.service.ts @@ -31,7 +31,7 @@ export class TagService { await this.externalHooks.run(`tag.after${action}`, [tag]); - return savedTag; + return await savedTag; } async delete(id: string) { @@ -41,7 +41,7 @@ export class TagService { await this.externalHooks.run('tag.afterDelete', [id]); - return deleteResult; + return await deleteResult; } async getAll(options?: T): Promise> { @@ -59,9 +59,9 @@ export class TagService { }) as GetAllResult; } - return this.tagRepository.find({ + return await (this.tagRepository.find({ select: ['id', 'name', 'createdAt', 'updatedAt'], - }) as Promise>; + }) as Promise>); } /** diff --git a/packages/cli/src/services/test-webhook-registrations.service.ts b/packages/cli/src/services/test-webhook-registrations.service.ts index 58a80dd758..098562e54a 100644 --- a/packages/cli/src/services/test-webhook-registrations.service.ts +++ b/packages/cli/src/services/test-webhook-registrations.service.ts @@ -46,7 +46,7 @@ export class TestWebhookRegistrationsService { } async get(key: string) { - return this.cacheService.getHashValue(this.cacheKey, key); + return await this.cacheService.getHashValue(this.cacheKey, key); } async getAllKeys() { diff --git a/packages/cli/src/services/user.service.ts b/packages/cli/src/services/user.service.ts index 1d3306d4be..8a0b5e1772 100644 --- a/packages/cli/src/services/user.service.ts +++ b/packages/cli/src/services/user.service.ts @@ -28,7 +28,7 @@ export class UserService { ) {} async update(userId: string, data: Partial) { - return this.userRepository.update(userId, data); + return await this.userRepository.update(userId, data); } getManager() { @@ -38,7 +38,7 @@ export class UserService { async updateSettings(userId: string, newSettings: Partial) { const { settings } = await this.userRepository.findOneOrFail({ where: { id: userId } }); - return this.userRepository.update(userId, { settings: { ...settings, ...newSettings } }); + return await this.userRepository.update(userId, { settings: { ...settings, ...newSettings } }); } generatePasswordResetToken(user: User, expiresIn = '20m') { @@ -156,7 +156,7 @@ export class UserService { resolve(publicUser); }); - return Promise.race([fetchPromise, timeoutPromise]); + return await Promise.race([fetchPromise, timeoutPromise]); } private async sendEmails( @@ -166,7 +166,7 @@ export class UserService { ) { const domain = this.urlService.getInstanceBaseUrl(); - return Promise.all( + return await Promise.all( Object.entries(toInviteUsers).map(async ([email, id]) => { const inviteAcceptUrl = `${domain}/signup?inviterId=${owner.id}&inviteeId=${id}`; const invitedUser: UserRequest.InviteResponse = { @@ -246,18 +246,19 @@ export class UserService { ); try { - await this.getManager().transaction(async (transactionManager) => - Promise.all( - toCreateUsers.map(async ({ email, role }) => { - const newUser = Object.assign(new User(), { - email, - globalRole: role === 'member' ? memberRole : adminRole, - }); - const savedUser = await transactionManager.save(newUser); - createdUsers.set(email, savedUser.id); - return savedUser; - }), - ), + await this.getManager().transaction( + async (transactionManager) => + await Promise.all( + toCreateUsers.map(async ({ email, role }) => { + const newUser = Object.assign(new User(), { + email, + globalRole: role === 'member' ? memberRole : adminRole, + }); + const savedUser = await transactionManager.save(newUser); + createdUsers.set(email, savedUser.id); + return savedUser; + }), + ), ); } catch (error) { ErrorReporter.error(error); diff --git a/packages/cli/src/services/webhook.service.ts b/packages/cli/src/services/webhook.service.ts index d8bf6fe579..9840d90dec 100644 --- a/packages/cli/src/services/webhook.service.ts +++ b/packages/cli/src/services/webhook.service.ts @@ -43,7 +43,7 @@ export class WebhookService { * Find a matching webhook with zero dynamic path segments, e.g. `` or `user/profile`. */ private async findStaticWebhook(method: Method, path: string) { - return this.webhookRepository.findOneBy({ webhookPath: path, method }); + return await this.webhookRepository.findOneBy({ webhookPath: path, method }); } /** @@ -87,13 +87,13 @@ export class WebhookService { } async findWebhook(method: Method, path: string) { - return this.findCached(method, path); + return await this.findCached(method, path); } async storeWebhook(webhook: WebhookEntity) { void this.cacheService.set(webhook.cacheKey, webhook); - return this.webhookRepository.insert(webhook); + return await this.webhookRepository.insert(webhook); } createWebhook(data: Partial) { @@ -103,17 +103,17 @@ export class WebhookService { async deleteWorkflowWebhooks(workflowId: string) { const webhooks = await this.webhookRepository.findBy({ workflowId }); - return this.deleteWebhooks(webhooks); + return await this.deleteWebhooks(webhooks); } private async deleteWebhooks(webhooks: WebhookEntity[]) { void this.cacheService.deleteMany(webhooks.map((w) => w.cacheKey)); - return this.webhookRepository.remove(webhooks); + return await this.webhookRepository.remove(webhooks); } async getWebhookMethods(path: string) { - return this.webhookRepository + return await this.webhookRepository .find({ select: ['method'], where: { webhookPath: path } }) .then((rows) => rows.map((r) => r.method)); } diff --git a/packages/cli/src/shutdown/Shutdown.service.ts b/packages/cli/src/shutdown/Shutdown.service.ts index 14b3367c4b..1dd858f271 100644 --- a/packages/cli/src/shutdown/Shutdown.service.ts +++ b/packages/cli/src/shutdown/Shutdown.service.ts @@ -85,7 +85,7 @@ export class ShutdownService { const handlers = Object.values(this.handlersByPriority).reverse(); for (const handlerGroup of handlers) { await Promise.allSettled( - handlerGroup.map(async (handler) => this.shutdownComponent(handler)), + handlerGroup.map(async (handler) => await this.shutdownComponent(handler)), ); } } diff --git a/packages/cli/src/sso/saml/routes/saml.controller.ee.ts b/packages/cli/src/sso/saml/routes/saml.controller.ee.ts index 0dd3bb8ba2..cfa68bfdd6 100644 --- a/packages/cli/src/sso/saml/routes/saml.controller.ee.ts +++ b/packages/cli/src/sso/saml/routes/saml.controller.ee.ts @@ -107,7 +107,7 @@ export class SamlController { @NoAuthRequired() @Get(SamlUrls.acs, { middlewares: [samlLicensedMiddleware] }) async acsGet(req: SamlConfiguration.AcsRequest, res: express.Response) { - return this.acsHandler(req, res, 'redirect'); + return await this.acsHandler(req, res, 'redirect'); } /** @@ -117,7 +117,7 @@ export class SamlController { @NoAuthRequired() @Post(SamlUrls.acs, { middlewares: [samlLicensedMiddleware] }) async acsPost(req: SamlConfiguration.AcsRequest, res: express.Response) { - return this.acsHandler(req, res, 'post'); + return await this.acsHandler(req, res, 'post'); } /** @@ -198,7 +198,7 @@ export class SamlController { } catch { // ignore } - return this.handleInitSSO(res, redirectUrl); + return await this.handleInitSSO(res, redirectUrl); } /** @@ -209,7 +209,7 @@ export class SamlController { @Get(SamlUrls.configTest, { middlewares: [samlLicensedMiddleware] }) @RequireGlobalScope('saml:manage') async configTestGet(req: AuthenticatedRequest, res: express.Response) { - return this.handleInitSSO(res, getServiceProviderConfigTestReturnUrl()); + return await this.handleInitSSO(res, getServiceProviderConfigTestReturnUrl()); } private async handleInitSSO(res: express.Response, relayState?: string) { diff --git a/packages/cli/src/telemetry/index.ts b/packages/cli/src/telemetry/index.ts index 6fa2fdac18..1ead60a225 100644 --- a/packages/cli/src/telemetry/index.ts +++ b/packages/cli/src/telemetry/index.ts @@ -97,7 +97,7 @@ export class Telemetry { ...this.executionCountsBuffer[workflowId], }); - return promise; + return await promise; }); this.executionCountsBuffer = {}; @@ -117,7 +117,7 @@ export class Telemetry { read_only_instance: sourceControlPreferences.branchReadOnly, }; allPromises.push(this.track('pulse', pulsePacket)); - return Promise.all(allPromises); + return await Promise.all(allPromises); } async trackWorkflowExecution(properties: IExecutionTrackProperties): Promise { @@ -155,7 +155,7 @@ export class Telemetry { async trackN8nStop(): Promise { clearInterval(this.pulseIntervalReference); void this.track('User instance stopped'); - return new Promise(async (resolve) => { + return await new Promise(async (resolve) => { await this.postHog.stop(); if (this.rudderStack) { @@ -170,7 +170,7 @@ export class Telemetry { [key: string]: string | number | boolean | object | undefined | null; }): Promise { const { instanceId } = this.instanceSettings; - return new Promise((resolve) => { + return await new Promise((resolve) => { if (this.rudderStack) { this.rudderStack.identify( { @@ -191,7 +191,7 @@ export class Telemetry { { withPostHog } = { withPostHog: false }, // whether to additionally track with PostHog ): Promise { const { instanceId } = this.instanceSettings; - return new Promise((resolve) => { + return await new Promise((resolve) => { if (this.rudderStack) { const { user_id } = properties; const updatedProperties: ITelemetryTrackProperties = { diff --git a/packages/cli/src/workflows/workflowHistory/workflowHistory.service.ee.ts b/packages/cli/src/workflows/workflowHistory/workflowHistory.service.ee.ts index 853c7260f5..4bdc337bc3 100644 --- a/packages/cli/src/workflows/workflowHistory/workflowHistory.service.ee.ts +++ b/packages/cli/src/workflows/workflowHistory/workflowHistory.service.ee.ts @@ -19,7 +19,7 @@ export class WorkflowHistoryService { ) {} private async getSharedWorkflow(user: User, workflowId: string): Promise { - return this.sharedWorkflowRepository.findOne({ + return await this.sharedWorkflowRepository.findOne({ where: { ...(!user.hasGlobalScope('workflow:read') && { userId: user.id }), workflowId, @@ -37,7 +37,7 @@ export class WorkflowHistoryService { if (!sharedWorkflow) { throw new SharedWorkflowNotFoundError(''); } - return this.workflowHistoryRepository.find({ + return await this.workflowHistoryRepository.find({ where: { workflowId: sharedWorkflow.workflowId, }, diff --git a/packages/cli/src/workflows/workflowHistory/workflowHistoryManager.ee.ts b/packages/cli/src/workflows/workflowHistory/workflowHistoryManager.ee.ts index dac682c234..4898666c88 100644 --- a/packages/cli/src/workflows/workflowHistory/workflowHistoryManager.ee.ts +++ b/packages/cli/src/workflows/workflowHistory/workflowHistoryManager.ee.ts @@ -15,7 +15,7 @@ export class WorkflowHistoryManager { clearInterval(this.pruneTimer); } - this.pruneTimer = setInterval(async () => this.prune(), WORKFLOW_HISTORY_PRUNE_INTERVAL); + this.pruneTimer = setInterval(async () => await this.prune(), WORKFLOW_HISTORY_PRUNE_INTERVAL); } shutdown() { diff --git a/packages/cli/src/workflows/workflows.controller.ts b/packages/cli/src/workflows/workflows.controller.ts index b3fa0307ce..425d384dd3 100644 --- a/packages/cli/src/workflows/workflows.controller.ts +++ b/packages/cli/src/workflows/workflows.controller.ts @@ -329,7 +329,7 @@ export class WorkflowsController { } } - return this.workflowExecutionService.executeManually( + return await this.workflowExecutionService.executeManually( req.body, req.user, GenericHelpers.getSessionId(req), diff --git a/packages/cli/test/integration/ExternalSecrets/externalSecrets.api.test.ts b/packages/cli/test/integration/ExternalSecrets/externalSecrets.api.test.ts index 6eeb7e5b3d..868c695f4a 100644 --- a/packages/cli/test/integration/ExternalSecrets/externalSecrets.api.test.ts +++ b/packages/cli/test/integration/ExternalSecrets/externalSecrets.api.test.ts @@ -35,7 +35,7 @@ const testServer = setupTestServer({ const connectedDate = '2023-08-01T12:32:29.000Z'; async function setExternalSecretsSettings(settings: ExternalSecretsSettings) { - return Container.get(SettingsRepository).saveEncryptedSecretsProviderSettings( + return await Container.get(SettingsRepository).saveEncryptedSecretsProviderSettings( Container.get(Cipher).encrypt(settings), ); } @@ -45,7 +45,7 @@ async function getExternalSecretsSettings(): Promise { diff --git a/packages/cli/test/integration/executions.controller.test.ts b/packages/cli/test/integration/executions.controller.test.ts index 35c5dd12f0..711b0716b5 100644 --- a/packages/cli/test/integration/executions.controller.test.ts +++ b/packages/cli/test/integration/executions.controller.test.ts @@ -17,7 +17,7 @@ let owner: User; const saveExecution = async ({ belongingTo }: { belongingTo: User }) => { const workflow = await createWorkflow({}, belongingTo); - return createSuccessfulExecution(workflow); + return await createSuccessfulExecution(workflow); }; beforeEach(async () => { diff --git a/packages/cli/test/integration/ldap/ldap.api.test.ts b/packages/cli/test/integration/ldap/ldap.api.test.ts index 0a1ce8f437..96c7115ecc 100644 --- a/packages/cli/test/integration/ldap/ldap.api.test.ts +++ b/packages/cli/test/integration/ldap/ldap.api.test.ts @@ -93,7 +93,7 @@ const createLdapConfig = async (attributes: Partial = {}): Promise { diff --git a/packages/cli/test/integration/pruning.service.test.ts b/packages/cli/test/integration/pruning.service.test.ts index 14ed6b4874..59b601eb3a 100644 --- a/packages/cli/test/integration/pruning.service.test.ts +++ b/packages/cli/test/integration/pruning.service.test.ts @@ -47,7 +47,7 @@ describe('softDeleteOnPruningCycle()', () => { }); async function findAllExecutions() { - return Container.get(ExecutionRepository).find({ + return await Container.get(ExecutionRepository).find({ order: { id: 'asc' }, withDeleted: true, }); diff --git a/packages/cli/test/integration/security-audit/DatabaseRiskReporter.test.ts b/packages/cli/test/integration/security-audit/DatabaseRiskReporter.test.ts index 63c50caad9..e7ae638d97 100644 --- a/packages/cli/test/integration/security-audit/DatabaseRiskReporter.test.ts +++ b/packages/cli/test/integration/security-audit/DatabaseRiskReporter.test.ts @@ -55,7 +55,7 @@ test('should report expressions in queries', async () => { ], }; - return Container.get(WorkflowRepository).save(details); + return await Container.get(WorkflowRepository).save(details); }); await Promise.all(promises); @@ -110,7 +110,7 @@ test('should report expressions in query params', async () => { ], }; - return Container.get(WorkflowRepository).save(details); + return await Container.get(WorkflowRepository).save(details); }); await Promise.all(promises); @@ -162,7 +162,7 @@ test('should report unused query params', async () => { ], }; - return Container.get(WorkflowRepository).save(details); + return await Container.get(WorkflowRepository).save(details); }); await Promise.all(promises); diff --git a/packages/cli/test/integration/security-audit/FilesystemRiskReporter.test.ts b/packages/cli/test/integration/security-audit/FilesystemRiskReporter.test.ts index a3e3f69cc9..f6d8537c0d 100644 --- a/packages/cli/test/integration/security-audit/FilesystemRiskReporter.test.ts +++ b/packages/cli/test/integration/security-audit/FilesystemRiskReporter.test.ts @@ -47,7 +47,7 @@ test('should report filesystem interaction nodes', async () => { ], }); - return Container.get(WorkflowRepository).save(details); + return await Container.get(WorkflowRepository).save(details); }); await Promise.all(promises); diff --git a/packages/cli/test/integration/security-audit/InstanceRiskReporter.test.ts b/packages/cli/test/integration/security-audit/InstanceRiskReporter.test.ts index 102ff1c6d6..26e8560345 100644 --- a/packages/cli/test/integration/security-audit/InstanceRiskReporter.test.ts +++ b/packages/cli/test/integration/security-audit/InstanceRiskReporter.test.ts @@ -102,7 +102,7 @@ test('should not report webhooks having basic or header auth', async () => { ], }; - return Container.get(WorkflowRepository).save(details); + return await Container.get(WorkflowRepository).save(details); }); await Promise.all(promises); @@ -165,7 +165,7 @@ test('should not report webhooks validated by direct children', async () => { }, }; - return Container.get(WorkflowRepository).save(details); + return await Container.get(WorkflowRepository).save(details); }); await Promise.all(promises); diff --git a/packages/cli/test/integration/security-audit/NodesRiskReporter.test.ts b/packages/cli/test/integration/security-audit/NodesRiskReporter.test.ts index f10dbee30e..03386bef00 100644 --- a/packages/cli/test/integration/security-audit/NodesRiskReporter.test.ts +++ b/packages/cli/test/integration/security-audit/NodesRiskReporter.test.ts @@ -58,7 +58,7 @@ test('should report risky official nodes', async () => { ], }); - return Container.get(WorkflowRepository).save(details); + return await Container.get(WorkflowRepository).save(details); }); await Promise.all(promises); diff --git a/packages/cli/test/integration/security-audit/utils.ts b/packages/cli/test/integration/security-audit/utils.ts index 1ad445e950..bd0d5a6ead 100644 --- a/packages/cli/test/integration/security-audit/utils.ts +++ b/packages/cli/test/integration/security-audit/utils.ts @@ -53,7 +53,7 @@ export async function saveManualTriggerWorkflow() { ], }; - return Container.get(WorkflowRepository).save(details); + return await Container.get(WorkflowRepository).save(details); } export const MOCK_09990_N8N_VERSION = { diff --git a/packages/cli/test/integration/shared/db/credentials.ts b/packages/cli/test/integration/shared/db/credentials.ts index 67704e21ca..cc6c520869 100644 --- a/packages/cli/test/integration/shared/db/credentials.ts +++ b/packages/cli/test/integration/shared/db/credentials.ts @@ -55,17 +55,17 @@ export async function shareCredentialWithUsers(credential: CredentialsEntity, us roleId: role?.id, }), ); - return Container.get(SharedCredentialsRepository).save(newSharedCredentials); + return await Container.get(SharedCredentialsRepository).save(newSharedCredentials); } export function affixRoleToSaveCredential(role: Role) { return async (credentialPayload: CredentialPayload, { user }: { user: User }) => - saveCredential(credentialPayload, { user, role }); + await saveCredential(credentialPayload, { user, role }); } export async function getAllCredentials() { - return Container.get(CredentialsRepository).find(); + return await Container.get(CredentialsRepository).find(); } export const getCredentialById = async (id: string) => - Container.get(CredentialsRepository).findOneBy({ id }); + await Container.get(CredentialsRepository).findOneBy({ id }); diff --git a/packages/cli/test/integration/shared/db/executions.ts b/packages/cli/test/integration/shared/db/executions.ts index f68e5edf36..f5c6e5ddae 100644 --- a/packages/cli/test/integration/shared/db/executions.ts +++ b/packages/cli/test/integration/shared/db/executions.ts @@ -10,8 +10,8 @@ export async function createManyExecutions( workflow: WorkflowEntity, callback: (workflow: WorkflowEntity) => Promise, ) { - const executionsRequests = [...Array(amount)].map(async (_) => callback(workflow)); - return Promise.all(executionsRequests); + const executionsRequests = [...Array(amount)].map(async (_) => await callback(workflow)); + return await Promise.all(executionsRequests); } /** @@ -47,23 +47,29 @@ export async function createExecution( * Store a successful execution in the DB and assign it to a workflow. */ export async function createSuccessfulExecution(workflow: WorkflowEntity) { - return createExecution({ finished: true, status: 'success' }, workflow); + return await createExecution({ finished: true, status: 'success' }, workflow); } /** * Store an error execution in the DB and assign it to a workflow. */ export async function createErrorExecution(workflow: WorkflowEntity) { - return createExecution({ finished: false, stoppedAt: new Date(), status: 'failed' }, workflow); + return await createExecution( + { finished: false, stoppedAt: new Date(), status: 'failed' }, + workflow, + ); } /** * Store a waiting execution in the DB and assign it to a workflow. */ export async function createWaitingExecution(workflow: WorkflowEntity) { - return createExecution({ finished: false, waitTill: new Date(), status: 'waiting' }, workflow); + return await createExecution( + { finished: false, waitTill: new Date(), status: 'waiting' }, + workflow, + ); } export async function getAllExecutions() { - return Container.get(ExecutionRepository).find(); + return await Container.get(ExecutionRepository).find(); } diff --git a/packages/cli/test/integration/shared/db/roles.ts b/packages/cli/test/integration/shared/db/roles.ts index 9be0c43193..a525489093 100644 --- a/packages/cli/test/integration/shared/db/roles.ts +++ b/packages/cli/test/integration/shared/db/roles.ts @@ -2,31 +2,31 @@ import Container from 'typedi'; import { RoleService } from '@/services/role.service'; export async function getGlobalOwnerRole() { - return Container.get(RoleService).findGlobalOwnerRole(); + return await Container.get(RoleService).findGlobalOwnerRole(); } export async function getGlobalMemberRole() { - return Container.get(RoleService).findGlobalMemberRole(); + return await Container.get(RoleService).findGlobalMemberRole(); } export async function getGlobalAdminRole() { - return Container.get(RoleService).findGlobalAdminRole(); + return await Container.get(RoleService).findGlobalAdminRole(); } export async function getWorkflowOwnerRole() { - return Container.get(RoleService).findWorkflowOwnerRole(); + return await Container.get(RoleService).findWorkflowOwnerRole(); } export async function getWorkflowEditorRole() { - return Container.get(RoleService).findWorkflowEditorRole(); + return await Container.get(RoleService).findWorkflowEditorRole(); } export async function getCredentialOwnerRole() { - return Container.get(RoleService).findCredentialOwnerRole(); + return await Container.get(RoleService).findCredentialOwnerRole(); } export async function getAllRoles() { - return Promise.all([ + return await Promise.all([ getGlobalOwnerRole(), getGlobalMemberRole(), getWorkflowOwnerRole(), diff --git a/packages/cli/test/integration/shared/db/users.ts b/packages/cli/test/integration/shared/db/users.ts index ce7515b4b7..c4e95cd9c3 100644 --- a/packages/cli/test/integration/shared/db/users.ts +++ b/packages/cli/test/integration/shared/db/users.ts @@ -27,7 +27,7 @@ export async function createUser(attributes: Partial = {}): Promise }); user.computeIsOwner(); - return Container.get(UserRepository).save(user); + return await Container.get(UserRepository).save(user); } export async function createLdapUser(attributes: Partial, ldapId: string): Promise { @@ -70,15 +70,15 @@ export async function createUserWithMfaEnabled( } export async function createOwner() { - return createUser({ globalRole: await getGlobalOwnerRole() }); + return await createUser({ globalRole: await getGlobalOwnerRole() }); } export async function createMember() { - return createUser({ globalRole: await getGlobalMemberRole() }); + return await createUser({ globalRole: await getGlobalMemberRole() }); } export async function createAdmin() { - return createUser({ globalRole: await getGlobalAdminRole() }); + return await createUser({ globalRole: await getGlobalAdminRole() }); } export async function createUserShell(globalRole: Role): Promise { @@ -92,7 +92,7 @@ export async function createUserShell(globalRole: Role): Promise { shell.email = randomEmail(); } - return Container.get(UserRepository).save(shell); + return await Container.get(UserRepository).save(shell); } /** @@ -120,27 +120,27 @@ export async function createManyUsers( ), ); - return Container.get(UserRepository).save(users); + return await Container.get(UserRepository).save(users); } export async function addApiKey(user: User): Promise { user.apiKey = randomApiKey(); - return Container.get(UserRepository).save(user); + return await Container.get(UserRepository).save(user); } export const getAllUsers = async () => - Container.get(UserRepository).find({ + await Container.get(UserRepository).find({ relations: ['globalRole', 'authIdentities'], }); export const getUserById = async (id: string) => - Container.get(UserRepository).findOneOrFail({ + await Container.get(UserRepository).findOneOrFail({ where: { id }, relations: ['globalRole', 'authIdentities'], }); export const getLdapIdentities = async () => - Container.get(AuthIdentityRepository).find({ + await Container.get(AuthIdentityRepository).find({ where: { providerType: 'ldap' }, relations: ['user'], }); diff --git a/packages/cli/test/integration/shared/db/workflowHistory.ts b/packages/cli/test/integration/shared/db/workflowHistory.ts index 8233d80077..ab4109cef7 100644 --- a/packages/cli/test/integration/shared/db/workflowHistory.ts +++ b/packages/cli/test/integration/shared/db/workflowHistory.ts @@ -7,7 +7,7 @@ export async function createWorkflowHistoryItem( workflowId: string, data?: Partial, ) { - return Container.get(WorkflowHistoryRepository).save({ + return await Container.get(WorkflowHistoryRepository).save({ authors: 'John Smith', connections: {}, nodes: [ @@ -32,12 +32,13 @@ export async function createManyWorkflowHistoryItems( time?: Date, ) { const baseTime = (time ?? new Date()).valueOf(); - return Promise.all( - [...Array(count)].map(async (_, i) => - createWorkflowHistoryItem(workflowId, { - createdAt: new Date(baseTime + i), - updatedAt: new Date(baseTime + i), - }), + return await Promise.all( + [...Array(count)].map( + async (_, i) => + await createWorkflowHistoryItem(workflowId, { + createdAt: new Date(baseTime + i), + updatedAt: new Date(baseTime + i), + }), ), ); } diff --git a/packages/cli/test/integration/shared/db/workflows.ts b/packages/cli/test/integration/shared/db/workflows.ts index 981b126cbd..6356d92dd8 100644 --- a/packages/cli/test/integration/shared/db/workflows.ts +++ b/packages/cli/test/integration/shared/db/workflows.ts @@ -11,8 +11,10 @@ export async function createManyWorkflows( attributes: Partial = {}, user?: User, ) { - const workflowRequests = [...Array(amount)].map(async (_) => createWorkflow(attributes, user)); - return Promise.all(workflowRequests); + const workflowRequests = [...Array(amount)].map( + async (_) => await createWorkflow(attributes, user), + ); + return await Promise.all(workflowRequests); } /** @@ -60,11 +62,11 @@ export async function shareWorkflowWithUsers(workflow: WorkflowEntity, users: Us workflow, role, })); - return Container.get(SharedWorkflowRepository).save(sharedWorkflows); + return await Container.get(SharedWorkflowRepository).save(sharedWorkflows); } export async function getWorkflowSharing(workflow: WorkflowEntity) { - return Container.get(SharedWorkflowRepository).findBy({ + return await Container.get(SharedWorkflowRepository).findBy({ workflowId: workflow.id, }); } @@ -115,8 +117,8 @@ export async function createWorkflowWithTrigger( } export async function getAllWorkflows() { - return Container.get(WorkflowRepository).find(); + return await Container.get(WorkflowRepository).find(); } export const getWorkflowById = async (id: string) => - Container.get(WorkflowRepository).findOneBy({ id }); + await Container.get(WorkflowRepository).findOneBy({ id }); diff --git a/packages/cli/test/integration/shared/utils/testServer.ts b/packages/cli/test/integration/shared/utils/testServer.ts index 496c3711f6..3d9e2cd2bf 100644 --- a/packages/cli/test/integration/shared/utils/testServer.ts +++ b/packages/cli/test/integration/shared/utils/testServer.ts @@ -38,7 +38,7 @@ function prefix(pathSegment: string) { url.pathname = pathSegment + url.pathname; request.url = url.toString(); - return request; + return await request; }; } diff --git a/packages/cli/test/integration/variables.test.ts b/packages/cli/test/integration/variables.test.ts index ae8f03fe91..8b84f67dcf 100644 --- a/packages/cli/test/integration/variables.test.ts +++ b/packages/cli/test/integration/variables.test.ts @@ -26,7 +26,7 @@ async function createVariable(key: string, value: string) { } async function getVariableByKey(key: string) { - return Container.get(VariablesRepository).findOne({ + return await Container.get(VariablesRepository).findOne({ where: { key, }, @@ -34,7 +34,7 @@ async function getVariableByKey(key: string) { } async function getVariableById(id: string) { - return Container.get(VariablesRepository).findOne({ + return await Container.get(VariablesRepository).findOne({ where: { id, }, diff --git a/packages/cli/test/integration/workflowHistory.api.test.ts b/packages/cli/test/integration/workflowHistory.api.test.ts index b3beb574a3..1c4d1a9064 100644 --- a/packages/cli/test/integration/workflowHistory.api.test.ts +++ b/packages/cli/test/integration/workflowHistory.api.test.ts @@ -60,8 +60,9 @@ describe('GET /workflow-history/:workflowId', () => { const versions = await Promise.all( new Array(10) .fill(undefined) - .map(async (_, i) => - createWorkflowHistoryItem(workflow.id, { createdAt: new Date(Date.now() + i) }), + .map( + async (_, i) => + await createWorkflowHistoryItem(workflow.id, { createdAt: new Date(Date.now() + i) }), ), ); @@ -84,13 +85,14 @@ describe('GET /workflow-history/:workflowId', () => { const versions = await Promise.all( new Array(10) .fill(undefined) - .map(async (_, i) => - createWorkflowHistoryItem(workflow.id, { createdAt: new Date(Date.now() + i) }), + .map( + async (_, i) => + await createWorkflowHistoryItem(workflow.id, { createdAt: new Date(Date.now() + i) }), ), ); const versions2 = await Promise.all( - new Array(10).fill(undefined).map(async (_) => createWorkflowHistoryItem(workflow2.id)), + new Array(10).fill(undefined).map(async (_) => await createWorkflowHistoryItem(workflow2.id)), ); const last = versions.sort((a, b) => b.createdAt.valueOf() - a.createdAt.valueOf())[0]! as any; @@ -111,8 +113,9 @@ describe('GET /workflow-history/:workflowId', () => { const versions = await Promise.all( new Array(10) .fill(undefined) - .map(async (_, i) => - createWorkflowHistoryItem(workflow.id, { createdAt: new Date(Date.now() + i) }), + .map( + async (_, i) => + await createWorkflowHistoryItem(workflow.id, { createdAt: new Date(Date.now() + i) }), ), ); @@ -134,8 +137,9 @@ describe('GET /workflow-history/:workflowId', () => { const versions = await Promise.all( new Array(10) .fill(undefined) - .map(async (_, i) => - createWorkflowHistoryItem(workflow.id, { createdAt: new Date(Date.now() + i) }), + .map( + async (_, i) => + await createWorkflowHistoryItem(workflow.id, { createdAt: new Date(Date.now() + i) }), ), ); diff --git a/packages/cli/test/integration/workflowHistoryManager.test.ts b/packages/cli/test/integration/workflowHistoryManager.test.ts index fe8690efce..181c7c9266 100644 --- a/packages/cli/test/integration/workflowHistoryManager.test.ts +++ b/packages/cli/test/integration/workflowHistoryManager.test.ts @@ -102,7 +102,7 @@ describe('Workflow History Manager', () => { const createWorkflowHistory = async (ageInDays = 2) => { const workflow = await createWorkflow(); const time = DateTime.now().minus({ days: ageInDays }).toJSDate(); - return createManyWorkflowHistoryItems(workflow.id, 10, time); + return await createManyWorkflowHistoryItems(workflow.id, 10, time); }; const pruneAndAssertCount = async (finalCount = 10, initialCount = 10) => { diff --git a/packages/cli/test/teardown.ts b/packages/cli/test/teardown.ts index 339708d412..57ab933419 100644 --- a/packages/cli/test/teardown.ts +++ b/packages/cli/test/teardown.ts @@ -17,7 +17,9 @@ export default async () => { .filter(({ Database: dbName }) => dbName.startsWith(testDbPrefix)) .map(({ Database: dbName }) => dbName); - const promises = databases.map(async (dbName) => connection.query(`DROP DATABASE ${dbName};`)); + const promises = databases.map( + async (dbName) => await connection.query(`DROP DATABASE ${dbName};`), + ); await Promise.all(promises); await connection.destroy(); }; diff --git a/packages/cli/test/unit/ExternalSecrets/ExternalSecretsManager.test.ts b/packages/cli/test/unit/ExternalSecrets/ExternalSecretsManager.test.ts index b8a728d5f5..b77b33bd02 100644 --- a/packages/cli/test/unit/ExternalSecrets/ExternalSecretsManager.test.ts +++ b/packages/cli/test/unit/ExternalSecrets/ExternalSecretsManager.test.ts @@ -67,7 +67,7 @@ describe('External Secrets Manager', () => { mockProvidersInstance.setProviders({ dummy: ErrorProvider, }); - expect(async () => manager!.init()).not.toThrow(); + expect(async () => await manager!.init()).not.toThrow(); }); test('should not throw errors during shutdown', async () => { diff --git a/packages/cli/test/unit/Helpers.ts b/packages/cli/test/unit/Helpers.ts index 2504f1dfcf..50b9f43489 100644 --- a/packages/cli/test/unit/Helpers.ts +++ b/packages/cli/test/unit/Helpers.ts @@ -5,7 +5,7 @@ import type { INodeTypeData } from 'n8n-workflow'; * the macrotask queue and so called at the next iteration of the event loop * after all promises in the microtask queue have settled first. */ -export const flushPromises = async () => new Promise(setImmediate); +export const flushPromises = async () => await new Promise(setImmediate); export function mockNodeTypesData( nodeNames: string[], diff --git a/packages/cli/test/unit/PermissionChecker.test.ts b/packages/cli/test/unit/PermissionChecker.test.ts index 07c24fa11f..7c68fd3d94 100644 --- a/packages/cli/test/unit/PermissionChecker.test.ts +++ b/packages/cli/test/unit/PermissionChecker.test.ts @@ -123,7 +123,7 @@ describe('check()', () => { ], }); - expect(async () => permissionChecker.check(workflow, userId)).not.toThrow(); + expect(async () => await permissionChecker.check(workflow, userId)).not.toThrow(); }); test('should allow if requesting user is instance owner', async () => { @@ -153,7 +153,7 @@ describe('check()', () => { ], }); - expect(async () => permissionChecker.check(workflow, owner.id)).not.toThrow(); + expect(async () => await permissionChecker.check(workflow, owner.id)).not.toThrow(); }); test('should allow if workflow creds are valid subset', async () => { @@ -200,7 +200,7 @@ describe('check()', () => { ], }); - expect(async () => permissionChecker.check(workflow, owner.id)).not.toThrow(); + expect(async () => await permissionChecker.check(workflow, owner.id)).not.toThrow(); }); test('should deny if workflow creds are not valid subset', async () => { diff --git a/packages/cli/test/unit/UserManagementMailer.test.ts b/packages/cli/test/unit/UserManagementMailer.test.ts index 06ed1ab12d..8e9de8d3a9 100644 --- a/packages/cli/test/unit/UserManagementMailer.test.ts +++ b/packages/cli/test/unit/UserManagementMailer.test.ts @@ -22,7 +22,7 @@ describe('UserManagementMailer', () => { test('not be called when SMTP not set up', async () => { const userManagementMailer = new UserManagementMailer(); // NodeMailer.verifyConnection gets called only explicitly - await expect(async () => userManagementMailer.verifyConnection()).rejects.toThrow(); + await expect(async () => await userManagementMailer.verifyConnection()).rejects.toThrow(); expect(NodeMailer.prototype.verifyConnection).toHaveBeenCalledTimes(0); }); @@ -34,7 +34,7 @@ describe('UserManagementMailer', () => { const userManagementMailer = new UserManagementMailer(); // NodeMailer.verifyConnection gets called only explicitly - expect(async () => userManagementMailer.verifyConnection()).not.toThrow(); + expect(async () => await userManagementMailer.verifyConnection()).not.toThrow(); }); }); }); diff --git a/packages/cli/test/unit/services/communityPackages.service.test.ts b/packages/cli/test/unit/services/communityPackages.service.test.ts index 1d2a74e369..fe69d82629 100644 --- a/packages/cli/test/unit/services/communityPackages.service.test.ts +++ b/packages/cli/test/unit/services/communityPackages.service.test.ts @@ -173,7 +173,7 @@ describe('CommunityPackagesService', () => { mocked(exec).mockImplementation(erroringExecMock); - const call = async () => communityPackagesService.executeNpmCommand('ls'); + const call = async () => await communityPackagesService.executeNpmCommand('ls'); await expect(call).rejects.toThrowError(RESPONSE_ERROR_MESSAGES.PACKAGE_NOT_FOUND); diff --git a/packages/cli/test/unit/shutdown/Shutdown.service.test.ts b/packages/cli/test/unit/shutdown/Shutdown.service.test.ts index b253929cbb..ca85f78564 100644 --- a/packages/cli/test/unit/shutdown/Shutdown.service.test.ts +++ b/packages/cli/test/unit/shutdown/Shutdown.service.test.ts @@ -104,7 +104,7 @@ describe('ShutdownService', () => { }); it('should throw error if app is not shutting down', async () => { - await expect(async () => shutdownService.waitForShutdown()).rejects.toThrow( + await expect(async () => await shutdownService.waitForShutdown()).rejects.toThrow( 'App is not shutting down', ); }); diff --git a/packages/core/src/ActiveWorkflows.ts b/packages/core/src/ActiveWorkflows.ts index d42729b1f2..f331166fe4 100644 --- a/packages/core/src/ActiveWorkflows.ts +++ b/packages/core/src/ActiveWorkflows.ts @@ -209,8 +209,8 @@ export class ActiveWorkflows { const w = this.activeWorkflows[workflowId]; - w.triggerResponses?.forEach(async (r) => this.close(r, workflowId, 'trigger')); - w.pollResponses?.forEach(async (r) => this.close(r, workflowId, 'poller')); + w.triggerResponses?.forEach(async (r) => await this.close(r, workflowId, 'trigger')); + w.pollResponses?.forEach(async (r) => await this.close(r, workflowId, 'poller')); delete this.activeWorkflows[workflowId]; @@ -221,8 +221,8 @@ export class ActiveWorkflows { for (const workflowId of Object.keys(this.activeWorkflows)) { const w = this.activeWorkflows[workflowId]; - w.triggerResponses?.forEach(async (r) => this.close(r, workflowId, 'trigger')); - w.pollResponses?.forEach(async (r) => this.close(r, workflowId, 'poller')); + w.triggerResponses?.forEach(async (r) => await this.close(r, workflowId, 'trigger')); + w.pollResponses?.forEach(async (r) => await this.close(r, workflowId, 'poller')); } } diff --git a/packages/core/src/BinaryData/BinaryData.service.ts b/packages/core/src/BinaryData/BinaryData.service.ts index ad8a071b8f..8aadee3454 100644 --- a/packages/core/src/BinaryData/BinaryData.service.ts +++ b/packages/core/src/BinaryData/BinaryData.service.ts @@ -111,20 +111,20 @@ export class BinaryDataService { } async toBuffer(bufferOrStream: Buffer | Readable) { - return toBuffer(bufferOrStream); + return await toBuffer(bufferOrStream); } async getAsStream(binaryDataId: string, chunkSize?: number) { const [mode, fileId] = binaryDataId.split(':'); - return this.getManager(mode).getAsStream(fileId, chunkSize); + return await this.getManager(mode).getAsStream(fileId, chunkSize); } async getAsBuffer(binaryData: IBinaryData) { if (binaryData.id) { const [mode, fileId] = binaryData.id.split(':'); - return this.getManager(mode).getAsBuffer(fileId); + return await this.getManager(mode).getAsBuffer(fileId); } return Buffer.from(binaryData.data, BINARY_ENCODING); @@ -139,7 +139,7 @@ export class BinaryDataService { async getMetadata(binaryDataId: string) { const [mode, fileId] = binaryDataId.split(':'); - return this.getManager(mode).getMetadata(fileId); + return await this.getManager(mode).getMetadata(fileId); } async deleteMany(ids: BinaryData.IdsForDeletion) { @@ -159,10 +159,14 @@ export class BinaryDataService { const returnInputData = (inputData as INodeExecutionData[][]).map( async (executionDataArray) => { if (executionDataArray) { - return Promise.all( + return await Promise.all( executionDataArray.map(async (executionData) => { if (executionData.binary) { - return this.duplicateBinaryDataInExecData(workflowId, executionId, executionData); + return await this.duplicateBinaryDataInExecData( + workflowId, + executionId, + executionData, + ); } return executionData; @@ -174,7 +178,7 @@ export class BinaryDataService { }, ); - return Promise.all(returnInputData); + return await Promise.all(returnInputData); } return inputData as INodeExecutionData[][]; @@ -217,13 +221,13 @@ export class BinaryDataService { const [_mode, fileId] = binaryDataId.split(':'); - return manager?.copyByFileId(workflowId, executionId, fileId).then((newFileId) => ({ + return await manager?.copyByFileId(workflowId, executionId, fileId).then((newFileId) => ({ newId: this.createBinaryDataId(newFileId), key, })); }); - return Promise.all(bdPromises).then((b) => { + return await Promise.all(bdPromises).then((b) => { return b.reduce((acc, curr) => { if (acc.binary && curr) { acc.binary[curr.key].id = curr.newId; diff --git a/packages/core/src/BinaryData/FileSystem.manager.ts b/packages/core/src/BinaryData/FileSystem.manager.ts index 1e076ad4c7..5b7250d9eb 100644 --- a/packages/core/src/BinaryData/FileSystem.manager.ts +++ b/packages/core/src/BinaryData/FileSystem.manager.ts @@ -61,13 +61,13 @@ export class FileSystemManager implements BinaryData.Manager { throw new FileNotFoundError(filePath); } - return fs.readFile(filePath); + return await fs.readFile(filePath); } async getMetadata(fileId: string): Promise { const filePath = this.resolvePath(`${fileId}.metadata`); - return jsonParse(await fs.readFile(filePath, { encoding: 'utf-8' })); + return await jsonParse(await fs.readFile(filePath, { encoding: 'utf-8' })); } async deleteMany(ids: BinaryData.IdsForDeletion) { diff --git a/packages/core/src/BinaryData/ObjectStore.manager.ts b/packages/core/src/BinaryData/ObjectStore.manager.ts index 37a0f944d1..f42eba7f29 100644 --- a/packages/core/src/BinaryData/ObjectStore.manager.ts +++ b/packages/core/src/BinaryData/ObjectStore.manager.ts @@ -34,11 +34,11 @@ export class ObjectStoreManager implements BinaryData.Manager { } async getAsBuffer(fileId: string) { - return this.objectStoreService.get(fileId, { mode: 'buffer' }); + return await this.objectStoreService.get(fileId, { mode: 'buffer' }); } async getAsStream(fileId: string) { - return this.objectStoreService.get(fileId, { mode: 'stream' }); + return await this.objectStoreService.get(fileId, { mode: 'stream' }); } async getMetadata(fileId: string): Promise { @@ -102,6 +102,6 @@ export class ObjectStoreManager implements BinaryData.Manager { } private async toBuffer(bufferOrStream: Buffer | Readable) { - return toBuffer(bufferOrStream); + return await toBuffer(bufferOrStream); } } diff --git a/packages/core/src/BinaryData/utils.ts b/packages/core/src/BinaryData/utils.ts index f1d692067b..3bf99ceaeb 100644 --- a/packages/core/src/BinaryData/utils.ts +++ b/packages/core/src/BinaryData/utils.ts @@ -34,7 +34,7 @@ export async function doesNotExist(dir: string) { export async function toBuffer(body: Buffer | Readable) { if (Buffer.isBuffer(body)) return body; - return new Promise((resolve, reject) => { + return await new Promise((resolve, reject) => { body .once('error', (cause) => { if ('code' in cause && cause.code === 'Z_DATA_ERROR') diff --git a/packages/core/src/NodeExecuteFunctions.ts b/packages/core/src/NodeExecuteFunctions.ts index 73a9621248..db14c76437 100644 --- a/packages/core/src/NodeExecuteFunctions.ts +++ b/packages/core/src/NodeExecuteFunctions.ts @@ -754,7 +754,7 @@ export async function proxyRequestToAxios( } }; } else { - requestFn = async () => axios(axiosConfig); + requestFn = async () => await axios(axiosConfig); } try { @@ -969,14 +969,14 @@ export function getBinaryPath(binaryDataId: string): string { * Returns binary file metadata */ export async function getBinaryMetadata(binaryDataId: string): Promise { - return Container.get(BinaryDataService).getMetadata(binaryDataId); + return await Container.get(BinaryDataService).getMetadata(binaryDataId); } /** * Returns binary file stream for piping */ export async function getBinaryStream(binaryDataId: string, chunkSize?: number): Promise { - return Container.get(BinaryDataService).getAsStream(binaryDataId, chunkSize); + return await Container.get(BinaryDataService).getAsStream(binaryDataId, chunkSize); } export function assertBinaryData( @@ -1024,7 +1024,7 @@ export async function getBinaryDataBuffer( inputIndex: number, ): Promise { const binaryData = inputData.main[inputIndex]![itemIndex]!.binary![propertyName]!; - return Container.get(BinaryDataService).getAsBuffer(binaryData); + return await Container.get(BinaryDataService).getAsBuffer(binaryData); } /** @@ -1041,7 +1041,7 @@ export async function setBinaryDataBuffer( workflowId: string, executionId: string, ): Promise { - return Container.get(BinaryDataService).store( + return await Container.get(BinaryDataService).store( workflowId, executionId, bufferOrStream, @@ -1100,7 +1100,7 @@ export async function copyBinaryFile( returnData.fileName = path.parse(filePath).base; } - return Container.get(BinaryDataService).copyBinaryFile( + return await Container.get(BinaryDataService).copyBinaryFile( workflowId, executionId, returnData, @@ -1197,7 +1197,7 @@ async function prepareBinaryData( } } - return setBinaryDataBuffer(returnData, binaryData, workflowId, executionId); + return await setBinaryDataBuffer(returnData, binaryData, workflowId, executionId); } /** @@ -1289,7 +1289,7 @@ export async function requestOAuth2( }); } if (isN8nRequest) { - return this.helpers.httpRequest(newRequestOptions).catch(async (error: AxiosError) => { + return await this.helpers.httpRequest(newRequestOptions).catch(async (error: AxiosError) => { if (error.response?.status === 401) { Logger.debug( `OAuth2 token for "${credentialsType}" used by node "${node.name}" expired. Should revalidate.`, @@ -1346,7 +1346,7 @@ export async function requestOAuth2( }); } - return this.helpers.httpRequest(refreshedRequestOption); + return await this.helpers.httpRequest(refreshedRequestOption); } throw error; }); @@ -1356,7 +1356,7 @@ export async function requestOAuth2( ? 401 : oAuth2Options?.tokenExpiredStatusCode; - return this.helpers + return await this.helpers .request(newRequestOptions) .then((response) => { const requestOptions = newRequestOptions as any; @@ -1433,7 +1433,7 @@ export async function requestOAuth2( }); } - return this.helpers.request(newRequestOptions); + return await this.helpers.request(newRequestOptions); } // Unknown error so simply throw it @@ -1506,10 +1506,10 @@ export async function requestOAuth1( oauth.authorize(requestOptions as unknown as clientOAuth1.RequestOptions, token), ); if (isN8nRequest) { - return this.helpers.httpRequest(requestOptions as IHttpRequestOptions); + return await this.helpers.httpRequest(requestOptions as IHttpRequestOptions); } - return this.helpers.request(requestOptions).catch(async (error: IResponseError) => { + return await this.helpers.request(requestOptions).catch(async (error: IResponseError) => { // Unknown error so simply throw it throw error; }); @@ -2984,7 +2984,7 @@ const getRequestHelperFunctions = ( requestOptions, additionalCredentialOptions, ): Promise { - return httpRequestWithAuthentication.call( + return await httpRequestWithAuthentication.call( this, credentialsType, requestOptions, @@ -2996,7 +2996,7 @@ const getRequestHelperFunctions = ( }, request: async (uriOrObject, options) => - proxyRequestToAxios(workflow, additionalData, node, uriOrObject, options), + await proxyRequestToAxios(workflow, additionalData, node, uriOrObject, options), async requestWithAuthentication( this, @@ -3004,7 +3004,7 @@ const getRequestHelperFunctions = ( requestOptions, additionalCredentialOptions, ): Promise { - return requestWithAuthentication.call( + return await requestWithAuthentication.call( this, credentialsType, requestOptions, @@ -3020,7 +3020,7 @@ const getRequestHelperFunctions = ( credentialsType: string, requestOptions: OptionsWithUrl | RequestPromiseOptions, ): Promise { - return requestOAuth1.call(this, credentialsType, requestOptions); + return await requestOAuth1.call(this, credentialsType, requestOptions); }, async requestOAuth2( @@ -3029,7 +3029,7 @@ const getRequestHelperFunctions = ( requestOptions: OptionsWithUri | RequestPromiseOptions, oAuth2Options?: IOAuth2Options, ): Promise { - return requestOAuth2.call( + return await requestOAuth2.call( this, credentialsType, requestOptions, @@ -3139,7 +3139,7 @@ const getFileSystemHelperFunctions = (node: INode): FileSystemHelperFunctions => level: 'warning', }); } - return fsWriteFile(filePath, content, { encoding: 'binary', flag }); + return await fsWriteFile(filePath, content, { encoding: 'binary', flag }); }, }); @@ -3148,7 +3148,7 @@ const getNodeHelperFunctions = ( workflowId: string, ): NodeHelperFunctions => ({ copyBinaryFile: async (filePath, fileName, mimeType) => - copyBinaryFile(workflowId, executionId!, filePath, fileName, mimeType), + await copyBinaryFile(workflowId, executionId!, filePath, fileName, mimeType), }); const getBinaryHelperFunctions = ( @@ -3159,11 +3159,11 @@ const getBinaryHelperFunctions = ( getBinaryStream, getBinaryMetadata, binaryToBuffer: async (body: Buffer | Readable) => - Container.get(BinaryDataService).toBuffer(body), + await Container.get(BinaryDataService).toBuffer(body), prepareBinaryData: async (binaryData, filePath, mimeType) => - prepareBinaryData(binaryData, executionId!, workflowId, filePath, mimeType), + await prepareBinaryData(binaryData, executionId!, workflowId, filePath, mimeType), setBinaryDataBuffer: async (data, binaryData) => - setBinaryDataBuffer(data, binaryData, workflowId, executionId!), + await setBinaryDataBuffer(data, binaryData, workflowId, executionId!), copyBinaryFile: async () => { throw new ApplicationError('`copyBinaryFile` has been removed. Please upgrade this node.'); }, @@ -3213,7 +3213,8 @@ export function getExecutePollFunctions( }, getMode: () => mode, getActivationMode: () => activation, - getCredentials: async (type) => getCredentials(workflow, node, type, additionalData, mode), + getCredentials: async (type) => + await getCredentials(workflow, node, type, additionalData, mode), getNodeParameter: ( parameterName: string, fallbackValue?: any, @@ -3275,7 +3276,8 @@ export function getExecuteTriggerFunctions( }, getMode: () => mode, getActivationMode: () => activation, - getCredentials: async (type) => getCredentials(workflow, node, type, additionalData, mode), + getCredentials: async (type) => + await getCredentials(workflow, node, type, additionalData, mode), getNodeParameter: ( parameterName: string, fallbackValue?: any, @@ -3333,7 +3335,7 @@ export function getExecuteFunctions( ...executionCancellationFunctions(abortSignal), getMode: () => mode, getCredentials: async (type, itemIndex) => - getCredentials( + await getCredentials( workflow, node, type, @@ -3365,19 +3367,20 @@ export function getExecuteFunctions( workflowInfo: IExecuteWorkflowInfo, inputData?: INodeExecutionData[], ): Promise { - return additionalData + return await additionalData .executeWorkflow(workflowInfo, additionalData, { parentWorkflowId: workflow.id?.toString(), inputData, parentWorkflowSettings: workflow.settings, node, }) - .then(async (result) => - Container.get(BinaryDataService).duplicateBinaryData( - workflow.id, - additionalData.executionId!, - result, - ), + .then( + async (result) => + await Container.get(BinaryDataService).duplicateBinaryData( + workflow.id, + additionalData.executionId!, + result, + ), ); }, getContext(type: ContextType): IContextObject { @@ -3390,7 +3393,7 @@ export function getExecuteFunctions( // TODO: Not implemented yet, and maybe also not needed inputIndex?: number, ): Promise { - return getInputConnectionData.call( + return await getInputConnectionData.call( this, workflow, runExecutionData, @@ -3482,7 +3485,7 @@ export function getExecuteFunctions( return dataProxy.getDataProxy(); }, binaryToBuffer: async (body: Buffer | Readable) => - Container.get(BinaryDataService).toBuffer(body), + await Container.get(BinaryDataService).toBuffer(body), async putExecutionToWait(waitTill: Date): Promise { runExecutionData.waitTill = toUtcDate(waitTill, getTimezone(workflow)); if (additionalData.setExecutionStatus) { @@ -3581,7 +3584,7 @@ export function getExecuteFunctions( assertBinaryData: (itemIndex, propertyName) => assertBinaryData(inputData, node, itemIndex, propertyName, 0), getBinaryDataBuffer: async (itemIndex, propertyName) => - getBinaryDataBuffer(inputData, itemIndex, propertyName, 0), + await getBinaryDataBuffer(inputData, itemIndex, propertyName, 0), returnJsonArray, normalizeItems, @@ -3632,7 +3635,7 @@ export function getExecuteSingleFunctions( return NodeHelpers.getContext(runExecutionData, type, node); }, getCredentials: async (type) => - getCredentials( + await getCredentials( workflow, node, type, @@ -3726,7 +3729,7 @@ export function getExecuteSingleFunctions( assertBinaryData: (propertyName, inputIndex = 0) => assertBinaryData(inputData, node, itemIndex, propertyName, inputIndex), getBinaryDataBuffer: async (propertyName, inputIndex = 0) => - getBinaryDataBuffer(inputData, itemIndex, propertyName, inputIndex), + await getBinaryDataBuffer(inputData, itemIndex, propertyName, inputIndex), }, }; })(workflow, runExecutionData, connectionInputData, inputData, node, itemIndex); @@ -3736,7 +3739,7 @@ export function getCredentialTestFunctions(): ICredentialTestFunctions { return { helpers: { request: async (uriOrObject: string | object, options?: object) => { - return proxyRequestToAxios(undefined, undefined, undefined, uriOrObject, options); + return await proxyRequestToAxios(undefined, undefined, undefined, uriOrObject, options); }, }, }; @@ -3755,7 +3758,7 @@ export function getLoadOptionsFunctions( return { ...getCommonWorkflowFunctions(workflow, node, additionalData), getCredentials: async (type) => - getCredentials(workflow, node, type, additionalData, 'internal'), + await getCredentials(workflow, node, type, additionalData, 'internal'), getCurrentNodeParameter: ( parameterPath: string, options?: IGetNodeParameterOptions, @@ -3832,7 +3835,8 @@ export function getExecuteHookFunctions( return ((workflow: Workflow, node: INode) => { return { ...getCommonWorkflowFunctions(workflow, node, additionalData), - getCredentials: async (type) => getCredentials(workflow, node, type, additionalData, mode), + getCredentials: async (type) => + await getCredentials(workflow, node, type, additionalData, mode), getMode: () => mode, getActivationMode: () => activation, getNodeParameter: ( @@ -3904,7 +3908,8 @@ export function getExecuteWebhookFunctions( } return additionalData.httpRequest.body; }, - getCredentials: async (type) => getCredentials(workflow, node, type, additionalData, mode), + getCredentials: async (type) => + await getCredentials(workflow, node, type, additionalData, mode), getHeaderData(): IncomingHttpHeaders { if (additionalData.httpRequest === undefined) { throw new ApplicationError('Request is missing'); @@ -3937,7 +3942,7 @@ export function getExecuteWebhookFunctions( }; const runIndex = 0; - return getInputConnectionData.call( + return await getInputConnectionData.call( this, workflow, runExecutionData, diff --git a/packages/core/src/ObjectStore/ObjectStore.service.ee.ts b/packages/core/src/ObjectStore/ObjectStore.service.ee.ts index bf2757b655..bcfeb47264 100644 --- a/packages/core/src/ObjectStore/ObjectStore.service.ee.ts +++ b/packages/core/src/ObjectStore/ObjectStore.service.ee.ts @@ -65,7 +65,7 @@ export class ObjectStoreService { async checkConnection() { if (this.isReady) return; - return this.request('HEAD', this.host, this.bucket.name); + return await this.request('HEAD', this.host, this.bucket.name); } /** @@ -74,7 +74,7 @@ export class ObjectStoreService { * @doc https://docs.aws.amazon.com/AmazonS3/latest/API/API_PutObject.html */ async put(filename: string, buffer: Buffer, metadata: BinaryData.PreWriteMetadata = {}) { - if (this.isReadOnly) return this.blockWrite(filename); + if (this.isReadOnly) return await this.blockWrite(filename); const headers: Record = { 'Content-Length': buffer.length, @@ -86,7 +86,7 @@ export class ObjectStoreService { const path = `/${this.bucket.name}/${filename}`; - return this.request('PUT', this.host, path, { headers, body: buffer }); + return await this.request('PUT', this.host, path, { headers, body: buffer }); } /** @@ -131,7 +131,7 @@ export class ObjectStoreService { async deleteOne(fileId: string) { const path = `${this.bucket.name}/${fileId}`; - return this.request('DELETE', this.host, path); + return await this.request('DELETE', this.host, path); } /** @@ -156,7 +156,7 @@ export class ObjectStoreService { const path = `${this.bucket.name}/?delete`; - return this.request('POST', this.host, path, { headers, body }); + return await this.request('POST', this.host, path, { headers, body }); } /** diff --git a/packages/core/src/ObjectStore/utils.ts b/packages/core/src/ObjectStore/utils.ts index 1ecad915f9..6dc1e4df77 100644 --- a/packages/core/src/ObjectStore/utils.ts +++ b/packages/core/src/ObjectStore/utils.ts @@ -7,12 +7,12 @@ export function isStream(maybeStream: unknown): maybeStream is Stream { } export async function parseXml(xml: string): Promise { - return parseStringPromise(xml, { + return await (parseStringPromise(xml, { explicitArray: false, ignoreAttrs: true, tagNameProcessors: [firstCharLowerCase], valueProcessors: [parseNumbers, parseBooleans], - }) as Promise; + }) as Promise); } export function writeBlockedMessage(filename: string) { diff --git a/packages/core/src/WorkflowExecute.ts b/packages/core/src/WorkflowExecute.ts index 781aea9fdb..fd9a7082a2 100644 --- a/packages/core/src/WorkflowExecute.ts +++ b/packages/core/src/WorkflowExecute.ts @@ -308,7 +308,7 @@ export class WorkflowExecute { return; } - return this.additionalData.hooks.executeHookFunctions(hookName, parameters); + return await this.additionalData.hooks.executeHookFunctions(hookName, parameters); } moveNodeMetadata(): void { @@ -1657,14 +1657,19 @@ export class WorkflowExecute { })() .then(async () => { if (this.status === 'canceled' && executionError === undefined) { - return this.processSuccessExecution( + return await this.processSuccessExecution( startedAt, workflow, new WorkflowOperationError('Workflow has been canceled or timed out!'), closeFunction, ); } - return this.processSuccessExecution(startedAt, workflow, executionError, closeFunction); + return await this.processSuccessExecution( + startedAt, + workflow, + executionError, + closeFunction, + ); }) .catch(async (error) => { const fullRunData = this.getFullRunData(startedAt); @@ -1708,7 +1713,7 @@ export class WorkflowExecute { return fullRunData; }); - return returnPromise.then(resolve); + return await returnPromise.then(resolve); }); } diff --git a/packages/nodes-base/nodes/ActionNetwork/GenericFunctions.ts b/packages/nodes-base/nodes/ActionNetwork/GenericFunctions.ts index d1cfc439ad..14af98492b 100644 --- a/packages/nodes-base/nodes/ActionNetwork/GenericFunctions.ts +++ b/packages/nodes-base/nodes/ActionNetwork/GenericFunctions.ts @@ -38,7 +38,7 @@ export async function actionNetworkApiRequest( delete options.qs; } - return this.helpers.requestWithAuthentication.call(this, 'actionNetworkApi', options); + return await this.helpers.requestWithAuthentication.call(this, 'actionNetworkApi', options); } /** @@ -218,7 +218,7 @@ export const adjustEventPayload = adjustLocation; // ---------------------------------------- async function loadResource(this: ILoadOptionsFunctions, resource: string) { - return handleListing.call(this, 'GET', `/${resource}`, {}, {}, { returnAll: true }); + return await handleListing.call(this, 'GET', `/${resource}`, {}, {}, { returnAll: true }); } export const resourceLoaders = { diff --git a/packages/nodes-base/nodes/Airtable/v1/GenericFunctions.ts b/packages/nodes-base/nodes/Airtable/v1/GenericFunctions.ts index 3b2b3c7b61..ea0f6b4273 100644 --- a/packages/nodes-base/nodes/Airtable/v1/GenericFunctions.ts +++ b/packages/nodes-base/nodes/Airtable/v1/GenericFunctions.ts @@ -59,7 +59,7 @@ export async function apiRequest( delete options.body; } const authenticationMethod = this.getNodeParameter('authentication', 0) as string; - return this.helpers.requestWithAuthentication.call(this, authenticationMethod, options); + return await this.helpers.requestWithAuthentication.call(this, authenticationMethod, options); } /** diff --git a/packages/nodes-base/nodes/Airtable/v2/AirtableV2.node.ts b/packages/nodes-base/nodes/Airtable/v2/AirtableV2.node.ts index a8f99ce4f2..1fe3e9d0c5 100644 --- a/packages/nodes-base/nodes/Airtable/v2/AirtableV2.node.ts +++ b/packages/nodes-base/nodes/Airtable/v2/AirtableV2.node.ts @@ -26,6 +26,6 @@ export class AirtableV2 implements INodeType { }; async execute(this: IExecuteFunctions) { - return router.call(this); + return await router.call(this); } } diff --git a/packages/nodes-base/nodes/Airtable/v2/transport/index.ts b/packages/nodes-base/nodes/Airtable/v2/transport/index.ts index 44aa041a66..bf2ff876fb 100644 --- a/packages/nodes-base/nodes/Airtable/v2/transport/index.ts +++ b/packages/nodes-base/nodes/Airtable/v2/transport/index.ts @@ -46,7 +46,7 @@ export async function apiRequest( } const authenticationMethod = this.getNodeParameter('authentication', 0) as string; - return this.helpers.requestWithAuthentication.call(this, authenticationMethod, options); + return await this.helpers.requestWithAuthentication.call(this, authenticationMethod, options); } /** diff --git a/packages/nodes-base/nodes/ApiTemplateIo/ApiTemplateIo.node.ts b/packages/nodes-base/nodes/ApiTemplateIo/ApiTemplateIo.node.ts index 765a3fc76c..480a0ec0de 100644 --- a/packages/nodes-base/nodes/ApiTemplateIo/ApiTemplateIo.node.ts +++ b/packages/nodes-base/nodes/ApiTemplateIo/ApiTemplateIo.node.ts @@ -350,11 +350,11 @@ export class ApiTemplateIo implements INodeType { methods = { loadOptions: { async getImageTemplates(this: ILoadOptionsFunctions): Promise { - return loadResource.call(this, 'image'); + return await loadResource.call(this, 'image'); }, async getPdfTemplates(this: ILoadOptionsFunctions): Promise { - return loadResource.call(this, 'pdf'); + return await loadResource.call(this, 'pdf'); }, }, }; diff --git a/packages/nodes-base/nodes/ApiTemplateIo/GenericFunctions.ts b/packages/nodes-base/nodes/ApiTemplateIo/GenericFunctions.ts index a5d90db168..49f6be68b1 100644 --- a/packages/nodes-base/nodes/ApiTemplateIo/GenericFunctions.ts +++ b/packages/nodes-base/nodes/ApiTemplateIo/GenericFunctions.ts @@ -74,7 +74,7 @@ export function validateJSON(json: string | object | undefined): any { } export async function downloadImage(this: IExecuteFunctions, url: string) { - return this.helpers.request({ + return await this.helpers.request({ uri: url, method: 'GET', json: false, diff --git a/packages/nodes-base/nodes/Asana/GenericFunctions.ts b/packages/nodes-base/nodes/Asana/GenericFunctions.ts index 106ede80c4..ac89b8abce 100644 --- a/packages/nodes-base/nodes/Asana/GenericFunctions.ts +++ b/packages/nodes-base/nodes/Asana/GenericFunctions.ts @@ -34,7 +34,7 @@ export async function asanaApiRequest( }; const credentialType = authenticationMethod === 'accessToken' ? 'asanaApi' : 'asanaOAuth2Api'; - return this.helpers.requestWithAuthentication.call(this, credentialType, options); + return await this.helpers.requestWithAuthentication.call(this, credentialType, options); } export async function asanaApiRequestAllItems( diff --git a/packages/nodes-base/nodes/Aws/Comprehend/GenericFunctions.ts b/packages/nodes-base/nodes/Aws/Comprehend/GenericFunctions.ts index f8b6bee8b8..65896effb4 100644 --- a/packages/nodes-base/nodes/Aws/Comprehend/GenericFunctions.ts +++ b/packages/nodes-base/nodes/Aws/Comprehend/GenericFunctions.ts @@ -29,7 +29,7 @@ export async function awsApiRequest( headers, region: credentials?.region as string, } as IHttpRequestOptions; - return this.helpers.requestWithAuthentication.call(this, 'aws', requestOptions); + return await this.helpers.requestWithAuthentication.call(this, 'aws', requestOptions); } export async function awsApiRequestREST( diff --git a/packages/nodes-base/nodes/Aws/Rekognition/GenericFunctions.ts b/packages/nodes-base/nodes/Aws/Rekognition/GenericFunctions.ts index 0b284c3e1e..e619242645 100644 --- a/packages/nodes-base/nodes/Aws/Rekognition/GenericFunctions.ts +++ b/packages/nodes-base/nodes/Aws/Rekognition/GenericFunctions.ts @@ -41,7 +41,7 @@ export async function awsApiRequest( if (Object.keys(option).length !== 0) { Object.assign(requestOptions, option); } - return this.helpers.requestWithAuthentication.call(this, 'aws', requestOptions); + return await this.helpers.requestWithAuthentication.call(this, 'aws', requestOptions); } export async function awsApiRequestREST( diff --git a/packages/nodes-base/nodes/Aws/S3/V1/GenericFunctions.ts b/packages/nodes-base/nodes/Aws/S3/V1/GenericFunctions.ts index 1203a9022e..26a99a6d70 100644 --- a/packages/nodes-base/nodes/Aws/S3/V1/GenericFunctions.ts +++ b/packages/nodes-base/nodes/Aws/S3/V1/GenericFunctions.ts @@ -39,7 +39,7 @@ export async function awsApiRequest( if (Object.keys(option).length !== 0) { Object.assign(requestOptions, option); } - return this.helpers.requestWithAuthentication.call(this, 'aws', requestOptions); + return await this.helpers.requestWithAuthentication.call(this, 'aws', requestOptions); } export async function awsApiRequestREST( diff --git a/packages/nodes-base/nodes/Aws/S3/V2/GenericFunctions.ts b/packages/nodes-base/nodes/Aws/S3/V2/GenericFunctions.ts index 8c1d51487c..8e5e3ba668 100644 --- a/packages/nodes-base/nodes/Aws/S3/V2/GenericFunctions.ts +++ b/packages/nodes-base/nodes/Aws/S3/V2/GenericFunctions.ts @@ -37,7 +37,7 @@ export async function awsApiRequest( if (Object.keys(option).length !== 0) { Object.assign(requestOptions, option); } - return this.helpers.requestWithAuthentication.call(this, 'aws', requestOptions); + return await this.helpers.requestWithAuthentication.call(this, 'aws', requestOptions); } export async function awsApiRequestREST( diff --git a/packages/nodes-base/nodes/Aws/Textract/GenericFunctions.ts b/packages/nodes-base/nodes/Aws/Textract/GenericFunctions.ts index addd787ce7..003eaf40b6 100644 --- a/packages/nodes-base/nodes/Aws/Textract/GenericFunctions.ts +++ b/packages/nodes-base/nodes/Aws/Textract/GenericFunctions.ts @@ -174,7 +174,7 @@ export async function validateCredentials( const response = await this.helpers.request(options); - return new Promise((resolve, reject) => { + return await new Promise((resolve, reject) => { parseString(response as string, { explicitArray: false }, (err, data) => { if (err) { return reject(err); diff --git a/packages/nodes-base/nodes/BambooHr/v1/methods/credentialTest.ts b/packages/nodes-base/nodes/BambooHr/v1/methods/credentialTest.ts index 93b942fb9e..5a095aef69 100644 --- a/packages/nodes-base/nodes/BambooHr/v1/methods/credentialTest.ts +++ b/packages/nodes-base/nodes/BambooHr/v1/methods/credentialTest.ts @@ -26,7 +26,7 @@ async function validateCredentials( url: `https://api.bamboohr.com/api/gateway.php/${subdomain}/v1/employees/directory`, }; - return this.helpers.request(options); + return await this.helpers.request(options); } export async function bambooHrApiCredentialTest( diff --git a/packages/nodes-base/nodes/Bannerbear/Bannerbear.node.ts b/packages/nodes-base/nodes/Bannerbear/Bannerbear.node.ts index 49c3a504b4..975fc600ac 100644 --- a/packages/nodes-base/nodes/Bannerbear/Bannerbear.node.ts +++ b/packages/nodes-base/nodes/Bannerbear/Bannerbear.node.ts @@ -144,7 +144,7 @@ export class Bannerbear implements INodeType { const promise = async (uid: string) => { let data: IDataObject = {}; - return new Promise((resolve, reject) => { + return await new Promise((resolve, reject) => { const timeout = setInterval(async () => { data = await bannerbearApiRequest.call(this, 'GET', `/images/${uid}`); diff --git a/packages/nodes-base/nodes/Baserow/GenericFunctions.ts b/packages/nodes-base/nodes/Baserow/GenericFunctions.ts index d53c4eb532..b4b71f2c0e 100644 --- a/packages/nodes-base/nodes/Baserow/GenericFunctions.ts +++ b/packages/nodes-base/nodes/Baserow/GenericFunctions.ts @@ -146,7 +146,7 @@ export class TableFieldMapper { jwtToken: string, ): Promise { const endpoint = `/api/database/fields/table/${table}/`; - return baserowApiRequest.call(this, 'GET', endpoint, jwtToken); + return await baserowApiRequest.call(this, 'GET', endpoint, jwtToken); } createMappings(tableFields: LoadedResource[]) { diff --git a/packages/nodes-base/nodes/Beeminder/Beeminder.node.functions.ts b/packages/nodes-base/nodes/Beeminder/Beeminder.node.functions.ts index 7e7f294826..90e058068a 100644 --- a/packages/nodes-base/nodes/Beeminder/Beeminder.node.functions.ts +++ b/packages/nodes-base/nodes/Beeminder/Beeminder.node.functions.ts @@ -16,7 +16,7 @@ export async function createDatapoint( const endpoint = `/users/${credentials.user}/goals/${data.goalName}/datapoints.json`; - return beeminderApiRequest.call(this, 'POST', endpoint, data); + return await beeminderApiRequest.call(this, 'POST', endpoint, data); } export async function getAllDatapoints( @@ -28,10 +28,10 @@ export async function getAllDatapoints( const endpoint = `/users/${credentials.user}/goals/${data.goalName}/datapoints.json`; if (data.count !== undefined) { - return beeminderApiRequest.call(this, 'GET', endpoint, {}, data); + return await beeminderApiRequest.call(this, 'GET', endpoint, {}, data); } - return beeminderApiRequestAllItems.call(this, 'GET', endpoint, {}, data); + return await beeminderApiRequestAllItems.call(this, 'GET', endpoint, {}, data); } export async function updateDatapoint( @@ -42,7 +42,7 @@ export async function updateDatapoint( const endpoint = `/users/${credentials.user}/goals/${data.goalName}/datapoints/${data.datapointId}.json`; - return beeminderApiRequest.call(this, 'PUT', endpoint, data); + return await beeminderApiRequest.call(this, 'PUT', endpoint, data); } export async function deleteDatapoint( @@ -53,5 +53,5 @@ export async function deleteDatapoint( const endpoint = `/users/${credentials.user}/goals/${data.goalName}/datapoints/${data.datapointId}.json`; - return beeminderApiRequest.call(this, 'DELETE', endpoint); + return await beeminderApiRequest.call(this, 'DELETE', endpoint); } diff --git a/packages/nodes-base/nodes/Beeminder/GenericFunctions.ts b/packages/nodes-base/nodes/Beeminder/GenericFunctions.ts index c776b17116..ec9c7c5059 100644 --- a/packages/nodes-base/nodes/Beeminder/GenericFunctions.ts +++ b/packages/nodes-base/nodes/Beeminder/GenericFunctions.ts @@ -34,7 +34,7 @@ export async function beeminderApiRequest( delete options.qs; } - return this.helpers.requestWithAuthentication.call(this, 'beeminderApi', options); + return await this.helpers.requestWithAuthentication.call(this, 'beeminderApi', options); } export async function beeminderApiRequestAllItems( diff --git a/packages/nodes-base/nodes/Bitwarden/Bitwarden.node.ts b/packages/nodes-base/nodes/Bitwarden/Bitwarden.node.ts index 58656f906f..2a7ca10685 100644 --- a/packages/nodes-base/nodes/Bitwarden/Bitwarden.node.ts +++ b/packages/nodes-base/nodes/Bitwarden/Bitwarden.node.ts @@ -94,11 +94,11 @@ export class Bitwarden implements INodeType { methods = { loadOptions: { async getGroups(this: ILoadOptionsFunctions) { - return loadResource.call(this, 'groups'); + return await loadResource.call(this, 'groups'); }, async getCollections(this: ILoadOptionsFunctions) { - return loadResource.call(this, 'collections'); + return await loadResource.call(this, 'collections'); }, }, }; diff --git a/packages/nodes-base/nodes/Brevo/GenericFunctions.ts b/packages/nodes-base/nodes/Brevo/GenericFunctions.ts index bed24ef97e..55a9958cc8 100644 --- a/packages/nodes-base/nodes/Brevo/GenericFunctions.ts +++ b/packages/nodes-base/nodes/Brevo/GenericFunctions.ts @@ -325,7 +325,7 @@ export namespace BrevoWebhookApi { options, )) as string; - return jsonParse(webhooks); + return await jsonParse(webhooks); }; export const createWebHook = async ( @@ -355,7 +355,7 @@ export namespace BrevoWebhookApi { options, ); - return jsonParse(webhookId as string); + return await jsonParse(webhookId as string); }; export const deleteWebhook = async (ref: IHookFunctions, webhookId: string) => { @@ -371,6 +371,6 @@ export namespace BrevoWebhookApi { body, }; - return ref.helpers.requestWithAuthentication.call(ref, credentialsName, options); + return await ref.helpers.requestWithAuthentication.call(ref, credentialsName, options); }; } diff --git a/packages/nodes-base/nodes/Calendly/GenericFunctions.ts b/packages/nodes-base/nodes/Calendly/GenericFunctions.ts index af6f532623..b94dd6220d 100644 --- a/packages/nodes-base/nodes/Calendly/GenericFunctions.ts +++ b/packages/nodes-base/nodes/Calendly/GenericFunctions.ts @@ -57,7 +57,7 @@ export async function calendlyApiRequest( delete options.qs; } options = Object.assign({}, options, option); - return this.helpers.requestWithAuthentication.call(this, 'calendlyApi', options); + return await this.helpers.requestWithAuthentication.call(this, 'calendlyApi', options); } export async function validateCredentials( @@ -89,5 +89,5 @@ export async function validateCredentials( uri: 'https://calendly.com/api/v1/users/me', }); } - return this.helpers.request(options); + return await this.helpers.request(options); } diff --git a/packages/nodes-base/nodes/Clockify/GenericFunctions.ts b/packages/nodes-base/nodes/Clockify/GenericFunctions.ts index dfd82b2835..0ae788c7a9 100644 --- a/packages/nodes-base/nodes/Clockify/GenericFunctions.ts +++ b/packages/nodes-base/nodes/Clockify/GenericFunctions.ts @@ -30,7 +30,7 @@ export async function clockifyApiRequest( json: true, useQuerystring: true, }; - return this.helpers.requestWithAuthentication.call(this, 'clockifyApi', options); + return await this.helpers.requestWithAuthentication.call(this, 'clockifyApi', options); } export async function clockifyApiRequestAllItems( diff --git a/packages/nodes-base/nodes/Cockpit/CollectionFunctions.ts b/packages/nodes-base/nodes/Cockpit/CollectionFunctions.ts index 9c9647ca32..81a2d6e62d 100644 --- a/packages/nodes-base/nodes/Cockpit/CollectionFunctions.ts +++ b/packages/nodes-base/nodes/Cockpit/CollectionFunctions.ts @@ -20,7 +20,7 @@ export async function createCollectionEntry( }; } - return cockpitApiRequest.call(this, 'post', `/collections/save/${resourceName}`, body); + return await cockpitApiRequest.call(this, 'post', `/collections/save/${resourceName}`, body); } export async function getAllCollectionEntries( @@ -76,11 +76,11 @@ export async function getAllCollectionEntries( body.lang = options.language as string; } - return cockpitApiRequest.call(this, 'post', `/collections/get/${resourceName}`, body); + return await cockpitApiRequest.call(this, 'post', `/collections/get/${resourceName}`, body); } export async function getAllCollectionNames( this: IExecuteFunctions | ILoadOptionsFunctions, ): Promise { - return cockpitApiRequest.call(this, 'GET', '/collections/listCollections', {}); + return await cockpitApiRequest.call(this, 'GET', '/collections/listCollections', {}); } diff --git a/packages/nodes-base/nodes/Cockpit/FormFunctions.ts b/packages/nodes-base/nodes/Cockpit/FormFunctions.ts index 6ca1f58f51..28ec4834bc 100644 --- a/packages/nodes-base/nodes/Cockpit/FormFunctions.ts +++ b/packages/nodes-base/nodes/Cockpit/FormFunctions.ts @@ -11,5 +11,5 @@ export async function submitForm( form, }; - return cockpitApiRequest.call(this, 'post', `/forms/submit/${resourceName}`, body); + return await cockpitApiRequest.call(this, 'post', `/forms/submit/${resourceName}`, body); } diff --git a/packages/nodes-base/nodes/Cockpit/SingletonFunctions.ts b/packages/nodes-base/nodes/Cockpit/SingletonFunctions.ts index 0bcca3053b..9261d0e4cf 100644 --- a/packages/nodes-base/nodes/Cockpit/SingletonFunctions.ts +++ b/packages/nodes-base/nodes/Cockpit/SingletonFunctions.ts @@ -5,11 +5,11 @@ export async function getSingleton( this: IExecuteFunctions | ILoadOptionsFunctions, resourceName: string, ): Promise { - return cockpitApiRequest.call(this, 'get', `/singletons/get/${resourceName}`); + return await cockpitApiRequest.call(this, 'get', `/singletons/get/${resourceName}`); } export async function getAllSingletonNames( this: IExecuteFunctions | ILoadOptionsFunctions, ): Promise { - return cockpitApiRequest.call(this, 'GET', '/singletons/listSingletons', {}); + return await cockpitApiRequest.call(this, 'GET', '/singletons/listSingletons', {}); } diff --git a/packages/nodes-base/nodes/Code/PythonSandbox.ts b/packages/nodes-base/nodes/Code/PythonSandbox.ts index 48940bace2..fd1ab3f564 100644 --- a/packages/nodes-base/nodes/Code/PythonSandbox.ts +++ b/packages/nodes-base/nodes/Code/PythonSandbox.ts @@ -40,7 +40,7 @@ export class PythonSandbox extends Sandbox { } async runCode(): Promise { - return this.runCodeInPython(); + return await this.runCodeInPython(); } async runCodeAllItems() { diff --git a/packages/nodes-base/nodes/Copper/GenericFunctions.ts b/packages/nodes-base/nodes/Copper/GenericFunctions.ts index 9bfae5a255..fce3bde9c0 100644 --- a/packages/nodes-base/nodes/Copper/GenericFunctions.ts +++ b/packages/nodes-base/nodes/Copper/GenericFunctions.ts @@ -184,7 +184,7 @@ export async function handleListing( const option = { resolveWithFullResponse: true }; if (returnAll) { - return copperApiRequestAllItems.call(this, method, endpoint, body, qs, uri, option); + return await copperApiRequestAllItems.call(this, method, endpoint, body, qs, uri, option); } const limit = this.getNodeParameter('limit', 0); diff --git a/packages/nodes-base/nodes/Cortex/GenericFunctions.ts b/packages/nodes-base/nodes/Cortex/GenericFunctions.ts index 31a27cb77e..193cf51599 100644 --- a/packages/nodes-base/nodes/Cortex/GenericFunctions.ts +++ b/packages/nodes-base/nodes/Cortex/GenericFunctions.ts @@ -39,7 +39,7 @@ export async function cortexApiRequest( delete options.qs; } - return this.helpers.requestWithAuthentication.call(this, 'cortexApi', options); + return await this.helpers.requestWithAuthentication.call(this, 'cortexApi', options); } export function getEntityLabel(entity: IDataObject): string { diff --git a/packages/nodes-base/nodes/CustomerIo/GenericFunctions.ts b/packages/nodes-base/nodes/CustomerIo/GenericFunctions.ts index 2ea9b213f8..5efc6729e1 100644 --- a/packages/nodes-base/nodes/CustomerIo/GenericFunctions.ts +++ b/packages/nodes-base/nodes/CustomerIo/GenericFunctions.ts @@ -43,7 +43,7 @@ export async function customerIoApiRequest( options.url = `https://beta-api.customer.io/v1/api${endpoint}`; } - return this.helpers.requestWithAuthentication.call(this, 'customerIoApi', options); + return await this.helpers.requestWithAuthentication.call(this, 'customerIoApi', options); } export function eventExists(currentEvents: string[], webhookEvents: IDataObject) { diff --git a/packages/nodes-base/nodes/Dhl/GenericFunctions.ts b/packages/nodes-base/nodes/Dhl/GenericFunctions.ts index 8d2d3fdcfe..6f63f7b9af 100644 --- a/packages/nodes-base/nodes/Dhl/GenericFunctions.ts +++ b/packages/nodes-base/nodes/Dhl/GenericFunctions.ts @@ -67,5 +67,5 @@ export async function validateCredentials( json: true, }; - return this.helpers.request(options); + return await this.helpers.request(options); } diff --git a/packages/nodes-base/nodes/Discord/test/v2/node/channel/create.test.ts b/packages/nodes-base/nodes/Discord/test/v2/node/channel/create.test.ts index f1497191bc..cbd0fbb077 100644 --- a/packages/nodes-base/nodes/Discord/test/v2/node/channel/create.test.ts +++ b/packages/nodes-base/nodes/Discord/test/v2/node/channel/create.test.ts @@ -61,6 +61,6 @@ describe('Test DiscordV2, channel => create', () => { }; for (const testData of tests) { - test(testData.description, async () => testNode(testData, nodeTypes)); + test(testData.description, async () => await testNode(testData, nodeTypes)); } }); diff --git a/packages/nodes-base/nodes/Discord/test/v2/node/channel/deleteChannel.test.ts b/packages/nodes-base/nodes/Discord/test/v2/node/channel/deleteChannel.test.ts index abb5f6df42..75477631a9 100644 --- a/packages/nodes-base/nodes/Discord/test/v2/node/channel/deleteChannel.test.ts +++ b/packages/nodes-base/nodes/Discord/test/v2/node/channel/deleteChannel.test.ts @@ -57,6 +57,6 @@ describe('Test DiscordV2, channel => deleteChannel', () => { }; for (const testData of tests) { - test(testData.description, async () => testNode(testData, nodeTypes)); + test(testData.description, async () => await testNode(testData, nodeTypes)); } }); diff --git a/packages/nodes-base/nodes/Discord/test/v2/node/channel/get.test.ts b/packages/nodes-base/nodes/Discord/test/v2/node/channel/get.test.ts index 61fa6d8da6..45f98089c7 100644 --- a/packages/nodes-base/nodes/Discord/test/v2/node/channel/get.test.ts +++ b/packages/nodes-base/nodes/Discord/test/v2/node/channel/get.test.ts @@ -107,6 +107,6 @@ describe('Test DiscordV2, channel => get', () => { }; for (const testData of tests) { - test(testData.description, async () => testNode(testData, nodeTypes)); + test(testData.description, async () => await testNode(testData, nodeTypes)); } }); diff --git a/packages/nodes-base/nodes/Discord/test/v2/node/channel/getAll.test.ts b/packages/nodes-base/nodes/Discord/test/v2/node/channel/getAll.test.ts index 342cb9b62a..41c7cb1b57 100644 --- a/packages/nodes-base/nodes/Discord/test/v2/node/channel/getAll.test.ts +++ b/packages/nodes-base/nodes/Discord/test/v2/node/channel/getAll.test.ts @@ -136,6 +136,6 @@ describe('Test DiscordV2, channel => getAll', () => { }; for (const testData of tests) { - test(testData.description, async () => testNode(testData, nodeTypes)); + test(testData.description, async () => await testNode(testData, nodeTypes)); } }); diff --git a/packages/nodes-base/nodes/Discord/test/v2/node/channel/update.test.ts b/packages/nodes-base/nodes/Discord/test/v2/node/channel/update.test.ts index c42c56a8ff..bd7e7851c6 100644 --- a/packages/nodes-base/nodes/Discord/test/v2/node/channel/update.test.ts +++ b/packages/nodes-base/nodes/Discord/test/v2/node/channel/update.test.ts @@ -64,6 +64,6 @@ describe('Test DiscordV2, channel => update', () => { }; for (const testData of tests) { - test(testData.description, async () => testNode(testData, nodeTypes)); + test(testData.description, async () => await testNode(testData, nodeTypes)); } }); diff --git a/packages/nodes-base/nodes/Discord/test/v2/node/member/getAll.test.ts b/packages/nodes-base/nodes/Discord/test/v2/node/member/getAll.test.ts index 80589af952..13d8297a70 100644 --- a/packages/nodes-base/nodes/Discord/test/v2/node/member/getAll.test.ts +++ b/packages/nodes-base/nodes/Discord/test/v2/node/member/getAll.test.ts @@ -85,6 +85,6 @@ describe('Test DiscordV2, member => getAll', () => { }; for (const testData of tests) { - test(testData.description, async () => testNode(testData, nodeTypes)); + test(testData.description, async () => await testNode(testData, nodeTypes)); } }); diff --git a/packages/nodes-base/nodes/Discord/test/v2/node/member/roleAdd.test.ts b/packages/nodes-base/nodes/Discord/test/v2/node/member/roleAdd.test.ts index 1fe60cd181..836c54158d 100644 --- a/packages/nodes-base/nodes/Discord/test/v2/node/member/roleAdd.test.ts +++ b/packages/nodes-base/nodes/Discord/test/v2/node/member/roleAdd.test.ts @@ -49,6 +49,6 @@ describe('Test DiscordV2, member => roleAdd', () => { }; for (const testData of tests) { - test(testData.description, async () => testNode(testData, nodeTypes)); + test(testData.description, async () => await testNode(testData, nodeTypes)); } }); diff --git a/packages/nodes-base/nodes/Discord/test/v2/node/member/roleRemove.test.ts b/packages/nodes-base/nodes/Discord/test/v2/node/member/roleRemove.test.ts index c9befeca61..e2cb066f13 100644 --- a/packages/nodes-base/nodes/Discord/test/v2/node/member/roleRemove.test.ts +++ b/packages/nodes-base/nodes/Discord/test/v2/node/member/roleRemove.test.ts @@ -57,6 +57,6 @@ describe('Test DiscordV2, member => roleRemove', () => { }; for (const testData of tests) { - test(testData.description, async () => testNode(testData, nodeTypes)); + test(testData.description, async () => await testNode(testData, nodeTypes)); } }); diff --git a/packages/nodes-base/nodes/Discord/test/v2/node/message/deleteMessage.test.ts b/packages/nodes-base/nodes/Discord/test/v2/node/message/deleteMessage.test.ts index 5a2fc3c578..c7810af48f 100644 --- a/packages/nodes-base/nodes/Discord/test/v2/node/message/deleteMessage.test.ts +++ b/packages/nodes-base/nodes/Discord/test/v2/node/message/deleteMessage.test.ts @@ -49,6 +49,6 @@ describe('Test DiscordV2, message => deleteMessage', () => { }; for (const testData of tests) { - test(testData.description, async () => testNode(testData, nodeTypes)); + test(testData.description, async () => await testNode(testData, nodeTypes)); } }); diff --git a/packages/nodes-base/nodes/Discord/test/v2/node/message/get.test.ts b/packages/nodes-base/nodes/Discord/test/v2/node/message/get.test.ts index 5dd36d8711..efea97d942 100644 --- a/packages/nodes-base/nodes/Discord/test/v2/node/message/get.test.ts +++ b/packages/nodes-base/nodes/Discord/test/v2/node/message/get.test.ts @@ -68,6 +68,6 @@ describe('Test DiscordV2, message => get', () => { }; for (const testData of tests) { - test(testData.description, async () => testNode(testData, nodeTypes)); + test(testData.description, async () => await testNode(testData, nodeTypes)); } }); diff --git a/packages/nodes-base/nodes/Discord/test/v2/node/message/getAll.test.ts b/packages/nodes-base/nodes/Discord/test/v2/node/message/getAll.test.ts index 0d2d4d7702..70adce6cf0 100644 --- a/packages/nodes-base/nodes/Discord/test/v2/node/message/getAll.test.ts +++ b/packages/nodes-base/nodes/Discord/test/v2/node/message/getAll.test.ts @@ -93,6 +93,6 @@ describe('Test DiscordV2, message => getAll', () => { }; for (const testData of tests) { - test(testData.description, async () => testNode(testData, nodeTypes)); + test(testData.description, async () => await testNode(testData, nodeTypes)); } }); diff --git a/packages/nodes-base/nodes/Discord/test/v2/node/message/react.test.ts b/packages/nodes-base/nodes/Discord/test/v2/node/message/react.test.ts index c830c6a580..780a3029bf 100644 --- a/packages/nodes-base/nodes/Discord/test/v2/node/message/react.test.ts +++ b/packages/nodes-base/nodes/Discord/test/v2/node/message/react.test.ts @@ -49,6 +49,6 @@ describe('Test DiscordV2, message => react', () => { }; for (const testData of tests) { - test(testData.description, async () => testNode(testData, nodeTypes)); + test(testData.description, async () => await testNode(testData, nodeTypes)); } }); diff --git a/packages/nodes-base/nodes/Discord/test/v2/node/message/send.test.ts b/packages/nodes-base/nodes/Discord/test/v2/node/message/send.test.ts index 81292e3fdc..3094cec6e3 100644 --- a/packages/nodes-base/nodes/Discord/test/v2/node/message/send.test.ts +++ b/packages/nodes-base/nodes/Discord/test/v2/node/message/send.test.ts @@ -102,6 +102,6 @@ describe('Test DiscordV2, message => send', () => { }; for (const testData of tests) { - test(testData.description, async () => testNode(testData, nodeTypes)); + test(testData.description, async () => await testNode(testData, nodeTypes)); } }); diff --git a/packages/nodes-base/nodes/Discord/test/v2/node/webhook/sendLegacy.test.ts b/packages/nodes-base/nodes/Discord/test/v2/node/webhook/sendLegacy.test.ts index 04c1e77218..e19917d245 100644 --- a/packages/nodes-base/nodes/Discord/test/v2/node/webhook/sendLegacy.test.ts +++ b/packages/nodes-base/nodes/Discord/test/v2/node/webhook/sendLegacy.test.ts @@ -99,6 +99,6 @@ describe('Test DiscordV2, webhook => sendLegacy', () => { }; for (const testData of tests) { - test(testData.description, async () => testNode(testData, nodeTypes)); + test(testData.description, async () => await testNode(testData, nodeTypes)); } }); diff --git a/packages/nodes-base/nodes/Discord/v2/DiscordV2.node.ts b/packages/nodes-base/nodes/Discord/v2/DiscordV2.node.ts index cfec663665..6e9c433ec8 100644 --- a/packages/nodes-base/nodes/Discord/v2/DiscordV2.node.ts +++ b/packages/nodes-base/nodes/Discord/v2/DiscordV2.node.ts @@ -30,6 +30,6 @@ export class DiscordV2 implements INodeType { }; async execute(this: IExecuteFunctions): Promise { - return router.call(this); + return await router.call(this); } } diff --git a/packages/nodes-base/nodes/Dropbox/GenericFunctions.ts b/packages/nodes-base/nodes/Dropbox/GenericFunctions.ts index 274fdf5276..fedf8a8134 100644 --- a/packages/nodes-base/nodes/Dropbox/GenericFunctions.ts +++ b/packages/nodes-base/nodes/Dropbox/GenericFunctions.ts @@ -85,7 +85,7 @@ export async function dropboxpiRequestAllItems( } export async function getRootDirectory(this: IHookFunctions | IExecuteFunctions) { - return dropboxApiRequest.call( + return await dropboxApiRequest.call( this, 'POST', 'https://api.dropboxapi.com/2/users/get_current_account', diff --git a/packages/nodes-base/nodes/Dropcontact/Dropcontact.node.ts b/packages/nodes-base/nodes/Dropcontact/Dropcontact.node.ts index b7c673b0cb..0eddec8ac9 100644 --- a/packages/nodes-base/nodes/Dropcontact/Dropcontact.node.ts +++ b/packages/nodes-base/nodes/Dropcontact/Dropcontact.node.ts @@ -294,7 +294,7 @@ export class Dropcontact implements INodeType { if (!simplify) { const waitTime = this.getNodeParameter('options.waitTime', 0, 45) as number; - const delay = async (ms: any) => new Promise((res) => setTimeout(res, ms * 1000)); + const delay = async (ms: any) => await new Promise((res) => setTimeout(res, ms * 1000)); await delay(waitTime); responseData = await dropcontactApiRequest.call( this, diff --git a/packages/nodes-base/nodes/Dropcontact/GenericFunction.ts b/packages/nodes-base/nodes/Dropcontact/GenericFunction.ts index 4e09f3981e..a8f8dd89ce 100644 --- a/packages/nodes-base/nodes/Dropcontact/GenericFunction.ts +++ b/packages/nodes-base/nodes/Dropcontact/GenericFunction.ts @@ -34,7 +34,7 @@ export async function dropcontactApiRequest( delete options.qs; } - return this.helpers.requestWithAuthentication.call(this, 'dropcontactApi', options); + return await this.helpers.requestWithAuthentication.call(this, 'dropcontactApi', options); } export function mapPairedItemsFrom(iterable: Iterable | ArrayLike): IPairedItemData[] { diff --git a/packages/nodes-base/nodes/Elastic/ElasticSecurity/GenericFunctions.ts b/packages/nodes-base/nodes/Elastic/ElasticSecurity/GenericFunctions.ts index c1cf82b25b..5b588cf167 100644 --- a/packages/nodes-base/nodes/Elastic/ElasticSecurity/GenericFunctions.ts +++ b/packages/nodes-base/nodes/Elastic/ElasticSecurity/GenericFunctions.ts @@ -97,7 +97,7 @@ export async function handleListing( const returnAll = this.getNodeParameter('returnAll', 0); if (returnAll) { - return elasticSecurityApiRequestAllItems.call(this, method, endpoint, body, qs); + return await elasticSecurityApiRequestAllItems.call(this, method, endpoint, body, qs); } const responseData = await elasticSecurityApiRequestAllItems.call( diff --git a/packages/nodes-base/nodes/EmailReadImap/v1/EmailReadImapV1.node.ts b/packages/nodes-base/nodes/EmailReadImap/v1/EmailReadImapV1.node.ts index fd4aa0714e..1b987c9d55 100644 --- a/packages/nodes-base/nodes/EmailReadImap/v1/EmailReadImapV1.node.ts +++ b/packages/nodes-base/nodes/EmailReadImap/v1/EmailReadImapV1.node.ts @@ -332,7 +332,7 @@ export class EmailReadImapV1 implements INodeType { .getPartData(message, attachmentPart) .then(async (partData) => { // Return it in the format n8n expects - return this.helpers.prepareBinaryData( + return await this.helpers.prepareBinaryData( partData as Buffer, attachmentPart.disposition.params.filename as string, ); @@ -341,7 +341,7 @@ export class EmailReadImapV1 implements INodeType { attachmentPromises.push(attachmentPromise); } - return Promise.all(attachmentPromises); + return await Promise.all(attachmentPromises); }; // Returns all the new unseen messages @@ -588,7 +588,7 @@ export class EmailReadImapV1 implements INodeType { // Connect to the IMAP server and open the mailbox // that we get informed whenever a new email arrives - return imapConnect(config).then(async (conn) => { + return await imapConnect(config).then(async (conn) => { conn.on('error', async (error) => { const errorCode = error.code.toUpperCase(); if (['ECONNRESET', 'EPIPE'].includes(errorCode as string)) { diff --git a/packages/nodes-base/nodes/EmailReadImap/v2/EmailReadImapV2.node.ts b/packages/nodes-base/nodes/EmailReadImap/v2/EmailReadImapV2.node.ts index 7b6214c383..5c762046f1 100644 --- a/packages/nodes-base/nodes/EmailReadImap/v2/EmailReadImapV2.node.ts +++ b/packages/nodes-base/nodes/EmailReadImap/v2/EmailReadImapV2.node.ts @@ -359,13 +359,13 @@ export class EmailReadImapV2 implements INodeType { ?.filename as string, ); // Return it in the format n8n expects - return this.helpers.prepareBinaryData(partData as Buffer, fileName); + return await this.helpers.prepareBinaryData(partData as Buffer, fileName); }); attachmentPromises.push(attachmentPromise); } - return Promise.all(attachmentPromises); + return await Promise.all(attachmentPromises); }; // Returns all the new unseen messages @@ -618,7 +618,7 @@ export class EmailReadImapV2 implements INodeType { // Connect to the IMAP server and open the mailbox // that we get informed whenever a new email arrives - return imapConnect(config).then(async (conn) => { + return await imapConnect(config).then(async (conn) => { conn.on('close', async (_hadError: boolean) => { if (isCurrentlyReconnecting) { this.logger.debug('Email Read Imap: Connected closed for forced reconnecting'); diff --git a/packages/nodes-base/nodes/Emelia/Emelia.node.ts b/packages/nodes-base/nodes/Emelia/Emelia.node.ts index 7a767f1885..6c0a84525d 100644 --- a/packages/nodes-base/nodes/Emelia/Emelia.node.ts +++ b/packages/nodes-base/nodes/Emelia/Emelia.node.ts @@ -68,11 +68,11 @@ export class Emelia implements INodeType { loadOptions: { async getCampaigns(this: ILoadOptionsFunctions) { - return loadResource.call(this, 'campaign'); + return await loadResource.call(this, 'campaign'); }, async getContactLists(this: ILoadOptionsFunctions) { - return loadResource.call(this, 'contactList'); + return await loadResource.call(this, 'contactList'); }, }, }; diff --git a/packages/nodes-base/nodes/ExecuteCommand/ExecuteCommand.node.ts b/packages/nodes-base/nodes/ExecuteCommand/ExecuteCommand.node.ts index 6cec9a9a41..5250270038 100644 --- a/packages/nodes-base/nodes/ExecuteCommand/ExecuteCommand.node.ts +++ b/packages/nodes-base/nodes/ExecuteCommand/ExecuteCommand.node.ts @@ -26,7 +26,7 @@ async function execPromise(command: string): Promise { stdout: '', }; - return new Promise((resolve, _reject) => { + return await new Promise((resolve, _reject) => { exec(command, { cwd: process.cwd() }, (error, stdout, stderr) => { returnData.stdout = stdout.trim(); returnData.stderr = stderr.trim(); diff --git a/packages/nodes-base/nodes/FacebookLeadAds/GenericFunctions.ts b/packages/nodes-base/nodes/FacebookLeadAds/GenericFunctions.ts index dd341c56f3..759547f601 100644 --- a/packages/nodes-base/nodes/FacebookLeadAds/GenericFunctions.ts +++ b/packages/nodes-base/nodes/FacebookLeadAds/GenericFunctions.ts @@ -123,7 +123,7 @@ export async function appWebhookSubscriptionCreate( appId: string, subscription: CreateFacebookAppWebhookSubscription, ) { - return facebookAppApiRequest.call(this, 'POST', `/${appId}/subscriptions`, { + return await facebookAppApiRequest.call(this, 'POST', `/${appId}/subscriptions`, { type: 'form', payload: { ...subscription }, }); @@ -134,7 +134,7 @@ export async function appWebhookSubscriptionDelete( appId: string, object: string, ) { - return facebookAppApiRequest.call(this, 'DELETE', `/${appId}/subscriptions`, { + return await facebookAppApiRequest.call(this, 'DELETE', `/${appId}/subscriptions`, { type: 'form', payload: { object }, }); @@ -159,7 +159,7 @@ export async function facebookEntityDetail( entityId: string, fields = 'id,name,access_token', ): Promise { - return facebookApiRequest.call(this, 'GET', `/${entityId}`, {}, { fields }); + return await facebookApiRequest.call(this, 'GET', `/${entityId}`, {}, { fields }); } export async function facebookPageApiRequest( @@ -197,7 +197,7 @@ export async function installAppOnPage( pageId: string, fields: string, ) { - return facebookPageApiRequest.call( + return await facebookPageApiRequest.call( this, 'POST', `/${pageId}/subscribed_apps`, diff --git a/packages/nodes-base/nodes/FileMaker/FileMaker.node.ts b/packages/nodes-base/nodes/FileMaker/FileMaker.node.ts index 8f54a7c8bb..45639a38c2 100644 --- a/packages/nodes-base/nodes/FileMaker/FileMaker.node.ts +++ b/packages/nodes-base/nodes/FileMaker/FileMaker.node.ts @@ -600,7 +600,7 @@ export class FileMaker implements INodeType { // Get all the available topics to display them to user so that they can // select them easily async getLayouts(this: ILoadOptionsFunctions): Promise { - return layoutsApiRequest.call(this); + return await layoutsApiRequest.call(this); }, async getResponseLayouts(this: ILoadOptionsFunctions): Promise { const returnData: INodePropertyOptions[] = []; diff --git a/packages/nodes-base/nodes/Form/v1/FormTriggerV1.node.ts b/packages/nodes-base/nodes/Form/v1/FormTriggerV1.node.ts index c9096ddcb6..bf31639bd6 100644 --- a/packages/nodes-base/nodes/Form/v1/FormTriggerV1.node.ts +++ b/packages/nodes-base/nodes/Form/v1/FormTriggerV1.node.ts @@ -93,6 +93,6 @@ export class FormTriggerV1 implements INodeType { } async webhook(this: IWebhookFunctions) { - return formWebhook(this); + return await formWebhook(this); } } diff --git a/packages/nodes-base/nodes/Form/v2/FormTriggerV2.node.ts b/packages/nodes-base/nodes/Form/v2/FormTriggerV2.node.ts index 864c24d508..8a59fca4d6 100644 --- a/packages/nodes-base/nodes/Form/v2/FormTriggerV2.node.ts +++ b/packages/nodes-base/nodes/Form/v2/FormTriggerV2.node.ts @@ -97,6 +97,6 @@ export class FormTriggerV2 implements INodeType { } async webhook(this: IWebhookFunctions) { - return formWebhook(this); + return await formWebhook(this); } } diff --git a/packages/nodes-base/nodes/Freshservice/GenericFunctions.ts b/packages/nodes-base/nodes/Freshservice/GenericFunctions.ts index da222cccaa..f1894288b9 100644 --- a/packages/nodes-base/nodes/Freshservice/GenericFunctions.ts +++ b/packages/nodes-base/nodes/Freshservice/GenericFunctions.ts @@ -106,7 +106,7 @@ export async function handleListing( const returnAll = this.getNodeParameter('returnAll', 0); if (returnAll) { - return freshserviceApiRequestAllItems.call(this, method, endpoint, body, qs); + return await freshserviceApiRequestAllItems.call(this, method, endpoint, body, qs); } const responseData = await freshserviceApiRequestAllItems.call(this, method, endpoint, body, qs); diff --git a/packages/nodes-base/nodes/FreshworksCrm/FreshworksCrm.node.ts b/packages/nodes-base/nodes/FreshworksCrm/FreshworksCrm.node.ts index 376e7eb63b..6d7b4f4e1b 100644 --- a/packages/nodes-base/nodes/FreshworksCrm/FreshworksCrm.node.ts +++ b/packages/nodes-base/nodes/FreshworksCrm/FreshworksCrm.node.ts @@ -139,15 +139,15 @@ export class FreshworksCrm implements INodeType { }, async getBusinessTypes(this: ILoadOptionsFunctions) { - return loadResource.call(this, 'business_types'); + return await loadResource.call(this, 'business_types'); }, async getCampaigns(this: ILoadOptionsFunctions) { - return loadResource.call(this, 'campaigns'); + return await loadResource.call(this, 'campaigns'); }, async getContactStatuses(this: ILoadOptionsFunctions) { - return loadResource.call(this, 'contact_statuses'); + return await loadResource.call(this, 'contact_statuses'); }, async getContactViews(this: ILoadOptionsFunctions) { @@ -169,27 +169,27 @@ export class FreshworksCrm implements INodeType { }, async getDealPaymentStatuses(this: ILoadOptionsFunctions) { - return loadResource.call(this, 'deal_payment_statuses'); + return await loadResource.call(this, 'deal_payment_statuses'); }, async getDealPipelines(this: ILoadOptionsFunctions) { - return loadResource.call(this, 'deal_pipelines'); + return await loadResource.call(this, 'deal_pipelines'); }, async getDealProducts(this: ILoadOptionsFunctions) { - return loadResource.call(this, 'deal_products'); + return await loadResource.call(this, 'deal_products'); }, async getDealReasons(this: ILoadOptionsFunctions) { - return loadResource.call(this, 'deal_reasons'); + return await loadResource.call(this, 'deal_reasons'); }, async getDealStages(this: ILoadOptionsFunctions) { - return loadResource.call(this, 'deal_stages'); + return await loadResource.call(this, 'deal_stages'); }, async getDealTypes(this: ILoadOptionsFunctions) { - return loadResource.call(this, 'deal_types'); + return await loadResource.call(this, 'deal_types'); }, async getDealViews(this: ILoadOptionsFunctions) { @@ -199,23 +199,23 @@ export class FreshworksCrm implements INodeType { }, async getIndustryTypes(this: ILoadOptionsFunctions) { - return loadResource.call(this, 'industry_types'); + return await loadResource.call(this, 'industry_types'); }, async getLifecycleStages(this: ILoadOptionsFunctions) { - return loadResource.call(this, 'lifecycle_stages'); + return await loadResource.call(this, 'lifecycle_stages'); }, async getOutcomes(this: ILoadOptionsFunctions) { - return loadResource.call(this, 'sales_activity_outcomes'); + return await loadResource.call(this, 'sales_activity_outcomes'); }, async getSalesActivityTypes(this: ILoadOptionsFunctions) { - return loadResource.call(this, 'sales_activity_types'); + return await loadResource.call(this, 'sales_activity_types'); }, async getTerritories(this: ILoadOptionsFunctions) { - return loadResource.call(this, 'territories'); + return await loadResource.call(this, 'territories'); }, async getUsers(this: ILoadOptionsFunctions) { diff --git a/packages/nodes-base/nodes/FreshworksCrm/GenericFunctions.ts b/packages/nodes-base/nodes/FreshworksCrm/GenericFunctions.ts index 7438c72dd4..8d4f33bb52 100644 --- a/packages/nodes-base/nodes/FreshworksCrm/GenericFunctions.ts +++ b/packages/nodes-base/nodes/FreshworksCrm/GenericFunctions.ts @@ -110,7 +110,7 @@ export async function handleListing( const returnAll = this.getNodeParameter('returnAll', 0); if (returnAll) { - return freshworksCrmApiRequestAllItems.call(this, method, endpoint, body, qs); + return await freshworksCrmApiRequestAllItems.call(this, method, endpoint, body, qs); } const responseData = await freshworksCrmApiRequestAllItems.call(this, method, endpoint, body, qs); diff --git a/packages/nodes-base/nodes/Ghost/GenericFunctions.ts b/packages/nodes-base/nodes/Ghost/GenericFunctions.ts index 0178966870..d8ff358b75 100644 --- a/packages/nodes-base/nodes/Ghost/GenericFunctions.ts +++ b/packages/nodes-base/nodes/Ghost/GenericFunctions.ts @@ -40,7 +40,7 @@ export async function ghostApiRequest( json: true, }; - return this.helpers.requestWithAuthentication.call(this, credentialType, options); + return await this.helpers.requestWithAuthentication.call(this, credentialType, options); } export async function ghostApiRequestAllItems( diff --git a/packages/nodes-base/nodes/GoToWebinar/GenericFunctions.ts b/packages/nodes-base/nodes/GoToWebinar/GenericFunctions.ts index 6ccdcb16bb..f86af87550 100644 --- a/packages/nodes-base/nodes/GoToWebinar/GenericFunctions.ts +++ b/packages/nodes-base/nodes/GoToWebinar/GenericFunctions.ts @@ -139,7 +139,7 @@ export async function handleGetAll( qs.limit = this.getNodeParameter('limit', 0); } - return goToWebinarApiRequestAllItems.call(this, 'GET', endpoint, qs, body, resource); + return await goToWebinarApiRequestAllItems.call(this, 'GET', endpoint, qs, body, resource); } export async function loadWebinars(this: ILoadOptionsFunctions) { diff --git a/packages/nodes-base/nodes/GoToWebinar/GoToWebinar.node.ts b/packages/nodes-base/nodes/GoToWebinar/GoToWebinar.node.ts index 1628b34b2c..411ffe6542 100644 --- a/packages/nodes-base/nodes/GoToWebinar/GoToWebinar.node.ts +++ b/packages/nodes-base/nodes/GoToWebinar/GoToWebinar.node.ts @@ -110,13 +110,13 @@ export class GoToWebinar implements INodeType { methods = { loadOptions: { async getWebinars(this: ILoadOptionsFunctions) { - return loadWebinars.call(this); + return await loadWebinars.call(this); }, async getAnswers(this: ILoadOptionsFunctions) { - return loadAnswers.call(this); + return await loadAnswers.call(this); }, async getWebinarSessions(this: ILoadOptionsFunctions) { - return loadWebinarSessions.call(this); + return await loadWebinarSessions.call(this); }, // Get all the timezones to display them to user so that they can // select them easily @@ -135,12 +135,12 @@ export class GoToWebinar implements INodeType { async getRegistranSimpleQuestions( this: ILoadOptionsFunctions, ): Promise { - return loadRegistranSimpleQuestions.call(this); + return await loadRegistranSimpleQuestions.call(this); }, async getRegistranMultiChoiceQuestions( this: ILoadOptionsFunctions, ): Promise { - return loadRegistranMultiChoiceQuestions.call(this); + return await loadRegistranMultiChoiceQuestions.call(this); }, }, }; diff --git a/packages/nodes-base/nodes/Google/Analytics/v2/GoogleAnalyticsV2.node.ts b/packages/nodes-base/nodes/Google/Analytics/v2/GoogleAnalyticsV2.node.ts index 1ae20cf865..7c42fdc246 100644 --- a/packages/nodes-base/nodes/Google/Analytics/v2/GoogleAnalyticsV2.node.ts +++ b/packages/nodes-base/nodes/Google/Analytics/v2/GoogleAnalyticsV2.node.ts @@ -22,6 +22,6 @@ export class GoogleAnalyticsV2 implements INodeType { methods = { loadOptions, listSearch }; async execute(this: IExecuteFunctions): Promise { - return router.call(this); + return await router.call(this); } } diff --git a/packages/nodes-base/nodes/Google/BigQuery/test/v2/node/executeQuery.test.ts b/packages/nodes-base/nodes/Google/BigQuery/test/v2/node/executeQuery.test.ts index 26cfd317e2..d7aa4ad566 100644 --- a/packages/nodes-base/nodes/Google/BigQuery/test/v2/node/executeQuery.test.ts +++ b/packages/nodes-base/nodes/Google/BigQuery/test/v2/node/executeQuery.test.ts @@ -71,6 +71,6 @@ describe('Test Google BigQuery V2, executeQuery', () => { }; for (const testData of tests) { - test(testData.description, async () => testNode(testData, nodeTypes)); + test(testData.description, async () => await testNode(testData, nodeTypes)); } }); diff --git a/packages/nodes-base/nodes/Google/BigQuery/test/v2/node/insert.autoMapMode.test.ts b/packages/nodes-base/nodes/Google/BigQuery/test/v2/node/insert.autoMapMode.test.ts index 838b665eed..b1ca9f279b 100644 --- a/packages/nodes-base/nodes/Google/BigQuery/test/v2/node/insert.autoMapMode.test.ts +++ b/packages/nodes-base/nodes/Google/BigQuery/test/v2/node/insert.autoMapMode.test.ts @@ -78,6 +78,6 @@ describe('Test Google BigQuery V2, insert auto map', () => { }; for (const testData of tests) { - test(testData.description, async () => testNode(testData, nodeTypes)); + test(testData.description, async () => await testNode(testData, nodeTypes)); } }); diff --git a/packages/nodes-base/nodes/Google/BigQuery/test/v2/node/insert.manualMode.test.ts b/packages/nodes-base/nodes/Google/BigQuery/test/v2/node/insert.manualMode.test.ts index d0c3863e72..6d3dea8d92 100644 --- a/packages/nodes-base/nodes/Google/BigQuery/test/v2/node/insert.manualMode.test.ts +++ b/packages/nodes-base/nodes/Google/BigQuery/test/v2/node/insert.manualMode.test.ts @@ -75,6 +75,6 @@ describe('Test Google BigQuery V2, insert define manually', () => { }; for (const testData of tests) { - test(testData.description, async () => testNode(testData, nodeTypes)); + test(testData.description, async () => await testNode(testData, nodeTypes)); } }); diff --git a/packages/nodes-base/nodes/Google/BigQuery/v2/GoogleBigQueryV2.node.ts b/packages/nodes-base/nodes/Google/BigQuery/v2/GoogleBigQueryV2.node.ts index 07ef323406..9d55d46328 100644 --- a/packages/nodes-base/nodes/Google/BigQuery/v2/GoogleBigQueryV2.node.ts +++ b/packages/nodes-base/nodes/Google/BigQuery/v2/GoogleBigQueryV2.node.ts @@ -23,6 +23,6 @@ export class GoogleBigQueryV2 implements INodeType { methods = { loadOptions, listSearch }; async execute(this: IExecuteFunctions): Promise { - return router.call(this); + return await router.call(this); } } diff --git a/packages/nodes-base/nodes/Google/Drive/v2/GoogleDriveV2.node.ts b/packages/nodes-base/nodes/Google/Drive/v2/GoogleDriveV2.node.ts index 9ca8ec449c..5e147d81bf 100644 --- a/packages/nodes-base/nodes/Google/Drive/v2/GoogleDriveV2.node.ts +++ b/packages/nodes-base/nodes/Google/Drive/v2/GoogleDriveV2.node.ts @@ -22,6 +22,6 @@ export class GoogleDriveV2 implements INodeType { methods = { listSearch }; async execute(this: IExecuteFunctions) { - return router.call(this); + return await router.call(this); } } diff --git a/packages/nodes-base/nodes/Google/GenericFunctions.ts b/packages/nodes-base/nodes/Google/GenericFunctions.ts index c89f3e8241..8658366df5 100644 --- a/packages/nodes-base/nodes/Google/GenericFunctions.ts +++ b/packages/nodes-base/nodes/Google/GenericFunctions.ts @@ -103,5 +103,5 @@ export async function getGoogleAccessToken( json: true, }; - return this.helpers.request(options); + return await this.helpers.request(options); } diff --git a/packages/nodes-base/nodes/Google/Gmail/GenericFunctions.ts b/packages/nodes-base/nodes/Google/Gmail/GenericFunctions.ts index b9bfe8f7f9..2d84624fa0 100644 --- a/packages/nodes-base/nodes/Google/Gmail/GenericFunctions.ts +++ b/packages/nodes-base/nodes/Google/Gmail/GenericFunctions.ts @@ -638,7 +638,7 @@ export async function replyToEmail( threadId, }; - return googleApiRequest.call(this, 'POST', '/gmail/v1/users/me/messages/send', body, qs); + return await googleApiRequest.call(this, 'POST', '/gmail/v1/users/me/messages/send', body, qs); } export async function simplifyOutput( diff --git a/packages/nodes-base/nodes/Google/Sheet/v1/GoogleSheet.ts b/packages/nodes-base/nodes/Google/Sheet/v1/GoogleSheet.ts index aad614bebd..41aa3a24a7 100644 --- a/packages/nodes-base/nodes/Google/Sheet/v1/GoogleSheet.ts +++ b/packages/nodes-base/nodes/Google/Sheet/v1/GoogleSheet.ts @@ -281,7 +281,7 @@ export class GoogleSheet { keyRowIndex, usePathForKeyRow, ); - return this.appendData(range, data, valueInputMode); + return await this.appendData(range, data, valueInputMode); } getColumnWithOffset(startColumn: string, offset: number): string { @@ -456,7 +456,7 @@ export class GoogleSheet { } } - return this.batchUpdate(updateData, valueInputMode); + return await this.batchUpdate(updateData, valueInputMode); } /** diff --git a/packages/nodes-base/nodes/Google/Sheet/v2/GoogleSheetsV2.node.ts b/packages/nodes-base/nodes/Google/Sheet/v2/GoogleSheetsV2.node.ts index 0fb3dacdc7..912ca8a797 100644 --- a/packages/nodes-base/nodes/Google/Sheet/v2/GoogleSheetsV2.node.ts +++ b/packages/nodes-base/nodes/Google/Sheet/v2/GoogleSheetsV2.node.ts @@ -26,6 +26,6 @@ export class GoogleSheetsV2 implements INodeType { }; async execute(this: IExecuteFunctions) { - return router.call(this); + return await router.call(this); } } diff --git a/packages/nodes-base/nodes/Google/Sheet/v2/helpers/GoogleSheet.ts b/packages/nodes-base/nodes/Google/Sheet/v2/helpers/GoogleSheet.ts index ef3e4b9130..5203dee6e0 100644 --- a/packages/nodes-base/nodes/Google/Sheet/v2/helpers/GoogleSheet.ts +++ b/packages/nodes-base/nodes/Google/Sheet/v2/helpers/GoogleSheet.ts @@ -401,7 +401,7 @@ export class GoogleSheet { columnNamesList, useAppend ? null : '', ); - return this.appendData(range, data, valueInputMode, lastRow, useAppend); + return await this.appendData(range, data, valueInputMode, lastRow, useAppend); } getColumnWithOffset(startColumn: string, offset: number): string { diff --git a/packages/nodes-base/nodes/HighLevel/GenericFunctions.ts b/packages/nodes-base/nodes/HighLevel/GenericFunctions.ts index 25af2a5d24..59fa6c9617 100644 --- a/packages/nodes-base/nodes/HighLevel/GenericFunctions.ts +++ b/packages/nodes-base/nodes/HighLevel/GenericFunctions.ts @@ -152,7 +152,7 @@ export async function highLevelApiRequest( delete options.qs; } options = Object.assign({}, options, option); - return this.helpers.requestWithAuthentication.call(this, 'highLevelApi', options); + return await this.helpers.requestWithAuthentication.call(this, 'highLevelApi', options); } export async function opportunityUpdatePreSendAction( diff --git a/packages/nodes-base/nodes/HomeAssistant/HomeAssistant.node.ts b/packages/nodes-base/nodes/HomeAssistant/HomeAssistant.node.ts index 30f8396353..3fd0f241e6 100644 --- a/packages/nodes-base/nodes/HomeAssistant/HomeAssistant.node.ts +++ b/packages/nodes-base/nodes/HomeAssistant/HomeAssistant.node.ts @@ -158,18 +158,18 @@ export class HomeAssistant implements INodeType { loadOptions: { async getAllEntities(this: ILoadOptionsFunctions): Promise { - return getHomeAssistantEntities.call(this); + return await getHomeAssistantEntities.call(this); }, async getCameraEntities(this: ILoadOptionsFunctions): Promise { - return getHomeAssistantEntities.call(this, 'camera'); + return await getHomeAssistantEntities.call(this, 'camera'); }, async getDomains(this: ILoadOptionsFunctions): Promise { - return getHomeAssistantServices.call(this); + return await getHomeAssistantServices.call(this); }, async getDomainServices(this: ILoadOptionsFunctions): Promise { const currentDomain = this.getCurrentNodeParameter('domain') as string; if (currentDomain) { - return getHomeAssistantServices.call(this, currentDomain); + return await getHomeAssistantServices.call(this, currentDomain); } else { return []; } diff --git a/packages/nodes-base/nodes/HttpRequest/GenericFunctions.ts b/packages/nodes-base/nodes/HttpRequest/GenericFunctions.ts index ccf6a14a12..4227961724 100644 --- a/packages/nodes-base/nodes/HttpRequest/GenericFunctions.ts +++ b/packages/nodes-base/nodes/HttpRequest/GenericFunctions.ts @@ -145,8 +145,8 @@ export async function reduceAsync( reducer: (acc: Awaited>, cur: T) => Promise, init: Promise = Promise.resolve({} as R), ): Promise { - return arr.reduce(async (promiseAcc, item) => { - return reducer(await promiseAcc, item); + return await arr.reduce(async (promiseAcc, item) => { + return await reducer(await promiseAcc, item); }, init); } @@ -157,12 +157,12 @@ export const prepareRequestBody = async ( defaultReducer: BodyParametersReducer, ) => { if (bodyType === 'json' && version >= 4) { - return parameters.reduce(async (acc, entry) => { + return await parameters.reduce(async (acc, entry) => { const result = await acc; set(result, entry.name, entry.value); return result; }, Promise.resolve({})); } else { - return reduceAsync(parameters, defaultReducer); + return await reduceAsync(parameters, defaultReducer); } }; diff --git a/packages/nodes-base/nodes/HttpRequest/test/binaryData/HttpRequest.test.ts b/packages/nodes-base/nodes/HttpRequest/test/binaryData/HttpRequest.test.ts index 4ef4a17201..32734939c2 100644 --- a/packages/nodes-base/nodes/HttpRequest/test/binaryData/HttpRequest.test.ts +++ b/packages/nodes-base/nodes/HttpRequest/test/binaryData/HttpRequest.test.ts @@ -40,6 +40,6 @@ describe('Test Binary Data Download', () => { const nodeTypes = setup(tests); for (const testData of tests) { - test(testData.description, async () => equalityTest(testData, nodeTypes)); + test(testData.description, async () => await equalityTest(testData, nodeTypes)); } }); diff --git a/packages/nodes-base/nodes/HttpRequest/test/node/HttpRequest.test.ts b/packages/nodes-base/nodes/HttpRequest/test/node/HttpRequest.test.ts index d594a04a19..1c466ddade 100644 --- a/packages/nodes-base/nodes/HttpRequest/test/node/HttpRequest.test.ts +++ b/packages/nodes-base/nodes/HttpRequest/test/node/HttpRequest.test.ts @@ -169,6 +169,6 @@ describe('Test HTTP Request Node', () => { const nodeTypes = setup(tests); for (const testData of tests) { - test(testData.description, async () => equalityTest(testData, nodeTypes)); + test(testData.description, async () => await equalityTest(testData, nodeTypes)); } }); diff --git a/packages/nodes-base/nodes/Hubspot/V1/GenericFunctions.ts b/packages/nodes-base/nodes/Hubspot/V1/GenericFunctions.ts index 2e9ce210c1..758f8bad04 100644 --- a/packages/nodes-base/nodes/Hubspot/V1/GenericFunctions.ts +++ b/packages/nodes-base/nodes/Hubspot/V1/GenericFunctions.ts @@ -2015,5 +2015,5 @@ export async function validateCredentials( options.headers = { Authorization: `Bearer ${appToken}` }; } - return this.helpers.request(options); + return await this.helpers.request(options); } diff --git a/packages/nodes-base/nodes/Hubspot/V2/GenericFunctions.ts b/packages/nodes-base/nodes/Hubspot/V2/GenericFunctions.ts index a0a8a8f570..2cd2e54782 100644 --- a/packages/nodes-base/nodes/Hubspot/V2/GenericFunctions.ts +++ b/packages/nodes-base/nodes/Hubspot/V2/GenericFunctions.ts @@ -2004,5 +2004,5 @@ export async function validateCredentials( options.headers = { Authorization: `Bearer ${appToken}` }; } - return this.helpers.request(options); + return await this.helpers.request(options); } diff --git a/packages/nodes-base/nodes/ItemLists/V1/ItemListsV1.node.ts b/packages/nodes-base/nodes/ItemLists/V1/ItemListsV1.node.ts index 545b83502d..674a9eb42d 100644 --- a/packages/nodes-base/nodes/ItemLists/V1/ItemListsV1.node.ts +++ b/packages/nodes-base/nodes/ItemLists/V1/ItemListsV1.node.ts @@ -1384,7 +1384,7 @@ return 0;`, } return [newItems]; } else if (operation === 'summarize') { - return summarize.execute.call(this, items); + return await summarize.execute.call(this, items); } else { throw new NodeOperationError(this.getNode(), `Operation '${operation}' is not recognized`); } diff --git a/packages/nodes-base/nodes/ItemLists/V2/ItemListsV2.node.ts b/packages/nodes-base/nodes/ItemLists/V2/ItemListsV2.node.ts index 52ca465e49..7b1738dc07 100644 --- a/packages/nodes-base/nodes/ItemLists/V2/ItemListsV2.node.ts +++ b/packages/nodes-base/nodes/ItemLists/V2/ItemListsV2.node.ts @@ -1431,7 +1431,7 @@ return 0;`, } return [newItems]; } else if (operation === 'summarize') { - return summarize.execute.call(this, items); + return await summarize.execute.call(this, items); } else { throw new NodeOperationError(this.getNode(), `Operation '${operation}' is not recognized`); } diff --git a/packages/nodes-base/nodes/ItemLists/V3/ItemListsV3.node.ts b/packages/nodes-base/nodes/ItemLists/V3/ItemListsV3.node.ts index 84c8826619..d2408eb277 100644 --- a/packages/nodes-base/nodes/ItemLists/V3/ItemListsV3.node.ts +++ b/packages/nodes-base/nodes/ItemLists/V3/ItemListsV3.node.ts @@ -19,6 +19,6 @@ export class ItemListsV3 implements INodeType { } async execute(this: IExecuteFunctions) { - return router.call(this); + return await router.call(this); } } diff --git a/packages/nodes-base/nodes/Jira/Jira.node.ts b/packages/nodes-base/nodes/Jira/Jira.node.ts index 8699125d21..ed71ec7f9a 100644 --- a/packages/nodes-base/nodes/Jira/Jira.node.ts +++ b/packages/nodes-base/nodes/Jira/Jira.node.ts @@ -356,7 +356,7 @@ export class Jira implements INodeType { // Get all the users to display them to user so that they can // select them easily async getUsers(this: ILoadOptionsFunctions): Promise { - return getUsers.call(this); + return await getUsers.call(this); }, // Get all the groups to display them to user so that they can diff --git a/packages/nodes-base/nodes/KoBoToolbox/GenericFunctions.ts b/packages/nodes-base/nodes/KoBoToolbox/GenericFunctions.ts index dcc06652e7..cb9b6b6a2c 100644 --- a/packages/nodes-base/nodes/KoBoToolbox/GenericFunctions.ts +++ b/packages/nodes-base/nodes/KoBoToolbox/GenericFunctions.ts @@ -84,7 +84,7 @@ export async function koBoToolboxRawRequest( option.url = (credentials.URL as string) + option.url; } - return this.helpers.httpRequestWithAuthentication.call(this, 'koBoToolboxApi', option); + return await this.helpers.httpRequestWithAuthentication.call(this, 'koBoToolboxApi', option); } function parseGeoPoint(geoPoint: string): null | number[] { diff --git a/packages/nodes-base/nodes/Lemlist/GenericFunctions.ts b/packages/nodes-base/nodes/Lemlist/GenericFunctions.ts index 5257c0234e..0a786c4019 100644 --- a/packages/nodes-base/nodes/Lemlist/GenericFunctions.ts +++ b/packages/nodes-base/nodes/Lemlist/GenericFunctions.ts @@ -41,7 +41,7 @@ export async function lemlistApiRequest( Object.assign(options, option); } - return this.helpers.requestWithAuthentication.call(this, 'lemlistApi', options); + return await this.helpers.requestWithAuthentication.call(this, 'lemlistApi', options); } /** diff --git a/packages/nodes-base/nodes/Linear/GenericFunctions.ts b/packages/nodes-base/nodes/Linear/GenericFunctions.ts index 2ccd7631a7..3a21e9d641 100644 --- a/packages/nodes-base/nodes/Linear/GenericFunctions.ts +++ b/packages/nodes-base/nodes/Linear/GenericFunctions.ts @@ -92,7 +92,7 @@ export async function validateCredentials( json: true, }; - return this.helpers.request(options); + return await this.helpers.request(options); } //@ts-ignore diff --git a/packages/nodes-base/nodes/LocalFileTrigger/LocalFileTrigger.node.ts b/packages/nodes-base/nodes/LocalFileTrigger/LocalFileTrigger.node.ts index 3b9dce008d..1acb8d4c47 100644 --- a/packages/nodes-base/nodes/LocalFileTrigger/LocalFileTrigger.node.ts +++ b/packages/nodes-base/nodes/LocalFileTrigger/LocalFileTrigger.node.ts @@ -241,7 +241,7 @@ export class LocalFileTrigger implements INodeType { } async function closeFunction() { - return watcher.close(); + return await watcher.close(); } return { diff --git a/packages/nodes-base/nodes/Magento/Magento2.node.ts b/packages/nodes-base/nodes/Magento/Magento2.node.ts index e23620e452..c9daf7e795 100644 --- a/packages/nodes-base/nodes/Magento/Magento2.node.ts +++ b/packages/nodes-base/nodes/Magento/Magento2.node.ts @@ -277,10 +277,10 @@ export class Magento2 implements INodeType { async getFilterableCustomerAttributes( this: ILoadOptionsFunctions, ): Promise { - return getProductAttributes.call(this, (attribute) => attribute.is_filterable); + return await getProductAttributes.call(this, (attribute) => attribute.is_filterable); }, async getProductAttributes(this: ILoadOptionsFunctions): Promise { - return getProductAttributes.call(this); + return await getProductAttributes.call(this); }, // async getProductAttributesFields(this: ILoadOptionsFunctions): Promise { // return getProductAttributes.call(this, undefined, { name: '*', value: '*', description: 'All properties' }); @@ -288,12 +288,15 @@ export class Magento2 implements INodeType { async getFilterableProductAttributes( this: ILoadOptionsFunctions, ): Promise { - return getProductAttributes.call(this, (attribute) => attribute.is_searchable === '1'); + return await getProductAttributes.call( + this, + (attribute) => attribute.is_searchable === '1', + ); }, async getSortableProductAttributes( this: ILoadOptionsFunctions, ): Promise { - return getProductAttributes.call(this, (attribute) => attribute.used_for_sort_by); + return await getProductAttributes.call(this, (attribute) => attribute.used_for_sort_by); }, async getOrderAttributes(this: ILoadOptionsFunctions): Promise { return getOrderFields() diff --git a/packages/nodes-base/nodes/Mailchimp/GenericFunctions.ts b/packages/nodes-base/nodes/Mailchimp/GenericFunctions.ts index c1b4c5ca69..7301e6f984 100644 --- a/packages/nodes-base/nodes/Mailchimp/GenericFunctions.ts +++ b/packages/nodes-base/nodes/Mailchimp/GenericFunctions.ts @@ -23,7 +23,7 @@ async function getMetadata( url: credentials.metadataUrl as string, json: true, }; - return this.helpers.request(options); + return await this.helpers.request(options); } export async function mailchimpApiRequest( diff --git a/packages/nodes-base/nodes/Mailjet/GenericFunctions.ts b/packages/nodes-base/nodes/Mailjet/GenericFunctions.ts index e4b55fcdd9..d72857a08c 100644 --- a/packages/nodes-base/nodes/Mailjet/GenericFunctions.ts +++ b/packages/nodes-base/nodes/Mailjet/GenericFunctions.ts @@ -50,7 +50,7 @@ export async function mailjetApiRequest( delete options.body; } - return this.helpers.requestWithAuthentication.call(this, credentialType, options); + return await this.helpers.requestWithAuthentication.call(this, credentialType, options); } export async function mailjetApiRequestAllItems( diff --git a/packages/nodes-base/nodes/Matrix/GenericFunctions.ts b/packages/nodes-base/nodes/Matrix/GenericFunctions.ts index 4231df3c8b..7747921ebe 100644 --- a/packages/nodes-base/nodes/Matrix/GenericFunctions.ts +++ b/packages/nodes-base/nodes/Matrix/GenericFunctions.ts @@ -64,7 +64,7 @@ export async function handleMatrixCall( ): Promise { if (resource === 'account') { if (operation === 'me') { - return matrixApiRequest.call(this, 'GET', '/account/whoami'); + return await matrixApiRequest.call(this, 'GET', '/account/whoami'); } } else if (resource === 'room') { if (operation === 'create') { @@ -78,20 +78,20 @@ export async function handleMatrixCall( if (roomAlias) { body.room_alias_name = roomAlias; } - return matrixApiRequest.call(this, 'POST', '/createRoom', body); + return await matrixApiRequest.call(this, 'POST', '/createRoom', body); } else if (operation === 'join') { const roomIdOrAlias = this.getNodeParameter('roomIdOrAlias', index) as string; - return matrixApiRequest.call(this, 'POST', `/rooms/${roomIdOrAlias}/join`); + return await matrixApiRequest.call(this, 'POST', `/rooms/${roomIdOrAlias}/join`); } else if (operation === 'leave') { const roomId = this.getNodeParameter('roomId', index) as string; - return matrixApiRequest.call(this, 'POST', `/rooms/${roomId}/leave`); + return await matrixApiRequest.call(this, 'POST', `/rooms/${roomId}/leave`); } else if (operation === 'invite') { const roomId = this.getNodeParameter('roomId', index) as string; const userId = this.getNodeParameter('userId', index) as string; const body: IDataObject = { user_id: userId, }; - return matrixApiRequest.call(this, 'POST', `/rooms/${roomId}/invite`, body); + return await matrixApiRequest.call(this, 'POST', `/rooms/${roomId}/invite`, body); } else if (operation === 'kick') { const roomId = this.getNodeParameter('roomId', index) as string; const userId = this.getNodeParameter('userId', index) as string; @@ -100,7 +100,7 @@ export async function handleMatrixCall( user_id: userId, reason, }; - return matrixApiRequest.call(this, 'POST', `/rooms/${roomId}/kick`, body); + return await matrixApiRequest.call(this, 'POST', `/rooms/${roomId}/kick`, body); } } else if (resource === 'message') { if (operation === 'create') { @@ -119,7 +119,7 @@ export async function handleMatrixCall( body.body = fallbackText; } const messageId = uuid(); - return matrixApiRequest.call( + return await matrixApiRequest.call( this, 'PUT', `/rooms/${roomId}/send/m.room.message/${messageId}`, @@ -181,7 +181,7 @@ export async function handleMatrixCall( if (operation === 'get') { const roomId = this.getNodeParameter('roomId', index) as string; const eventId = this.getNodeParameter('eventId', index) as string; - return matrixApiRequest.call(this, 'GET', `/rooms/${roomId}/event/${eventId}`); + return await matrixApiRequest.call(this, 'GET', `/rooms/${roomId}/event/${eventId}`); } } else if (resource === 'media') { if (operation === 'upload') { @@ -225,7 +225,7 @@ export async function handleMatrixCall( url: uploadRequestResult.content_uri, }; const messageId = uuid(); - return matrixApiRequest.call( + return await matrixApiRequest.call( this, 'PUT', `/rooms/${roomId}/send/m.room.message/${messageId}`, diff --git a/packages/nodes-base/nodes/Mattermost/v1/MattermostV1.node.ts b/packages/nodes-base/nodes/Mattermost/v1/MattermostV1.node.ts index c099142f03..744a6f3709 100644 --- a/packages/nodes-base/nodes/Mattermost/v1/MattermostV1.node.ts +++ b/packages/nodes-base/nodes/Mattermost/v1/MattermostV1.node.ts @@ -22,6 +22,6 @@ export class MattermostV1 implements INodeType { methods = { loadOptions }; async execute(this: IExecuteFunctions) { - return router.call(this); + return await router.call(this); } } diff --git a/packages/nodes-base/nodes/Mattermost/v1/transport/index.ts b/packages/nodes-base/nodes/Mattermost/v1/transport/index.ts index d18d5e5967..9d5e3d8c54 100644 --- a/packages/nodes-base/nodes/Mattermost/v1/transport/index.ts +++ b/packages/nodes-base/nodes/Mattermost/v1/transport/index.ts @@ -32,7 +32,7 @@ export async function apiRequest( skipSslCertificateValidation: credentials.allowUnauthorizedCerts as boolean, }; - return this.helpers.httpRequestWithAuthentication.call(this, 'mattermostApi', options); + return await this.helpers.httpRequestWithAuthentication.call(this, 'mattermostApi', options); } export async function apiRequestAllItems( diff --git a/packages/nodes-base/nodes/Microsoft/Dynamics/MicrosoftDynamicsCrm.node.ts b/packages/nodes-base/nodes/Microsoft/Dynamics/MicrosoftDynamicsCrm.node.ts index 6b4f6844aa..37f39b0498 100644 --- a/packages/nodes-base/nodes/Microsoft/Dynamics/MicrosoftDynamicsCrm.node.ts +++ b/packages/nodes-base/nodes/Microsoft/Dynamics/MicrosoftDynamicsCrm.node.ts @@ -62,49 +62,49 @@ export class MicrosoftDynamicsCrm implements INodeType { methods = { loadOptions: { async getAccountCategories(this: ILoadOptionsFunctions): Promise { - return getPicklistOptions.call(this, 'account', 'accountcategorycode'); + return await getPicklistOptions.call(this, 'account', 'accountcategorycode'); }, async getAccountRatingCodes(this: ILoadOptionsFunctions): Promise { - return getPicklistOptions.call(this, 'account', 'accountratingcode'); + return await getPicklistOptions.call(this, 'account', 'accountratingcode'); }, async getAddressTypes(this: ILoadOptionsFunctions): Promise { - return getPicklistOptions.call(this, 'account', 'address1_addresstypecode'); + return await getPicklistOptions.call(this, 'account', 'address1_addresstypecode'); }, async getBusinessTypes(this: ILoadOptionsFunctions): Promise { - return getPicklistOptions.call(this, 'account', 'businesstypecode'); + return await getPicklistOptions.call(this, 'account', 'businesstypecode'); }, async getCustomerSizeCodes(this: ILoadOptionsFunctions): Promise { - return getPicklistOptions.call(this, 'account', 'customersizecode'); + return await getPicklistOptions.call(this, 'account', 'customersizecode'); }, async getCustomerTypeCodes(this: ILoadOptionsFunctions): Promise { - return getPicklistOptions.call(this, 'account', 'customertypecode'); + return await getPicklistOptions.call(this, 'account', 'customertypecode'); }, async getIndustryCodes(this: ILoadOptionsFunctions): Promise { - return getPicklistOptions.call(this, 'account', 'industrycode'); + return await getPicklistOptions.call(this, 'account', 'industrycode'); }, async getPaymentTermsCodes(this: ILoadOptionsFunctions): Promise { - return getPicklistOptions.call(this, 'account', 'paymenttermscode'); + return await getPicklistOptions.call(this, 'account', 'paymenttermscode'); }, async getPreferredAppointmentDayCodes( this: ILoadOptionsFunctions, ): Promise { - return getPicklistOptions.call(this, 'account', 'preferredappointmentdaycode'); + return await getPicklistOptions.call(this, 'account', 'preferredappointmentdaycode'); }, async getPreferredAppointmentTimeCodes( this: ILoadOptionsFunctions, ): Promise { - return getPicklistOptions.call(this, 'account', 'preferredappointmenttimecode'); + return await getPicklistOptions.call(this, 'account', 'preferredappointmenttimecode'); }, async getPreferredContactMethodCodes( this: ILoadOptionsFunctions, ): Promise { - return getPicklistOptions.call(this, 'account', 'preferredcontactmethodcode'); + return await getPicklistOptions.call(this, 'account', 'preferredcontactmethodcode'); }, async getShippingMethodCodes(this: ILoadOptionsFunctions): Promise { - return getPicklistOptions.call(this, 'account', 'shippingmethodcode'); + return await getPicklistOptions.call(this, 'account', 'shippingmethodcode'); }, async getTerritoryCodes(this: ILoadOptionsFunctions): Promise { - return getPicklistOptions.call(this, 'account', 'territorycode'); + return await getPicklistOptions.call(this, 'account', 'territorycode'); }, async getAccountFields(this: ILoadOptionsFunctions): Promise { const fields = await getEntityFields.call(this, 'account'); diff --git a/packages/nodes-base/nodes/Microsoft/Excel/test/v2/node/table/addTable.test.ts b/packages/nodes-base/nodes/Microsoft/Excel/test/v2/node/table/addTable.test.ts index 22d9c91505..85529fab1b 100644 --- a/packages/nodes-base/nodes/Microsoft/Excel/test/v2/node/table/addTable.test.ts +++ b/packages/nodes-base/nodes/Microsoft/Excel/test/v2/node/table/addTable.test.ts @@ -65,6 +65,6 @@ describe('Test MicrosoftExcelV2, table => addTable', () => { }; for (const testData of tests) { - test(testData.description, async () => testNode(testData, nodeTypes)); + test(testData.description, async () => await testNode(testData, nodeTypes)); } }); diff --git a/packages/nodes-base/nodes/Microsoft/Excel/test/v2/node/table/append.test.ts b/packages/nodes-base/nodes/Microsoft/Excel/test/v2/node/table/append.test.ts index 1b7a8ca77d..ce96554e49 100644 --- a/packages/nodes-base/nodes/Microsoft/Excel/test/v2/node/table/append.test.ts +++ b/packages/nodes-base/nodes/Microsoft/Excel/test/v2/node/table/append.test.ts @@ -86,6 +86,6 @@ describe('Test MicrosoftExcelV2, table => append', () => { }; for (const testData of tests) { - test(testData.description, async () => testNode(testData, nodeTypes)); + test(testData.description, async () => await testNode(testData, nodeTypes)); } }); diff --git a/packages/nodes-base/nodes/Microsoft/Excel/test/v2/node/table/convertToRange.test.ts b/packages/nodes-base/nodes/Microsoft/Excel/test/v2/node/table/convertToRange.test.ts index 90a4b78e3e..987410fe68 100644 --- a/packages/nodes-base/nodes/Microsoft/Excel/test/v2/node/table/convertToRange.test.ts +++ b/packages/nodes-base/nodes/Microsoft/Excel/test/v2/node/table/convertToRange.test.ts @@ -61,6 +61,6 @@ describe('Test MicrosoftExcelV2, table => convertToRange', () => { }; for (const testData of tests) { - test(testData.description, async () => testNode(testData, nodeTypes)); + test(testData.description, async () => await testNode(testData, nodeTypes)); } }); diff --git a/packages/nodes-base/nodes/Microsoft/Excel/test/v2/node/table/deleteTable.test.ts b/packages/nodes-base/nodes/Microsoft/Excel/test/v2/node/table/deleteTable.test.ts index 80b2d95631..9865e38e17 100644 --- a/packages/nodes-base/nodes/Microsoft/Excel/test/v2/node/table/deleteTable.test.ts +++ b/packages/nodes-base/nodes/Microsoft/Excel/test/v2/node/table/deleteTable.test.ts @@ -52,6 +52,6 @@ describe('Test MicrosoftExcelV2, table => deleteTable', () => { }; for (const testData of tests) { - test(testData.description, async () => testNode(testData, nodeTypes)); + test(testData.description, async () => await testNode(testData, nodeTypes)); } }); diff --git a/packages/nodes-base/nodes/Microsoft/Excel/test/v2/node/table/getColumns.test.ts b/packages/nodes-base/nodes/Microsoft/Excel/test/v2/node/table/getColumns.test.ts index 1fe39281c3..e69bd72165 100644 --- a/packages/nodes-base/nodes/Microsoft/Excel/test/v2/node/table/getColumns.test.ts +++ b/packages/nodes-base/nodes/Microsoft/Excel/test/v2/node/table/getColumns.test.ts @@ -66,6 +66,6 @@ describe('Test MicrosoftExcelV2, table => getColumns', () => { }; for (const testData of tests) { - test(testData.description, async () => testNode(testData, nodeTypes)); + test(testData.description, async () => await testNode(testData, nodeTypes)); } }); diff --git a/packages/nodes-base/nodes/Microsoft/Excel/test/v2/node/table/getRows.test.ts b/packages/nodes-base/nodes/Microsoft/Excel/test/v2/node/table/getRows.test.ts index a80565d582..32485b683d 100644 --- a/packages/nodes-base/nodes/Microsoft/Excel/test/v2/node/table/getRows.test.ts +++ b/packages/nodes-base/nodes/Microsoft/Excel/test/v2/node/table/getRows.test.ts @@ -90,6 +90,6 @@ describe('Test MicrosoftExcelV2, table => getRows', () => { }; for (const testData of tests) { - test(testData.description, async () => testNode(testData, nodeTypes)); + test(testData.description, async () => await testNode(testData, nodeTypes)); } }); diff --git a/packages/nodes-base/nodes/Microsoft/Excel/test/v2/node/table/lookup.test.ts b/packages/nodes-base/nodes/Microsoft/Excel/test/v2/node/table/lookup.test.ts index 5c1987e200..0a415ea2a5 100644 --- a/packages/nodes-base/nodes/Microsoft/Excel/test/v2/node/table/lookup.test.ts +++ b/packages/nodes-base/nodes/Microsoft/Excel/test/v2/node/table/lookup.test.ts @@ -107,6 +107,6 @@ describe('Test MicrosoftExcelV2, table => lookup', () => { }; for (const testData of tests) { - test(testData.description, async () => testNode(testData, nodeTypes)); + test(testData.description, async () => await testNode(testData, nodeTypes)); } }); diff --git a/packages/nodes-base/nodes/Microsoft/Excel/test/v2/node/workbook/addWorksheet.test.ts b/packages/nodes-base/nodes/Microsoft/Excel/test/v2/node/workbook/addWorksheet.test.ts index 286742018d..861cf1ae8a 100644 --- a/packages/nodes-base/nodes/Microsoft/Excel/test/v2/node/workbook/addWorksheet.test.ts +++ b/packages/nodes-base/nodes/Microsoft/Excel/test/v2/node/workbook/addWorksheet.test.ts @@ -82,6 +82,6 @@ describe('Test MicrosoftExcelV2, workbook => addWorksheet', () => { }; for (const testData of tests) { - test(testData.description, async () => testNode(testData, nodeTypes)); + test(testData.description, async () => await testNode(testData, nodeTypes)); } }); diff --git a/packages/nodes-base/nodes/Microsoft/Excel/test/v2/node/workbook/deleteWorkbook.test.ts b/packages/nodes-base/nodes/Microsoft/Excel/test/v2/node/workbook/deleteWorkbook.test.ts index e8b8f267b0..fa02581e73 100644 --- a/packages/nodes-base/nodes/Microsoft/Excel/test/v2/node/workbook/deleteWorkbook.test.ts +++ b/packages/nodes-base/nodes/Microsoft/Excel/test/v2/node/workbook/deleteWorkbook.test.ts @@ -52,6 +52,6 @@ describe('Test MicrosoftExcelV2, workbook => deleteWorkbook', () => { }; for (const testData of tests) { - test(testData.description, async () => testNode(testData, nodeTypes)); + test(testData.description, async () => await testNode(testData, nodeTypes)); } }); diff --git a/packages/nodes-base/nodes/Microsoft/Excel/test/v2/node/workbook/getAll.test.ts b/packages/nodes-base/nodes/Microsoft/Excel/test/v2/node/workbook/getAll.test.ts index 0af0d596ad..66eb450ad3 100644 --- a/packages/nodes-base/nodes/Microsoft/Excel/test/v2/node/workbook/getAll.test.ts +++ b/packages/nodes-base/nodes/Microsoft/Excel/test/v2/node/workbook/getAll.test.ts @@ -67,6 +67,6 @@ describe('Test MicrosoftExcelV2, workbook => getAll', () => { }; for (const testData of tests) { - test(testData.description, async () => testNode(testData, nodeTypes)); + test(testData.description, async () => await testNode(testData, nodeTypes)); } }); diff --git a/packages/nodes-base/nodes/Microsoft/Excel/test/v2/node/worksheet/append.test.ts b/packages/nodes-base/nodes/Microsoft/Excel/test/v2/node/worksheet/append.test.ts index 0516c329cc..1f3e4fe306 100644 --- a/packages/nodes-base/nodes/Microsoft/Excel/test/v2/node/worksheet/append.test.ts +++ b/packages/nodes-base/nodes/Microsoft/Excel/test/v2/node/worksheet/append.test.ts @@ -49,6 +49,6 @@ describe('Test MicrosoftExcelV2, worksheet => append', () => { const nodeTypes = setup(tests); for (const testData of tests) { - test(testData.description, async () => equalityTest(testData, nodeTypes)); + test(testData.description, async () => await equalityTest(testData, nodeTypes)); } }); diff --git a/packages/nodes-base/nodes/Microsoft/Excel/test/v2/node/worksheet/clear.test.ts b/packages/nodes-base/nodes/Microsoft/Excel/test/v2/node/worksheet/clear.test.ts index d018e6dc58..aba4f434ea 100644 --- a/packages/nodes-base/nodes/Microsoft/Excel/test/v2/node/worksheet/clear.test.ts +++ b/packages/nodes-base/nodes/Microsoft/Excel/test/v2/node/worksheet/clear.test.ts @@ -61,6 +61,6 @@ describe('Test MicrosoftExcelV2, worksheet => clear', () => { }; for (const testData of tests) { - test(testData.description, async () => testNode(testData, nodeTypes)); + test(testData.description, async () => await testNode(testData, nodeTypes)); } }); diff --git a/packages/nodes-base/nodes/Microsoft/Excel/test/v2/node/worksheet/deleteWorksheet.test.ts b/packages/nodes-base/nodes/Microsoft/Excel/test/v2/node/worksheet/deleteWorksheet.test.ts index 482fc8fa97..b019735289 100644 --- a/packages/nodes-base/nodes/Microsoft/Excel/test/v2/node/worksheet/deleteWorksheet.test.ts +++ b/packages/nodes-base/nodes/Microsoft/Excel/test/v2/node/worksheet/deleteWorksheet.test.ts @@ -60,6 +60,6 @@ describe('Test MicrosoftExcelV2, worksheet => deleteWorksheet', () => { }; for (const testData of tests) { - test(testData.description, async () => testNode(testData, nodeTypes)); + test(testData.description, async () => await testNode(testData, nodeTypes)); } }); diff --git a/packages/nodes-base/nodes/Microsoft/Excel/test/v2/node/worksheet/getAll.test.ts b/packages/nodes-base/nodes/Microsoft/Excel/test/v2/node/worksheet/getAll.test.ts index 5dd8c4feec..79fc34ed4d 100644 --- a/packages/nodes-base/nodes/Microsoft/Excel/test/v2/node/worksheet/getAll.test.ts +++ b/packages/nodes-base/nodes/Microsoft/Excel/test/v2/node/worksheet/getAll.test.ts @@ -69,6 +69,6 @@ describe('Test MicrosoftExcelV2, worksheet => getAll', () => { }; for (const testData of tests) { - test(testData.description, async () => testNode(testData, nodeTypes)); + test(testData.description, async () => await testNode(testData, nodeTypes)); } }); diff --git a/packages/nodes-base/nodes/Microsoft/Excel/test/v2/node/worksheet/readRows.test.ts b/packages/nodes-base/nodes/Microsoft/Excel/test/v2/node/worksheet/readRows.test.ts index 6202e62e89..99ee7aabe5 100644 --- a/packages/nodes-base/nodes/Microsoft/Excel/test/v2/node/worksheet/readRows.test.ts +++ b/packages/nodes-base/nodes/Microsoft/Excel/test/v2/node/worksheet/readRows.test.ts @@ -46,6 +46,6 @@ describe('Test MicrosoftExcelV2, worksheet => readRows', () => { const nodeTypes = setup(tests); for (const testData of tests) { - test(testData.description, async () => equalityTest(testData, nodeTypes)); + test(testData.description, async () => await equalityTest(testData, nodeTypes)); } }); diff --git a/packages/nodes-base/nodes/Microsoft/Excel/test/v2/node/worksheet/update.test.ts b/packages/nodes-base/nodes/Microsoft/Excel/test/v2/node/worksheet/update.test.ts index 9736fb8162..a3c4c71d81 100644 --- a/packages/nodes-base/nodes/Microsoft/Excel/test/v2/node/worksheet/update.test.ts +++ b/packages/nodes-base/nodes/Microsoft/Excel/test/v2/node/worksheet/update.test.ts @@ -59,6 +59,6 @@ describe('Test MicrosoftExcelV2, worksheet => update', () => { const nodeTypes = setup(tests); for (const testData of tests) { - test(testData.description, async () => equalityTest(testData, nodeTypes)); + test(testData.description, async () => await equalityTest(testData, nodeTypes)); } }); diff --git a/packages/nodes-base/nodes/Microsoft/Excel/test/v2/node/worksheet/upsert.test.ts b/packages/nodes-base/nodes/Microsoft/Excel/test/v2/node/worksheet/upsert.test.ts index 20e2831a06..429ea3b1ca 100644 --- a/packages/nodes-base/nodes/Microsoft/Excel/test/v2/node/worksheet/upsert.test.ts +++ b/packages/nodes-base/nodes/Microsoft/Excel/test/v2/node/worksheet/upsert.test.ts @@ -61,6 +61,6 @@ describe('Test MicrosoftExcelV2, worksheet => upsert', () => { const nodeTypes = setup(tests); for (const testData of tests) { - test(testData.description, async () => equalityTest(testData, nodeTypes)); + test(testData.description, async () => await equalityTest(testData, nodeTypes)); } }); diff --git a/packages/nodes-base/nodes/Microsoft/Excel/v2/MicrosoftExcelV2.node.ts b/packages/nodes-base/nodes/Microsoft/Excel/v2/MicrosoftExcelV2.node.ts index 797165df3e..e3bfcbac95 100644 --- a/packages/nodes-base/nodes/Microsoft/Excel/v2/MicrosoftExcelV2.node.ts +++ b/packages/nodes-base/nodes/Microsoft/Excel/v2/MicrosoftExcelV2.node.ts @@ -22,6 +22,6 @@ export class MicrosoftExcelV2 implements INodeType { methods = { listSearch, loadOptions }; async execute(this: IExecuteFunctions) { - return router.call(this); + return await router.call(this); } } diff --git a/packages/nodes-base/nodes/Microsoft/Outlook/test/v2/node/calendar/create.test.ts b/packages/nodes-base/nodes/Microsoft/Outlook/test/v2/node/calendar/create.test.ts index e5ffebe495..234d008fe7 100644 --- a/packages/nodes-base/nodes/Microsoft/Outlook/test/v2/node/calendar/create.test.ts +++ b/packages/nodes-base/nodes/Microsoft/Outlook/test/v2/node/calendar/create.test.ts @@ -73,6 +73,6 @@ describe('Test MicrosoftOutlookV2, calendar => create', () => { }; for (const testData of tests) { - test(testData.description, async () => testNode(testData, nodeTypes)); + test(testData.description, async () => await testNode(testData, nodeTypes)); } }); diff --git a/packages/nodes-base/nodes/Microsoft/Outlook/test/v2/node/calendar/delete.test.ts b/packages/nodes-base/nodes/Microsoft/Outlook/test/v2/node/calendar/delete.test.ts index ff79c798a8..8ec751632f 100644 --- a/packages/nodes-base/nodes/Microsoft/Outlook/test/v2/node/calendar/delete.test.ts +++ b/packages/nodes-base/nodes/Microsoft/Outlook/test/v2/node/calendar/delete.test.ts @@ -52,6 +52,6 @@ describe('Test MicrosoftOutlookV2, calendar => delete', () => { }; for (const testData of tests) { - test(testData.description, async () => testNode(testData, nodeTypes)); + test(testData.description, async () => await testNode(testData, nodeTypes)); } }); diff --git a/packages/nodes-base/nodes/Microsoft/Outlook/test/v2/node/calendar/get.test.ts b/packages/nodes-base/nodes/Microsoft/Outlook/test/v2/node/calendar/get.test.ts index 35d3b7f173..b579d89955 100644 --- a/packages/nodes-base/nodes/Microsoft/Outlook/test/v2/node/calendar/get.test.ts +++ b/packages/nodes-base/nodes/Microsoft/Outlook/test/v2/node/calendar/get.test.ts @@ -74,6 +74,6 @@ describe('Test MicrosoftOutlookV2, calendar => get', () => { }; for (const testData of tests) { - test(testData.description, async () => testNode(testData, nodeTypes)); + test(testData.description, async () => await testNode(testData, nodeTypes)); } }); diff --git a/packages/nodes-base/nodes/Microsoft/Outlook/test/v2/node/calendar/getAll.test.ts b/packages/nodes-base/nodes/Microsoft/Outlook/test/v2/node/calendar/getAll.test.ts index 114d27baa8..1e85bc12b7 100644 --- a/packages/nodes-base/nodes/Microsoft/Outlook/test/v2/node/calendar/getAll.test.ts +++ b/packages/nodes-base/nodes/Microsoft/Outlook/test/v2/node/calendar/getAll.test.ts @@ -93,6 +93,6 @@ describe('Test MicrosoftOutlookV2, calendar => getAll', () => { }; for (const testData of tests) { - test(testData.description, async () => testNode(testData, nodeTypes)); + test(testData.description, async () => await testNode(testData, nodeTypes)); } }); diff --git a/packages/nodes-base/nodes/Microsoft/Outlook/test/v2/node/calendar/update.test.ts b/packages/nodes-base/nodes/Microsoft/Outlook/test/v2/node/calendar/update.test.ts index 60d68db8ee..2ebea471cf 100644 --- a/packages/nodes-base/nodes/Microsoft/Outlook/test/v2/node/calendar/update.test.ts +++ b/packages/nodes-base/nodes/Microsoft/Outlook/test/v2/node/calendar/update.test.ts @@ -73,6 +73,6 @@ describe('Test MicrosoftOutlookV2, calendar => update', () => { }; for (const testData of tests) { - test(testData.description, async () => testNode(testData, nodeTypes)); + test(testData.description, async () => await testNode(testData, nodeTypes)); } }); diff --git a/packages/nodes-base/nodes/Microsoft/Outlook/test/v2/node/contact/create.test.ts b/packages/nodes-base/nodes/Microsoft/Outlook/test/v2/node/contact/create.test.ts index a0d09bd87a..364da5918d 100644 --- a/packages/nodes-base/nodes/Microsoft/Outlook/test/v2/node/contact/create.test.ts +++ b/packages/nodes-base/nodes/Microsoft/Outlook/test/v2/node/contact/create.test.ts @@ -102,6 +102,6 @@ describe('Test MicrosoftOutlookV2, contact => create', () => { }; for (const testData of tests) { - test(testData.description, async () => testNode(testData, nodeTypes)); + test(testData.description, async () => await testNode(testData, nodeTypes)); } }); diff --git a/packages/nodes-base/nodes/Microsoft/Outlook/test/v2/node/contact/update.test.ts b/packages/nodes-base/nodes/Microsoft/Outlook/test/v2/node/contact/update.test.ts index 84f367691f..13832486e0 100644 --- a/packages/nodes-base/nodes/Microsoft/Outlook/test/v2/node/contact/update.test.ts +++ b/packages/nodes-base/nodes/Microsoft/Outlook/test/v2/node/contact/update.test.ts @@ -113,6 +113,6 @@ describe('Test MicrosoftOutlookV2, contact => update', () => { }; for (const testData of tests) { - test(testData.description, async () => testNode(testData, nodeTypes)); + test(testData.description, async () => await testNode(testData, nodeTypes)); } }); diff --git a/packages/nodes-base/nodes/Microsoft/Outlook/test/v2/node/draft/create.test.ts b/packages/nodes-base/nodes/Microsoft/Outlook/test/v2/node/draft/create.test.ts index 8a344f411e..b9ca16b504 100644 --- a/packages/nodes-base/nodes/Microsoft/Outlook/test/v2/node/draft/create.test.ts +++ b/packages/nodes-base/nodes/Microsoft/Outlook/test/v2/node/draft/create.test.ts @@ -142,6 +142,6 @@ describe('Test MicrosoftOutlookV2, draft => create', () => { }; for (const testData of tests) { - test(testData.description, async () => testNode(testData, nodeTypes)); + test(testData.description, async () => await testNode(testData, nodeTypes)); } }); diff --git a/packages/nodes-base/nodes/Microsoft/Outlook/test/v2/node/draft/send.test.ts b/packages/nodes-base/nodes/Microsoft/Outlook/test/v2/node/draft/send.test.ts index 210dabed68..d87f1b66fe 100644 --- a/packages/nodes-base/nodes/Microsoft/Outlook/test/v2/node/draft/send.test.ts +++ b/packages/nodes-base/nodes/Microsoft/Outlook/test/v2/node/draft/send.test.ts @@ -57,6 +57,6 @@ describe('Test MicrosoftOutlookV2, draft => send', () => { }; for (const testData of tests) { - test(testData.description, async () => testNode(testData, nodeTypes)); + test(testData.description, async () => await testNode(testData, nodeTypes)); } }); diff --git a/packages/nodes-base/nodes/Microsoft/Outlook/test/v2/node/event/create.test.ts b/packages/nodes-base/nodes/Microsoft/Outlook/test/v2/node/event/create.test.ts index 38605a67ec..9ac1296aa4 100644 --- a/packages/nodes-base/nodes/Microsoft/Outlook/test/v2/node/event/create.test.ts +++ b/packages/nodes-base/nodes/Microsoft/Outlook/test/v2/node/event/create.test.ts @@ -150,6 +150,6 @@ describe('Test MicrosoftOutlookV2, contact => event', () => { }; for (const testData of tests) { - test(testData.description, async () => testNode(testData, nodeTypes)); + test(testData.description, async () => await testNode(testData, nodeTypes)); } }); diff --git a/packages/nodes-base/nodes/Microsoft/Outlook/test/v2/node/folder/create.test.ts b/packages/nodes-base/nodes/Microsoft/Outlook/test/v2/node/folder/create.test.ts index ceec75919c..bcc5826bea 100644 --- a/packages/nodes-base/nodes/Microsoft/Outlook/test/v2/node/folder/create.test.ts +++ b/packages/nodes-base/nodes/Microsoft/Outlook/test/v2/node/folder/create.test.ts @@ -65,6 +65,6 @@ describe('Test MicrosoftOutlookV2, contact => folder', () => { }; for (const testData of tests) { - test(testData.description, async () => testNode(testData, nodeTypes)); + test(testData.description, async () => await testNode(testData, nodeTypes)); } }); diff --git a/packages/nodes-base/nodes/Microsoft/Outlook/test/v2/node/folderMessage/getAll.test.ts b/packages/nodes-base/nodes/Microsoft/Outlook/test/v2/node/folderMessage/getAll.test.ts index 081320390c..aff8f0c203 100644 --- a/packages/nodes-base/nodes/Microsoft/Outlook/test/v2/node/folderMessage/getAll.test.ts +++ b/packages/nodes-base/nodes/Microsoft/Outlook/test/v2/node/folderMessage/getAll.test.ts @@ -60,6 +60,6 @@ describe('Test MicrosoftOutlookV2, folderMessage => getAll', () => { }; for (const testData of tests) { - test(testData.description, async () => testNode(testData, nodeTypes)); + test(testData.description, async () => await testNode(testData, nodeTypes)); } }); diff --git a/packages/nodes-base/nodes/Microsoft/Outlook/test/v2/node/message/move.test.ts b/packages/nodes-base/nodes/Microsoft/Outlook/test/v2/node/message/move.test.ts index 3d0d88abc3..ac1415d23c 100644 --- a/packages/nodes-base/nodes/Microsoft/Outlook/test/v2/node/message/move.test.ts +++ b/packages/nodes-base/nodes/Microsoft/Outlook/test/v2/node/message/move.test.ts @@ -56,6 +56,6 @@ describe('Test MicrosoftOutlookV2, message => move', () => { }; for (const testData of tests) { - test(testData.description, async () => testNode(testData, nodeTypes)); + test(testData.description, async () => await testNode(testData, nodeTypes)); } }); diff --git a/packages/nodes-base/nodes/Microsoft/Outlook/test/v2/node/message/reply.test.ts b/packages/nodes-base/nodes/Microsoft/Outlook/test/v2/node/message/reply.test.ts index 0fbc694ece..2f4af23db7 100644 --- a/packages/nodes-base/nodes/Microsoft/Outlook/test/v2/node/message/reply.test.ts +++ b/packages/nodes-base/nodes/Microsoft/Outlook/test/v2/node/message/reply.test.ts @@ -123,6 +123,6 @@ describe('Test MicrosoftOutlookV2, message => reply', () => { }; for (const testData of tests) { - test(testData.description, async () => testNode(testData, nodeTypes)); + test(testData.description, async () => await testNode(testData, nodeTypes)); } }); diff --git a/packages/nodes-base/nodes/Microsoft/Outlook/test/v2/node/message/send.test.ts b/packages/nodes-base/nodes/Microsoft/Outlook/test/v2/node/message/send.test.ts index 92334903d7..cc5b6e5a43 100644 --- a/packages/nodes-base/nodes/Microsoft/Outlook/test/v2/node/message/send.test.ts +++ b/packages/nodes-base/nodes/Microsoft/Outlook/test/v2/node/message/send.test.ts @@ -62,6 +62,6 @@ describe('Test MicrosoftOutlookV2, message => send', () => { }; for (const testData of tests) { - test(testData.description, async () => testNode(testData, nodeTypes)); + test(testData.description, async () => await testNode(testData, nodeTypes)); } }); diff --git a/packages/nodes-base/nodes/Microsoft/Outlook/v1/GenericFunctions.ts b/packages/nodes-base/nodes/Microsoft/Outlook/v1/GenericFunctions.ts index 1bd0e37644..87aea4dfb6 100644 --- a/packages/nodes-base/nodes/Microsoft/Outlook/v1/GenericFunctions.ts +++ b/packages/nodes-base/nodes/Microsoft/Outlook/v1/GenericFunctions.ts @@ -229,7 +229,7 @@ export async function binaryToAttachments( items: INodeExecutionData[], i: number, ) { - return Promise.all( + return await Promise.all( attachments.map(async (attachment) => { const binaryPropertyName = attachment.binaryPropertyName as string; const binaryData = this.helpers.assertBinaryData(i, binaryPropertyName); diff --git a/packages/nodes-base/nodes/Microsoft/Outlook/v2/MicrosoftOutlookV2.node.ts b/packages/nodes-base/nodes/Microsoft/Outlook/v2/MicrosoftOutlookV2.node.ts index d1e115f5de..7ac465f0b2 100644 --- a/packages/nodes-base/nodes/Microsoft/Outlook/v2/MicrosoftOutlookV2.node.ts +++ b/packages/nodes-base/nodes/Microsoft/Outlook/v2/MicrosoftOutlookV2.node.ts @@ -22,6 +22,6 @@ export class MicrosoftOutlookV2 implements INodeType { methods = { loadOptions, listSearch }; async execute(this: IExecuteFunctions) { - return router.call(this); + return await router.call(this); } } diff --git a/packages/nodes-base/nodes/Microsoft/Outlook/v2/methods/listSearch.ts b/packages/nodes-base/nodes/Microsoft/Outlook/v2/methods/listSearch.ts index c0ded584c0..c0110ac8bc 100644 --- a/packages/nodes-base/nodes/Microsoft/Outlook/v2/methods/listSearch.ts +++ b/packages/nodes-base/nodes/Microsoft/Outlook/v2/methods/listSearch.ts @@ -50,7 +50,7 @@ export async function searchContacts( filter?: string, paginationToken?: string, ): Promise { - return search.call(this, '/contacts', 'displayName', filter, paginationToken); + return await search.call(this, '/contacts', 'displayName', filter, paginationToken); } export async function searchCalendars( @@ -58,7 +58,7 @@ export async function searchCalendars( filter?: string, paginationToken?: string, ): Promise { - return search.call(this, '/calendars', 'name', filter, paginationToken); + return await search.call(this, '/calendars', 'name', filter, paginationToken); } export async function searchDrafts( diff --git a/packages/nodes-base/nodes/Microsoft/Sql/GenericFunctions.ts b/packages/nodes-base/nodes/Microsoft/Sql/GenericFunctions.ts index 3e743b07a7..86276979bf 100644 --- a/packages/nodes-base/nodes/Microsoft/Sql/GenericFunctions.ts +++ b/packages/nodes-base/nodes/Microsoft/Sql/GenericFunctions.ts @@ -67,10 +67,10 @@ export async function executeQueryQueue( tables: ITables, buildQueryQueue: (data: OperationInputData) => Array>, ): Promise { - return Promise.all( + return await Promise.all( Object.keys(tables).map(async (table) => { const columnsResults = Object.keys(tables[table]).map(async (columnString) => { - return Promise.all( + return await Promise.all( buildQueryQueue({ table, columnString, @@ -78,7 +78,7 @@ export async function executeQueryQueue( }), ); }); - return Promise.all(columnsResults); + return await Promise.all(columnsResults); }), ); } @@ -121,7 +121,7 @@ const escapeTableName = (table: string) => { }; export async function insertOperation(tables: ITables, pool: mssql.ConnectionPool) { - return executeQueryQueue( + return await executeQueryQueue( tables, ({ table, columnString, items }: OperationInputData): Array> => { return chunk(items, 1000).map(async (insertValues) => { @@ -141,14 +141,14 @@ export async function insertOperation(tables: ITables, pool: mssql.ConnectionPoo columnString, )}) VALUES ${valuesPlaceholder.join(', ')};`; - return request.query(query); + return await request.query(query); }); }, ); } export async function updateOperation(tables: ITables, pool: mssql.ConnectionPool) { - return executeQueryQueue( + return await executeQueryQueue( tables, ({ table, columnString, items }: OperationInputData): Array> => { return items.map(async (item) => { @@ -168,7 +168,7 @@ export async function updateOperation(tables: ITables, pool: mssql.ConnectionPoo ', ', )} WHERE ${condition};`; - return request.query(query); + return await request.query(query); }); }, ); @@ -197,11 +197,11 @@ export async function deleteOperation(tables: ITables, pool: mssql.ConnectionPoo table, )} WHERE [${deleteKey}] IN (${valuesPlaceholder.join(', ')});`; - return request.query(query); + return await request.query(query); }); - return Promise.all(queryQueue); + return await Promise.all(queryQueue); }); - return Promise.all(deleteKeyResults); + return await Promise.all(deleteKeyResults); }), ); diff --git a/packages/nodes-base/nodes/MySql/test/v1/executeQuery.test.ts b/packages/nodes-base/nodes/MySql/test/v1/executeQuery.test.ts index 459e347592..55c4007eda 100644 --- a/packages/nodes-base/nodes/MySql/test/v1/executeQuery.test.ts +++ b/packages/nodes-base/nodes/MySql/test/v1/executeQuery.test.ts @@ -55,6 +55,6 @@ describe('Test MySqlV1, executeQuery', () => { }; for (const testData of tests) { - test(testData.description, async () => testNode(testData, nodeTypes)); + test(testData.description, async () => await testNode(testData, nodeTypes)); } }); diff --git a/packages/nodes-base/nodes/MySql/v1/GenericFunctions.ts b/packages/nodes-base/nodes/MySql/v1/GenericFunctions.ts index 0f9976abca..0cdf6ce19d 100644 --- a/packages/nodes-base/nodes/MySql/v1/GenericFunctions.ts +++ b/packages/nodes-base/nodes/MySql/v1/GenericFunctions.ts @@ -25,7 +25,7 @@ export async function createConnection( } } - return mysql2.createConnection(baseCredentials); + return await mysql2.createConnection(baseCredentials); } export async function searchTables( diff --git a/packages/nodes-base/nodes/MySql/v1/MySqlV1.node.ts b/packages/nodes-base/nodes/MySql/v1/MySqlV1.node.ts index ae3def25eb..e3fd6a073f 100644 --- a/packages/nodes-base/nodes/MySql/v1/MySqlV1.node.ts +++ b/packages/nodes-base/nodes/MySql/v1/MySqlV1.node.ts @@ -316,7 +316,7 @@ export class MySqlV1 implements INodeType { ); } - return connection.query(rawQuery); + return await connection.query(rawQuery); }); returnItems = ((await Promise.all(queryQueue)) as mysql2.OkPacket[][]).reduce( @@ -398,8 +398,9 @@ export class MySqlV1 implements INodeType { const updateSQL = `UPDATE ${table} SET ${columns .map((column) => `${column} = ?`) .join(',')} WHERE ${updateKey} = ?;`; - const queryQueue = updateItems.map(async (item) => - connection.query(updateSQL, Object.values(item).concat(item[updateKey])), + const queryQueue = updateItems.map( + async (item) => + await connection.query(updateSQL, Object.values(item).concat(item[updateKey])), ); const queryResult = await Promise.all(queryQueue); returnItems = this.helpers.returnJsonArray( diff --git a/packages/nodes-base/nodes/MySql/v2/MySqlV2.node.ts b/packages/nodes-base/nodes/MySql/v2/MySqlV2.node.ts index e8dc7c05ee..2adf717a5c 100644 --- a/packages/nodes-base/nodes/MySql/v2/MySqlV2.node.ts +++ b/packages/nodes-base/nodes/MySql/v2/MySqlV2.node.ts @@ -25,6 +25,6 @@ export class MySqlV2 implements INodeType { methods = { listSearch, loadOptions, credentialTest }; async execute(this: IExecuteFunctions): Promise { - return router.call(this); + return await router.call(this); } } diff --git a/packages/nodes-base/nodes/MySql/v2/transport/index.ts b/packages/nodes-base/nodes/MySql/v2/transport/index.ts index 5abf51a21e..47b7a8c57c 100644 --- a/packages/nodes-base/nodes/MySql/v2/transport/index.ts +++ b/packages/nodes-base/nodes/MySql/v2/transport/index.ts @@ -133,6 +133,6 @@ export async function createPool( .connect(tunnelConfig); }); - return poolSetup; + return await poolSetup; } } diff --git a/packages/nodes-base/nodes/N8n/test/node/N8n.test.ts b/packages/nodes-base/nodes/N8n/test/node/N8n.test.ts index 6d1797cb2e..e4b0b4d930 100644 --- a/packages/nodes-base/nodes/N8n/test/node/N8n.test.ts +++ b/packages/nodes-base/nodes/N8n/test/node/N8n.test.ts @@ -31,6 +31,6 @@ describe('Test N8n Node, expect base_url to be received from credentials', () => }; for (const testData of tests) { - test(testData.description, async () => testNode(testData, nodeTypes)); + test(testData.description, async () => await testNode(testData, nodeTypes)); } }); diff --git a/packages/nodes-base/nodes/NocoDB/GenericFunctions.ts b/packages/nodes-base/nodes/NocoDB/GenericFunctions.ts index 4ca66f7818..5edc220c5a 100644 --- a/packages/nodes-base/nodes/NocoDB/GenericFunctions.ts +++ b/packages/nodes-base/nodes/NocoDB/GenericFunctions.ts @@ -63,7 +63,7 @@ export async function apiRequest( delete options.body; } - return this.helpers.requestWithAuthentication.call(this, authenticationMethod, options); + return await this.helpers.requestWithAuthentication.call(this, authenticationMethod, options); } /** diff --git a/packages/nodes-base/nodes/Postgres/v1/genericFunctions.ts b/packages/nodes-base/nodes/Postgres/v1/genericFunctions.ts index 1f858abcd4..9420c427a1 100644 --- a/packages/nodes-base/nodes/Postgres/v1/genericFunctions.ts +++ b/packages/nodes-base/nodes/Postgres/v1/genericFunctions.ts @@ -120,7 +120,7 @@ export async function pgQuery( if (mode === 'multiple') { return (await db.multi(pgp.helpers.concat(allQueries))).flat(1); } else if (mode === 'transaction') { - return db.tx(async (t) => { + return await db.tx(async (t) => { const result: IDataObject[] = []; for (let i = 0; i < allQueries.length; i++) { try { @@ -141,7 +141,7 @@ export async function pgQuery( return result; }); } else if (mode === 'independently') { - return db.task(async (t) => { + return await db.task(async (t) => { const result: IDataObject[] = []; for (let i = 0; i < allQueries.length; i++) { try { @@ -215,7 +215,7 @@ export async function pgQueryV2( }) .flat(); } else if (mode === 'transaction') { - return db.tx(async (t) => { + return await db.tx(async (t) => { const result: INodeExecutionData[] = []; for (let i = 0; i < allQueries.length; i++) { try { @@ -239,7 +239,7 @@ export async function pgQueryV2( return result; }); } else if (mode === 'independently') { - return db.task(async (t) => { + return await db.task(async (t) => { const result: INodeExecutionData[] = []; for (let i = 0; i < allQueries.length; i++) { try { @@ -308,9 +308,9 @@ export async function pgInsert( if (mode === 'multiple') { const query = pgp.helpers.insert(getItemsCopy(items, columnNames, guardedColumns), cs) + returning; - return db.any(query); + return await db.any(query); } else if (mode === 'transaction') { - return db.tx(async (t) => { + return await db.tx(async (t) => { const result: IDataObject[] = []; for (let i = 0; i < items.length; i++) { const itemCopy = getItemCopy(items[i], columnNames, guardedColumns); @@ -329,7 +329,7 @@ export async function pgInsert( return result; }); } else if (mode === 'independently') { - return db.task(async (t) => { + return await db.task(async (t) => { const result: IDataObject[] = []; for (let i = 0; i < items.length; i++) { const itemCopy = getItemCopy(items[i], columnNames, guardedColumns); @@ -407,7 +407,7 @@ export async function pgInsertV2( }) .flat(); } else if (mode === 'transaction') { - return db.tx(async (t) => { + return await db.tx(async (t) => { const result: IDataObject[] = []; for (let i = 0; i < items.length; i++) { const itemCopy = getItemCopy(items[i], columnNames, guardedColumns); @@ -432,7 +432,7 @@ export async function pgInsertV2( return result; }); } else if (mode === 'independently') { - return db.task(async (t) => { + return await db.task(async (t) => { const result: IDataObject[] = []; for (let i = 0; i < items.length; i++) { const itemCopy = getItemCopy(items[i], columnNames, guardedColumns); @@ -532,14 +532,14 @@ export async function pgUpdate( }) .join(' AND ') + returning; - return db.any(query); + return await db.any(query); } else { const where = ' WHERE ' + // eslint-disable-next-line n8n-local-rules/no-interpolation-in-regular-string updateKeys.map((entry) => pgp.as.name(entry.name) + ' = ${' + entry.prop + '}').join(' AND '); if (mode === 'transaction') { - return db.tx(async (t) => { + return await db.tx(async (t) => { const result: IDataObject[] = []; for (let i = 0; i < items.length; i++) { const itemCopy = getItemCopy(items[i], columnNames, guardedColumns); @@ -565,7 +565,7 @@ export async function pgUpdate( return result; }); } else if (mode === 'independently') { - return db.task(async (t) => { + return await db.task(async (t) => { const result: IDataObject[] = []; for (let i = 0; i < items.length; i++) { const itemCopy = getItemCopy(items[i], columnNames, guardedColumns); @@ -667,7 +667,7 @@ export async function pgUpdateV2( // eslint-disable-next-line n8n-local-rules/no-interpolation-in-regular-string updateKeys.map((entry) => pgp.as.name(entry.name) + ' = ${' + entry.prop + '}').join(' AND '); if (mode === 'transaction') { - return db.tx(async (t) => { + return await db.tx(async (t) => { const result: IDataObject[] = []; for (let i = 0; i < items.length; i++) { const itemCopy = getItemCopy(items[i], columnNames, guardedColumns); @@ -695,7 +695,7 @@ export async function pgUpdateV2( return result; }); } else if (mode === 'independently') { - return db.task(async (t) => { + return await db.task(async (t) => { const result: IDataObject[] = []; for (let i = 0; i < items.length; i++) { const itemCopy = getItemCopy(items[i], columnNames, guardedColumns); diff --git a/packages/nodes-base/nodes/Postgres/v2/PostgresV2.node.ts b/packages/nodes-base/nodes/Postgres/v2/PostgresV2.node.ts index a113d1756c..a8f5c03965 100644 --- a/packages/nodes-base/nodes/Postgres/v2/PostgresV2.node.ts +++ b/packages/nodes-base/nodes/Postgres/v2/PostgresV2.node.ts @@ -23,6 +23,6 @@ export class PostgresV2 implements INodeType { methods = { credentialTest, listSearch, loadOptions, resourceMapping }; async execute(this: IExecuteFunctions): Promise { - return router.call(this); + return await router.call(this); } } diff --git a/packages/nodes-base/nodes/Postgres/v2/actions/database/deleteTable.operation.ts b/packages/nodes-base/nodes/Postgres/v2/actions/database/deleteTable.operation.ts index c0c367dcc7..1e35dad292 100644 --- a/packages/nodes-base/nodes/Postgres/v2/actions/database/deleteTable.operation.ts +++ b/packages/nodes-base/nodes/Postgres/v2/actions/database/deleteTable.operation.ts @@ -158,5 +158,5 @@ export async function execute( queries.push(queryWithValues); } - return runQueries(queries, items, nodeOptions); + return await runQueries(queries, items, nodeOptions); } diff --git a/packages/nodes-base/nodes/Postgres/v2/actions/database/executeQuery.operation.ts b/packages/nodes-base/nodes/Postgres/v2/actions/database/executeQuery.operation.ts index e61ed0a72a..c4aaa8e8de 100644 --- a/packages/nodes-base/nodes/Postgres/v2/actions/database/executeQuery.operation.ts +++ b/packages/nodes-base/nodes/Postgres/v2/actions/database/executeQuery.operation.ts @@ -107,5 +107,5 @@ export async function execute( queries.push({ query, values }); } - return runQueries(queries, items, nodeOptions); + return await runQueries(queries, items, nodeOptions); } diff --git a/packages/nodes-base/nodes/Postgres/v2/actions/database/insert.operation.ts b/packages/nodes-base/nodes/Postgres/v2/actions/database/insert.operation.ts index d712222b03..01cce7d890 100644 --- a/packages/nodes-base/nodes/Postgres/v2/actions/database/insert.operation.ts +++ b/packages/nodes-base/nodes/Postgres/v2/actions/database/insert.operation.ts @@ -232,5 +232,5 @@ export async function execute( queries.push({ query, values }); } - return runQueries(queries, items, nodeOptions); + return await runQueries(queries, items, nodeOptions); } diff --git a/packages/nodes-base/nodes/Postgres/v2/actions/database/select.operation.ts b/packages/nodes-base/nodes/Postgres/v2/actions/database/select.operation.ts index 0a6d3cfa14..36f6ec89cb 100644 --- a/packages/nodes-base/nodes/Postgres/v2/actions/database/select.operation.ts +++ b/packages/nodes-base/nodes/Postgres/v2/actions/database/select.operation.ts @@ -133,5 +133,5 @@ export async function execute( queries.push(queryWithValues); } - return runQueries(queries, items, nodeOptions); + return await runQueries(queries, items, nodeOptions); } diff --git a/packages/nodes-base/nodes/Postgres/v2/actions/database/upsert.operation.ts b/packages/nodes-base/nodes/Postgres/v2/actions/database/upsert.operation.ts index 8af21b9c37..2d164491be 100644 --- a/packages/nodes-base/nodes/Postgres/v2/actions/database/upsert.operation.ts +++ b/packages/nodes-base/nodes/Postgres/v2/actions/database/upsert.operation.ts @@ -304,5 +304,5 @@ export async function execute( queries.push({ query, values }); } - return runQueries(queries, items, nodeOptions); + return await runQueries(queries, items, nodeOptions); } diff --git a/packages/nodes-base/nodes/QuickBooks/GenericFunctions.ts b/packages/nodes-base/nodes/QuickBooks/GenericFunctions.ts index dca9ee5702..85a73ca4f3 100644 --- a/packages/nodes-base/nodes/QuickBooks/GenericFunctions.ts +++ b/packages/nodes-base/nodes/QuickBooks/GenericFunctions.ts @@ -169,7 +169,7 @@ export async function handleListing( } if (returnAll) { - return quickBooksApiRequestAllItems.call(this, 'GET', endpoint, qs, {}, resource); + return await quickBooksApiRequestAllItems.call(this, 'GET', endpoint, qs, {}, resource); } else { const limit = this.getNodeParameter('limit', i); qs.query += ` MAXRESULTS ${limit}`; diff --git a/packages/nodes-base/nodes/QuickBooks/QuickBooks.node.ts b/packages/nodes-base/nodes/QuickBooks/QuickBooks.node.ts index c804a64bb8..8d10c9a439 100644 --- a/packages/nodes-base/nodes/QuickBooks/QuickBooks.node.ts +++ b/packages/nodes-base/nodes/QuickBooks/QuickBooks.node.ts @@ -145,39 +145,39 @@ export class QuickBooks implements INodeType { methods = { loadOptions: { async getCustomers(this: ILoadOptionsFunctions) { - return loadResource.call(this, 'customer'); + return await loadResource.call(this, 'customer'); }, async getCustomFields(this: ILoadOptionsFunctions) { - return loadResource.call(this, 'preferences'); + return await loadResource.call(this, 'preferences'); }, async getDepartments(this: ILoadOptionsFunctions) { - return loadResource.call(this, 'department'); + return await loadResource.call(this, 'department'); }, async getItems(this: ILoadOptionsFunctions) { - return loadResource.call(this, 'item'); + return await loadResource.call(this, 'item'); }, async getMemos(this: ILoadOptionsFunctions) { - return loadResource.call(this, 'CreditMemo'); + return await loadResource.call(this, 'CreditMemo'); }, async getPurchases(this: ILoadOptionsFunctions) { - return loadResource.call(this, 'purchase'); + return await loadResource.call(this, 'purchase'); }, async getTaxCodeRefs(this: ILoadOptionsFunctions) { - return loadResource.call(this, 'TaxCode'); + return await loadResource.call(this, 'TaxCode'); }, async getTerms(this: ILoadOptionsFunctions) { - return loadResource.call(this, 'Term'); + return await loadResource.call(this, 'Term'); }, async getVendors(this: ILoadOptionsFunctions) { - return loadResource.call(this, 'vendor'); + return await loadResource.call(this, 'vendor'); }, }, }; diff --git a/packages/nodes-base/nodes/RabbitMQ/GenericFunctions.ts b/packages/nodes-base/nodes/RabbitMQ/GenericFunctions.ts index a48e9707b1..d98b8bd515 100644 --- a/packages/nodes-base/nodes/RabbitMQ/GenericFunctions.ts +++ b/packages/nodes-base/nodes/RabbitMQ/GenericFunctions.ts @@ -36,7 +36,7 @@ export async function rabbitmqConnect( } } - return new Promise(async (resolve, reject) => { + return await new Promise(async (resolve, reject) => { try { const connection = await amqplib.connect(credentialData, optsData); @@ -73,7 +73,7 @@ export async function rabbitmqConnectQueue( ): Promise { const channel = await rabbitmqConnect.call(this, options); - return new Promise(async (resolve, reject) => { + return await new Promise(async (resolve, reject) => { try { await channel.assertQueue(queue, options); @@ -104,7 +104,7 @@ export async function rabbitmqConnectExchange( ): Promise { const channel = await rabbitmqConnect.call(this, options); - return new Promise(async (resolve, reject) => { + return await new Promise(async (resolve, reject) => { try { await channel.assertExchange(exchange, type, options); resolve(channel); diff --git a/packages/nodes-base/nodes/Redis/utils.ts b/packages/nodes-base/nodes/Redis/utils.ts index 6aca6b13bf..2e59eb67e4 100644 --- a/packages/nodes-base/nodes/Redis/utils.ts +++ b/packages/nodes-base/nodes/Redis/utils.ts @@ -94,13 +94,13 @@ export async function getValue(client: RedisClientType, keyName: string, type?: } if (type === 'string') { - return client.get(keyName); + return await client.get(keyName); } else if (type === 'hash') { - return client.hGetAll(keyName); + return await client.hGetAll(keyName); } else if (type === 'list') { - return client.lRange(keyName, 0, -1); + return await client.lRange(keyName, 0, -1); } else if (type === 'sets') { - return client.sMembers(keyName); + return await client.sMembers(keyName); } } diff --git a/packages/nodes-base/nodes/Rocketchat/GenericFunctions.ts b/packages/nodes-base/nodes/Rocketchat/GenericFunctions.ts index e45a307fc2..77c0ce955d 100644 --- a/packages/nodes-base/nodes/Rocketchat/GenericFunctions.ts +++ b/packages/nodes-base/nodes/Rocketchat/GenericFunctions.ts @@ -23,7 +23,7 @@ export async function rocketchatApiRequest( if (Object.keys(options.body as IDataObject).length === 0) { delete options.body; } - return this.helpers.requestWithAuthentication.call(this, 'rocketchatApi', options); + return await this.helpers.requestWithAuthentication.call(this, 'rocketchatApi', options); } export function validateJSON(json: string | undefined): any { diff --git a/packages/nodes-base/nodes/RssFeedRead/test/node/RssFeedRead.test.ts b/packages/nodes-base/nodes/RssFeedRead/test/node/RssFeedRead.test.ts index 442fc6eb69..62806e448c 100644 --- a/packages/nodes-base/nodes/RssFeedRead/test/node/RssFeedRead.test.ts +++ b/packages/nodes-base/nodes/RssFeedRead/test/node/RssFeedRead.test.ts @@ -21,6 +21,6 @@ describe('Test HTTP Request Node', () => { const nodeTypes = setup(tests); for (const testData of tests) { - test(testData.description, async () => equalityTest(testData, nodeTypes)); + test(testData.description, async () => await equalityTest(testData, nodeTypes)); } }); diff --git a/packages/nodes-base/nodes/Rundeck/RundeckApi.ts b/packages/nodes-base/nodes/Rundeck/RundeckApi.ts index ff2cb11e6f..bab3fc5f11 100644 --- a/packages/nodes-base/nodes/Rundeck/RundeckApi.ts +++ b/packages/nodes-base/nodes/Rundeck/RundeckApi.ts @@ -67,10 +67,10 @@ export class RundeckApi { query.filter = filter; } - return this.request('POST', `/api/14/job/${jobId}/run`, body, query); + return await this.request('POST', `/api/14/job/${jobId}/run`, body, query); } async getJobMetadata(jobId: string): Promise { - return this.request('GET', `/api/18/job/${jobId}/info`, {}, {}); + return await this.request('GET', `/api/18/job/${jobId}/info`, {}, {}); } } diff --git a/packages/nodes-base/nodes/Salesforce/GenericFunctions.ts b/packages/nodes-base/nodes/Salesforce/GenericFunctions.ts index cdaa585a8f..8f7da0f772 100644 --- a/packages/nodes-base/nodes/Salesforce/GenericFunctions.ts +++ b/packages/nodes-base/nodes/Salesforce/GenericFunctions.ts @@ -79,7 +79,7 @@ async function getAccessToken( json: true, }; - return this.helpers.request(options); + return await this.helpers.request(options); } export async function salesforceApiRequest( diff --git a/packages/nodes-base/nodes/Schedule/ScheduleTrigger.node.ts b/packages/nodes-base/nodes/Schedule/ScheduleTrigger.node.ts index 920e4e6373..b4602b05f9 100644 --- a/packages/nodes-base/nodes/Schedule/ScheduleTrigger.node.ts +++ b/packages/nodes-base/nodes/Schedule/ScheduleTrigger.node.ts @@ -459,7 +459,7 @@ export class ScheduleTrigger implements INodeType { try { const cronJob = new CronJob( cronExpression, - async () => executeTrigger({ activated: false } as IRecurencyRule), + async () => await executeTrigger({ activated: false } as IRecurencyRule), undefined, true, timezone, @@ -476,7 +476,7 @@ export class ScheduleTrigger implements INodeType { const seconds = interval[i].secondsInterval as number; intervalValue *= seconds; const intervalObj = setInterval( - async () => executeTrigger({ activated: false } as IRecurencyRule), + async () => await executeTrigger({ activated: false } as IRecurencyRule), intervalValue, ) as NodeJS.Timeout; intervalArr.push(intervalObj); @@ -486,7 +486,7 @@ export class ScheduleTrigger implements INodeType { const minutes = interval[i].minutesInterval as number; intervalValue *= 60 * minutes; const intervalObj = setInterval( - async () => executeTrigger({ activated: false } as IRecurencyRule), + async () => await executeTrigger({ activated: false } as IRecurencyRule), intervalValue, ) as NodeJS.Timeout; intervalArr.push(intervalObj); @@ -500,7 +500,7 @@ export class ScheduleTrigger implements INodeType { if (hour === 1) { const cronJob = new CronJob( cronExpression, - async () => executeTrigger({ activated: false } as IRecurencyRule), + async () => await executeTrigger({ activated: false } as IRecurencyRule), undefined, true, timezone, @@ -510,7 +510,7 @@ export class ScheduleTrigger implements INodeType { const cronJob = new CronJob( cronExpression, async () => - executeTrigger({ + await executeTrigger({ activated: true, index: i, intervalSize: hour, @@ -533,7 +533,7 @@ export class ScheduleTrigger implements INodeType { if (day === 1) { const cronJob = new CronJob( cronExpression, - async () => executeTrigger({ activated: false } as IRecurencyRule), + async () => await executeTrigger({ activated: false } as IRecurencyRule), undefined, true, timezone, @@ -543,7 +543,7 @@ export class ScheduleTrigger implements INodeType { const cronJob = new CronJob( cronExpression, async () => - executeTrigger({ + await executeTrigger({ activated: true, index: i, intervalSize: day, @@ -568,7 +568,7 @@ export class ScheduleTrigger implements INodeType { if (week === 1) { const cronJob = new CronJob( cronExpression, - async () => executeTrigger({ activated: false } as IRecurencyRule), + async () => await executeTrigger({ activated: false } as IRecurencyRule), undefined, true, timezone, @@ -578,7 +578,7 @@ export class ScheduleTrigger implements INodeType { const cronJob = new CronJob( cronExpression, async () => - executeTrigger({ + await executeTrigger({ activated: true, index: i, intervalSize: week, @@ -602,7 +602,7 @@ export class ScheduleTrigger implements INodeType { if (month === 1) { const cronJob = new CronJob( cronExpression, - async () => executeTrigger({ activated: false } as IRecurencyRule), + async () => await executeTrigger({ activated: false } as IRecurencyRule), undefined, true, timezone, @@ -612,7 +612,7 @@ export class ScheduleTrigger implements INodeType { const cronJob = new CronJob( cronExpression, async () => - executeTrigger({ + await executeTrigger({ activated: true, index: i, intervalSize: month, diff --git a/packages/nodes-base/nodes/Segment/GenericFunctions.ts b/packages/nodes-base/nodes/Segment/GenericFunctions.ts index 9b90251f0f..8df8cc85bf 100644 --- a/packages/nodes-base/nodes/Segment/GenericFunctions.ts +++ b/packages/nodes-base/nodes/Segment/GenericFunctions.ts @@ -29,5 +29,5 @@ export async function segmentApiRequest( if (!Object.keys(body as IDataObject).length) { delete options.body; } - return this.helpers.requestWithAuthentication.call(this, 'segmentApi', options); + return await this.helpers.requestWithAuthentication.call(this, 'segmentApi', options); } diff --git a/packages/nodes-base/nodes/SendGrid/GenericFunctions.ts b/packages/nodes-base/nodes/SendGrid/GenericFunctions.ts index d03e8aff34..ee7339ede3 100644 --- a/packages/nodes-base/nodes/SendGrid/GenericFunctions.ts +++ b/packages/nodes-base/nodes/SendGrid/GenericFunctions.ts @@ -34,7 +34,7 @@ export async function sendGridApiRequest( Object.assign(options, option); } - return this.helpers.requestWithAuthentication.call(this, 'sendGridApi', options); + return await this.helpers.requestWithAuthentication.call(this, 'sendGridApi', options); } export async function sendGridApiRequestAllItems( diff --git a/packages/nodes-base/nodes/Shopify/GenericFunctions.ts b/packages/nodes-base/nodes/Shopify/GenericFunctions.ts index 36bdc03fa3..54872b2fe4 100644 --- a/packages/nodes-base/nodes/Shopify/GenericFunctions.ts +++ b/packages/nodes-base/nodes/Shopify/GenericFunctions.ts @@ -78,7 +78,7 @@ export async function shopifyApiRequest( } } - return this.helpers.requestWithAuthentication.call(this, credentialType, options, { + return await this.helpers.requestWithAuthentication.call(this, credentialType, options, { oauth2: oAuth2Options, }); } diff --git a/packages/nodes-base/nodes/Snowflake/GenericFunctions.ts b/packages/nodes-base/nodes/Snowflake/GenericFunctions.ts index 52bad21752..6e2d332048 100644 --- a/packages/nodes-base/nodes/Snowflake/GenericFunctions.ts +++ b/packages/nodes-base/nodes/Snowflake/GenericFunctions.ts @@ -1,13 +1,13 @@ import type snowflake from 'snowflake-sdk'; export async function connect(conn: snowflake.Connection) { - return new Promise((resolve, reject) => { + return await new Promise((resolve, reject) => { conn.connect((error) => (error ? reject(error) : resolve())); }); } export async function destroy(conn: snowflake.Connection) { - return new Promise((resolve, reject) => { + return await new Promise((resolve, reject) => { conn.destroy((error) => (error ? reject(error) : resolve())); }); } @@ -17,7 +17,7 @@ export async function execute( sqlText: string, binds: snowflake.InsertBinds, ) { - return new Promise((resolve, reject) => { + return await new Promise((resolve, reject) => { conn.execute({ sqlText, binds, diff --git a/packages/nodes-base/nodes/Splunk/GenericFunctions.ts b/packages/nodes-base/nodes/Splunk/GenericFunctions.ts index 903dd21228..b661f9e573 100644 --- a/packages/nodes-base/nodes/Splunk/GenericFunctions.ts +++ b/packages/nodes-base/nodes/Splunk/GenericFunctions.ts @@ -90,7 +90,7 @@ export function formatSearch(responseData: SplunkSearchResponse) { // ---------------------------------------- export async function parseXml(xml: string) { - return new Promise((resolve, reject) => { + return await new Promise((resolve, reject) => { parseString(xml, { explicitArray: false }, (error, result) => { error ? reject(error) : resolve(result); }); diff --git a/packages/nodes-base/nodes/Strapi/GenericFunctions.ts b/packages/nodes-base/nodes/Strapi/GenericFunctions.ts index a2b1e0fb4e..9f786105b8 100644 --- a/packages/nodes-base/nodes/Strapi/GenericFunctions.ts +++ b/packages/nodes-base/nodes/Strapi/GenericFunctions.ts @@ -87,7 +87,7 @@ export async function getToken( uri: credentials.apiVersion === 'v4' ? `${url}/api/auth/local` : `${url}/auth/local`, json: true, }; - return this.helpers.request(options) as Promise<{ jwt: string }>; + return await (this.helpers.request(options) as Promise<{ jwt: string }>); } export async function strapiApiRequestAllItems( diff --git a/packages/nodes-base/nodes/Stripe/Stripe.node.ts b/packages/nodes-base/nodes/Stripe/Stripe.node.ts index a58285f125..e50e35f30a 100644 --- a/packages/nodes-base/nodes/Stripe/Stripe.node.ts +++ b/packages/nodes-base/nodes/Stripe/Stripe.node.ts @@ -113,7 +113,7 @@ export class Stripe implements INodeType { methods = { loadOptions: { async getCustomers(this: ILoadOptionsFunctions) { - return loadResource.call(this, 'customer'); + return await loadResource.call(this, 'customer'); }, async getCurrencies(this: ILoadOptionsFunctions): Promise { const returnData: INodePropertyOptions[] = []; diff --git a/packages/nodes-base/nodes/Stripe/helpers.ts b/packages/nodes-base/nodes/Stripe/helpers.ts index 9bc714428c..6057f6f210 100644 --- a/packages/nodes-base/nodes/Stripe/helpers.ts +++ b/packages/nodes-base/nodes/Stripe/helpers.ts @@ -32,7 +32,7 @@ export async function stripeApiRequest( delete options.qs; } - return this.helpers.requestWithAuthentication.call(this, 'stripeApi', options); + return await this.helpers.requestWithAuthentication.call(this, 'stripeApi', options); } /** diff --git a/packages/nodes-base/nodes/Supabase/GenericFunctions.ts b/packages/nodes-base/nodes/Supabase/GenericFunctions.ts index 1c5a60c975..3716dca3ea 100644 --- a/packages/nodes-base/nodes/Supabase/GenericFunctions.ts +++ b/packages/nodes-base/nodes/Supabase/GenericFunctions.ts @@ -312,7 +312,7 @@ export async function validateCredentials( json: true, }; - return this.helpers.request(options); + return await this.helpers.request(options); } export function mapPairedItemsFrom(iterable: Iterable | ArrayLike): IPairedItemData[] { diff --git a/packages/nodes-base/nodes/SurveyMonkey/SurveyMonkeyTrigger.node.ts b/packages/nodes-base/nodes/SurveyMonkey/SurveyMonkeyTrigger.node.ts index 289b4ebd49..b299e9a21e 100644 --- a/packages/nodes-base/nodes/SurveyMonkey/SurveyMonkeyTrigger.node.ts +++ b/packages/nodes-base/nodes/SurveyMonkey/SurveyMonkeyTrigger.node.ts @@ -491,7 +491,7 @@ export class SurveyMonkeyTrigger implements INodeType { return {}; } - return new Promise((resolve, _reject) => { + return await new Promise((resolve, _reject) => { const data: Buffer[] = []; req.on('data', (chunk) => { diff --git a/packages/nodes-base/nodes/SyncroMSP/v1/SyncroMspV1.node.ts b/packages/nodes-base/nodes/SyncroMSP/v1/SyncroMspV1.node.ts index 07a17ef81a..18f6568d04 100644 --- a/packages/nodes-base/nodes/SyncroMSP/v1/SyncroMspV1.node.ts +++ b/packages/nodes-base/nodes/SyncroMSP/v1/SyncroMspV1.node.ts @@ -51,6 +51,6 @@ export class SyncroMspV1 implements INodeType { }; async execute(this: IExecuteFunctions) { - return router.call(this); + return await router.call(this); } } diff --git a/packages/nodes-base/nodes/SyncroMSP/v1/transport/index.ts b/packages/nodes-base/nodes/SyncroMSP/v1/transport/index.ts index 32c9acf290..13096d6962 100644 --- a/packages/nodes-base/nodes/SyncroMSP/v1/transport/index.ts +++ b/packages/nodes-base/nodes/SyncroMSP/v1/transport/index.ts @@ -79,5 +79,5 @@ export async function validateCredentials( url: `https://${subdomain}.syncromsp.com/api/v1//me`, }; - return this.helpers.request(options); + return await this.helpers.request(options); } diff --git a/packages/nodes-base/nodes/Taiga/GenericFunctions.ts b/packages/nodes-base/nodes/Taiga/GenericFunctions.ts index d75883f8dd..71395a5b2e 100644 --- a/packages/nodes-base/nodes/Taiga/GenericFunctions.ts +++ b/packages/nodes-base/nodes/Taiga/GenericFunctions.ts @@ -132,7 +132,7 @@ export async function handleListing( const returnAll = this.getNodeParameter('returnAll', i); if (returnAll) { - return taigaApiRequestAllItems.call(this, method, endpoint, body, qs); + return await taigaApiRequestAllItems.call(this, method, endpoint, body, qs); } else { qs.limit = this.getNodeParameter('limit', i); responseData = await taigaApiRequestAllItems.call(this, method, endpoint, body, qs); @@ -151,5 +151,5 @@ export function throwOnEmptyUpdate(this: IExecuteFunctions, resource: Resource) } export async function getVersionForUpdate(this: IExecuteFunctions, endpoint: string) { - return taigaApiRequest.call(this, 'GET', endpoint).then((response) => response.version); + return await taigaApiRequest.call(this, 'GET', endpoint).then((response) => response.version); } diff --git a/packages/nodes-base/nodes/TheHive/GenericFunctions.ts b/packages/nodes-base/nodes/TheHive/GenericFunctions.ts index 14132a0feb..acf574c8fe 100644 --- a/packages/nodes-base/nodes/TheHive/GenericFunctions.ts +++ b/packages/nodes-base/nodes/TheHive/GenericFunctions.ts @@ -42,7 +42,7 @@ export async function theHiveApiRequest( if (Object.keys(query).length === 0) { delete options.qs; } - return this.helpers.requestWithAuthentication.call(this, 'theHiveApi', options); + return await this.helpers.requestWithAuthentication.call(this, 'theHiveApi', options); } // Helpers functions diff --git a/packages/nodes-base/nodes/TheHiveProject/TheHiveProject.node.ts b/packages/nodes-base/nodes/TheHiveProject/TheHiveProject.node.ts index 257a4b7cc7..4c71dabb1c 100644 --- a/packages/nodes-base/nodes/TheHiveProject/TheHiveProject.node.ts +++ b/packages/nodes-base/nodes/TheHiveProject/TheHiveProject.node.ts @@ -10,6 +10,6 @@ export class TheHiveProject implements INodeType { methods = { loadOptions, listSearch, resourceMapping }; async execute(this: IExecuteFunctions) { - return router.call(this); + return await router.call(this); } } diff --git a/packages/nodes-base/nodes/TheHiveProject/actions/router.ts b/packages/nodes-base/nodes/TheHiveProject/actions/router.ts index 04f86e1d8f..0cb6866eda 100644 --- a/packages/nodes-base/nodes/TheHiveProject/actions/router.ts +++ b/packages/nodes-base/nodes/TheHiveProject/actions/router.ts @@ -76,5 +76,5 @@ export async function router(this: IExecuteFunctions): Promise { - return listResource.call(this, 'listCase', 'title', 'title', 'cases', filter, paginationToken); + return await listResource.call( + this, + 'listCase', + 'title', + 'title', + 'cases', + filter, + paginationToken, + ); } export async function commentSearch( @@ -70,7 +78,7 @@ export async function commentSearch( filter?: string, paginationToken?: string, ): Promise { - return listResource.call( + return await listResource.call( this, 'listComment', 'message', @@ -86,7 +94,15 @@ export async function alertSearch( filter?: string, paginationToken?: string, ): Promise { - return listResource.call(this, 'listAlert', 'title', 'title', 'alerts', filter, paginationToken); + return await listResource.call( + this, + 'listAlert', + 'title', + 'title', + 'alerts', + filter, + paginationToken, + ); } export async function taskSearch( @@ -94,7 +110,15 @@ export async function taskSearch( filter?: string, paginationToken?: string, ): Promise { - return listResource.call(this, 'listTask', 'title', 'title', undefined, filter, paginationToken); + return await listResource.call( + this, + 'listTask', + 'title', + 'title', + undefined, + filter, + paginationToken, + ); } export async function pageSearch( @@ -171,7 +195,7 @@ export async function logSearch( filter?: string, paginationToken?: string, ): Promise { - return listResource.call( + return await listResource.call( this, 'listLog', 'message', diff --git a/packages/nodes-base/nodes/TheHiveProject/transport/requestApi.ts b/packages/nodes-base/nodes/TheHiveProject/transport/requestApi.ts index 0b832f9cbe..2080c1bdc9 100644 --- a/packages/nodes-base/nodes/TheHiveProject/transport/requestApi.ts +++ b/packages/nodes-base/nodes/TheHiveProject/transport/requestApi.ts @@ -38,5 +38,5 @@ export async function theHiveApiRequest( if (Object.keys(query).length === 0) { delete options.qs; } - return this.helpers.requestWithAuthentication.call(this, 'theHiveProjectApi', options); + return await this.helpers.requestWithAuthentication.call(this, 'theHiveProjectApi', options); } diff --git a/packages/nodes-base/nodes/Todoist/v1/Service.ts b/packages/nodes-base/nodes/Todoist/v1/Service.ts index 058b3c6964..fa5112a5bf 100644 --- a/packages/nodes-base/nodes/Todoist/v1/Service.ts +++ b/packages/nodes-base/nodes/Todoist/v1/Service.ts @@ -18,7 +18,7 @@ export class TodoistService implements Service { operation: OperationType, itemIndex: number, ): Promise { - return this.handlers[operation].handleOperation(ctx, itemIndex); + return await this.handlers[operation].handleOperation(ctx, itemIndex); } private handlers = { diff --git a/packages/nodes-base/nodes/Todoist/v2/Service.ts b/packages/nodes-base/nodes/Todoist/v2/Service.ts index 058b3c6964..fa5112a5bf 100644 --- a/packages/nodes-base/nodes/Todoist/v2/Service.ts +++ b/packages/nodes-base/nodes/Todoist/v2/Service.ts @@ -18,7 +18,7 @@ export class TodoistService implements Service { operation: OperationType, itemIndex: number, ): Promise { - return this.handlers[operation].handleOperation(ctx, itemIndex); + return await this.handlers[operation].handleOperation(ctx, itemIndex); } private handlers = { diff --git a/packages/nodes-base/nodes/Trello/GenericFunctions.ts b/packages/nodes-base/nodes/Trello/GenericFunctions.ts index c653b28050..d7873cc491 100644 --- a/packages/nodes-base/nodes/Trello/GenericFunctions.ts +++ b/packages/nodes-base/nodes/Trello/GenericFunctions.ts @@ -28,7 +28,7 @@ export async function apiRequest( json: true, }; - return this.helpers.requestWithAuthentication.call(this, 'trelloApi', options); + return await this.helpers.requestWithAuthentication.call(this, 'trelloApi', options); } export async function apiRequestAllItems( diff --git a/packages/nodes-base/nodes/Twake/GenericFunctions.ts b/packages/nodes-base/nodes/Twake/GenericFunctions.ts index bb6a5791fe..30375bf813 100644 --- a/packages/nodes-base/nodes/Twake/GenericFunctions.ts +++ b/packages/nodes-base/nodes/Twake/GenericFunctions.ts @@ -29,5 +29,5 @@ export async function twakeApiRequest( // options.uri = `${credentials!.hostUrl}/api/v1${resource}`; // } - return this.helpers.requestWithAuthentication.call(this, 'twakeCloudApi', options); + return await this.helpers.requestWithAuthentication.call(this, 'twakeCloudApi', options); } diff --git a/packages/nodes-base/nodes/Twilio/GenericFunctions.ts b/packages/nodes-base/nodes/Twilio/GenericFunctions.ts index af6007d29e..118495ce9f 100644 --- a/packages/nodes-base/nodes/Twilio/GenericFunctions.ts +++ b/packages/nodes-base/nodes/Twilio/GenericFunctions.ts @@ -33,7 +33,7 @@ export async function twilioApiRequest( json: true, }; - return this.helpers.requestWithAuthentication.call(this, 'twilioApi', options); + return await this.helpers.requestWithAuthentication.call(this, 'twilioApi', options); } const XML_CHAR_MAP: { [key: string]: string } = { diff --git a/packages/nodes-base/nodes/UrlScanIo/GenericFunctions.ts b/packages/nodes-base/nodes/UrlScanIo/GenericFunctions.ts index d69867a7f6..5b42d87460 100644 --- a/packages/nodes-base/nodes/UrlScanIo/GenericFunctions.ts +++ b/packages/nodes-base/nodes/UrlScanIo/GenericFunctions.ts @@ -25,7 +25,7 @@ export async function urlScanIoApiRequest( delete options.qs; } - return this.helpers.requestWithAuthentication.call(this, 'urlScanIoApi', options); + return await this.helpers.requestWithAuthentication.call(this, 'urlScanIoApi', options); } export async function handleListing( diff --git a/packages/nodes-base/nodes/Venafi/ProtectCloud/GenericFunctions.ts b/packages/nodes-base/nodes/Venafi/ProtectCloud/GenericFunctions.ts index 58e2066a32..2f7ff69d99 100644 --- a/packages/nodes-base/nodes/Venafi/ProtectCloud/GenericFunctions.ts +++ b/packages/nodes-base/nodes/Venafi/ProtectCloud/GenericFunctions.ts @@ -121,7 +121,7 @@ export async function encryptPassphrase( let encryptedKeyStorePass = ''; const promise = async () => { - return new Promise((resolve, reject) => { + return await new Promise((resolve, reject) => { nacl_factory.instantiate((nacl: any) => { try { const passphraseUTF8 = nacl.encode_utf8(passphrase) as string; @@ -142,5 +142,5 @@ export async function encryptPassphrase( }); }); }; - return promise(); + return await promise(); } diff --git a/packages/nodes-base/nodes/Wait/Wait.node.ts b/packages/nodes-base/nodes/Wait/Wait.node.ts index 7788ca6685..888994a4a7 100644 --- a/packages/nodes-base/nodes/Wait/Wait.node.ts +++ b/packages/nodes-base/nodes/Wait/Wait.node.ts @@ -392,15 +392,15 @@ export class Wait extends Webhook { async webhook(context: IWebhookFunctions) { const resume = context.getNodeParameter('resume', 0) as string; - if (resume === 'form') return formWebhook(context); - return super.webhook(context); + if (resume === 'form') return await formWebhook(context); + return await super.webhook(context); } async execute(context: IExecuteFunctions): Promise { const resume = context.getNodeParameter('resume', 0) as string; if (['webhook', 'form'].includes(resume)) { - return this.configureAndPutToWait(context); + return await this.configureAndPutToWait(context); } let waitTill: Date; @@ -433,14 +433,14 @@ export class Wait extends Webhook { if (waitValue < 65000) { // If wait time is shorter than 65 seconds leave execution active because // we just check the database every 60 seconds. - return new Promise((resolve) => { + return await new Promise((resolve) => { const timer = setTimeout(() => resolve([context.getInputData()]), waitValue); context.onExecutionCancellation(() => clearTimeout(timer)); }); } // If longer than 65 seconds put execution to wait - return this.putToWait(context, waitTill); + return await this.putToWait(context, waitTill); } private async configureAndPutToWait(context: IExecuteFunctions) { @@ -471,7 +471,7 @@ export class Wait extends Webhook { } } - return this.putToWait(context, waitTill); + return await this.putToWait(context, waitTill); } private async putToWait(context: IExecuteFunctions, waitTill: Date) { diff --git a/packages/nodes-base/nodes/Webflow/GenericFunctions.ts b/packages/nodes-base/nodes/Webflow/GenericFunctions.ts index c91eead9b2..459b6d7d75 100644 --- a/packages/nodes-base/nodes/Webflow/GenericFunctions.ts +++ b/packages/nodes-base/nodes/Webflow/GenericFunctions.ts @@ -47,7 +47,7 @@ export async function webflowApiRequest( if (Object.keys(options.body as IDataObject).length === 0) { delete options.body; } - return this.helpers.requestWithAuthentication.call(this, credentialsType, options); + return await this.helpers.requestWithAuthentication.call(this, credentialsType, options); } export async function webflowApiRequestAllItems( diff --git a/packages/nodes-base/nodes/Webhook/Webhook.node.ts b/packages/nodes-base/nodes/Webhook/Webhook.node.ts index e0a194cdb3..3311a4bdfc 100644 --- a/packages/nodes-base/nodes/Webhook/Webhook.node.ts +++ b/packages/nodes-base/nodes/Webhook/Webhook.node.ts @@ -119,11 +119,11 @@ export class Webhook extends Node { } if (options.binaryData) { - return this.handleBinaryData(context); + return await this.handleBinaryData(context); } if (req.contentType === 'multipart/form-data') { - return this.handleFormData(context); + return await this.handleFormData(context); } const nodeVersion = context.getNode().typeVersion; diff --git a/packages/nodes-base/nodes/Wekan/GenericFunctions.ts b/packages/nodes-base/nodes/Wekan/GenericFunctions.ts index 4308ec8eb4..b91143888a 100644 --- a/packages/nodes-base/nodes/Wekan/GenericFunctions.ts +++ b/packages/nodes-base/nodes/Wekan/GenericFunctions.ts @@ -29,5 +29,5 @@ export async function apiRequest( json: true, }; - return this.helpers.requestWithAuthentication.call(this, 'wekanApi', options); + return await this.helpers.requestWithAuthentication.call(this, 'wekanApi', options); } diff --git a/packages/nodes-base/nodes/WooCommerce/GenericFunctions.ts b/packages/nodes-base/nodes/WooCommerce/GenericFunctions.ts index ea64cb34da..004c783146 100644 --- a/packages/nodes-base/nodes/WooCommerce/GenericFunctions.ts +++ b/packages/nodes-base/nodes/WooCommerce/GenericFunctions.ts @@ -39,7 +39,7 @@ export async function woocommerceApiRequest( delete options.form; } options = Object.assign({}, options, option); - return this.helpers.requestWithAuthentication.call(this, 'wooCommerceApi', options); + return await this.helpers.requestWithAuthentication.call(this, 'wooCommerceApi', options); } export async function woocommerceApiRequestAllItems( diff --git a/packages/nodes-base/nodes/Wufoo/GenericFunctions.ts b/packages/nodes-base/nodes/Wufoo/GenericFunctions.ts index 025fc46710..4277cb50e6 100644 --- a/packages/nodes-base/nodes/Wufoo/GenericFunctions.ts +++ b/packages/nodes-base/nodes/Wufoo/GenericFunctions.ts @@ -33,5 +33,5 @@ export async function wufooApiRequest( delete options.body; } - return this.helpers.requestWithAuthentication.call(this, 'wufooApi', options); + return await this.helpers.requestWithAuthentication.call(this, 'wufooApi', options); } diff --git a/packages/nodes-base/nodes/Zendesk/GenericFunctions.ts b/packages/nodes-base/nodes/Zendesk/GenericFunctions.ts index edc968f899..47ee5ab6d8 100644 --- a/packages/nodes-base/nodes/Zendesk/GenericFunctions.ts +++ b/packages/nodes-base/nodes/Zendesk/GenericFunctions.ts @@ -53,7 +53,7 @@ export async function zendeskApiRequest( const credentialType = authenticationMethod === 'apiToken' ? 'zendeskApi' : 'zendeskOAuth2Api'; - return this.helpers.requestWithAuthentication.call(this, credentialType, options); + return await this.helpers.requestWithAuthentication.call(this, credentialType, options); } /** diff --git a/packages/nodes-base/nodes/Zoho/GenericFunctions.ts b/packages/nodes-base/nodes/Zoho/GenericFunctions.ts index 4351ab1c4c..2536fb68c6 100644 --- a/packages/nodes-base/nodes/Zoho/GenericFunctions.ts +++ b/packages/nodes-base/nodes/Zoho/GenericFunctions.ts @@ -125,7 +125,7 @@ export async function handleListing( const returnAll = this.getNodeParameter('returnAll', 0); if (returnAll) { - return zohoApiRequestAllItems.call(this, method, endpoint, body, qs); + return await zohoApiRequestAllItems.call(this, method, endpoint, body, qs); } const responseData = await zohoApiRequestAllItems.call(this, method, endpoint, body, qs); diff --git a/packages/nodes-base/nodes/Zoho/ZohoCrm.node.ts b/packages/nodes-base/nodes/Zoho/ZohoCrm.node.ts index 62dd144715..4b56e030d2 100644 --- a/packages/nodes-base/nodes/Zoho/ZohoCrm.node.ts +++ b/packages/nodes-base/nodes/Zoho/ZohoCrm.node.ts @@ -212,93 +212,93 @@ export class ZohoCrm implements INodeType { // standard fields - called from `makeGetAllFields` async getAccountFields(this: ILoadOptionsFunctions) { - return getFields.call(this, 'account'); + return await getFields.call(this, 'account'); }, async getContactFields(this: ILoadOptionsFunctions) { - return getFields.call(this, 'contact'); + return await getFields.call(this, 'contact'); }, async getDealFields(this: ILoadOptionsFunctions) { - return getFields.call(this, 'deal'); + return await getFields.call(this, 'deal'); }, async getInvoiceFields(this: ILoadOptionsFunctions) { - return getFields.call(this, 'invoice'); + return await getFields.call(this, 'invoice'); }, async getLeadFields(this: ILoadOptionsFunctions) { - return getFields.call(this, 'lead'); + return await getFields.call(this, 'lead'); }, async getProductFields(this: ILoadOptionsFunctions) { - return getFields.call(this, 'product'); + return await getFields.call(this, 'product'); }, async getPurchaseOrderFields(this: ILoadOptionsFunctions) { - return getFields.call(this, 'purchase_order'); + return await getFields.call(this, 'purchase_order'); }, async getVendorOrderFields(this: ILoadOptionsFunctions) { - return getFields.call(this, 'vendor'); + return await getFields.call(this, 'vendor'); }, async getQuoteFields(this: ILoadOptionsFunctions) { - return getFields.call(this, 'quote'); + return await getFields.call(this, 'quote'); }, async getSalesOrderFields(this: ILoadOptionsFunctions) { - return getFields.call(this, 'sales_order'); + return await getFields.call(this, 'sales_order'); }, async getVendorFields(this: ILoadOptionsFunctions) { - return getFields.call(this, 'vendor'); + return await getFields.call(this, 'vendor'); }, // custom fields async getCustomAccountFields(this: ILoadOptionsFunctions) { - return getFields.call(this, 'account', { onlyCustom: true }); + return await getFields.call(this, 'account', { onlyCustom: true }); }, async getCustomContactFields(this: ILoadOptionsFunctions) { - return getFields.call(this, 'contact', { onlyCustom: true }); + return await getFields.call(this, 'contact', { onlyCustom: true }); }, async getCustomDealFields(this: ILoadOptionsFunctions) { - return getFields.call(this, 'deal', { onlyCustom: true }); + return await getFields.call(this, 'deal', { onlyCustom: true }); }, async getCustomInvoiceFields(this: ILoadOptionsFunctions) { - return getFields.call(this, 'invoice', { onlyCustom: true }); + return await getFields.call(this, 'invoice', { onlyCustom: true }); }, async getCustomLeadFields(this: ILoadOptionsFunctions) { - return getFields.call(this, 'lead', { onlyCustom: true }); + return await getFields.call(this, 'lead', { onlyCustom: true }); }, async getCustomProductFields(this: ILoadOptionsFunctions) { - return getFields.call(this, 'product', { onlyCustom: true }); + return await getFields.call(this, 'product', { onlyCustom: true }); }, async getCustomPurchaseOrderFields(this: ILoadOptionsFunctions) { - return getFields.call(this, 'purchase_order', { onlyCustom: true }); + return await getFields.call(this, 'purchase_order', { onlyCustom: true }); }, async getCustomVendorOrderFields(this: ILoadOptionsFunctions) { - return getFields.call(this, 'vendor', { onlyCustom: true }); + return await getFields.call(this, 'vendor', { onlyCustom: true }); }, async getCustomQuoteFields(this: ILoadOptionsFunctions) { - return getFields.call(this, 'quote', { onlyCustom: true }); + return await getFields.call(this, 'quote', { onlyCustom: true }); }, async getCustomSalesOrderFields(this: ILoadOptionsFunctions) { - return getFields.call(this, 'sales_order', { onlyCustom: true }); + return await getFields.call(this, 'sales_order', { onlyCustom: true }); }, async getCustomVendorFields(this: ILoadOptionsFunctions) { - return getFields.call(this, 'vendor', { onlyCustom: true }); + return await getFields.call(this, 'vendor', { onlyCustom: true }); }, // ---------------------------------------- @@ -306,23 +306,23 @@ export class ZohoCrm implements INodeType { // ---------------------------------------- async getAccountType(this: ILoadOptionsFunctions) { - return getPicklistOptions.call(this, 'account', 'Account_Type'); + return await getPicklistOptions.call(this, 'account', 'Account_Type'); }, async getDealStage(this: ILoadOptionsFunctions) { - return getPicklistOptions.call(this, 'deal', 'Stage'); + return await getPicklistOptions.call(this, 'deal', 'Stage'); }, async getPurchaseOrderStatus(this: ILoadOptionsFunctions) { - return getPicklistOptions.call(this, 'purchaseOrder', 'Status'); + return await getPicklistOptions.call(this, 'purchaseOrder', 'Status'); }, async getSalesOrderStatus(this: ILoadOptionsFunctions) { - return getPicklistOptions.call(this, 'salesOrder', 'Status'); + return await getPicklistOptions.call(this, 'salesOrder', 'Status'); }, async getQuoteStage(this: ILoadOptionsFunctions) { - return getPicklistOptions.call(this, 'quote', 'Quote_Stage'); + return await getPicklistOptions.call(this, 'quote', 'Quote_Stage'); }, }, }; diff --git a/packages/nodes-base/nodes/Zoom/GenericFunctions.ts b/packages/nodes-base/nodes/Zoom/GenericFunctions.ts index 156fb7f993..16ed299fb9 100644 --- a/packages/nodes-base/nodes/Zoom/GenericFunctions.ts +++ b/packages/nodes-base/nodes/Zoom/GenericFunctions.ts @@ -49,7 +49,7 @@ export async function zoomApiRequest( } async function wait() { - return new Promise((resolve, _reject) => { + return await new Promise((resolve, _reject) => { setTimeout(() => { resolve(true); }, 1000); diff --git a/packages/nodes-base/test/nodes/Helpers.ts b/packages/nodes-base/test/nodes/Helpers.ts index 9cf4dbe8e6..c92d14095c 100644 --- a/packages/nodes-base/test/nodes/Helpers.ts +++ b/packages/nodes-base/test/nodes/Helpers.ts @@ -104,7 +104,7 @@ export class CredentialsHelper extends ICredentialsHelper { ): Promise { const credentialType = this.credentialTypes.getByName(typeName); if (typeof credentialType.authenticate === 'function') { - return credentialType.authenticate(credentials, requestParams); + return await credentialType.authenticate(credentials, requestParams); } return requestParams; } @@ -390,7 +390,7 @@ export const testWorkflows = (workflows: string[]) => { const nodeTypes = setup(tests); for (const testData of tests) { - test(testData.description, async () => equalityTest(testData, nodeTypes)); + test(testData.description, async () => await equalityTest(testData, nodeTypes)); } }; diff --git a/packages/workflow/.eslintrc.js b/packages/workflow/.eslintrc.js index 79997056e7..f7f7251b35 100644 --- a/packages/workflow/.eslintrc.js +++ b/packages/workflow/.eslintrc.js @@ -16,5 +16,9 @@ module.exports = { '@typescript-eslint/no-redundant-type-constituents': 'warn', '@typescript-eslint/prefer-nullish-coalescing': 'warn', '@typescript-eslint/prefer-optional-chain': 'warn', + /** + * https://github.com/typescript-eslint/typescript-eslint/blob/main/packages/eslint-plugin/docs/rules/return-await.md + */ + '@typescript-eslint/return-await': ['error', 'always'], }, }; diff --git a/packages/workflow/src/DeferredPromise.ts b/packages/workflow/src/DeferredPromise.ts index 80e088e1bd..1feededead 100644 --- a/packages/workflow/src/DeferredPromise.ts +++ b/packages/workflow/src/DeferredPromise.ts @@ -6,9 +6,9 @@ export interface IDeferredPromise { } export async function createDeferredPromise(): Promise> { - return new Promise>((resolveCreate) => { + return await new Promise>((resolveCreate) => { const promise = new Promise((resolve, reject) => { - resolveCreate({ promise: async () => promise, resolve, reject }); + resolveCreate({ promise: async () => await promise, resolve, reject }); }); }); } diff --git a/packages/workflow/src/RoutingNode.ts b/packages/workflow/src/RoutingNode.ts index 0a635d06bf..6ae44673b5 100644 --- a/packages/workflow/src/RoutingNode.ts +++ b/packages/workflow/src/RoutingNode.ts @@ -284,7 +284,7 @@ export class RoutingNode { runIndex: number, ): Promise { if (typeof action === 'function') { - return action.call(executeSingleFunctions, inputData, responseData); + return await action.call(executeSingleFunctions, inputData, responseData); } if (action.type === 'rootProperty') { try { @@ -534,19 +534,20 @@ export class RoutingNode { const executePaginationFunctions = { ...executeSingleFunctions, makeRoutingRequest: async (requestOptions: DeclarativeRestApiSettings.ResultOptions) => { - return this.rawRoutingRequest( + return await this.rawRoutingRequest( executeSingleFunctions, requestOptions, credentialType, credentialsDecrypted, - ).then(async (data) => - this.postProcessResponseData( - executeSingleFunctions, - data, - requestData, - itemIndex, - runIndex, - ), + ).then( + async (data) => + await this.postProcessResponseData( + executeSingleFunctions, + data, + requestData, + itemIndex, + runIndex, + ), ); }, }; @@ -649,14 +650,15 @@ export class RoutingNode { requestData, credentialType, credentialsDecrypted, - ).then(async (data) => - this.postProcessResponseData( - executeSingleFunctions, - data, - requestData, - itemIndex, - runIndex, - ), + ).then( + async (data) => + await this.postProcessResponseData( + executeSingleFunctions, + data, + requestData, + itemIndex, + runIndex, + ), ); (requestData.options[optionsType] as IDataObject)[properties.offsetParameter] = @@ -694,14 +696,15 @@ export class RoutingNode { requestData, credentialType, credentialsDecrypted, - ).then(async (data) => - this.postProcessResponseData( - executeSingleFunctions, - data, - requestData, - itemIndex, - runIndex, - ), + ).then( + async (data) => + await this.postProcessResponseData( + executeSingleFunctions, + data, + requestData, + itemIndex, + runIndex, + ), ); } return responseData; diff --git a/packages/workflow/src/Workflow.ts b/packages/workflow/src/Workflow.ts index c5d6cb91dd..65d72a9dcb 100644 --- a/packages/workflow/src/Workflow.ts +++ b/packages/workflow/src/Workflow.ts @@ -1058,7 +1058,7 @@ export class Workflow { webhookData, ); - return webhookFn.call(thisArgs); + return await webhookFn.call(thisArgs); } /** @@ -1143,7 +1143,7 @@ export class Workflow { return triggerResponse; } // In all other modes simply start the trigger - return nodeType.trigger.call(triggerFunctions); + return await nodeType.trigger.call(triggerFunctions); } /** @@ -1172,7 +1172,7 @@ export class Workflow { }); } - return nodeType.poll.call(pollFunctions); + return await nodeType.poll.call(pollFunctions); } /** @@ -1208,7 +1208,9 @@ export class Workflow { webhookData, closeFunctions, ); - return nodeType instanceof Node ? nodeType.webhook(context) : nodeType.webhook.call(context); + return nodeType instanceof Node + ? await nodeType.webhook(context) + : await nodeType.webhook.call(context); } /** @@ -1322,7 +1324,7 @@ export class Workflow { : await nodeType.execute.call(context); const closeFunctionsResults = await Promise.allSettled( - closeFunctions.map(async (fn) => fn()), + closeFunctions.map(async (fn) => await fn()), ); const closingErrors = closeFunctionsResults diff --git a/packages/workflow/src/utils.ts b/packages/workflow/src/utils.ts index e5c56d8caa..8cff1329e1 100644 --- a/packages/workflow/src/utils.ts +++ b/packages/workflow/src/utils.ts @@ -107,7 +107,7 @@ export const jsonStringify = (obj: unknown, options: JSONStringifyOptions = {}): }; export const sleep = async (ms: number): Promise => - new Promise((resolve) => { + await new Promise((resolve) => { setTimeout(resolve, ms); }); diff --git a/packages/workflow/test/Helpers.ts b/packages/workflow/test/Helpers.ts index 37016d4a14..fb83faa90a 100644 --- a/packages/workflow/test/Helpers.ts +++ b/packages/workflow/test/Helpers.ts @@ -189,7 +189,7 @@ export function getExecuteFunctions( workflowInfo: IExecuteWorkflowInfo, inputData?: INodeExecutionData[], ): Promise { - return additionalData.executeWorkflow(workflowInfo, additionalData, { inputData }); + return await additionalData.executeWorkflow(workflowInfo, additionalData, { inputData }); }, getContext(type: string): IContextObject { return NodeHelpers.getContext(runExecutionData, type, node); From 639d34769e75096d0725f1f60861dc972b344abe 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, 17 Jan 2024 16:33:54 +0100 Subject: [PATCH 19/69] feat(core): Upgrade bull and ioredis to address CVE-2023-52079 (#8365) --- packages/cli/package.json | 6 +-- pnpm-lock.yaml | 103 ++++++++++++++++++-------------------- 2 files changed, 51 insertions(+), 58 deletions(-) diff --git a/packages/cli/package.json b/packages/cli/package.json index e49bc000e4..1f01f299d1 100644 --- a/packages/cli/package.json +++ b/packages/cli/package.json @@ -99,6 +99,7 @@ "dependencies": { "@n8n/client-oauth2": "workspace:*", "@n8n/localtunnel": "2.1.0", + "@n8n/n8n-nodes-langchain": "workspace:*", "@n8n/permissions": "workspace:*", "@n8n_io/license-sdk": "2.7.2", "@oclif/command": "1.8.18", @@ -111,7 +112,7 @@ "axios": "1.6.2", "basic-auth": "2.0.1", "bcryptjs": "2.4.3", - "bull": "4.10.2", + "bull": "4.12.1", "cache-manager": "5.2.3", "callsites": "3.1.0", "change-case": "4.1.2", @@ -137,7 +138,7 @@ "handlebars": "4.7.7", "infisical-node": "1.3.0", "inquirer": "7.3.3", - "ioredis": "5.2.4", + "ioredis": "5.3.2", "isbot": "3.6.13", "json-diff": "1.0.6", "jsonschema": "1.4.1", @@ -150,7 +151,6 @@ "n8n-core": "workspace:*", "n8n-editor-ui": "workspace:*", "n8n-nodes-base": "workspace:*", - "@n8n/n8n-nodes-langchain": "workspace:*", "n8n-workflow": "workspace:*", "nanoid": "3.3.6", "nodemailer": "6.8.0", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 9f12f9da06..8d074ecdf7 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -407,8 +407,8 @@ importers: specifier: 2.4.3 version: 2.4.3 bull: - specifier: 4.10.2 - version: 4.10.2 + specifier: 4.12.1 + version: 4.12.1 cache-manager: specifier: 5.2.3 version: 5.2.3 @@ -485,8 +485,8 @@ importers: specifier: 7.3.3 version: 7.3.3 ioredis: - specifier: 5.2.4 - version: 5.2.4 + specifier: 5.3.2 + version: 5.3.2 isbot: specifier: 3.6.13 version: 3.6.13 @@ -621,7 +621,7 @@ importers: version: 0.10.0(patch_hash=sk6omkefrosihg7lmqbzh7vfxe) typeorm: specifier: 0.3.17 - version: 0.3.17(ioredis@5.2.4)(mysql2@2.3.3)(pg@8.8.0)(sqlite3@5.1.6) + version: 0.3.17(ioredis@5.3.2)(mysql2@2.3.3)(pg@8.8.0)(sqlite3@5.1.6) uuid: specifier: 8.3.2 version: 8.3.2 @@ -733,7 +733,7 @@ importers: version: 8.2.0 ioredis-mock: specifier: ^8.8.1 - version: 8.8.1(@types/ioredis-mock@8.2.2)(ioredis@5.2.4) + version: 8.8.1(@types/ioredis-mock@8.2.2)(ioredis@5.3.2) ts-essentials: specifier: ^7.0.3 version: 7.0.3(typescript@5.3.2) @@ -6099,43 +6099,43 @@ packages: dev: false optional: true - /@msgpackr-extract/msgpackr-extract-darwin-arm64@2.2.0: - resolution: {integrity: sha512-Z9LFPzfoJi4mflGWV+rv7o7ZbMU5oAU9VmzCgL240KnqDW65Y2HFCT3MW06/ITJSnbVLacmcEJA8phywK7JinQ==} + /@msgpackr-extract/msgpackr-extract-darwin-arm64@3.0.2: + resolution: {integrity: sha512-9bfjwDxIDWmmOKusUcqdS4Rw+SETlp9Dy39Xui9BEGEk19dDwH0jhipwFzEff/pFg95NKymc6TOTbRKcWeRqyQ==} cpu: [arm64] os: [darwin] dev: false optional: true - /@msgpackr-extract/msgpackr-extract-darwin-x64@2.2.0: - resolution: {integrity: sha512-vq0tT8sjZsy4JdSqmadWVw6f66UXqUCabLmUVHZwUFzMgtgoIIQjT4VVRHKvlof3P/dMCkbMJ5hB1oJ9OWHaaw==} + /@msgpackr-extract/msgpackr-extract-darwin-x64@3.0.2: + resolution: {integrity: sha512-lwriRAHm1Yg4iDf23Oxm9n/t5Zpw1lVnxYU3HnJPTi2lJRkKTrps1KVgvL6m7WvmhYVt/FIsssWay+k45QHeuw==} cpu: [x64] os: [darwin] dev: false optional: true - /@msgpackr-extract/msgpackr-extract-linux-arm64@2.2.0: - resolution: {integrity: sha512-hlxxLdRmPyq16QCutUtP8Tm6RDWcyaLsRssaHROatgnkOxdleMTgetf9JsdncL8vLh7FVy/RN9i3XR5dnb9cRA==} + /@msgpackr-extract/msgpackr-extract-linux-arm64@3.0.2: + resolution: {integrity: sha512-FU20Bo66/f7He9Fp9sP2zaJ1Q8L9uLPZQDub/WlUip78JlPeMbVL8546HbZfcW9LNciEXc8d+tThSJjSC+tmsg==} cpu: [arm64] os: [linux] dev: false optional: true - /@msgpackr-extract/msgpackr-extract-linux-arm@2.2.0: - resolution: {integrity: sha512-SaJ3Qq4lX9Syd2xEo9u3qPxi/OB+5JO/ngJKK97XDpa1C587H9EWYO6KD8995DAjSinWvdHKRrCOXVUC5fvGOg==} + /@msgpackr-extract/msgpackr-extract-linux-arm@3.0.2: + resolution: {integrity: sha512-MOI9Dlfrpi2Cuc7i5dXdxPbFIgbDBGgKR5F2yWEa6FVEtSWncfVNKW5AKjImAQ6CZlBK9tympdsZJ2xThBiWWA==} cpu: [arm] os: [linux] dev: false optional: true - /@msgpackr-extract/msgpackr-extract-linux-x64@2.2.0: - resolution: {integrity: sha512-94y5PJrSOqUNcFKmOl7z319FelCLAE0rz/jPCWS+UtdMZvpa4jrQd+cJPQCLp2Fes1yAW/YUQj/Di6YVT3c3Iw==} + /@msgpackr-extract/msgpackr-extract-linux-x64@3.0.2: + resolution: {integrity: sha512-gsWNDCklNy7Ajk0vBBf9jEx04RUxuDQfBse918Ww+Qb9HCPoGzS+XJTLe96iN3BVK7grnLiYghP/M4L8VsaHeA==} cpu: [x64] os: [linux] dev: false optional: true - /@msgpackr-extract/msgpackr-extract-win32-x64@2.2.0: - resolution: {integrity: sha512-XrC0JzsqQSvOyM3t04FMLO6z5gCuhPE6k4FXuLK5xf52ZbdvcFe1yBmo7meCew9B8G2f0T9iu9t3kfTYRYROgA==} + /@msgpackr-extract/msgpackr-extract-win32-x64@3.0.2: + resolution: {integrity: sha512-O+6Gs8UeDbyFpbSh2CPEz/UOrrdWPTBYNblZK5CxxLisYt4kGX3Sc+czffFonyjiGSq3jWLwJS/CCJc7tBr4sQ==} cpu: [x64] os: [win32] dev: false @@ -10339,7 +10339,7 @@ packages: /@types/ioredis-mock@8.2.2: resolution: {integrity: sha512-bnbPHOjxy4TUDjRh61MMoK2QvDNZqrMDXJYrEDZP/HPFvBubR24CQ0DBi5lgWhLxG4lvVsXPRDXtZ03+JgonoQ==} dependencies: - ioredis: 5.2.4 + ioredis: 5.3.2 transitivePeerDependencies: - supports-color dev: true @@ -12709,17 +12709,15 @@ packages: - supports-color dev: false - /bull@4.10.2: - resolution: {integrity: sha512-xa65xtWjQsLqYU/eNaXxq9VRG8xd6qNsQEjR7yjYuae05xKrzbVMVj2QgrYsTMmSs/vsqJjHqHSRRiW1+IkGXQ==} + /bull@4.12.1: + resolution: {integrity: sha512-ft4hTmex7WGSHt56mydw9uRKskkvgiNwqTYiV9b6q3ubhplglQmjo9OZrHlcUVNwBqSBhnzlsJQ9N/Wd7nhENA==} engines: {node: '>=12'} dependencies: - cron-parser: 4.7.0 - debuglog: 1.0.1 + cron-parser: 4.9.0 get-port: 5.1.1 - ioredis: 5.2.4 + ioredis: 5.3.2 lodash: 4.17.21 - msgpackr: 1.8.1 - p-timeout: 3.2.0 + msgpackr: 1.10.1 semver: 7.5.4 uuid: 8.3.2 transitivePeerDependencies: @@ -13226,14 +13224,9 @@ packages: readable-stream: 2.3.7 dev: true - /cluster-key-slot@1.1.1: - resolution: {integrity: sha512-rwHwUfXL40Chm1r08yrhU3qpUvdVlgkKNeyeGPOxnW8/SyVDvgRaed/Uz54AqWNaTCAThlj6QAs3TZcKI0xDEw==} - engines: {node: '>=0.10.0'} - /cluster-key-slot@1.1.2: resolution: {integrity: sha512-RMr0FhtfXemyinomL4hrWcYJxmX6deFdCxpJzhDttxgO1+bcCnkk+9drydLVDmAMG7NE6aN/fl4F7ucU/90gAA==} engines: {node: '>=0.10.0'} - dev: false /co@4.6.0: resolution: {integrity: sha512-QVb0dM5HvG+uaxitm8wONl7jltx8dqhfU33DcqtOZcLSVIKSDDLDi7+0LbAKiyI8hD9u42m2YxXSkMGWThaecQ==} @@ -13639,11 +13632,11 @@ packages: moment-timezone: 0.5.37 dev: false - /cron-parser@4.7.0: - resolution: {integrity: sha512-BdAELR+MCT2ZWsIBhZKDuUqIUCBjHHulPJnm53OfdRLA4EWBjva3R+KM5NeidJuGsNXdEcZkjC7SCnkW5rAFSA==} + /cron-parser@4.9.0: + resolution: {integrity: sha512-p0SaNjrHOnQeR8/VnfGbmg9te2kfyYSQ7Sc/j/6DtPL3JQvKxmjO9TSjNFpujqV3vEYYBvNNvXSxzyksBWAx1Q==} engines: {node: '>=12.0.0'} dependencies: - luxon: 3.3.0 + luxon: 3.4.4 dev: false /cron@1.7.2: @@ -17276,7 +17269,7 @@ packages: engines: {node: '>=0.10.0'} dev: true - /ioredis-mock@8.8.1(@types/ioredis-mock@8.2.2)(ioredis@5.2.4): + /ioredis-mock@8.8.1(@types/ioredis-mock@8.2.2)(ioredis@5.3.2): resolution: {integrity: sha512-zXSaDf86EcDFVf8jMOirWU6Js4WcwLd/cxwJiCh9EbD1GoHfeE/fVqLhLz/l1MkyL85Fb6MwfF2Fr/9819Ul9Q==} engines: {node: '>=12.22'} peerDependencies: @@ -17288,7 +17281,7 @@ packages: '@types/ioredis-mock': 8.2.2 fengari: 0.1.4 fengari-interop: 0.1.3(fengari@0.1.4) - ioredis: 5.2.4 + ioredis: 5.3.2 semver: 7.5.4 dev: true @@ -17311,12 +17304,12 @@ packages: - supports-color dev: false - /ioredis@5.2.4: - resolution: {integrity: sha512-qIpuAEt32lZJQ0XyrloCRdlEdUUNGG9i0UOk6zgzK6igyudNWqEBxfH6OlbnOOoBBvr1WB02mm8fR55CnikRng==} + /ioredis@5.3.2: + resolution: {integrity: sha512-1DKMMzlIHM02eBBVOFQ1+AolGjs6+xEcM4PDL7NqOS6szq7H9jSaEkIUH6/a5Hl241LzW6JLSiAbNvTQjUupUA==} engines: {node: '>=12.22.0'} dependencies: '@ioredis/commands': 1.2.0 - cluster-key-slot: 1.1.1 + cluster-key-slot: 1.1.2 debug: 4.3.4(supports-color@8.1.1) denque: 2.1.0 lodash.defaults: 4.2.0 @@ -20530,25 +20523,25 @@ packages: /ms@2.1.3: resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==} - /msgpackr-extract@2.2.0: - resolution: {integrity: sha512-0YcvWSv7ZOGl9Od6Y5iJ3XnPww8O7WLcpYMDwX+PAA/uXLDtyw94PJv9GLQV/nnp3cWlDhMoyKZIQLrx33sWog==} + /msgpackr-extract@3.0.2: + resolution: {integrity: sha512-SdzXp4kD/Qf8agZ9+iTu6eql0m3kWm1A2y1hkpTeVNENutaB0BwHlSvAIaMxwntmRUAUjon2V4L8Z/njd0Ct8A==} hasBin: true dependencies: - node-gyp-build-optional-packages: 5.0.3 + node-gyp-build-optional-packages: 5.0.7 optionalDependencies: - '@msgpackr-extract/msgpackr-extract-darwin-arm64': 2.2.0 - '@msgpackr-extract/msgpackr-extract-darwin-x64': 2.2.0 - '@msgpackr-extract/msgpackr-extract-linux-arm': 2.2.0 - '@msgpackr-extract/msgpackr-extract-linux-arm64': 2.2.0 - '@msgpackr-extract/msgpackr-extract-linux-x64': 2.2.0 - '@msgpackr-extract/msgpackr-extract-win32-x64': 2.2.0 + '@msgpackr-extract/msgpackr-extract-darwin-arm64': 3.0.2 + '@msgpackr-extract/msgpackr-extract-darwin-x64': 3.0.2 + '@msgpackr-extract/msgpackr-extract-linux-arm': 3.0.2 + '@msgpackr-extract/msgpackr-extract-linux-arm64': 3.0.2 + '@msgpackr-extract/msgpackr-extract-linux-x64': 3.0.2 + '@msgpackr-extract/msgpackr-extract-win32-x64': 3.0.2 dev: false optional: true - /msgpackr@1.8.1: - resolution: {integrity: sha512-05fT4J8ZqjYlR4QcRDIhLCYKUOHXk7C/xa62GzMKj74l3up9k2QZ3LgFc6qWdsPHl91QA2WLWqWc8b8t7GLNNw==} + /msgpackr@1.10.1: + resolution: {integrity: sha512-r5VRLv9qouXuLiIBrLpl2d5ZvPt8svdQTl5/vMvE4nzDMyEX4sgW5yWhuBBj5UmgwOTWj8CIdSXn5sAfsHAWIQ==} optionalDependencies: - msgpackr-extract: 2.2.0 + msgpackr-extract: 3.0.2 dev: false /mssql@8.1.4: @@ -20796,8 +20789,8 @@ packages: engines: {node: '>= 6.13.0'} dev: false - /node-gyp-build-optional-packages@5.0.3: - resolution: {integrity: sha512-k75jcVzk5wnnc/FMxsf4udAoTEUv2jY3ycfdSd3yWu6Cnd1oee6/CfZJApyscA4FJOmdoixWwiwOyf16RzD5JA==} + /node-gyp-build-optional-packages@5.0.7: + resolution: {integrity: sha512-YlCCc6Wffkx0kHkmam79GKvDQ6x+QZkMjFGrIMxgFNILFvGSbCp2fCBC55pGTT9gVaz8Na5CLmxt/urtzRv36w==} hasBin: true dev: false optional: true @@ -25640,7 +25633,7 @@ packages: dev: false patched: true - /typeorm@0.3.17(ioredis@5.2.4)(mysql2@2.3.3)(pg@8.8.0)(sqlite3@5.1.6): + /typeorm@0.3.17(ioredis@5.3.2)(mysql2@2.3.3)(pg@8.8.0)(sqlite3@5.1.6): resolution: {integrity: sha512-UDjUEwIQalO9tWw9O2A4GU+sT3oyoUXheHJy4ft+RFdnRdQctdQ34L9SqE2p7LdwzafHx1maxT+bqXON+Qnmig==} engines: {node: '>= 12.9.0'} hasBin: true @@ -25707,7 +25700,7 @@ packages: debug: 4.3.4(supports-color@8.1.1) dotenv: 16.3.1 glob: 8.1.0 - ioredis: 5.2.4 + ioredis: 5.3.2 mkdirp: 2.1.3 mysql2: 2.3.3 pg: 8.8.0 From 3912c5e7abca98fe81d4c9c25894d30d8023ce56 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, 17 Jan 2024 16:41:01 +0100 Subject: [PATCH 20/69] feat(core): Upgrade axios and follow-redirects to address CVE-2023-26159 (#8366) --- packages/@n8n/client-oauth2/package.json | 2 +- packages/cli/package.json | 2 +- packages/core/package.json | 2 +- packages/editor-ui/package.json | 2 +- pnpm-lock.yaml | 81 ++++++++++++++---------- 5 files changed, 50 insertions(+), 39 deletions(-) diff --git a/packages/@n8n/client-oauth2/package.json b/packages/@n8n/client-oauth2/package.json index 85d24f69c8..e3b3ee7657 100644 --- a/packages/@n8n/client-oauth2/package.json +++ b/packages/@n8n/client-oauth2/package.json @@ -20,6 +20,6 @@ "dist/**/*" ], "dependencies": { - "axios": "1.6.2" + "axios": "1.6.5" } } diff --git a/packages/cli/package.json b/packages/cli/package.json index 1f01f299d1..ecc6c22d98 100644 --- a/packages/cli/package.json +++ b/packages/cli/package.json @@ -109,7 +109,7 @@ "@rudderstack/rudder-sdk-node": "1.0.6", "@sentry/integrations": "7.87.0", "@sentry/node": "7.87.0", - "axios": "1.6.2", + "axios": "1.6.5", "basic-auth": "2.0.1", "bcryptjs": "2.4.3", "bull": "4.12.1", diff --git a/packages/core/package.json b/packages/core/package.json index 6a788736a5..8d8052d2f1 100644 --- a/packages/core/package.json +++ b/packages/core/package.json @@ -50,7 +50,7 @@ "dependencies": { "@n8n/client-oauth2": "workspace:*", "aws4": "1.11.0", - "axios": "1.6.2", + "axios": "1.6.5", "concat-stream": "2.0.0", "cron": "1.7.2", "fast-glob": "3.2.12", diff --git a/packages/editor-ui/package.json b/packages/editor-ui/package.json index bfcb6c4682..53d1352652 100644 --- a/packages/editor-ui/package.json +++ b/packages/editor-ui/package.json @@ -47,7 +47,7 @@ "@n8n/permissions": "workspace:*", "@vueuse/components": "^10.5.0", "@vueuse/core": "^10.5.0", - "axios": "^1.6.2", + "axios": "1.6.5", "chart.js": "^4.4.0", "codemirror-lang-html-n8n": "^1.0.0", "codemirror-lang-n8n-expression": "^0.2.0", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 8d074ecdf7..711659fa28 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -170,8 +170,8 @@ importers: packages/@n8n/client-oauth2: dependencies: axios: - specifier: 1.6.2 - version: 1.6.2(debug@3.2.7) + specifier: 1.6.5 + version: 1.6.5(debug@3.2.7) packages/@n8n/nodes-langchain: dependencies: @@ -231,7 +231,7 @@ importers: version: 1.2.0 langchain: specifier: 0.0.198 - version: 0.0.198(@aws-sdk/client-bedrock-runtime@3.454.0)(@aws-sdk/credential-provider-node@3.451.0)(@getzep/zep-js@0.9.0)(@google-ai/generativelanguage@0.2.1)(@huggingface/inference@2.6.4)(@pinecone-database/pinecone@1.1.2)(@qdrant/js-client-rest@1.7.0)(@supabase/supabase-js@2.38.5)(@xata.io/client@0.25.3)(axios@1.6.2)(cohere-ai@6.2.2)(d3-dsv@2.0.0)(epub2@3.0.1)(html-to-text@9.0.5)(lodash@4.17.21)(mammoth@1.6.0)(pdf-parse@1.1.1)(pg@8.11.3)(redis@4.6.12)(typeorm@0.3.17) + version: 0.0.198(@aws-sdk/client-bedrock-runtime@3.454.0)(@aws-sdk/credential-provider-node@3.451.0)(@getzep/zep-js@0.9.0)(@google-ai/generativelanguage@0.2.1)(@huggingface/inference@2.6.4)(@pinecone-database/pinecone@1.1.2)(@qdrant/js-client-rest@1.7.0)(@supabase/supabase-js@2.38.5)(@xata.io/client@0.25.3)(axios@1.6.5)(cohere-ai@6.2.2)(d3-dsv@2.0.0)(epub2@3.0.1)(html-to-text@9.0.5)(lodash@4.17.21)(mammoth@1.6.0)(pdf-parse@1.1.1)(pg@8.11.3)(redis@4.6.12)(typeorm@0.3.17) lodash: specifier: 4.17.21 version: 4.17.21 @@ -398,8 +398,8 @@ importers: specifier: 7.87.0 version: 7.87.0 axios: - specifier: 1.6.2 - version: 1.6.2(debug@3.2.7) + specifier: 1.6.5 + version: 1.6.5(debug@3.2.7) basic-auth: specifier: 2.0.1 version: 2.0.1 @@ -747,8 +747,8 @@ importers: specifier: 1.11.0 version: 1.11.0 axios: - specifier: 1.6.2 - version: 1.6.2(debug@3.2.7) + specifier: 1.6.5 + version: 1.6.5(debug@3.2.7) concat-stream: specifier: 2.0.0 version: 2.0.0 @@ -1049,8 +1049,8 @@ importers: specifier: ^10.5.0 version: 10.5.0(vue@3.3.4) axios: - specifier: ^1.6.2 - version: 1.6.2(debug@3.2.7) + specifier: 1.6.5 + version: 1.6.5(debug@3.2.7) chart.js: specifier: ^4.4.0 version: 4.4.0 @@ -6086,8 +6086,8 @@ packages: /@mistralai/mistralai@0.0.7: resolution: {integrity: sha512-47FiV/GBnt6gug99ZfDBcBofYuYvqT5AyhUDdtktUbCN+gq52tmiAbtwc88k7hlyUWHzJ28VpHRDfNTRfaWKxA==} dependencies: - axios: 1.6.2(debug@3.2.7) - axios-retry: 4.0.0(axios@1.6.2) + axios: 1.6.5(debug@3.2.7) + axios-retry: 4.0.0(axios@1.6.5) transitivePeerDependencies: - debug dev: false @@ -12226,29 +12226,19 @@ packages: is-retry-allowed: 2.2.0 dev: false - /axios-retry@4.0.0(axios@1.6.2): + /axios-retry@4.0.0(axios@1.6.5): resolution: {integrity: sha512-F6P4HVGITD/v4z9Lw2mIA24IabTajvpDZmKa6zq/gGwn57wN5j1P3uWrAV0+diqnW6kTM2fTqmWNfgYWGmMuiA==} peerDependencies: axios: 0.x || 1.x dependencies: - axios: 1.6.2(debug@3.2.7) + axios: 1.6.5(debug@3.2.7) is-retry-allowed: 2.2.0 dev: false /axios@0.21.4: resolution: {integrity: sha512-ut5vewkiu8jjGBdqpM44XxjuCjq9LAKeHVmoVfHVzy8eHgxxq8SbAVQNovDA8mVi05kP0Ea/n/UzcSHcTJQfNg==} dependencies: - follow-redirects: 1.15.2(debug@3.2.7) - transitivePeerDependencies: - - debug - dev: false - - /axios@1.6.2(debug@3.2.7): - resolution: {integrity: sha512-7i24Ri4pmDRfJTR7LDBhsOTtcm+9kjX5WiY1X3wIisx6G9So3pfMkEiU7emUBe46oceVImccTEM3k6C5dbVW8A==} - dependencies: - follow-redirects: 1.15.2(debug@3.2.7) - form-data: 4.0.0 - proxy-from-env: 1.1.0 + follow-redirects: 1.15.5(debug@3.2.7) transitivePeerDependencies: - debug dev: false @@ -12256,11 +12246,32 @@ packages: /axios@1.6.2(debug@4.3.4): resolution: {integrity: sha512-7i24Ri4pmDRfJTR7LDBhsOTtcm+9kjX5WiY1X3wIisx6G9So3pfMkEiU7emUBe46oceVImccTEM3k6C5dbVW8A==} dependencies: - follow-redirects: 1.15.2(debug@4.3.4) + follow-redirects: 1.15.5(debug@4.3.4) form-data: 4.0.0 proxy-from-env: 1.1.0 transitivePeerDependencies: - debug + dev: false + + /axios@1.6.5(debug@3.2.7): + resolution: {integrity: sha512-Ii012v05KEVuUoFWmMW/UQv9aRIc3ZwkWDcM+h5Il8izZCtRVpDUfwpoFf7eOtajT3QiGR4yDUx7lPqHJULgbg==} + dependencies: + follow-redirects: 1.15.5(debug@3.2.7) + form-data: 4.0.0 + proxy-from-env: 1.1.0 + transitivePeerDependencies: + - debug + dev: false + + /axios@1.6.5(debug@4.3.4): + resolution: {integrity: sha512-Ii012v05KEVuUoFWmMW/UQv9aRIc3ZwkWDcM+h5Il8izZCtRVpDUfwpoFf7eOtajT3QiGR4yDUx7lPqHJULgbg==} + dependencies: + follow-redirects: 1.15.5(debug@4.3.4) + form-data: 4.0.0 + proxy-from-env: 1.1.0 + transitivePeerDependencies: + - debug + dev: true /babel-core@7.0.0-bridge.0(@babel/core@7.22.9): resolution: {integrity: sha512-poPX9mZH/5CSanm50Q+1toVci6pv5KSRv/5TWCwtzQS5XEwn40BcCrgIeMFWP9CKKIniKXNxoIOnOq4VVlGXhg==} @@ -15996,8 +16007,8 @@ packages: resolution: {integrity: sha512-GRnmB5gPyJpAhTQdSZTSp9uaPSvl09KoYcMQtsB9rQoOmzs9dH6ffeccH+Z+cv6P68Hu5bC6JjRh4Ah/mHSNRw==} dev: false - /follow-redirects@1.15.2(debug@3.2.7): - resolution: {integrity: sha512-VQLG33o04KaQ8uYi2tVNbdrWp1QWxNNea+nmIB4EVM28v0hmP17z7aG1+wAkNzVq4KeXTq3221ye5qTJP91JwA==} + /follow-redirects@1.15.5(debug@3.2.7): + resolution: {integrity: sha512-vSFWUON1B+yAw1VN4xMfxgn5fTUiaOzAJCKBwIIgT/+7CuGy9+r+5gITvP62j3RmaD5Ph65UaERdOSRGUzZtgw==} engines: {node: '>=4.0'} peerDependencies: debug: '*' @@ -16008,8 +16019,8 @@ packages: debug: 3.2.7(supports-color@5.5.0) dev: false - /follow-redirects@1.15.2(debug@4.3.4): - resolution: {integrity: sha512-VQLG33o04KaQ8uYi2tVNbdrWp1QWxNNea+nmIB4EVM28v0hmP17z7aG1+wAkNzVq4KeXTq3221ye5qTJP91JwA==} + /follow-redirects@1.15.5(debug@4.3.4): + resolution: {integrity: sha512-vSFWUON1B+yAw1VN4xMfxgn5fTUiaOzAJCKBwIIgT/+7CuGy9+r+5gITvP62j3RmaD5Ph65UaERdOSRGUzZtgw==} engines: {node: '>=4.0'} peerDependencies: debug: '*' @@ -17197,7 +17208,7 @@ packages: /infisical-node@1.3.0: resolution: {integrity: sha512-tTnnExRAO/ZyqiRdnSlBisErNToYWgtunMWh+8opClEt5qjX7l6HC/b4oGo2AuR2Pf41IR+oqo+dzkM1TCvlUA==} dependencies: - axios: 1.6.2(debug@3.2.7) + axios: 1.6.5(debug@3.2.7) dotenv: 16.3.1 tweetnacl: 1.0.3 tweetnacl-util: 0.15.1 @@ -18925,7 +18936,7 @@ packages: resolution: {integrity: sha512-Xq9nH7KlWZmXAtodXDDRE7vs6DU1gTU8zYDHDiWLSip45Egwq3plLHzPn27NgvzL2r1LMPC1vdqh98sQxtqj4A==} dev: false - /langchain@0.0.198(@aws-sdk/client-bedrock-runtime@3.454.0)(@aws-sdk/credential-provider-node@3.451.0)(@getzep/zep-js@0.9.0)(@google-ai/generativelanguage@0.2.1)(@huggingface/inference@2.6.4)(@pinecone-database/pinecone@1.1.2)(@qdrant/js-client-rest@1.7.0)(@supabase/supabase-js@2.38.5)(@xata.io/client@0.25.3)(axios@1.6.2)(cohere-ai@6.2.2)(d3-dsv@2.0.0)(epub2@3.0.1)(html-to-text@9.0.5)(lodash@4.17.21)(mammoth@1.6.0)(pdf-parse@1.1.1)(pg@8.11.3)(redis@4.6.12)(typeorm@0.3.17): + /langchain@0.0.198(@aws-sdk/client-bedrock-runtime@3.454.0)(@aws-sdk/credential-provider-node@3.451.0)(@getzep/zep-js@0.9.0)(@google-ai/generativelanguage@0.2.1)(@huggingface/inference@2.6.4)(@pinecone-database/pinecone@1.1.2)(@qdrant/js-client-rest@1.7.0)(@supabase/supabase-js@2.38.5)(@xata.io/client@0.25.3)(axios@1.6.5)(cohere-ai@6.2.2)(d3-dsv@2.0.0)(epub2@3.0.1)(html-to-text@9.0.5)(lodash@4.17.21)(mammoth@1.6.0)(pdf-parse@1.1.1)(pg@8.11.3)(redis@4.6.12)(typeorm@0.3.17): resolution: {integrity: sha512-YC0O1g8r61InCWyF5NmiQjdghdq6LKcgMrDZtqLbgDxAe4RoSldonm+5oNXS3yjCISG0j3s5Cty+yB7klqvUpg==} engines: {node: '>=18'} peerDependencies: @@ -19242,7 +19253,7 @@ packages: '@qdrant/js-client-rest': 1.7.0(typescript@5.3.2) '@supabase/supabase-js': 2.38.5 '@xata.io/client': 0.25.3(typescript@5.3.2) - axios: 1.6.2(debug@3.2.7) + axios: 1.6.5(debug@3.2.7) binary-extensions: 2.2.0 cohere-ai: 6.2.2 d3-dsv: 2.0.0 @@ -22416,7 +22427,7 @@ packages: resolution: {integrity: sha512-ofNX3TPfZPlWErVc2EDk66cIrfp9EXeKBsXFxf8ISXK57b10ANwRnKAlf5rQjxjRKqcUWmV0d3ZfOeVeYracMw==} engines: {node: '>=15.0.0'} dependencies: - axios: 1.6.2(debug@3.2.7) + axios: 1.6.5(debug@3.2.7) rusha: 0.8.14 transitivePeerDependencies: - debug @@ -24127,7 +24138,7 @@ packages: asn1.js: 5.4.1 asn1.js-rfc2560: 5.0.1(asn1.js@5.4.1) asn1.js-rfc5280: 3.0.0 - axios: 1.6.2(debug@3.2.7) + axios: 1.6.5(debug@3.2.7) big-integer: 1.6.51 bignumber.js: 9.1.2 binascii: 0.0.2 @@ -26684,7 +26695,7 @@ packages: engines: {node: '>=12.0.0'} hasBin: true dependencies: - axios: 1.6.2(debug@4.3.4) + axios: 1.6.5(debug@4.3.4) joi: 17.11.0 lodash: 4.17.21 minimist: 1.2.8 From 99457019f795636f56d80d3fc2c7e08055ace938 Mon Sep 17 00:00:00 2001 From: Tomi Turtiainen <10324676+tomi@users.noreply.github.com> Date: Wed, 17 Jan 2024 19:07:34 +0200 Subject: [PATCH 21/69] feat: Nudge users to become template creators if eligible (#8357) --- .../composables/becomeTemplateCreatorCta.ts | 18 ++++ cypress/e2e/37-become-creator-cta.cy.ts | 32 +++++++ packages/cli/src/Server.ts | 5 + .../cli/src/controllers/cta.controller.ts | 21 +++++ .../workflowStatistics.repository.ts | 24 ++++- packages/cli/src/services/cta.service.ts | 18 ++++ .../cli/test/integration/cta.service.test.ts | 54 +++++++++++ .../shared/db/workflowStatistics.ts | 21 +++++ packages/editor-ui/src/api/ctas.ts | 8 ++ .../BecomeTemplateCreatorCta.vue | 78 +++++++++++++++ .../becomeTemplateCreatorStore.ts | 94 +++++++++++++++++++ .../editor-ui/src/components/MainSidebar.vue | 8 ++ packages/editor-ui/src/constants.ts | 1 + .../src/plugins/i18n/locales/en.json | 6 +- 14 files changed, 385 insertions(+), 3 deletions(-) create mode 100644 cypress/composables/becomeTemplateCreatorCta.ts create mode 100644 cypress/e2e/37-become-creator-cta.cy.ts create mode 100644 packages/cli/src/controllers/cta.controller.ts create mode 100644 packages/cli/src/services/cta.service.ts create mode 100644 packages/cli/test/integration/cta.service.test.ts create mode 100644 packages/cli/test/integration/shared/db/workflowStatistics.ts create mode 100644 packages/editor-ui/src/api/ctas.ts create mode 100644 packages/editor-ui/src/components/BecomeTemplateCreatorCta/BecomeTemplateCreatorCta.vue create mode 100644 packages/editor-ui/src/components/BecomeTemplateCreatorCta/becomeTemplateCreatorStore.ts diff --git a/cypress/composables/becomeTemplateCreatorCta.ts b/cypress/composables/becomeTemplateCreatorCta.ts new file mode 100644 index 0000000000..55fc985c74 --- /dev/null +++ b/cypress/composables/becomeTemplateCreatorCta.ts @@ -0,0 +1,18 @@ +//#region Getters + +export const getBecomeTemplateCreatorCta = () => cy.getByTestId('become-template-creator-cta'); + +export const getCloseBecomeTemplateCreatorCtaButton = () => + cy.getByTestId('close-become-template-creator-cta'); + +//#endregion + +//#region Actions + +export const interceptCtaRequestWithResponse = (becomeCreator: boolean) => { + return cy.intercept('GET', `/rest/cta/become-creator`, { + body: becomeCreator, + }); +}; + +//#endregion diff --git a/cypress/e2e/37-become-creator-cta.cy.ts b/cypress/e2e/37-become-creator-cta.cy.ts new file mode 100644 index 0000000000..931208e5f3 --- /dev/null +++ b/cypress/e2e/37-become-creator-cta.cy.ts @@ -0,0 +1,32 @@ +import { + getBecomeTemplateCreatorCta, + getCloseBecomeTemplateCreatorCtaButton, + interceptCtaRequestWithResponse, +} from '../composables/becomeTemplateCreatorCta'; +import { WorkflowsPage as WorkflowsPageClass } from '../pages/workflows'; + +const WorkflowsPage = new WorkflowsPageClass(); + +describe('Become creator CTA', () => { + it('should not show the CTA if user is not eligible', () => { + interceptCtaRequestWithResponse(false).as('cta'); + cy.visit(WorkflowsPage.url); + + cy.wait('@cta'); + + getBecomeTemplateCreatorCta().should('not.exist'); + }); + + it('should show the CTA if the user is eligible', () => { + interceptCtaRequestWithResponse(true).as('cta'); + cy.visit(WorkflowsPage.url); + + cy.wait('@cta'); + + getBecomeTemplateCreatorCta().should('be.visible'); + + getCloseBecomeTemplateCreatorCtaButton().click(); + + getBecomeTemplateCreatorCta().should('not.exist'); + }); +}); diff --git a/packages/cli/src/Server.ts b/packages/cli/src/Server.ts index 9ddaab99e6..43c7e60a8a 100644 --- a/packages/cli/src/Server.ts +++ b/packages/cli/src/Server.ts @@ -280,6 +280,11 @@ export class Server extends AbstractServer { controllers.push(MFAController); } + if (!config.getEnv('endpoints.disableUi')) { + const { CtaController } = await import('@/controllers/cta.controller'); + controllers.push(CtaController); + } + controllers.forEach((controller) => registerController(app, controller)); } diff --git a/packages/cli/src/controllers/cta.controller.ts b/packages/cli/src/controllers/cta.controller.ts new file mode 100644 index 0000000000..9d06ff0812 --- /dev/null +++ b/packages/cli/src/controllers/cta.controller.ts @@ -0,0 +1,21 @@ +import express from 'express'; +import { Authorized, Get, RestController } from '@/decorators'; +import { AuthenticatedRequest } from '@/requests'; +import { CtaService } from '@/services/cta.service'; + +/** + * Controller for Call to Action (CTA) endpoints. CTAs are certain + * messages that are shown to users in the UI. + */ +@Authorized() +@RestController('/cta') +export class CtaController { + constructor(private readonly ctaService: CtaService) {} + + @Get('/become-creator') + async getCta(req: AuthenticatedRequest, res: express.Response) { + const becomeCreator = await this.ctaService.getBecomeCreatorCta(req.user.id); + + res.json(becomeCreator); + } +} diff --git a/packages/cli/src/databases/repositories/workflowStatistics.repository.ts b/packages/cli/src/databases/repositories/workflowStatistics.repository.ts index 5d6a9261da..b08a175b42 100644 --- a/packages/cli/src/databases/repositories/workflowStatistics.repository.ts +++ b/packages/cli/src/databases/repositories/workflowStatistics.repository.ts @@ -1,8 +1,11 @@ import { Service } from 'typedi'; import { DataSource, QueryFailedError, Repository } from 'typeorm'; import config from '@/config'; -import type { StatisticsNames } from '../entities/WorkflowStatistics'; -import { WorkflowStatistics } from '../entities/WorkflowStatistics'; +import { StatisticsNames, WorkflowStatistics } from '../entities/WorkflowStatistics'; +import type { User } from '@/databases/entities/User'; +import { WorkflowEntity } from '@/databases/entities/WorkflowEntity'; +import { SharedWorkflow } from '@/databases/entities/SharedWorkflow'; +import { Role } from '@/databases/entities/Role'; type StatisticsInsertResult = 'insert' | 'failed' | 'alreadyExists'; type StatisticsUpsertResult = StatisticsInsertResult | 'update'; @@ -98,4 +101,21 @@ export class WorkflowStatisticsRepository extends Repository throw error; } } + + async queryNumWorkflowsUserHasWithFiveOrMoreProdExecs(userId: User['id']): Promise { + return await this.createQueryBuilder('workflow_statistics') + .innerJoin(WorkflowEntity, 'workflow', 'workflow.id = workflow_statistics.workflowId') + .innerJoin( + SharedWorkflow, + 'shared_workflow', + 'shared_workflow.workflowId = workflow_statistics.workflowId', + ) + .innerJoin(Role, 'role', 'role.id = shared_workflow.roleId') + .where('shared_workflow.userId = :userId', { userId }) + .andWhere('workflow.active = :isActive', { isActive: true }) + .andWhere('workflow_statistics.name = :name', { name: StatisticsNames.productionSuccess }) + .andWhere('workflow_statistics.count >= 5') + .andWhere('role.name = :roleName', { roleName: 'owner' }) + .getCount(); + } } diff --git a/packages/cli/src/services/cta.service.ts b/packages/cli/src/services/cta.service.ts new file mode 100644 index 0000000000..e5c08a437b --- /dev/null +++ b/packages/cli/src/services/cta.service.ts @@ -0,0 +1,18 @@ +import { Service } from 'typedi'; +import { WorkflowStatisticsRepository } from '@/databases/repositories/workflowStatistics.repository'; +import type { User } from '@/databases/entities/User'; + +@Service() +export class CtaService { + constructor(private readonly workflowStatisticsRepository: WorkflowStatisticsRepository) {} + + async getBecomeCreatorCta(userId: User['id']) { + // There need to be at least 3 workflows with at least 5 executions + const numWfsWithOver5ProdExecutions = + await this.workflowStatisticsRepository.queryNumWorkflowsUserHasWithFiveOrMoreProdExecs( + userId, + ); + + return numWfsWithOver5ProdExecutions >= 3; + } +} diff --git a/packages/cli/test/integration/cta.service.test.ts b/packages/cli/test/integration/cta.service.test.ts new file mode 100644 index 0000000000..27cdc7b604 --- /dev/null +++ b/packages/cli/test/integration/cta.service.test.ts @@ -0,0 +1,54 @@ +import Container from 'typedi'; +import * as testDb from './shared/testDb'; +import { CtaService } from '@/services/cta.service'; +import { createUser } from './shared/db/users'; +import { createManyWorkflows } from './shared/db/workflows'; +import type { User } from '@/databases/entities/User'; +import { createWorkflowStatisticsItem } from './shared/db/workflowStatistics'; +import { StatisticsNames } from '@/databases/entities/WorkflowStatistics'; + +describe('CtaService', () => { + let ctaService: CtaService; + let user: User; + + beforeAll(async () => { + await testDb.init(); + + ctaService = Container.get(CtaService); + user = await createUser(); + }); + + afterAll(async () => { + await testDb.terminate(); + }); + + describe('getBecomeCreatorCta()', () => { + afterEach(async () => { + await testDb.truncate(['Workflow', 'SharedWorkflow']); + }); + + test.each([ + [false, 0, 0], + [false, 2, 5], + [false, 3, 4], + [true, 3, 5], + ])( + 'should return %p if user has %d active workflows with %d successful production executions', + async (expected, numWorkflows, numExecutions) => { + const workflows = await createManyWorkflows(numWorkflows, { active: true }, user); + + await Promise.all( + workflows.map( + async (workflow) => + await createWorkflowStatisticsItem(workflow.id, { + count: numExecutions, + name: StatisticsNames.productionSuccess, + }), + ), + ); + + expect(await ctaService.getBecomeCreatorCta(user.id)).toBe(expected); + }, + ); + }); +}); diff --git a/packages/cli/test/integration/shared/db/workflowStatistics.ts b/packages/cli/test/integration/shared/db/workflowStatistics.ts new file mode 100644 index 0000000000..e690cb726a --- /dev/null +++ b/packages/cli/test/integration/shared/db/workflowStatistics.ts @@ -0,0 +1,21 @@ +import Container from 'typedi'; +import { StatisticsNames, type WorkflowStatistics } from '@/databases/entities/WorkflowStatistics'; +import type { Workflow } from 'n8n-workflow'; +import { WorkflowStatisticsRepository } from '@/databases/repositories/workflowStatistics.repository'; + +export async function createWorkflowStatisticsItem( + workflowId: Workflow['id'], + data?: Partial, +) { + const entity = Container.get(WorkflowStatisticsRepository).create({ + count: 0, + latestEvent: new Date().toISOString(), + name: StatisticsNames.manualSuccess, + ...(data ?? {}), + workflowId, + }); + + await Container.get(WorkflowStatisticsRepository).insert(entity); + + return entity; +} diff --git a/packages/editor-ui/src/api/ctas.ts b/packages/editor-ui/src/api/ctas.ts new file mode 100644 index 0000000000..88a0e7da71 --- /dev/null +++ b/packages/editor-ui/src/api/ctas.ts @@ -0,0 +1,8 @@ +import type { IRestApiContext } from '@/Interface'; +import { get } from '@/utils/apiUtils'; + +export async function getBecomeCreatorCta(context: IRestApiContext): Promise { + const response = await get(context.baseUrl, '/cta/become-creator'); + + return response; +} diff --git a/packages/editor-ui/src/components/BecomeTemplateCreatorCta/BecomeTemplateCreatorCta.vue b/packages/editor-ui/src/components/BecomeTemplateCreatorCta/BecomeTemplateCreatorCta.vue new file mode 100644 index 0000000000..dc9958f4d6 --- /dev/null +++ b/packages/editor-ui/src/components/BecomeTemplateCreatorCta/BecomeTemplateCreatorCta.vue @@ -0,0 +1,78 @@ + + + + + diff --git a/packages/editor-ui/src/components/BecomeTemplateCreatorCta/becomeTemplateCreatorStore.ts b/packages/editor-ui/src/components/BecomeTemplateCreatorCta/becomeTemplateCreatorStore.ts new file mode 100644 index 0000000000..83785e0fae --- /dev/null +++ b/packages/editor-ui/src/components/BecomeTemplateCreatorCta/becomeTemplateCreatorStore.ts @@ -0,0 +1,94 @@ +import { DateTime } from 'luxon'; +import { defineStore } from 'pinia'; +import { computed, ref } from 'vue'; +import { STORES } from '@/constants'; +import { useCloudPlanStore } from '@/stores/cloudPlan.store'; +import { useStorage } from '@/composables/useStorage'; +import { useRootStore } from '@/stores/n8nRoot.store'; +import { getBecomeCreatorCta } from '@/api/ctas'; + +const LOCAL_STORAGE_KEY = 'N8N_BECOME_TEMPLATE_CREATOR_CTA_DISMISSED_AT'; +const RESHOW_DISMISSED_AFTER_DAYS = 30; +const POLL_INTERVAL_IN_MS = 15 * 60 * 1000; // 15 minutes + +export const useBecomeTemplateCreatorStore = defineStore(STORES.BECOME_TEMPLATE_CREATOR, () => { + const cloudPlanStore = useCloudPlanStore(); + const rootStore = useRootStore(); + + //#region State + + const dismissedAt = useStorage(LOCAL_STORAGE_KEY); + const ctaMeetsCriteria = ref(false); + const monitorCtasTimer = ref | null>(null); + + //#endregion State + + //#region Computed + + const isDismissed = computed(() => { + return dismissedAt.value ? !hasEnoughTimePassedSinceDismissal(dismissedAt.value) : false; + }); + + const showBecomeCreatorCta = computed(() => { + return ctaMeetsCriteria.value && !cloudPlanStore.userIsTrialing && !isDismissed.value; + }); + + //#endregion Computed + + //#region Actions + + const dismissCta = () => { + dismissedAt.value = DateTime.now().toISO(); + }; + + const fetchBecomeCreatorCta = async () => { + const becomeCreatorCta = await getBecomeCreatorCta(rootStore.getRestApiContext); + + ctaMeetsCriteria.value = becomeCreatorCta; + }; + + const fetchUserCtasIfNeeded = async () => { + if (isDismissed.value || cloudPlanStore.userIsTrialing || ctaMeetsCriteria.value) { + return; + } + + await fetchBecomeCreatorCta(); + }; + + const startMonitoringCta = () => { + if (monitorCtasTimer.value) { + return; + } + + // Initial check after 1s so we don't bombard the API immediately during startup + setTimeout(fetchUserCtasIfNeeded, 1000); + + monitorCtasTimer.value = setInterval(fetchUserCtasIfNeeded, POLL_INTERVAL_IN_MS); + }; + + const stopMonitoringCta = () => { + if (!monitorCtasTimer.value) { + return; + } + + clearInterval(monitorCtasTimer.value); + monitorCtasTimer.value = null; + }; + + //#endregion Actions + + return { + showBecomeCreatorCta, + dismissCta, + startMonitoringCta, + stopMonitoringCta, + }; +}); + +function hasEnoughTimePassedSinceDismissal(dismissedAt: string) { + const reshowAtTime = DateTime.fromISO(dismissedAt).plus({ + days: RESHOW_DISMISSED_AFTER_DAYS, + }); + + return reshowAtTime <= DateTime.now(); +} diff --git a/packages/editor-ui/src/components/MainSidebar.vue b/packages/editor-ui/src/components/MainSidebar.vue index 61f8003f15..c183635e60 100644 --- a/packages/editor-ui/src/components/MainSidebar.vue +++ b/packages/editor-ui/src/components/MainSidebar.vue @@ -23,6 +23,7 @@ @@ -81,7 +83,9 @@ import N8nTooltip from '../N8nTooltip'; import N8nIcon from '../N8nIcon'; import type { PropType } from 'vue'; import { defineComponent } from 'vue'; +import ConditionalRouterLink from '../ConditionalRouterLink'; import type { IMenuItem, RouteObject } from '../../types'; +import { doesMenuItemMatchCurrentRoute } from './routerUtil'; export default defineComponent({ name: 'N8nMenuItem', @@ -90,6 +94,7 @@ export default defineComponent({ ElMenuItem, N8nIcon, N8nTooltip, + ConditionalRouterLink, }, props: { item: { @@ -115,9 +120,11 @@ export default defineComponent({ }, activeTab: { type: String, + default: undefined, }, handleSelect: { type: Function as PropType<(item: IMenuItem) => void>, + default: undefined, }, }, computed: { @@ -151,18 +158,7 @@ export default defineComponent({ }, isActive(item: IMenuItem): boolean { if (this.mode === 'router') { - if (item.activateOnRoutePaths) { - return ( - Array.isArray(item.activateOnRoutePaths) && - item.activateOnRoutePaths.includes(this.currentRoute.path) - ); - } else if (item.activateOnRouteNames) { - return ( - Array.isArray(item.activateOnRouteNames) && - item.activateOnRouteNames.includes(this.currentRoute.name || '') - ); - } - return false; + return doesMenuItemMatchCurrentRoute(item, this.currentRoute); } else { return item.id === this.activeTab; } diff --git a/packages/design-system/src/components/N8nMenuItem/routerUtil.ts b/packages/design-system/src/components/N8nMenuItem/routerUtil.ts new file mode 100644 index 0000000000..64627db787 --- /dev/null +++ b/packages/design-system/src/components/N8nMenuItem/routerUtil.ts @@ -0,0 +1,42 @@ +import type { IMenuItem, RouteObject } from '@/types'; +import type { RouteLocationRaw } from 'vue-router'; + +/** + * Checks if the given menu item matches the current route. + */ +export function doesMenuItemMatchCurrentRoute(item: IMenuItem, currentRoute: RouteObject) { + let activateOnRouteNames: string[] = []; + if (Array.isArray(item.activateOnRouteNames)) { + activateOnRouteNames = item.activateOnRouteNames; + } else if (item.route && isNamedRouteLocation(item.route.to)) { + activateOnRouteNames = [item.route.to.name]; + } + + let activateOnRoutePaths: string[] = []; + if (Array.isArray(item.activateOnRoutePaths)) { + activateOnRoutePaths = item.activateOnRoutePaths; + } else if (item.route && isPathRouteLocation(item.route.to)) { + activateOnRoutePaths = [item.route.to.path]; + } + + return ( + activateOnRouteNames.includes(currentRoute.name ?? '') || + activateOnRoutePaths.includes(currentRoute.path) + ); +} + +function isPathRouteLocation(routeLocation?: RouteLocationRaw): routeLocation is { path: string } { + return ( + typeof routeLocation === 'object' && + 'path' in routeLocation && + typeof routeLocation.path === 'string' + ); +} + +function isNamedRouteLocation(routeLocation?: RouteLocationRaw): routeLocation is { name: string } { + return ( + typeof routeLocation === 'object' && + 'name' in routeLocation && + typeof routeLocation.name === 'string' + ); +} diff --git a/packages/design-system/src/types/menu.ts b/packages/design-system/src/types/menu.ts index c97bfdd1f2..4a0a853b02 100644 --- a/packages/design-system/src/types/menu.ts +++ b/packages/design-system/src/types/menu.ts @@ -1,4 +1,6 @@ import type { ElTooltipProps } from 'element-plus'; +import type { AnchorHTMLAttributes } from 'vue'; +import type { RouteLocationRaw, RouterLinkProps } from 'vue-router'; export type IMenuItem = { id: string; @@ -7,22 +9,32 @@ export type IMenuItem = { secondaryIcon?: { name: string; size?: 'xsmall' | 'small' | 'medium' | 'large' | 'xlarge'; - tooltip?: ElTooltipProps; + tooltip?: Partial; }; customIconSize?: 'medium' | 'small'; available?: boolean; position?: 'top' | 'bottom'; - type?: 'default' | 'link'; - properties?: ILinkMenuItemProperties; - // For router menus populate only one of those arrays: - // If menu item can be activated on certain route names (easy mode) + + /** Use this for external links */ + link?: ILinkMenuItemProperties; + /** Use this for defining a vue-router target */ + route?: RouterLinkProps; + /** + * If given, item will be activated on these route names. Note that if + * route is provided, it will be highlighted automatically + */ activateOnRouteNames?: string[]; - // For more specific matching, we can use paths activateOnRoutePaths?: string[]; + children?: IMenuItem[]; }; +export type IRouteMenuItemProperties = { + route: RouteLocationRaw; +}; + export type ILinkMenuItemProperties = { href: string; - newWindow?: boolean; + target?: AnchorHTMLAttributes['target']; + rel?: AnchorHTMLAttributes['rel']; }; diff --git a/packages/editor-ui/src/Interface.ts b/packages/editor-ui/src/Interface.ts index 61fd657531..3861dbfce3 100644 --- a/packages/editor-ui/src/Interface.ts +++ b/packages/editor-ui/src/Interface.ts @@ -1270,7 +1270,6 @@ export interface UIState { nodeViewOffsetPosition: XYPosition; nodeViewMoveInProgress: boolean; selectedNodes: INodeUi[]; - sidebarMenuItems: IMenuItem[]; nodeViewInitialized: boolean; addFirstStepOnLoad: boolean; executionSidebarAutoRefresh: boolean; diff --git a/packages/editor-ui/src/components/MainSidebar.vue b/packages/editor-ui/src/components/MainSidebar.vue index c183635e60..78be40f2ad 100644 --- a/packages/editor-ui/src/components/MainSidebar.vue +++ b/packages/editor-ui/src/components/MainSidebar.vue @@ -118,7 +118,6 @@ import { useUIStore } from '@/stores/ui.store'; import { useUsersStore } from '@/stores/users.store'; import { useVersionsStore } from '@/stores/versions.store'; import { useWorkflowsStore } from '@/stores/workflows.store'; -import { isNavigationFailure } from 'vue-router'; import ExecutionsUsage from '@/components/ExecutionsUsage.vue'; import BecomeTemplateCreatorCta from '@/components/BecomeTemplateCreatorCta/BecomeTemplateCreatorCta.vue'; import MainSidebarSourceControl from '@/components/MainSidebarSourceControl.vue'; @@ -205,38 +204,24 @@ export default defineComponent({ }, mainMenuItems(): IMenuItem[] { const items: IMenuItem[] = []; - const injectedItems = this.uiStore.sidebarMenuItems; const workflows: IMenuItem = { id: 'workflows', icon: 'network-wired', label: this.$locale.baseText('mainSidebar.workflows'), position: 'top', - activateOnRouteNames: [VIEWS.WORKFLOWS], + route: { to: { name: VIEWS.WORKFLOWS } }, + secondaryIcon: this.sourceControlStore.preferences.branchReadOnly + ? { + name: 'lock', + tooltip: { + content: this.$locale.baseText('mainSidebar.workflows.readOnlyEnv.tooltip'), + }, + } + : undefined, }; - if (this.sourceControlStore.preferences.branchReadOnly) { - workflows.secondaryIcon = { - name: 'lock', - tooltip: { - content: this.$locale.baseText('mainSidebar.workflows.readOnlyEnv.tooltip'), - }, - }; - } - - if (injectedItems && injectedItems.length > 0) { - for (const item of injectedItems) { - items.push({ - id: item.id, - icon: item.icon || '', - label: item.label || '', - position: item.position, - type: item.properties?.href ? 'link' : 'regular', - properties: item.properties, - } as IMenuItem); - } - } - + const defaultSettingsRoute = this.findFirstAccessibleSettingsRoute(); const regularItems: IMenuItem[] = [ workflows, { @@ -245,7 +230,7 @@ export default defineComponent({ label: this.$locale.baseText('mainSidebar.templates'), position: 'top', available: this.settingsStore.isTemplatesEnabled, - activateOnRouteNames: [VIEWS.TEMPLATES], + route: { to: { name: VIEWS.TEMPLATES } }, }, { id: 'credentials', @@ -253,7 +238,7 @@ export default defineComponent({ label: this.$locale.baseText('mainSidebar.credentials'), customIconSize: 'medium', position: 'top', - activateOnRouteNames: [VIEWS.CREDENTIALS], + route: { to: { name: VIEWS.CREDENTIALS } }, }, { id: 'variables', @@ -261,18 +246,17 @@ export default defineComponent({ label: this.$locale.baseText('mainSidebar.variables'), customIconSize: 'medium', position: 'top', - activateOnRouteNames: [VIEWS.VARIABLES], + route: { to: { name: VIEWS.VARIABLES } }, }, { id: 'executions', icon: 'tasks', label: this.$locale.baseText('mainSidebar.executions'), position: 'top', - activateOnRouteNames: [VIEWS.EXECUTIONS], + route: { to: { name: VIEWS.EXECUTIONS } }, }, { id: 'cloud-admin', - type: 'link', position: 'bottom', label: 'Admin Panel', icon: 'home', @@ -285,6 +269,7 @@ export default defineComponent({ position: 'bottom', available: this.canUserAccessSettings && this.usersStore.currentUser !== null, activateOnRouteNames: [VIEWS.USERS_SETTINGS, VIEWS.API_SETTINGS, VIEWS.PERSONAL_SETTINGS], + route: { to: defaultSettingsRoute }, }, { id: 'help', @@ -296,40 +281,36 @@ export default defineComponent({ id: 'quickstart', icon: 'video', label: this.$locale.baseText('mainSidebar.helpMenuItems.quickstart'), - type: 'link', - properties: { + link: { href: 'https://www.youtube.com/watch?v=1MwSoB0gnM4', - newWindow: true, + target: '_blank', }, }, { id: 'docs', icon: 'book', label: this.$locale.baseText('mainSidebar.helpMenuItems.documentation'), - type: 'link', - properties: { + link: { href: 'https://docs.n8n.io?utm_source=n8n_app&utm_medium=app_sidebar', - newWindow: true, + target: '_blank', }, }, { id: 'forum', icon: 'users', label: this.$locale.baseText('mainSidebar.helpMenuItems.forum'), - type: 'link', - properties: { + link: { href: 'https://community.n8n.io?utm_source=n8n_app&utm_medium=app_sidebar', - newWindow: true, + target: '_blank', }, }, { id: 'examples', icon: 'graduation-cap', label: this.$locale.baseText('mainSidebar.helpMenuItems.course'), - type: 'link', - properties: { + link: { href: 'https://www.youtube.com/watch?v=1MwSoB0gnM4', - newWindow: true, + target: '_blank', }, }, { @@ -421,46 +402,6 @@ export default defineComponent({ }, async handleSelect(key: string) { switch (key) { - case 'workflows': { - if (this.$router.currentRoute.value.name !== VIEWS.WORKFLOWS) { - this.goToRoute({ name: VIEWS.WORKFLOWS }); - } - break; - } - case 'templates': { - if (this.$router.currentRoute.value.name !== VIEWS.TEMPLATES) { - this.goToRoute({ name: VIEWS.TEMPLATES }); - } - break; - } - case 'credentials': { - if (this.$router.currentRoute.value.name !== VIEWS.CREDENTIALS) { - this.goToRoute({ name: VIEWS.CREDENTIALS }); - } - break; - } - case 'variables': { - if (this.$router.currentRoute.value.name !== VIEWS.VARIABLES) { - this.goToRoute({ name: VIEWS.VARIABLES }); - } - break; - } - case 'executions': { - if (this.$router.currentRoute.value.name !== VIEWS.EXECUTIONS) { - this.goToRoute({ name: VIEWS.EXECUTIONS }); - } - break; - } - case 'settings': { - const defaultRoute = this.findFirstAccessibleSettingsRoute(); - if (defaultRoute) { - const route = this.$router.resolve({ name: defaultRoute }); - if (this.$router.currentRoute.value.name !== defaultRoute) { - this.goToRoute(route.path); - } - } - break; - } case 'about': { this.trackHelpItemClick('about'); this.uiStore.openModal(ABOUT_MODAL_KEY); @@ -481,25 +422,18 @@ export default defineComponent({ break; } }, - goToRoute(route: string | { name: string }) { - this.$router.push(route).catch((failure) => { - console.log(failure); - // Catch navigation failures caused by route guards - if (!isNavigationFailure(failure)) { - console.error(failure); - } - }); - }, findFirstAccessibleSettingsRoute() { const settingsRoutes = this.$router .getRoutes() .find((route) => route.path === '/settings')! - .children.map((route) => route.name || ''); + .children.map((route) => route.name ?? ''); - let defaultSettingsRoute = null; + let defaultSettingsRoute = { name: VIEWS.USERS_SETTINGS }; for (const route of settingsRoutes) { if (this.canUserAccessRouteByName(route.toString())) { - defaultSettingsRoute = route; + defaultSettingsRoute = { + name: route.toString() as VIEWS, + }; break; } } diff --git a/packages/editor-ui/src/components/SettingsSidebar.vue b/packages/editor-ui/src/components/SettingsSidebar.vue index 84821d425d..6a7c1e03c3 100644 --- a/packages/editor-ui/src/components/SettingsSidebar.vue +++ b/packages/editor-ui/src/components/SettingsSidebar.vue @@ -49,7 +49,7 @@ export default defineComponent({ label: this.$locale.baseText('settings.usageAndPlan.title'), position: 'top', available: this.canAccessUsageAndPlan(), - activateOnRouteNames: [VIEWS.USAGE], + route: { to: { name: VIEWS.USAGE } }, }, { id: 'settings-personal', @@ -57,7 +57,7 @@ export default defineComponent({ label: this.$locale.baseText('settings.personal'), position: 'top', available: this.canAccessPersonalSettings(), - activateOnRouteNames: [VIEWS.PERSONAL_SETTINGS], + route: { to: { name: VIEWS.PERSONAL_SETTINGS } }, }, { id: 'settings-users', @@ -65,7 +65,7 @@ export default defineComponent({ label: this.$locale.baseText('settings.users'), position: 'top', available: this.canAccessUsersSettings(), - activateOnRouteNames: [VIEWS.USERS_SETTINGS], + route: { to: { name: VIEWS.USERS_SETTINGS } }, }, { id: 'settings-api', @@ -73,7 +73,7 @@ export default defineComponent({ label: this.$locale.baseText('settings.n8napi'), position: 'top', available: this.canAccessApiSettings(), - activateOnRouteNames: [VIEWS.API_SETTINGS], + route: { to: { name: VIEWS.API_SETTINGS } }, }, { id: 'settings-external-secrets', @@ -81,10 +81,7 @@ export default defineComponent({ label: this.$locale.baseText('settings.externalSecrets.title'), position: 'top', available: this.canAccessExternalSecrets(), - activateOnRouteNames: [ - VIEWS.EXTERNAL_SECRETS_SETTINGS, - VIEWS.EXTERNAL_SECRETS_PROVIDER_SETTINGS, - ], + route: { to: { name: VIEWS.EXTERNAL_SECRETS_SETTINGS } }, }, { id: 'settings-audit-logs', @@ -92,7 +89,7 @@ export default defineComponent({ label: this.$locale.baseText('settings.auditLogs.title'), position: 'top', available: this.canAccessAuditLogs(), - activateOnRouteNames: [VIEWS.AUDIT_LOGS], + route: { to: { name: VIEWS.AUDIT_LOGS } }, }, { id: 'settings-source-control', @@ -100,7 +97,7 @@ export default defineComponent({ label: this.$locale.baseText('settings.sourceControl.title'), position: 'top', available: this.canAccessSourceControl(), - activateOnRouteNames: [VIEWS.SOURCE_CONTROL], + route: { to: { name: VIEWS.SOURCE_CONTROL } }, }, { id: 'settings-sso', @@ -108,7 +105,7 @@ export default defineComponent({ label: this.$locale.baseText('settings.sso'), position: 'top', available: this.canAccessSso(), - activateOnRouteNames: [VIEWS.SSO_SETTINGS], + route: { to: { name: VIEWS.SSO_SETTINGS } }, }, { id: 'settings-ldap', @@ -116,7 +113,7 @@ export default defineComponent({ label: this.$locale.baseText('settings.ldap'), position: 'top', available: this.canAccessLdapSettings(), - activateOnRouteNames: [VIEWS.LDAP_SETTINGS], + route: { to: { name: VIEWS.LDAP_SETTINGS } }, }, { id: 'settings-workersview', @@ -126,7 +123,7 @@ export default defineComponent({ available: this.settingsStore.isQueueModeEnabled && hasPermission(['rbac'], { rbac: { scope: 'workersView:manage' } }), - activateOnRouteNames: [VIEWS.WORKER_VIEW], + route: { to: { name: VIEWS.WORKER_VIEW } }, }, ]; @@ -134,7 +131,7 @@ export default defineComponent({ if (item.uiLocations.includes('settings')) { menuItems.push({ id: item.id, - icon: item.icon || 'question', + icon: item.icon ?? 'question', label: this.$locale.baseText(item.featureName as BaseTextKey), position: 'top', available: true, @@ -149,7 +146,7 @@ export default defineComponent({ label: this.$locale.baseText('settings.log-streaming'), position: 'top', available: this.canAccessLogStreamingSettings(), - activateOnRouteNames: [VIEWS.LOG_STREAMING_SETTINGS], + route: { to: { name: VIEWS.LOG_STREAMING_SETTINGS } }, }); menuItems.push({ @@ -158,7 +155,7 @@ export default defineComponent({ label: this.$locale.baseText('settings.communityNodes'), position: 'top', available: this.canAccessCommunityNodes(), - activateOnRouteNames: [VIEWS.COMMUNITY_NODES], + route: { to: { name: VIEWS.COMMUNITY_NODES } }, }); return menuItems; @@ -211,51 +208,10 @@ export default defineComponent({ }, async handleSelect(key: string) { switch (key) { - case 'settings-personal': - await this.navigateTo(VIEWS.PERSONAL_SETTINGS); - break; - case 'settings-users': - await this.navigateTo(VIEWS.USERS_SETTINGS); - break; - case 'settings-api': - await this.navigateTo(VIEWS.API_SETTINGS); - break; - case 'settings-ldap': - await this.navigateTo(VIEWS.LDAP_SETTINGS); - break; - case 'settings-log-streaming': - await this.navigateTo(VIEWS.LOG_STREAMING_SETTINGS); - break; case 'users': // Fakedoor feature added via hooks when user management is disabled on cloud case 'logging': this.$router.push({ name: VIEWS.FAKE_DOOR, params: { featureId: key } }).catch(() => {}); break; - case 'settings-community-nodes': - await this.navigateTo(VIEWS.COMMUNITY_NODES); - break; - case 'settings-usage-and-plan': - await this.navigateTo(VIEWS.USAGE); - break; - case 'settings-sso': - await this.navigateTo(VIEWS.SSO_SETTINGS); - break; - case 'settings-external-secrets': - await this.navigateTo(VIEWS.EXTERNAL_SECRETS_SETTINGS); - break; - case 'settings-source-control': - if (this.$router.currentRoute.name !== VIEWS.SOURCE_CONTROL) { - void this.$router.push({ name: VIEWS.SOURCE_CONTROL }); - } - break; - case 'settings-audit-logs': - if (this.$router.currentRoute.name !== VIEWS.AUDIT_LOGS) { - void this.$router.push({ name: VIEWS.AUDIT_LOGS }); - } - break; - case 'settings-workersview': { - await this.navigateTo(VIEWS.WORKER_VIEW); - break; - } default: break; } diff --git a/packages/editor-ui/src/components/__tests__/ExecutionsList.test.ts b/packages/editor-ui/src/components/__tests__/ExecutionsList.test.ts index e66b6d43b3..ad20ade194 100644 --- a/packages/editor-ui/src/components/__tests__/ExecutionsList.test.ts +++ b/packages/editor-ui/src/components/__tests__/ExecutionsList.test.ts @@ -16,6 +16,7 @@ vi.mock('vue-router', () => ({ useRoute: vi.fn().mockReturnValue({ name: VIEWS.WORKFLOW_EXECUTIONS, }), + RouterLink: vi.fn(), })); let pinia: ReturnType; diff --git a/packages/editor-ui/src/components/__tests__/RBAC.test.ts b/packages/editor-ui/src/components/__tests__/RBAC.test.ts index 566fe71294..63bd7a1a58 100644 --- a/packages/editor-ui/src/components/__tests__/RBAC.test.ts +++ b/packages/editor-ui/src/components/__tests__/RBAC.test.ts @@ -9,6 +9,7 @@ vi.mock('vue-router', () => ({ path: '/workflows', params: {}, })), + RouterLink: vi.fn(), })); vi.mock('@/stores/rbac.store', () => ({ diff --git a/packages/editor-ui/src/composables/__tests__/useHistoryHelper.test.ts b/packages/editor-ui/src/composables/__tests__/useHistoryHelper.test.ts index a035654b1f..304902aed2 100644 --- a/packages/editor-ui/src/composables/__tests__/useHistoryHelper.test.ts +++ b/packages/editor-ui/src/composables/__tests__/useHistoryHelper.test.ts @@ -31,6 +31,7 @@ vi.mock('@/stores/ui.store', () => { }); vi.mock('vue-router', () => ({ useRoute: () => ({}), + RouterLink: vi.fn(), })); const TestComponent = defineComponent({ diff --git a/packages/editor-ui/src/router.ts b/packages/editor-ui/src/router.ts index 0e4bc8ffa9..c8cb1c9d25 100644 --- a/packages/editor-ui/src/router.ts +++ b/packages/editor-ui/src/router.ts @@ -7,7 +7,7 @@ import type { RouteLocationRaw, RouteLocationNormalized, } from 'vue-router'; -import { createRouter, createWebHistory } from 'vue-router'; +import { createRouter, createWebHistory, isNavigationFailure } from 'vue-router'; import { useExternalHooks } from '@/composables/useExternalHooks'; import { useSettingsStore } from '@/stores/settings.store'; import { useTemplatesStore } from '@/stores/templates.store'; @@ -784,73 +784,89 @@ const router = createRouter({ }); router.beforeEach(async (to: RouteLocationNormalized & RouteConfig, from, next) => { - /** - * Initialize application core - * This step executes before first route is loaded and is required for permission checks - */ + try { + /** + * Initialize application core + * This step executes before first route is loaded and is required for permission checks + */ - await initializeCore(); + await initializeCore(); - /** - * Redirect to setup page. User should be redirected to this only once - */ + /** + * Redirect to setup page. User should be redirected to this only once + */ - const settingsStore = useSettingsStore(); - if (settingsStore.showSetupPage) { - if (to.name === VIEWS.SETUP) { - return next(); + const settingsStore = useSettingsStore(); + if (settingsStore.showSetupPage) { + if (to.name === VIEWS.SETUP) { + return next(); + } + + return next({ name: VIEWS.SETUP }); } - return next({ name: VIEWS.SETUP }); - } + /** + * Verify user permissions for current route + */ - /** - * Verify user permissions for current route - */ + const routeMiddleware = to.meta?.middleware ?? []; + const routeMiddlewareOptions = to.meta?.middlewareOptions ?? {}; + for (const middlewareName of routeMiddleware) { + let nextCalled = false; + const middlewareNext = ((location: RouteLocationRaw): void => { + next(location); + nextCalled = true; + }) as NavigationGuardNext; - const routeMiddleware = to.meta?.middleware ?? []; - const routeMiddlewareOptions = to.meta?.middlewareOptions ?? {}; - for (const middlewareName of routeMiddleware) { - let nextCalled = false; - const middlewareNext = ((location: RouteLocationRaw): void => { - next(location); - nextCalled = true; - }) as NavigationGuardNext; + const middlewareOptions = routeMiddlewareOptions[middlewareName]; + const middlewareFn = middleware[middlewareName] as RouterMiddleware; + await middlewareFn(to, from, middlewareNext, middlewareOptions); - const middlewareOptions = routeMiddlewareOptions[middlewareName]; - const middlewareFn = middleware[middlewareName] as RouterMiddleware; - await middlewareFn(to, from, middlewareNext, middlewareOptions); + if (nextCalled) { + return; + } + } - if (nextCalled) { - return; + return next(); + } catch (failure) { + if (isNavigationFailure(failure)) { + console.log(failure); + } else { + console.error(failure); } } - - return next(); }); router.afterEach((to, from) => { - const telemetry = useTelemetry(); - const uiStore = useUIStore(); - const templatesStore = useTemplatesStore(); + try { + const telemetry = useTelemetry(); + const uiStore = useUIStore(); + const templatesStore = useTemplatesStore(); - /** - * Run external hooks - */ + /** + * Run external hooks + */ - void useExternalHooks().run('main.routeChange', { from, to }); + void useExternalHooks().run('main.routeChange', { from, to }); - /** - * Track current view for telemetry - */ + /** + * Track current view for telemetry + */ - uiStore.currentView = (to.name as string) ?? ''; - if (to.meta?.templatesEnabled) { - templatesStore.setSessionId(); - } else { - templatesStore.resetSessionId(); // reset telemetry session id when user leaves template pages + uiStore.currentView = (to.name as string) ?? ''; + if (to.meta?.templatesEnabled) { + templatesStore.setSessionId(); + } else { + templatesStore.resetSessionId(); // reset telemetry session id when user leaves template pages + } + telemetry.page(to); + } catch (failure) { + if (isNavigationFailure(failure)) { + console.log(failure); + } else { + console.error(failure); + } } - telemetry.page(to); }); export default router; diff --git a/packages/editor-ui/src/stores/ui.store.ts b/packages/editor-ui/src/stores/ui.store.ts index bfb12f54b8..fac4806621 100644 --- a/packages/editor-ui/src/stores/ui.store.ts +++ b/packages/editor-ui/src/stores/ui.store.ts @@ -44,7 +44,6 @@ import type { CloudUpdateLinkSourceType, CurlToJSONResponse, IFakeDoorLocation, - IMenuItem, INodeUi, IOnboardingCallPrompt, IUser, @@ -176,7 +175,6 @@ export const useUIStore = defineStore(STORES.UI, { nodeViewOffsetPosition: [0, 0], nodeViewMoveInProgress: false, selectedNodes: [], - sidebarMenuItems: [], nodeViewInitialized: false, addFirstStepOnLoad: false, executionSidebarAutoRefresh: true, @@ -528,10 +526,6 @@ export const useUIStore = defineStore(STORES.UI, { resetSelectedNodes(): void { this.selectedNodes = []; }, - addSidebarMenuItems(menuItems: IMenuItem[]) { - const updated = this.sidebarMenuItems.concat(menuItems); - this.sidebarMenuItems = updated; - }, setCurlCommand(payload: { name: string; command: string }): void { this.modals[payload.name] = { ...this.modals[payload.name], diff --git a/packages/editor-ui/src/views/__tests__/SamlOnboarding.test.ts b/packages/editor-ui/src/views/__tests__/SamlOnboarding.test.ts index 13158b217e..148a11083e 100644 --- a/packages/editor-ui/src/views/__tests__/SamlOnboarding.test.ts +++ b/packages/editor-ui/src/views/__tests__/SamlOnboarding.test.ts @@ -14,6 +14,7 @@ vi.mock('vue-router', () => { useRouter: () => ({ push, }), + RouterLink: vi.fn(), }; }); diff --git a/packages/editor-ui/src/views/__tests__/WorkflowHistory.test.ts b/packages/editor-ui/src/views/__tests__/WorkflowHistory.test.ts index ad39e22bcc..a0f9fa2bde 100644 --- a/packages/editor-ui/src/views/__tests__/WorkflowHistory.test.ts +++ b/packages/editor-ui/src/views/__tests__/WorkflowHistory.test.ts @@ -33,6 +33,7 @@ vi.mock('vue-router', () => { replace, resolve, }), + RouterLink: vi.fn(), }; }); diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 3fb9308c1c..5a583c874f 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -867,6 +867,9 @@ importers: vue-boring-avatars: specifier: ^1.3.0 version: 1.3.0(vue@3.3.4) + vue-router: + specifier: ^4.2.2 + version: 4.2.2(vue@3.3.4) xss: specifier: ^1.0.14 version: 1.0.14 From daba5bb250c3bee338dde96e6e815835dd21e6f1 Mon Sep 17 00:00:00 2001 From: Jon Date: Fri, 19 Jan 2024 11:28:41 +0000 Subject: [PATCH 33/69] fix(AWS SQS Node): Fix issue preventing data from being sent correctly (#8382) --- packages/nodes-base/nodes/Aws/SQS/AwsSqs.node.ts | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/packages/nodes-base/nodes/Aws/SQS/AwsSqs.node.ts b/packages/nodes-base/nodes/Aws/SQS/AwsSqs.node.ts index 9c5572ab85..bfd9d7fc1d 100644 --- a/packages/nodes-base/nodes/Aws/SQS/AwsSqs.node.ts +++ b/packages/nodes-base/nodes/Aws/SQS/AwsSqs.node.ts @@ -296,10 +296,16 @@ export class AwsSqs implements INodeType { const options = this.getNodeParameter('options', i, {}); const sendInputData = this.getNodeParameter('sendInputData', i) as boolean; - const message = sendInputData + let message = sendInputData ? JSON.stringify(items[i].json) - : (this.getNodeParameter('message', i) as string); - params.push(`MessageBody=${message}`); + : this.getNodeParameter('message', i); + + // This prevents [object Object] from being sent as message when sending json in an expression + if (typeof message === 'object') { + message = JSON.stringify(message); + } + + params.push(`MessageBody=${encodeURIComponent(message as string)}`); if (options.delaySeconds) { params.push(`DelaySeconds=${options.delaySeconds}`); From 2c143714817e62fcb2fec9b9cac208ef029f87fd Mon Sep 17 00:00:00 2001 From: Michael Kret <88898367+michael-radency@users.noreply.github.com> Date: Fri, 19 Jan 2024 13:39:18 +0200 Subject: [PATCH 34/69] fix(AMQP Trigger Node): Properly close connection after manual test step (#8396) --- packages/nodes-base/nodes/Amqp/AmqpTrigger.node.ts | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/packages/nodes-base/nodes/Amqp/AmqpTrigger.node.ts b/packages/nodes-base/nodes/Amqp/AmqpTrigger.node.ts index 7182fdfae5..e63bb9ad03 100644 --- a/packages/nodes-base/nodes/Amqp/AmqpTrigger.node.ts +++ b/packages/nodes-base/nodes/Amqp/AmqpTrigger.node.ts @@ -48,16 +48,18 @@ export class AmqpTrigger implements INodeType { name: 'clientname', type: 'string', default: '', - placeholder: 'for durable/persistent topic subscriptions, example: "n8n"', + placeholder: 'e.g. n8n', description: 'Leave empty for non-durable topic subscriptions or queues', + hint: 'for durable/persistent topic subscriptions', }, { displayName: 'Subscription', name: 'subscription', type: 'string', default: '', - placeholder: 'for durable/persistent topic subscriptions, example: "order-worker"', + placeholder: 'e.g. order-worker', description: 'Leave empty for non-durable topic subscriptions or queues', + hint: 'for durable/persistent topic subscriptions', }, { displayName: 'Options', @@ -270,6 +272,10 @@ export class AmqpTrigger implements INodeType { const manualTriggerFunction = async () => { await new Promise((resolve, reject) => { const timeoutHandler = setTimeout(() => { + container.removeAllListeners('receiver_open'); + container.removeAllListeners('message'); + connection.close(); + reject( new Error( 'Aborted, no message received within 30secs. This 30sec timeout is only set for "manually triggered execution". Active Workflows will listen indefinitely.', From 284d965b5acc0819ffc109729ce1d5d0b2352abb Mon Sep 17 00:00:00 2001 From: Michael Kret <88898367+michael-radency@users.noreply.github.com> Date: Fri, 19 Jan 2024 13:47:25 +0200 Subject: [PATCH 35/69] fix(core): Missing pairedItem fixes (#8394) --- packages/nodes-base/nodes/Airtable/v1/AirtableV1.node.ts | 2 ++ packages/nodes-base/nodes/Airtable/v1/GenericFunctions.ts | 5 +++++ .../nodes/Airtable/v2/actions/record/search.operation.ts | 2 ++ packages/nodes-base/nodes/Airtable/v2/transport/index.ts | 5 +++++ packages/nodes-base/nodes/Merge/v1/MergeV1.node.ts | 4 +++- packages/nodes-base/nodes/Merge/v2/MergeV2.node.ts | 4 +++- packages/nodes-base/nodes/NocoDB/GenericFunctions.ts | 5 +++++ packages/nodes-base/nodes/NocoDB/NocoDB.node.ts | 2 ++ packages/nodes-base/nodes/Notion/GenericFunctions.ts | 6 +++++- packages/nodes-base/nodes/Notion/v2/NotionV2.node.ts | 4 +++- packages/nodes-base/nodes/Redis/Redis.node.ts | 2 +- packages/nodes-base/nodes/Set/v1/SetV1.node.ts | 2 +- 12 files changed, 37 insertions(+), 6 deletions(-) diff --git a/packages/nodes-base/nodes/Airtable/v1/AirtableV1.node.ts b/packages/nodes-base/nodes/Airtable/v1/AirtableV1.node.ts index f24993b5a5..aebef1c310 100644 --- a/packages/nodes-base/nodes/Airtable/v1/AirtableV1.node.ts +++ b/packages/nodes-base/nodes/Airtable/v1/AirtableV1.node.ts @@ -718,10 +718,12 @@ export class AirtableV1 implements INodeType { const downloadFieldNames = ( this.getNodeParameter('downloadFieldNames', 0) as string ).split(','); + const pairedItem = generatePairedItemData(items.length); const data = await downloadRecordAttachments.call( this, responseData.records as IRecord[], downloadFieldNames, + pairedItem, ); return [data]; } diff --git a/packages/nodes-base/nodes/Airtable/v1/GenericFunctions.ts b/packages/nodes-base/nodes/Airtable/v1/GenericFunctions.ts index ea0f6b4273..d95c8b45fe 100644 --- a/packages/nodes-base/nodes/Airtable/v1/GenericFunctions.ts +++ b/packages/nodes-base/nodes/Airtable/v1/GenericFunctions.ts @@ -7,6 +7,7 @@ import type { IPollFunctions, ILoadOptionsFunctions, INodeExecutionData, + IPairedItemData, } from 'n8n-workflow'; interface IAttachment { @@ -100,10 +101,14 @@ export async function downloadRecordAttachments( this: IExecuteFunctions | IPollFunctions, records: IRecord[], fieldNames: string[], + pairedItem?: IPairedItemData[], ): Promise { const elements: INodeExecutionData[] = []; for (const record of records) { const element: INodeExecutionData = { json: {}, binary: {} }; + if (pairedItem) { + element.pairedItem = pairedItem; + } element.json = record as unknown as IDataObject; for (const fieldName of fieldNames) { if (record.fields[fieldName] !== undefined) { diff --git a/packages/nodes-base/nodes/Airtable/v2/actions/record/search.operation.ts b/packages/nodes-base/nodes/Airtable/v2/actions/record/search.operation.ts index 75f4e47d9a..e731f8b170 100644 --- a/packages/nodes-base/nodes/Airtable/v2/actions/record/search.operation.ts +++ b/packages/nodes-base/nodes/Airtable/v2/actions/record/search.operation.ts @@ -194,10 +194,12 @@ export async function execute( returnData = responseData.records as INodeExecutionData[]; if (options.downloadFields) { + const pairedItem = generatePairedItemData(items.length); return await downloadRecordAttachments.call( this, responseData.records as IRecord[], options.downloadFields as string[], + pairedItem, ); } diff --git a/packages/nodes-base/nodes/Airtable/v2/transport/index.ts b/packages/nodes-base/nodes/Airtable/v2/transport/index.ts index bf2ff876fb..591ce29deb 100644 --- a/packages/nodes-base/nodes/Airtable/v2/transport/index.ts +++ b/packages/nodes-base/nodes/Airtable/v2/transport/index.ts @@ -7,6 +7,7 @@ import type { IPollFunctions, ILoadOptionsFunctions, INodeExecutionData, + IPairedItemData, } from 'n8n-workflow'; import { ApplicationError } from 'n8n-workflow'; import type { IAttachment, IRecord } from '../helpers/interfaces'; @@ -87,6 +88,7 @@ export async function downloadRecordAttachments( this: IExecuteFunctions | IPollFunctions, records: IRecord[], fieldNames: string | string[], + pairedItem?: IPairedItemData[], ): Promise { if (typeof fieldNames === 'string') { fieldNames = fieldNames.split(',').map((item) => item.trim()); @@ -99,6 +101,9 @@ export async function downloadRecordAttachments( const elements: INodeExecutionData[] = []; for (const record of records) { const element: INodeExecutionData = { json: {}, binary: {} }; + if (pairedItem) { + element.pairedItem = pairedItem; + } element.json = flattenOutput(record as unknown as IDataObject); for (const fieldName of fieldNames) { if (record.fields[fieldName] !== undefined) { diff --git a/packages/nodes-base/nodes/Merge/v1/MergeV1.node.ts b/packages/nodes-base/nodes/Merge/v1/MergeV1.node.ts index d0f10e29b2..e8224d0eef 100644 --- a/packages/nodes-base/nodes/Merge/v1/MergeV1.node.ts +++ b/packages/nodes-base/nodes/Merge/v1/MergeV1.node.ts @@ -13,6 +13,7 @@ import type { import { deepCopy } from 'n8n-workflow'; import { oldVersionNotice } from '@utils/descriptions'; +import { generatePairedItemData } from '../../../utils/utilities'; const versionDescription: INodeTypeDescription = { displayName: 'Merge', @@ -477,7 +478,8 @@ export class MergeV1 implements INodeType { returnData.push.apply(returnData, this.getInputData(1)); } } else if (mode === 'wait') { - returnData.push({ json: {} }); + const pairedItem = generatePairedItemData(this.getInputData(0).length); + returnData.push({ json: {}, pairedItem }); } return [returnData]; diff --git a/packages/nodes-base/nodes/Merge/v2/MergeV2.node.ts b/packages/nodes-base/nodes/Merge/v2/MergeV2.node.ts index c246339c0d..2df8779658 100644 --- a/packages/nodes-base/nodes/Merge/v2/MergeV2.node.ts +++ b/packages/nodes-base/nodes/Merge/v2/MergeV2.node.ts @@ -29,6 +29,7 @@ import { } from './GenericFunctions'; import { optionsDescription } from './OptionsDescription'; +import { generatePairedItemData } from '../../../utils/utilities'; const versionDescription: INodeTypeDescription = { displayName: 'Merge', @@ -599,7 +600,8 @@ export class MergeV2 implements INodeType { returnData.push.apply(returnData, this.getInputData(1)); } if (output === 'empty') { - returnData.push({ json: {} }); + const itemData = generatePairedItemData(this.getInputData(0).length); + returnData.push({ json: {}, pairedItem: itemData }); } } } diff --git a/packages/nodes-base/nodes/NocoDB/GenericFunctions.ts b/packages/nodes-base/nodes/NocoDB/GenericFunctions.ts index 5edc220c5a..8900cc04e3 100644 --- a/packages/nodes-base/nodes/NocoDB/GenericFunctions.ts +++ b/packages/nodes-base/nodes/NocoDB/GenericFunctions.ts @@ -7,6 +7,7 @@ import type { IHookFunctions, ILoadOptionsFunctions, INodeExecutionData, + IPairedItemData, IPollFunctions, } from 'n8n-workflow'; import { jsonParse, NodeOperationError } from 'n8n-workflow'; @@ -106,11 +107,15 @@ export async function downloadRecordAttachments( this: IExecuteFunctions | IPollFunctions, records: IDataObject[], fieldNames: string[], + pairedItem?: IPairedItemData[], ): Promise { const elements: INodeExecutionData[] = []; for (const record of records) { const element: INodeExecutionData = { json: {}, binary: {} }; + if (pairedItem) { + element.pairedItem = pairedItem; + } element.json = record as unknown as IDataObject; for (const fieldName of fieldNames) { let attachments = record[fieldName] as IAttachment[]; diff --git a/packages/nodes-base/nodes/NocoDB/NocoDB.node.ts b/packages/nodes-base/nodes/NocoDB/NocoDB.node.ts index fd010508f0..4fffdaaa42 100644 --- a/packages/nodes-base/nodes/NocoDB/NocoDB.node.ts +++ b/packages/nodes-base/nodes/NocoDB/NocoDB.node.ts @@ -521,6 +521,7 @@ export class NocoDB implements INodeType { this, responseData as IDataObject[], downloadFieldNames, + [{ item: i }], ); data.push(...response); } @@ -584,6 +585,7 @@ export class NocoDB implements INodeType { this, [responseData as IDataObject], downloadFieldNames, + [{ item: i }], ); const newItem = { binary: data[0].binary, diff --git a/packages/nodes-base/nodes/Notion/GenericFunctions.ts b/packages/nodes-base/nodes/Notion/GenericFunctions.ts index 54f872b91d..73190752e3 100644 --- a/packages/nodes-base/nodes/Notion/GenericFunctions.ts +++ b/packages/nodes-base/nodes/Notion/GenericFunctions.ts @@ -9,6 +9,7 @@ import type { ILoadOptionsFunctions, INodeExecutionData, INodeProperties, + IPairedItemData, IPollFunctions, JsonObject, } from 'n8n-workflow'; @@ -860,12 +861,15 @@ export type FileRecord = { }; }; // prettier-ignore -export async function downloadFiles(this: IExecuteFunctions | IPollFunctions, records: FileRecord[]): Promise { +export async function downloadFiles(this: IExecuteFunctions | IPollFunctions, records: FileRecord[], pairedItem?: IPairedItemData[]): Promise { const elements: INodeExecutionData[] = []; for (const record of records) { const element: INodeExecutionData = { json: {}, binary: {} }; element.json = record as unknown as IDataObject; + if (pairedItem) { + element.pairedItems = pairedItem; + } for (const key of Object.keys(record.properties)) { if (record.properties[key].type === 'files') { if (record.properties[key].files.length) { diff --git a/packages/nodes-base/nodes/Notion/v2/NotionV2.node.ts b/packages/nodes-base/nodes/Notion/v2/NotionV2.node.ts index 4bb9afca54..2809f8ff9e 100644 --- a/packages/nodes-base/nodes/Notion/v2/NotionV2.node.ts +++ b/packages/nodes-base/nodes/Notion/v2/NotionV2.node.ts @@ -591,7 +591,9 @@ export class NotionV2 implements INodeType { responseData = responseData.results; } if (download) { - responseData = await downloadFiles.call(this, responseData as FileRecord[]); + responseData = await downloadFiles.call(this, responseData as FileRecord[], [ + { item: i }, + ]); } if (simple) { responseData = simplifyObjects(responseData, download); diff --git a/packages/nodes-base/nodes/Redis/Redis.node.ts b/packages/nodes-base/nodes/Redis/Redis.node.ts index 414176db94..70d2dfc681 100644 --- a/packages/nodes-base/nodes/Redis/Redis.node.ts +++ b/packages/nodes-base/nodes/Redis/Redis.node.ts @@ -530,7 +530,7 @@ export class Redis implements INodeType { let item: INodeExecutionData; for (let itemIndex = 0; itemIndex < items.length; itemIndex++) { - item = { json: {} }; + item = { json: {}, pairedItem: { item: itemIndex } }; if (operation === 'delete') { const keyDelete = this.getNodeParameter('key', itemIndex) as string; diff --git a/packages/nodes-base/nodes/Set/v1/SetV1.node.ts b/packages/nodes-base/nodes/Set/v1/SetV1.node.ts index d4a08a2e73..b505b62451 100644 --- a/packages/nodes-base/nodes/Set/v1/SetV1.node.ts +++ b/packages/nodes-base/nodes/Set/v1/SetV1.node.ts @@ -150,7 +150,7 @@ export class SetV1 implements INodeType { const nodeVersion = this.getNode().typeVersion; if (items.length === 0) { - items.push({ json: {} }); + items.push({ json: {}, pairedItem: { item: 0 } }); } const returnData: INodeExecutionData[] = []; From e43cf2fd715e21fd4e454c9e6b6d874306472360 Mon Sep 17 00:00:00 2001 From: Tomi Turtiainen <10324676+tomi@users.noreply.github.com> Date: Fri, 19 Jan 2024 14:28:44 +0200 Subject: [PATCH 36/69] fix(editor): Enable ctrl/cmd click in workflow editor header (#8387) --- .../N8nRadioButtons/RadioButtons.vue | 7 +- .../src/components/MainHeader/MainHeader.vue | 89 ++++++++++++------- 2 files changed, 59 insertions(+), 37 deletions(-) diff --git a/packages/design-system/src/components/N8nRadioButtons/RadioButtons.vue b/packages/design-system/src/components/N8nRadioButtons/RadioButtons.vue index b4cdb732b5..83acba928e 100644 --- a/packages/design-system/src/components/N8nRadioButtons/RadioButtons.vue +++ b/packages/design-system/src/components/N8nRadioButtons/RadioButtons.vue @@ -10,7 +10,7 @@ :active="modelValue === option.value" :size="size" :disabled="disabled || option.disabled" - @click.prevent.stop="onClick(option)" + @click.prevent.stop="onClick(option, $event)" /> @@ -47,12 +47,13 @@ export default defineComponent({ type: Boolean, }, }, + emits: ['update:modelValue'], methods: { - onClick(option: { label: string; value: string; disabled?: boolean }) { + onClick(option: { label: string; value: string; disabled?: boolean }, event: MouseEvent) { if (this.disabled || option.disabled) { return; } - this.$emit('update:modelValue', option.value); + this.$emit('update:modelValue', option.value, event); }, }, }); diff --git a/packages/editor-ui/src/components/MainHeader/MainHeader.vue b/packages/editor-ui/src/components/MainHeader/MainHeader.vue index e4b171555b..f7cffe5f16 100644 --- a/packages/editor-ui/src/components/MainHeader/MainHeader.vue +++ b/packages/editor-ui/src/components/MainHeader/MainHeader.vue @@ -16,7 +16,7 @@ From d2b3c1048e72ebbeb80559595331b7882a6d5472 Mon Sep 17 00:00:00 2001 From: Alex Grozav Date: Fri, 19 Jan 2024 15:21:49 +0200 Subject: [PATCH 37/69] feat(editor): Add additional email domains to our disallowed list for self-serve enterprise license (no-changelog) (#8161) --- packages/editor-ui/package.json | 1 + .../src/components/PersonalizationModal.vue | 37 ++++++++++--------- pnpm-lock.yaml | 8 ++++ 3 files changed, 29 insertions(+), 17 deletions(-) diff --git a/packages/editor-ui/package.json b/packages/editor-ui/package.json index 53d1352652..966cde4a97 100644 --- a/packages/editor-ui/package.json +++ b/packages/editor-ui/package.json @@ -52,6 +52,7 @@ "codemirror-lang-html-n8n": "^1.0.0", "codemirror-lang-n8n-expression": "^0.2.0", "dateformat": "^3.0.3", + "email-providers": "^2.0.1", "esprima-next": "5.8.4", "fast-json-stable-stringify": "^2.1.0", "file-saver": "^2.0.2", diff --git a/packages/editor-ui/src/components/PersonalizationModal.vue b/packages/editor-ui/src/components/PersonalizationModal.vue index 1c5ce8e07b..3ff7ca0941 100644 --- a/packages/editor-ui/src/components/PersonalizationModal.vue +++ b/packages/editor-ui/src/components/PersonalizationModal.vue @@ -55,9 +55,6 @@