From 0a5ab560b19f761ba07838b0e49124ed92bf484b 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: Fri, 13 Jan 2023 18:12:22 +0100 Subject: [PATCH] refactor: Upgrade typeorm to 0.3.x (#5151) --- packages/cli/package.json | 8 +- packages/cli/src/AbstractServer.ts | 5 +- packages/cli/src/ActiveWorkflowRunner.ts | 41 +++++----- .../cli/src/CommunityNodes/packageModel.ts | 9 ++- packages/cli/src/CredentialsHelper.ts | 6 +- packages/cli/src/Db.ts | 18 +++-- packages/cli/src/PublicApi/index.ts | 2 +- .../credentials/credentials.service.ts | 18 +++-- .../handlers/executions/executions.service.ts | 23 ++---- .../v1/handlers/users/users.service.ts | 2 +- .../handlers/workflows/workflows.handler.ts | 4 +- .../handlers/workflows/workflows.service.ts | 10 +-- packages/cli/src/Server.ts | 9 ++- packages/cli/src/TagHelpers.ts | 6 +- .../src/UserManagement/PermissionChecker.ts | 11 +-- .../UserManagement/UserManagementHelper.ts | 31 +++----- packages/cli/src/UserManagement/auth/jwt.ts | 3 +- .../cli/src/UserManagement/routes/auth.ts | 14 ++-- .../cli/src/UserManagement/routes/index.ts | 10 +-- .../cli/src/UserManagement/routes/owner.ts | 3 +- .../UserManagement/routes/passwordReset.ts | 6 +- .../cli/src/UserManagement/routes/users.ts | 12 +-- packages/cli/src/WaitTracker.ts | 10 ++- packages/cli/src/WaitingWebhooks.ts | 4 +- packages/cli/src/WorkflowCredentials.ts | 2 +- .../cli/src/WorkflowExecuteAdditionalData.ts | 8 +- packages/cli/src/WorkflowHelpers.ts | 43 +++++------ packages/cli/src/WorkflowRunner.ts | 6 +- packages/cli/src/api/e2e.api.ts | 9 ++- .../cli/src/audit/risks/credentials.risk.ts | 4 +- packages/cli/src/commands/BaseCommand.ts | 6 +- packages/cli/src/commands/db/revert.ts | 9 ++- packages/cli/src/commands/execute.ts | 8 +- .../cli/src/commands/import/credentials.ts | 12 +-- packages/cli/src/commands/import/workflow.ts | 12 +-- packages/cli/src/commands/start.ts | 2 +- .../cli/src/commands/user-management/reset.ts | 8 +- packages/cli/src/commands/worker.ts | 21 +++-- .../src/credentials/credentials.controller.ts | 2 +- .../src/credentials/credentials.service.ee.ts | 16 ++-- .../src/credentials/credentials.service.ts | 19 ++--- packages/cli/src/databases/entities/Role.ts | 7 +- .../databases/entities/SharedCredentials.ts | 14 ++-- .../src/databases/entities/SharedWorkflow.ts | 14 ++-- packages/cli/src/databases/entities/User.ts | 8 +- .../databases/entities/WorkflowStatistics.ts | 8 +- .../MessageEventBus/MessageEventBus.ts | 2 +- .../MessageEventBusDestination.ee.ts | 2 +- .../cli/src/executions/executions.service.ts | 10 ++- packages/cli/src/role/role.service.ts | 10 +-- packages/cli/src/user/user.service.ts | 9 ++- .../src/workflows/workflows.controller.ee.ts | 8 +- .../cli/src/workflows/workflows.controller.ts | 8 +- .../src/workflows/workflows.services.ee.ts | 13 ++-- .../cli/src/workflows/workflows.services.ts | 17 +++-- .../audit/credentials.risk.test.ts | 9 +-- .../integration/audit/database.risk.test.ts | 9 +-- .../integration/audit/filesystem.risk.test.ts | 9 +-- .../integration/audit/instance.risk.test.ts | 9 +-- .../test/integration/audit/nodes.risk.test.ts | 9 +-- .../cli/test/integration/auth.api.test.ts | 8 +- packages/cli/test/integration/auth.mw.test.ts | 6 +- .../integration/commands/reset.cmd.test.ts | 10 +-- .../test/integration/credentials.ee.test.ts | 20 +++-- .../cli/test/integration/credentials.test.ts | 56 +++++++------- .../cli/test/integration/eventbus.test.ts | 16 ++-- .../cli/test/integration/license.api.test.ts | 8 +- packages/cli/test/integration/me.api.test.ts | 38 +++++----- .../cli/test/integration/nodes.api.test.ts | 8 +- .../cli/test/integration/owner.api.test.ts | 14 ++-- .../integration/passwordReset.api.test.ts | 18 ++--- .../integration/publicApi/credentials.test.ts | 58 +++++++------- .../integration/publicApi/executions.test.ts | 27 +++---- .../integration/publicApi/workflows.test.ts | 76 ++++++++++--------- .../test/integration/shared/augmentation.d.ts | 2 +- .../cli/test/integration/shared/testDb.ts | 50 ++++++------ packages/cli/test/integration/shared/utils.ts | 2 +- .../cli/test/integration/users.api.test.ts | 47 +++++------- .../workflows.controller.ee.test.ts | 8 +- .../integration/workflows.controller.test.ts | 8 +- packages/cli/test/teardown.ts | 7 +- .../cli/test/unit/PermissionChecker.test.ts | 10 +-- packages/cli/test/unit/Telemetry.test.ts | 2 +- .../cli/test/unit/WorkflowCredentials.test.ts | 8 +- pnpm-lock.yaml | 69 +++++++---------- 85 files changed, 573 insertions(+), 630 deletions(-) diff --git a/packages/cli/package.json b/packages/cli/package.json index 0341343183c61..d69a4f5488e26 100644 --- a/packages/cli/package.json +++ b/packages/cli/package.json @@ -161,7 +161,7 @@ "lodash.uniqby": "^4.7.0", "lodash.unset": "^4.5.2", "luxon": "^3.1.0", - "mysql2": "~2.3.0", + "mysql2": "~2.3.3", "n8n-core": "~0.151.0", "n8n-editor-ui": "~0.177.0", "n8n-nodes-base": "~0.209.0", @@ -175,7 +175,7 @@ "passport": "^0.6.0", "passport-cookie": "^1.0.9", "passport-jwt": "^4.0.0", - "pg": "^8.3.0", + "pg": "^8.8.0", "picocolors": "^1.0.0", "posthog-node": "^2.2.2", "prom-client": "^13.1.0", @@ -184,12 +184,12 @@ "semver": "^7.3.8", "shelljs": "^0.8.5", "source-map-support": "^0.5.21", - "sqlite3": "^5.1.2", + "sqlite3": "^5.1.4", "sse-channel": "^4.0.0", "swagger-ui-express": "^4.3.0", "syslog-client": "^1.1.1", "tslib": "1.14.1", - "typeorm": "0.2.45", + "typeorm": "0.3.11", "uuid": "^8.3.2", "validator": "13.7.0", "winston": "^3.3.3", diff --git a/packages/cli/src/AbstractServer.ts b/packages/cli/src/AbstractServer.ts index 75f80d958ace5..89aac219723bd 100644 --- a/packages/cli/src/AbstractServer.ts +++ b/packages/cli/src/AbstractServer.ts @@ -6,7 +6,6 @@ import bodyParser from 'body-parser'; import bodyParserXml from 'body-parser-xml'; import compression from 'compression'; import parseUrl from 'parseurl'; -import { getConnectionManager } from 'typeorm'; import type { RedisOptions } from 'ioredis'; import { @@ -162,10 +161,10 @@ export abstract class AbstractServer { this.app.get('/healthz', async (req, res) => { Logger.debug('Health check started!'); - const connection = getConnectionManager().get(); + const connection = Db.getConnection(); try { - if (!connection.isConnected) { + if (!connection.isInitialized) { // Connection is not active throw new ServiceUnavailableError('No active database connection!'); } diff --git a/packages/cli/src/ActiveWorkflowRunner.ts b/packages/cli/src/ActiveWorkflowRunner.ts index 7958d65d6b271..ec91c45268236 100644 --- a/packages/cli/src/ActiveWorkflowRunner.ts +++ b/packages/cli/src/ActiveWorkflowRunner.ts @@ -200,18 +200,18 @@ export class ActiveWorkflowRunner { path = path.slice(0, -1); } - let webhook = await Db.collections.Webhook.findOne({ + let webhook = await Db.collections.Webhook.findOneBy({ webhookPath: path, method: httpMethod, }); let webhookId: string | undefined; // check if path is dynamic - if (webhook === undefined) { + if (webhook === null) { // check if a dynamic webhook path exists const pathElements = path.split('/'); webhookId = pathElements.shift(); - const dynamicWebhooks = await Db.collections.Webhook.find({ + const dynamicWebhooks = await Db.collections.Webhook.findBy({ webhookId, method: httpMethod, pathLength: pathElements.length, @@ -243,7 +243,7 @@ export class ActiveWorkflowRunner { webhook = dynamicWebhook; } }); - if (webhook === undefined) { + if (webhook === null) { throw new ResponseHelper.NotFoundError( `The requested webhook "${httpMethod} ${path}" is not registered.`, WEBHOOK_PROD_UNREGISTERED_HINT, @@ -263,10 +263,11 @@ export class ActiveWorkflowRunner { }); } - const workflowData = await Db.collections.Workflow.findOne(webhook.workflowId, { + const workflowData = await Db.collections.Workflow.findOne({ + where: { id: webhook.workflowId }, relations: ['shared', 'shared.user', 'shared.user.globalRole'], }); - if (workflowData === undefined) { + if (workflowData === null) { throw new ResponseHelper.NotFoundError( `Could not find workflow with id "${webhook.workflowId}"`, ); @@ -331,20 +332,19 @@ export class ActiveWorkflowRunner { /** * Gets all request methods associated with a single webhook - * - * @param {string} path webhook path */ async getWebhookMethods(path: string): Promise { - const webhooks = await Db.collections.Webhook.find({ webhookPath: path }); + const webhooks = await Db.collections.Webhook.find({ + select: ['method'], + where: { webhookPath: path }, + }); // Gather all request methods in string array - const webhookMethods: string[] = webhooks.map((webhook) => webhook.method); - return webhookMethods; + return webhooks.map((webhook) => webhook.method); } /** * Returns the ids of the currently active workflows - * */ async getActiveWorkflows(user?: User): Promise { let activeWorkflows: WorkflowEntity[] = []; @@ -378,7 +378,10 @@ export class ActiveWorkflowRunner { * @param {string} id The id of the workflow to check */ async isActive(id: string): Promise { - const workflow = await Db.collections.Workflow.findOne(id); + const workflow = await Db.collections.Workflow.findOne({ + select: ['active'], + where: { id }, + }); return !!workflow?.active; } @@ -434,6 +437,7 @@ export class ActiveWorkflowRunner { try { // eslint-disable-next-line no-await-in-loop + // TODO: this should happen in a transaction, that way we don't need to manually remove this in `catch` await Db.collections.Webhook.insert(webhook); const webhookExists = await workflow.runWebhookMethod( 'checkExists', @@ -503,10 +507,11 @@ export class ActiveWorkflowRunner { * */ async removeWorkflowWebhooks(workflowId: string): Promise { - const workflowData = await Db.collections.Workflow.findOne(workflowId, { + const workflowData = await Db.collections.Workflow.findOne({ + where: { id: workflowId }, relations: ['shared', 'shared.user', 'shared.user.globalRole'], }); - if (workflowData === undefined) { + if (workflowData === null) { throw new Error(`Could not find workflow with id "${workflowId}"`); } @@ -772,7 +777,8 @@ export class ActiveWorkflowRunner { let workflowInstance: Workflow; try { if (workflowData === undefined) { - workflowData = (await Db.collections.Workflow.findOne(workflowId, { + workflowData = (await Db.collections.Workflow.findOne({ + where: { id: workflowId }, relations: ['shared', 'shared.user', 'shared.user.globalRole'], })) as IWorkflowDb; } @@ -883,7 +889,7 @@ export class ActiveWorkflowRunner { /** * Add a workflow to the activation queue. * Meaning it will keep on trying to activate it in regular - * amounts indefinetly. + * amounts indefinitely. */ addQueuedWorkflowActivation( activationMode: WorkflowActivateMode, @@ -962,6 +968,7 @@ export class ActiveWorkflowRunner { * * @param {string} workflowId The id of the workflow to deactivate */ + // TODO: this should happen in a transaction async remove(workflowId: string): Promise { if (this.activeWorkflows !== null) { // Remove all the webhooks of the workflow diff --git a/packages/cli/src/CommunityNodes/packageModel.ts b/packages/cli/src/CommunityNodes/packageModel.ts index 8b6fabc3b0378..36c02b3615c7f 100644 --- a/packages/cli/src/CommunityNodes/packageModel.ts +++ b/packages/cli/src/CommunityNodes/packageModel.ts @@ -3,10 +3,11 @@ import * as Db from '@/Db'; import { InstalledNodes } from '@db/entities/InstalledNodes'; import { InstalledPackages } from '@db/entities/InstalledPackages'; -export async function findInstalledPackage( - packageName: string, -): Promise { - return Db.collections.InstalledPackages.findOne(packageName, { relations: ['installedNodes'] }); +export async function findInstalledPackage(packageName: string): Promise { + return Db.collections.InstalledPackages.findOne({ + where: { packageName }, + relations: ['installedNodes'], + }); } export async function isPackageInstalled(packageName: string): Promise { diff --git a/packages/cli/src/CredentialsHelper.ts b/packages/cli/src/CredentialsHelper.ts index 11f59ec17cbe5..c04b4bf8ef82e 100644 --- a/packages/cli/src/CredentialsHelper.ts +++ b/packages/cli/src/CredentialsHelper.ts @@ -270,7 +270,7 @@ export class CredentialsHelper extends ICredentialsHelper { relations: ['credentials'], where: { credentials: { id: nodeCredential.id, type }, userId }, }).then((shared) => shared.credentials) - : await Db.collections.Credentials.findOneOrFail({ id: nodeCredential.id, type }); + : await Db.collections.Credentials.findOneByOrFail({ id: nodeCredential.id, type }); if (!credential) { throw new Error( @@ -765,8 +765,8 @@ export async function getCredentialForUser( */ export async function getCredentialWithoutUser( credentialId: string, -): Promise { - return Db.collections.Credentials.findOne(credentialId); +): Promise { + return Db.collections.Credentials.findOneBy({ id: credentialId }); } export function createCredentialsFromCredentialsEntity( diff --git a/packages/cli/src/Db.ts b/packages/cli/src/Db.ts index 1c1bad58d916f..0bb52e1205f68 100644 --- a/packages/cli/src/Db.ts +++ b/packages/cli/src/Db.ts @@ -4,12 +4,10 @@ /* eslint-disable no-case-declarations */ /* eslint-disable @typescript-eslint/naming-convention */ import { - Connection, - ConnectionOptions, - createConnection, + DataSource as Connection, + DataSourceOptions as ConnectionOptions, EntityManager, EntityTarget, - getRepository, LoggerOptions, ObjectLiteral, Repository, @@ -34,6 +32,8 @@ export const collections = {} as IDatabaseCollections; export let connection: Connection; +export const getConnection = () => connection!; + export async function transaction(fn: (entityManager: EntityManager) => Promise): Promise { return connection.transaction(fn); } @@ -41,7 +41,7 @@ export async function transaction(fn: (entityManager: EntityManager) => Promi export function linkRepository( entityClass: EntityTarget, ): Repository { - return getRepository(entityClass, connection.name); + return connection.getRepository(entityClass); } export async function getConnectionOptions(dbType: DatabaseType): Promise { @@ -124,7 +124,8 @@ export async function init( migrationsTransactionMode: 'each', }); - connection = await createConnection(connectionOptions); + connection = new Connection(connectionOptions); + await connection.initialize(); if (!testConnectionOptions && dbType === 'sqlite') { // This specific migration changes database metadata. @@ -146,8 +147,9 @@ export async function init( // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access if (migrations.length === 0) { - await connection.close(); - connection = await createConnection(connectionOptions); + await connection.destroy(); + connection = new Connection(connectionOptions); + await connection.initialize(); } } diff --git a/packages/cli/src/PublicApi/index.ts b/packages/cli/src/PublicApi/index.ts index d8344a8ec7314..212b06984c23e 100644 --- a/packages/cli/src/PublicApi/index.ts +++ b/packages/cli/src/PublicApi/index.ts @@ -86,7 +86,7 @@ async function createApiRouter( _scopes: unknown, schema: OpenAPIV3.ApiKeySecurityScheme, ): Promise => { - const apiKey = req.headers[schema.name.toLowerCase()]; + const apiKey = req.headers[schema.name.toLowerCase()] as string; const user = await Db.collections.User.findOne({ where: { apiKey }, relations: ['globalRole'], 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 f2f43dd4d8ff2..cc66093bdf18c 100644 --- a/packages/cli/src/PublicApi/v1/handlers/credentials/credentials.service.ts +++ b/packages/cli/src/PublicApi/v1/handlers/credentials/credentials.service.ts @@ -1,4 +1,3 @@ -import type { FindConditions } from 'typeorm'; import { UserSettings, Credentials } from 'n8n-core'; import { IDataObject, INodeProperties, INodePropertyOptions } from 'n8n-workflow'; import * as Db from '@/Db'; @@ -10,17 +9,22 @@ import { ExternalHooks } from '@/ExternalHooks'; import { IDependency, IJsonSchema } from '../../../types'; import { CredentialRequest } from '@/requests'; -export async function getCredentials(credentialId: string): Promise { - return Db.collections.Credentials.findOne(credentialId); +export async function getCredentials(credentialId: string): Promise { + return Db.collections.Credentials.findOneBy({ id: credentialId }); } export async function getSharedCredentials( userId: string, credentialId: string, relations?: string[], -): Promise { - const where: FindConditions = { userId, credentialsId: credentialId }; - return Db.collections.SharedCredentials.findOne({ where, relations }); +): Promise { + return Db.collections.SharedCredentials.findOne({ + where: { + userId, + credentialsId: credentialId, + }, + relations, + }); } export async function createCredential( @@ -53,7 +57,7 @@ export async function saveCredential( user: User, encryptedData: ICredentialsDb, ): Promise { - const role = await Db.collections.Role.findOneOrFail({ + const role = await Db.collections.Role.findOneByOrFail({ name: 'owner', scope: 'credential', }); diff --git a/packages/cli/src/PublicApi/v1/handlers/executions/executions.service.ts b/packages/cli/src/PublicApi/v1/handlers/executions/executions.service.ts index d471897bbd09a..75289bd5dbec7 100644 --- a/packages/cli/src/PublicApi/v1/handlers/executions/executions.service.ts +++ b/packages/cli/src/PublicApi/v1/handlers/executions/executions.service.ts @@ -1,13 +1,12 @@ import { parse } from 'flatted'; -import { In, Not, Raw, LessThan, IsNull, FindOperator } from 'typeorm'; +import { In, Not, Raw, LessThan, IsNull, FindOptionsWhere } from 'typeorm'; import * as Db from '@/Db'; import type { IExecutionFlattedDb, IExecutionResponseApi } from '@/Interfaces'; -import { ExecutionEntity } from '@db/entities/ExecutionEntity'; -import { ExecutionStatus } from '@/PublicApi/types'; +import type { ExecutionStatus } from '@/PublicApi/types'; function prepareExecutionData( - execution: IExecutionFlattedDb | undefined, + execution: IExecutionFlattedDb | null, ): IExecutionResponseApi | undefined { if (!execution) return undefined; @@ -21,11 +20,10 @@ function prepareExecutionData( } function getStatusCondition(status: ExecutionStatus) { - const condition: { - finished?: boolean; - waitTill?: FindOperator; - stoppedAt?: FindOperator; - } = {}; + const condition: Pick< + FindOptionsWhere, + 'finished' | 'waitTill' | 'stoppedAt' + > = {}; if (status === 'success') { condition.finished = true; @@ -65,12 +63,7 @@ export async function getExecutions(params: { status?: ExecutionStatus; excludedExecutionsIds?: string[]; }): Promise { - type WhereClause = Record< - string, - string | boolean | FindOperator> - >; - - let where: WhereClause = {}; + let where: FindOptionsWhere = {}; if (params.lastId && params.excludedExecutionsIds?.length) { where.id = Raw((id) => `${id} < :lastId AND ${id} NOT IN (:...excludedExecutionsIds)`, { diff --git a/packages/cli/src/PublicApi/v1/handlers/users/users.service.ts b/packages/cli/src/PublicApi/v1/handlers/users/users.service.ts index 9a1c4b4dd0578..befc4326cbb37 100644 --- a/packages/cli/src/PublicApi/v1/handlers/users/users.service.ts +++ b/packages/cli/src/PublicApi/v1/handlers/users/users.service.ts @@ -7,7 +7,7 @@ export function isInstanceOwner(user: User): boolean { } export async function getWorkflowOwnerRole(): Promise { - return Db.collections.Role.findOneOrFail({ + return Db.collections.Role.findOneByOrFail({ name: 'owner', scope: 'workflow', }); diff --git a/packages/cli/src/PublicApi/v1/handlers/workflows/workflows.handler.ts b/packages/cli/src/PublicApi/v1/handlers/workflows/workflows.handler.ts index 0dc03d1c02f11..f758b26097051 100644 --- a/packages/cli/src/PublicApi/v1/handlers/workflows/workflows.handler.ts +++ b/packages/cli/src/PublicApi/v1/handlers/workflows/workflows.handler.ts @@ -1,6 +1,6 @@ import express from 'express'; -import { FindConditions, FindManyOptions, In } from 'typeorm'; +import { FindManyOptions, FindOptionsWhere, In } from 'typeorm'; import * as ActiveWorkflowRunner from '@/ActiveWorkflowRunner'; import config from '@/config'; @@ -100,7 +100,7 @@ export = { let workflows: WorkflowEntity[]; let count: number; - const where: FindConditions = { + const where: FindOptionsWhere = { ...(active !== undefined && { active }), }; const query: FindManyOptions = { 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 dbebeb5766c53..d3fd74c470aa0 100644 --- a/packages/cli/src/PublicApi/v1/handlers/workflows/workflows.service.ts +++ b/packages/cli/src/PublicApi/v1/handlers/workflows/workflows.service.ts @@ -17,7 +17,7 @@ function insertIf(condition: boolean, elements: string[]): string[] { export async function getSharedWorkflowIds(user: User): Promise { const sharedWorkflows = await Db.collections.SharedWorkflow.find({ - where: { user }, + where: { userId: user.id }, }); return sharedWorkflows.map(({ workflowId }) => workflowId); @@ -26,10 +26,10 @@ export async function getSharedWorkflowIds(user: User): Promise { export async function getSharedWorkflow( user: User, workflowId?: string | undefined, -): Promise { +): Promise { return Db.collections.SharedWorkflow.findOne({ where: { - ...(!isInstanceOwner(user) && { user }), + ...(!isInstanceOwner(user) && { userId: user.id }), ...(workflowId && { workflowId }), }, relations: [...insertIf(!config.getEnv('workflowTagsDisabled'), ['workflow.tags']), 'workflow'], @@ -45,14 +45,14 @@ export async function getSharedWorkflows( ): Promise { return Db.collections.SharedWorkflow.find({ where: { - ...(!isInstanceOwner(user) && { user }), + ...(!isInstanceOwner(user) && { userId: user.id }), ...(options.workflowIds && { workflowId: In(options.workflowIds) }), }, ...(options.relations && { relations: options.relations }), }); } -export async function getWorkflowById(id: string): Promise { +export async function getWorkflowById(id: string): Promise { return Db.collections.Workflow.findOne({ where: { id }, }); diff --git a/packages/cli/src/Server.ts b/packages/cli/src/Server.ts index a4b625b6f86ed..02278f08a7dc0 100644 --- a/packages/cli/src/Server.ts +++ b/packages/cli/src/Server.ts @@ -1246,9 +1246,9 @@ class Server extends AbstractServer { await queue.stopJob(job); } - const executionDb = (await Db.collections.Execution.findOne( - req.params.id, - )) as IExecutionFlattedDb; + const executionDb = (await Db.collections.Execution.findOneBy({ + id: req.params.id, + })) as IExecutionFlattedDb; const fullExecutionData = ResponseHelper.unflattenExecutionData(executionDb); const returnData: IExecutionsStopData = { @@ -1452,9 +1452,10 @@ export async function start(): Promise { // Set up event handling initEvents(); - const workflow = await Db.collections.Workflow!.findOne({ + const workflow = await Db.collections.Workflow.findOne({ select: ['createdAt'], order: { createdAt: 'ASC' }, + where: {}, }); await InternalHooksManager.getInstance().onServerStarted(diagnosticInfo, workflow?.createdAt); } diff --git a/packages/cli/src/TagHelpers.ts b/packages/cli/src/TagHelpers.ts index fc89e5d3b7a00..39313ea5aa356 100644 --- a/packages/cli/src/TagHelpers.ts +++ b/packages/cli/src/TagHelpers.ts @@ -1,10 +1,10 @@ /* eslint-disable no-param-reassign */ /* eslint-disable @typescript-eslint/explicit-module-boundary-types */ -import { EntityManager, getConnection } from 'typeorm'; +import type { EntityManager } from 'typeorm'; +import { getConnection } from '@/Db'; import { TagEntity } from '@db/entities/TagEntity'; - -import { ITagToImport, ITagWithCountDb, IWorkflowToImport } from '@/Interfaces'; +import type { ITagToImport, ITagWithCountDb, IWorkflowToImport } from '@/Interfaces'; // ---------------------------------- // utils diff --git a/packages/cli/src/UserManagement/PermissionChecker.ts b/packages/cli/src/UserManagement/PermissionChecker.ts index 332994ffe1ff7..7073eb524701f 100644 --- a/packages/cli/src/UserManagement/PermissionChecker.ts +++ b/packages/cli/src/UserManagement/PermissionChecker.ts @@ -5,11 +5,11 @@ import { Workflow, WorkflowOperationError, } from 'n8n-workflow'; -import { FindConditions, In } from 'typeorm'; +import { FindOptionsWhere, In } from 'typeorm'; import * as Db from '@/Db'; import config from '@/config'; import type { SharedCredentials } from '@db/entities/SharedCredentials'; -import { getRole, getWorkflowOwner, isSharingEnabled } from './UserManagementHelper'; +import { getRoleId, getWorkflowOwner, isSharingEnabled } from './UserManagementHelper'; import { WorkflowsService } from '@/workflows/workflows.services'; import { UserService } from '@/user/user.service'; @@ -28,7 +28,8 @@ export class PermissionChecker { // allow if requesting user is instance owner - const user = await Db.collections.User.findOneOrFail(userId, { + const user = await Db.collections.User.findOneOrFail({ + where: { id: userId }, relations: ['globalRole'], }); @@ -47,11 +48,11 @@ export class PermissionChecker { workflowUserIds = workflowSharings.map((s) => s.userId); } - const credentialsWhere: FindConditions = { userId: In(workflowUserIds) }; + const credentialsWhere: FindOptionsWhere = { userId: In(workflowUserIds) }; if (!isSharingEnabled()) { // If credential sharing is not enabled, get only credentials owned by this user - credentialsWhere.role = await getRole('credential', 'owner'); + credentialsWhere.roleId = await getRoleId('credential', 'owner'); } const credentialSharings = await Db.collections.SharedCredentials.find({ diff --git a/packages/cli/src/UserManagement/UserManagementHelper.ts b/packages/cli/src/UserManagement/UserManagementHelper.ts index 4116282dcbdcb..2123bcdfea3e4 100644 --- a/packages/cli/src/UserManagement/UserManagementHelper.ts +++ b/packages/cli/src/UserManagement/UserManagementHelper.ts @@ -1,6 +1,5 @@ /* eslint-disable @typescript-eslint/no-unused-vars */ /* eslint-disable @typescript-eslint/no-non-null-assertion */ -import { INode, NodeOperationError, Workflow } from 'n8n-workflow'; import { In } from 'typeorm'; import express from 'express'; import { compare, genSaltSync, hash } from 'bcryptjs'; @@ -21,7 +20,7 @@ export async function getWorkflowOwner(workflowId: string): Promise { const workflowOwnerRole = await RoleService.get({ name: 'owner', scope: 'workflow' }); const sharedWorkflow = await Db.collections.SharedWorkflow.findOneOrFail({ - where: { workflowId, role: workflowOwnerRole }, + where: { workflowId, roleId: workflowOwnerRole?.id ?? undefined }, relations: ['user', 'user.globalRole'], }); @@ -59,37 +58,28 @@ export function isUserManagementDisabled(): boolean { ); } -async function getInstanceOwnerRole(): Promise { - const ownerRole = await Db.collections.Role.findOneOrFail({ +export async function getRoleId(scope: Role['scope'], name: Role['name']): Promise { + return Db.collections.Role.findOneOrFail({ + select: ['id'], where: { - name: 'owner', - scope: 'global', + name, + scope, }, - }); - return ownerRole; + }).then((role) => role.id); } export async function getInstanceOwner(): Promise { - const ownerRole = await getInstanceOwnerRole(); + const ownerRoleId = await getRoleId('global', 'owner'); const owner = await Db.collections.User.findOneOrFail({ relations: ['globalRole'], where: { - globalRole: ownerRole, + globalRoleId: ownerRoleId, }, }); return owner; } -export async function getRole(scope: Role['scope'], name: Role['name']): Promise { - return Db.collections.Role.findOneOrFail({ - where: { - name, - scope, - }, - }); -} - /** * Return the n8n instance base URL without trailing slash. */ @@ -168,7 +158,8 @@ export function addInviteLinktoUser(user: PublicUser, inviterId: string): Public } export async function getUserById(userId: string): Promise { - const user = await Db.collections.User.findOneOrFail(userId, { + const user = await Db.collections.User.findOneOrFail({ + where: { id: userId }, relations: ['globalRole'], }); return user; diff --git a/packages/cli/src/UserManagement/auth/jwt.ts b/packages/cli/src/UserManagement/auth/jwt.ts index fb3eea487dd6c..63309657ad63e 100644 --- a/packages/cli/src/UserManagement/auth/jwt.ts +++ b/packages/cli/src/UserManagement/auth/jwt.ts @@ -36,7 +36,8 @@ export function issueJWT(user: User): JwtToken { } export async function resolveJwtContent(jwtPayload: JwtPayload): Promise { - const user = await Db.collections.User.findOne(jwtPayload.id, { + const user = await Db.collections.User.findOne({ + where: { id: jwtPayload.id }, relations: ['globalRole'], }); diff --git a/packages/cli/src/UserManagement/routes/auth.ts b/packages/cli/src/UserManagement/routes/auth.ts index 27486cceaf060..388789d1e3a08 100644 --- a/packages/cli/src/UserManagement/routes/auth.ts +++ b/packages/cli/src/UserManagement/routes/auth.ts @@ -30,14 +30,12 @@ export function authenticationMethods(this: N8nApp): void { throw new Error('Password is required to log in'); } - let user: User | undefined; + let user: User | null; try { - user = await Db.collections.User.findOne( - { email }, - { - relations: ['globalRole'], - }, - ); + user = await Db.collections.User.findOne({ + where: { email }, + relations: ['globalRole'], + }); } catch (error) { throw new Error('Unable to access database.'); } @@ -77,7 +75,7 @@ export function authenticationMethods(this: N8nApp): void { } try { - user = await Db.collections.User.findOneOrFail({ relations: ['globalRole'] }); + user = await Db.collections.User.findOneOrFail({ relations: ['globalRole'], where: {} }); } catch (error) { throw new ResponseHelper.InternalServerError( 'No users found in database - did you wipe the users table? Create at least one user.', diff --git a/packages/cli/src/UserManagement/routes/index.ts b/packages/cli/src/UserManagement/routes/index.ts index 2725471b6aa18..0e584413b783d 100644 --- a/packages/cli/src/UserManagement/routes/index.ts +++ b/packages/cli/src/UserManagement/routes/index.ts @@ -54,12 +54,10 @@ export function addRoutes(this: N8nApp, ignoredEndpoints: string[], restEndpoint // skip authentication if user management is disabled if (isUserManagementDisabled()) { - req.user = await Db.collections.User.findOneOrFail( - {}, - { - relations: ['globalRole'], - }, - ); + req.user = await Db.collections.User.findOneOrFail({ + relations: ['globalRole'], + where: {}, + }); return next(); } diff --git a/packages/cli/src/UserManagement/routes/owner.ts b/packages/cli/src/UserManagement/routes/owner.ts index 84c159009fff9..21511db258df8 100644 --- a/packages/cli/src/UserManagement/routes/owner.ts +++ b/packages/cli/src/UserManagement/routes/owner.ts @@ -52,8 +52,9 @@ export function ownerNamespace(this: N8nApp): void { throw new ResponseHelper.BadRequestError('First and last names are mandatory'); } - let owner = await Db.collections.User.findOne(userId, { + let owner = await Db.collections.User.findOne({ relations: ['globalRole'], + where: { id: userId }, }); if (!owner || (owner.globalRole.scope === 'global' && owner.globalRole.name !== 'owner')) { diff --git a/packages/cli/src/UserManagement/routes/passwordReset.ts b/packages/cli/src/UserManagement/routes/passwordReset.ts index 0410f3107ad92..d6d97c71e3497 100644 --- a/packages/cli/src/UserManagement/routes/passwordReset.ts +++ b/packages/cli/src/UserManagement/routes/passwordReset.ts @@ -52,7 +52,7 @@ export function passwordResetNamespace(this: N8nApp): void { } // User should just be able to reset password if one is already present - const user = await Db.collections.User.findOne({ email, password: Not(IsNull()) }); + const user = await Db.collections.User.findOneBy({ email, password: Not(IsNull()) }); if (!user?.password) { Logger.debug( @@ -133,7 +133,7 @@ export function passwordResetNamespace(this: N8nApp): void { // Timestamp is saved in seconds const currentTimestamp = Math.floor(Date.now() / 1000); - const user = await Db.collections.User.findOne({ + const user = await Db.collections.User.findOneBy({ id, resetPasswordToken, resetPasswordTokenExpiration: MoreThanOrEqual(currentTimestamp), @@ -184,7 +184,7 @@ export function passwordResetNamespace(this: N8nApp): void { // Timestamp is saved in seconds const currentTimestamp = Math.floor(Date.now() / 1000); - const user = await Db.collections.User.findOne({ + const user = await Db.collections.User.findOneBy({ id: userId, resetPasswordToken, resetPasswordTokenExpiration: MoreThanOrEqual(currentTimestamp), diff --git a/packages/cli/src/UserManagement/routes/users.ts b/packages/cli/src/UserManagement/routes/users.ts index a19eb30db6787..c3e707d6290d2 100644 --- a/packages/cli/src/UserManagement/routes/users.ts +++ b/packages/cli/src/UserManagement/routes/users.ts @@ -85,7 +85,7 @@ export function usersNamespace(this: N8nApp): void { createUsers[invite.email.toLowerCase()] = null; }); - const role = await Db.collections.Role.findOne({ scope: 'global', name: 'member' }); + const role = await Db.collections.Role.findOneBy({ scope: 'global', name: 'member' }); if (!role) { Logger.error( @@ -434,7 +434,7 @@ export function usersNamespace(this: N8nApp): void { .getRepository(SharedWorkflow) .find({ select: ['workflowId'], - where: { userId: userToDelete.id, role: workflowOwnerRole }, + where: { userId: userToDelete.id, roleId: workflowOwnerRole?.id }, }) .then((sharedWorkflows) => sharedWorkflows.map(({ workflowId }) => workflowId)); @@ -459,7 +459,7 @@ export function usersNamespace(this: N8nApp): void { .getRepository(SharedCredentials) .find({ select: ['credentialsId'], - where: { user: userToDelete, role: credentialOwnerRole }, + where: { userId: userToDelete.id, roleId: credentialOwnerRole?.id }, }) .then((sharedCredentials) => sharedCredentials.map(({ credentialsId }) => credentialsId), @@ -495,11 +495,11 @@ export function usersNamespace(this: N8nApp): void { const [ownedSharedWorkflows, ownedSharedCredentials] = await Promise.all([ Db.collections.SharedWorkflow.find({ relations: ['workflow'], - where: { user: userToDelete, role: workflowOwnerRole }, + where: { userId: userToDelete.id, roleId: workflowOwnerRole?.id }, }), Db.collections.SharedCredentials.find({ relations: ['credentials'], - where: { user: userToDelete, role: credentialOwnerRole }, + where: { userId: userToDelete.id, roleId: credentialOwnerRole?.id }, }), ]); @@ -546,7 +546,7 @@ export function usersNamespace(this: N8nApp): void { ); } - const reinvitee = await Db.collections.User.findOne({ id: idToReinvite }); + const reinvitee = await Db.collections.User.findOneBy({ id: idToReinvite }); if (!reinvitee) { Logger.debug( diff --git a/packages/cli/src/WaitTracker.ts b/packages/cli/src/WaitTracker.ts index df86526421dd1..63ee67a06d5cd 100644 --- a/packages/cli/src/WaitTracker.ts +++ b/packages/cli/src/WaitTracker.ts @@ -107,9 +107,9 @@ export class WaitTrackerClass { } // Also check in database - const execution = await Db.collections.Execution.findOne(executionId); + const execution = await Db.collections.Execution.findOneBy({ id: executionId }); - if (execution === undefined || !execution.waitTill) { + if (execution === null || !execution.waitTill) { throw new Error(`The execution ID "${executionId}" could not be found.`); } @@ -146,9 +146,11 @@ export class WaitTrackerClass { (async () => { // Get the data to execute - const fullExecutionDataFlatted = await Db.collections.Execution.findOne(executionId); + const fullExecutionDataFlatted = await Db.collections.Execution.findOneBy({ + id: executionId, + }); - if (fullExecutionDataFlatted === undefined) { + if (fullExecutionDataFlatted === null) { throw new Error(`The execution with the id "${executionId}" does not exist.`); } diff --git a/packages/cli/src/WaitingWebhooks.ts b/packages/cli/src/WaitingWebhooks.ts index 5915cf89879dd..7f8cee74a96c5 100644 --- a/packages/cli/src/WaitingWebhooks.ts +++ b/packages/cli/src/WaitingWebhooks.ts @@ -41,9 +41,9 @@ export class WaitingWebhooks { const executionId = pathParts.shift(); const path = pathParts.join('/'); - const execution = await Db.collections.Execution.findOne(executionId); + const execution = await Db.collections.Execution.findOneBy({ id: executionId }); - if (execution === undefined) { + if (execution === null) { throw new ResponseHelper.NotFoundError(`The execution "${executionId} does not exist.`); } diff --git a/packages/cli/src/WorkflowCredentials.ts b/packages/cli/src/WorkflowCredentials.ts index 87f3c2dcf9f89..228c7b9485088 100644 --- a/packages/cli/src/WorkflowCredentials.ts +++ b/packages/cli/src/WorkflowCredentials.ts @@ -33,7 +33,7 @@ export async function WorkflowCredentials(nodes: INode[]): Promise { // Wrap everything in try/catch to make sure that no errors bubble up and all get caught here try { - let workflowData; + let workflowData: WorkflowEntity | null = null; if (workflowId !== workflowErrorData.workflow.id) { // To make this code easier to understand, we split it in 2 parts: // 1) Fetch the owner of the errored workflows and then @@ -99,7 +99,7 @@ export async function executeErrorWorkflow( const user = await getWorkflowOwner(workflowErrorData.workflow.id!); if (user.globalRole.name === 'owner') { - workflowData = await Db.collections.Workflow.findOne({ id: workflowId }); + workflowData = await Db.collections.Workflow.findOneBy({ id: workflowId }); } else { const sharedWorkflowData = await Db.collections.SharedWorkflow.findOne({ where: { workflowId, userId: user.id }, @@ -110,10 +110,10 @@ export async function executeErrorWorkflow( } } } else { - workflowData = await Db.collections.Workflow.findOne({ id: workflowId }); + workflowData = await Db.collections.Workflow.findOneBy({ id: workflowId }); } - if (workflowData === undefined) { + if (workflowData === null) { // The error workflow could not be found Logger.error( // eslint-disable-next-line @typescript-eslint/restrict-template-expressions @@ -254,21 +254,13 @@ export async function saveStaticDataById( /** * Returns the static data of workflow - * - * @param {(string)} workflowId The id of the workflow to get static data of */ -// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types export async function getStaticDataById(workflowId: string) { - const workflowData = await Db.collections.Workflow.findOne(workflowId, { + const workflowData = await Db.collections.Workflow.findOne({ select: ['staticData'], + where: { id: workflowId }, }); - - if (workflowData === undefined) { - return {}; - } - - // eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing - return workflowData.staticData || {}; + return workflowData?.staticData ?? {}; } /** @@ -312,7 +304,7 @@ export async function replaceInvalidCredentials(workflow: WorkflowEntity): Promi credentialsByName[nodeCredentialType] = {}; } if (credentialsByName[nodeCredentialType][name] === undefined) { - const credentials = await Db.collections.Credentials.find({ + const credentials = await Db.collections.Credentials.findBy({ name, type: nodeCredentialType, }); @@ -348,7 +340,7 @@ export async function replaceInvalidCredentials(workflow: WorkflowEntity): Promi // check if credentials for ID-type are not yet cached if (credentialsById[nodeCredentialType][nodeCredentials.id] === undefined) { // check first if ID-type combination exists - const credentials = await Db.collections.Credentials.findOne({ + const credentials = await Db.collections.Credentials.findOneBy({ id: nodeCredentials.id, type: nodeCredentialType, }); @@ -362,7 +354,7 @@ export async function replaceInvalidCredentials(workflow: WorkflowEntity): Promi continue; } // no credentials found for ID, check if some exist for name - const credsByName = await Db.collections.Credentials.find({ + const credsByName = await Db.collections.Credentials.findBy({ name: nodeCredentials.name, type: nodeCredentialType, }); @@ -414,14 +406,17 @@ export async function isBelowOnboardingThreshold(user: User): Promise { let belowThreshold = true; const skippedTypes = ['n8n-nodes-base.start', 'n8n-nodes-base.stickyNote']; - const workflowOwnerRole = await Db.collections.Role.findOne({ - name: 'owner', - scope: 'workflow', - }); + const workflowOwnerRoleId = await Db.collections.Role.findOne({ + select: ['id'], + where: { + name: 'owner', + scope: 'workflow', + }, + }).then((role) => role?.id); const ownedWorkflowsIds = await Db.collections.SharedWorkflow.find({ where: { - user, - role: workflowOwnerRole, + userId: user.id, + roleId: workflowOwnerRoleId, }, select: ['workflowId'], }).then((ownedWorkflows) => ownedWorkflows.map(({ workflowId }) => workflowId)); diff --git a/packages/cli/src/WorkflowRunner.ts b/packages/cli/src/WorkflowRunner.ts index 019d3655f6579..b8e97df541f87 100644 --- a/packages/cli/src/WorkflowRunner.ts +++ b/packages/cli/src/WorkflowRunner.ts @@ -527,9 +527,9 @@ export class WorkflowRunner { reject(error); } - const executionDb = (await Db.collections.Execution.findOne( - executionId, - )) as IExecutionFlattedDb; + const executionDb = (await Db.collections.Execution.findOneBy({ + id: executionId, + })) as IExecutionFlattedDb; const fullExecutionData = ResponseHelper.unflattenExecutionData(executionDb); const runData = { data: fullExecutionData.data, diff --git a/packages/cli/src/api/e2e.api.ts b/packages/cli/src/api/e2e.api.ts index d5c80349cbd84..27752cc9ae764 100644 --- a/packages/cli/src/api/e2e.api.ts +++ b/packages/cli/src/api/e2e.api.ts @@ -99,11 +99,14 @@ e2eController.post('/db/setup-owner', bodyParser.json(), async (req, res) => { } const globalRole = await Db.collections.Role.findOneOrFail({ - name: 'owner', - scope: 'global', + select: ['id'], + where: { + name: 'owner', + scope: 'global', + }, }); - const owner = await Db.collections.User.findOneOrFail({ globalRole }); + const owner = await Db.collections.User.findOneByOrFail({ globalRoleId: globalRole.id }); await Db.collections.User.update(owner.id, { email: req.body.email, diff --git a/packages/cli/src/audit/risks/credentials.risk.ts b/packages/cli/src/audit/risks/credentials.risk.ts index d3c03162cf4d9..df39f24fe3e28 100644 --- a/packages/cli/src/audit/risks/credentials.risk.ts +++ b/packages/cli/src/audit/risks/credentials.risk.ts @@ -1,4 +1,4 @@ -import { MoreThanOrEqual } from 'typeorm'; +import { FindOperator, MoreThanOrEqual } from 'typeorm'; import { DateUtils } from 'typeorm/util/DateUtils'; import * as Db from '@/Db'; import config from '@/config'; @@ -46,7 +46,7 @@ async function getExecutionsInPastDays(days: number) { return Db.collections.Execution.find({ select: ['workflowData'], where: { - startedAt: MoreThanOrEqual(utcDate), + startedAt: MoreThanOrEqual(utcDate) as unknown as FindOperator, }, }); } diff --git a/packages/cli/src/commands/BaseCommand.ts b/packages/cli/src/commands/BaseCommand.ts index ff9c47d9a1fcb..07467eea2c321 100644 --- a/packages/cli/src/commands/BaseCommand.ts +++ b/packages/cli/src/commands/BaseCommand.ts @@ -38,12 +38,12 @@ export abstract class BaseCommand extends Command { }; async getInstanceOwner(): Promise { - const globalRole = await Db.collections.Role.findOneOrFail({ + const globalRole = await Db.collections.Role.findOneByOrFail({ name: 'owner', scope: 'global', }); - const owner = await Db.collections.User.findOne({ globalRole }); + const owner = await Db.collections.User.findOneBy({ globalRoleId: globalRole.id }); if (owner) return owner; @@ -53,6 +53,6 @@ export abstract class BaseCommand extends Command { await Db.collections.User.save(user); - return Db.collections.User.findOneOrFail({ globalRole }); + return Db.collections.User.findOneByOrFail({ globalRoleId: globalRole.id }); } } diff --git a/packages/cli/src/commands/db/revert.ts b/packages/cli/src/commands/db/revert.ts index 888f6b2e489d8..856d04550a075 100644 --- a/packages/cli/src/commands/db/revert.ts +++ b/packages/cli/src/commands/db/revert.ts @@ -1,7 +1,7 @@ /* eslint-disable @typescript-eslint/no-unsafe-member-access */ /* eslint-disable no-console */ import { Command, flags } from '@oclif/command'; -import { Connection, ConnectionOptions, createConnection } from 'typeorm'; +import { DataSource as Connection, DataSourceOptions as ConnectionOptions } from 'typeorm'; import { LoggerProxy } from 'n8n-workflow'; import { getLogger } from '@/Logger'; @@ -34,11 +34,12 @@ export class DbRevertMigrationCommand extends Command { dropSchema: false, logging: ['query', 'error', 'schema'], }; - connection = await createConnection(connectionOptions); + connection = new Connection(connectionOptions); + await connection.initialize(); await connection.undoLastMigration(); - await connection.close(); + await connection.destroy(); } catch (error) { - if (connection) await connection.close(); + if (connection?.isInitialized) await connection.destroy(); console.error('Error reverting last migration. See log messages for details.'); // eslint-disable-next-line @typescript-eslint/no-unsafe-argument diff --git a/packages/cli/src/commands/execute.ts b/packages/cli/src/commands/execute.ts index cb5475428e828..76d4da4297fc4 100644 --- a/packages/cli/src/commands/execute.ts +++ b/packages/cli/src/commands/execute.ts @@ -72,7 +72,7 @@ export class Execute extends Command { } let workflowId: string | undefined; - let workflowData: IWorkflowBase | undefined; + let workflowData: IWorkflowBase | null = null; if (flags.file) { // Path to workflow is given try { @@ -91,7 +91,7 @@ export class Execute extends Command { // Do a basic check if the data in the file looks right // TODO: Later check with the help of TypeScript data if it is valid or not if ( - workflowData === undefined || + workflowData === null || workflowData.nodes === undefined || workflowData.connections === undefined ) { @@ -108,8 +108,8 @@ export class Execute extends Command { if (flags.id) { // Id of workflow is given workflowId = flags.id; - workflowData = await Db.collections.Workflow.findOne(workflowId); - if (workflowData === undefined) { + workflowData = await Db.collections.Workflow.findOneBy({ id: workflowId }); + if (workflowData === null) { console.info(`The workflow with the id "${workflowId}" does not exist.`); process.exit(1); } diff --git a/packages/cli/src/commands/import/credentials.ts b/packages/cli/src/commands/import/credentials.ts index bcfc62ce120eb..3d834396c5179 100644 --- a/packages/cli/src/commands/import/credentials.ts +++ b/packages/cli/src/commands/import/credentials.ts @@ -14,7 +14,7 @@ import { LoggerProxy } from 'n8n-workflow'; import fs from 'fs'; import glob from 'fast-glob'; -import { EntityManager, getConnection } from 'typeorm'; +import type { EntityManager } from 'typeorm'; import { getLogger } from '@/Logger'; import * as Db from '@/Db'; import { User } from '@db/entities/User'; @@ -100,7 +100,7 @@ export class ImportCredentialsCommand extends Command { totalImported = files.length; - await getConnection().transaction(async (transactionManager) => { + await Db.getConnection().transaction(async (transactionManager) => { this.transactionManager = transactionManager; for (const file of files) { const credential = JSON.parse(fs.readFileSync(file, { encoding: 'utf8' })); @@ -128,7 +128,7 @@ export class ImportCredentialsCommand extends Command { ); } - await getConnection().transaction(async (transactionManager) => { + await Db.getConnection().transaction(async (transactionManager) => { this.transactionManager = transactionManager; for (const credential of credentials) { if (typeof credential.data === 'object') { @@ -187,7 +187,9 @@ export class ImportCredentialsCommand extends Command { where: { name: 'owner', scope: 'global' }, }); - const owner = await Db.collections.User.findOne({ globalRole: ownerGlobalRole }); + const owner = + ownerGlobalRole && + (await Db.collections.User.findOneBy({ globalRoleId: ownerGlobalRole.id })); if (!owner) { throw new Error(`Failed to find owner. ${FIX_INSTRUCTION}`); @@ -197,7 +199,7 @@ export class ImportCredentialsCommand extends Command { } private async getAssignee(userId: string) { - const user = await Db.collections.User.findOne(userId); + const user = await Db.collections.User.findOneBy({ id: userId }); if (!user) { throw new Error(`Failed to find user with ID ${userId}`); diff --git a/packages/cli/src/commands/import/workflow.ts b/packages/cli/src/commands/import/workflow.ts index e38a1dc9d16cd..27447ca4645d9 100644 --- a/packages/cli/src/commands/import/workflow.ts +++ b/packages/cli/src/commands/import/workflow.ts @@ -16,7 +16,7 @@ import { INode, INodeCredentialsDetails, LoggerProxy } from 'n8n-workflow'; import fs from 'fs'; import glob from 'fast-glob'; import { UserSettings } from 'n8n-core'; -import { EntityManager, getConnection } from 'typeorm'; +import type { EntityManager } from 'typeorm'; import { v4 as uuid } from 'uuid'; import { getLogger } from '@/Logger'; import * as Db from '@/Db'; @@ -123,7 +123,7 @@ export class ImportWorkflowsCommand extends Command { totalImported = files.length; - await getConnection().transaction(async (transactionManager) => { + await Db.getConnection().transaction(async (transactionManager) => { this.transactionManager = transactionManager; for (const file of files) { @@ -158,7 +158,7 @@ export class ImportWorkflowsCommand extends Command { totalImported = workflows.length; - await getConnection().transaction(async (transactionManager) => { + await Db.getConnection().transaction(async (transactionManager) => { this.transactionManager = transactionManager; for (const workflow of workflows) { @@ -229,7 +229,9 @@ export class ImportWorkflowsCommand extends Command { where: { name: 'owner', scope: 'global' }, }); - const owner = await Db.collections.User.findOne({ globalRole: ownerGlobalRole }); + const owner = + ownerGlobalRole && + (await Db.collections.User.findOneBy({ globalRoleId: ownerGlobalRole?.id })); if (!owner) { throw new Error(`Failed to find owner. ${FIX_INSTRUCTION}`); @@ -239,7 +241,7 @@ export class ImportWorkflowsCommand extends Command { } private async getAssignee(userId: string) { - const user = await Db.collections.User.findOne(userId); + const user = await Db.collections.User.findOneBy({ id: userId }); if (!user) { throw new Error(`Failed to find user with ID ${userId}`); diff --git a/packages/cli/src/commands/start.ts b/packages/cli/src/commands/start.ts index 32d66158dcc54..1c47c39e6abcf 100644 --- a/packages/cli/src/commands/start.ts +++ b/packages/cli/src/commands/start.ts @@ -304,7 +304,7 @@ export class Start extends Command { await UserSettings.getEncryptionKey(); // Load settings from database and set them to config. - const databaseSettings = await Db.collections.Settings.find({ loadOnStartup: true }); + const databaseSettings = await Db.collections.Settings.findBy({ loadOnStartup: true }); databaseSettings.forEach((setting) => { config.set(setting.key, JSON.parse(setting.value)); }); diff --git a/packages/cli/src/commands/user-management/reset.ts b/packages/cli/src/commands/user-management/reset.ts index a111b57dfb5cb..be0f715c477ff 100644 --- a/packages/cli/src/commands/user-management/reset.ts +++ b/packages/cli/src/commands/user-management/reset.ts @@ -9,23 +9,23 @@ export class Reset extends BaseCommand { async run(): Promise { const owner = await this.getInstanceOwner(); - const ownerWorkflowRole = await Db.collections.Role.findOneOrFail({ + const ownerWorkflowRole = await Db.collections.Role.findOneByOrFail({ name: 'owner', scope: 'workflow', }); - const ownerCredentialRole = await Db.collections.Role.findOneOrFail({ + const ownerCredentialRole = await Db.collections.Role.findOneByOrFail({ name: 'owner', scope: 'credential', }); await Db.collections.SharedWorkflow.update( - { user: { id: Not(owner.id) }, role: ownerWorkflowRole }, + { userId: Not(owner.id), roleId: ownerWorkflowRole.id }, { user: owner }, ); await Db.collections.SharedCredentials.update( - { user: { id: Not(owner.id) }, role: ownerCredentialRole }, + { userId: Not(owner.id), roleId: ownerCredentialRole.id }, { user: owner }, ); diff --git a/packages/cli/src/commands/worker.ts b/packages/cli/src/commands/worker.ts index 67d024f25421c..5478543e74e2b 100644 --- a/packages/cli/src/commands/worker.ts +++ b/packages/cli/src/commands/worker.ts @@ -20,8 +20,6 @@ import { sleep, } from 'n8n-workflow'; -import { FindOneOptions, getConnectionManager } from 'typeorm'; - import { CredentialsOverwrites } from '@/CredentialsOverwrites'; import { CredentialTypes } from '@/CredentialTypes'; import * as Db from '@/Db'; @@ -125,7 +123,7 @@ export class Worker extends Command { async runJob(job: Queue.Job, nodeTypes: INodeTypes): Promise { const { executionId, loadStaticData } = job.data; - const executionDb = await Db.collections.Execution.findOne(executionId); + const executionDb = await Db.collections.Execution.findOneBy({ id: executionId }); if (!executionDb) { LoggerProxy.error( @@ -145,14 +143,13 @@ export class Worker extends Command { let { staticData } = currentExecutionDb.workflowData; if (loadStaticData) { - const findOptions = { + const workflowData = await Db.collections.Workflow.findOne({ select: ['id', 'staticData'], - } as FindOneOptions; - const workflowData = await Db.collections.Workflow.findOne( - currentExecutionDb.workflowData.id, - findOptions, - ); - if (workflowData === undefined) { + where: { + id: currentExecutionDb.workflowData.id, + }, + }); + if (workflowData === null) { LoggerProxy.error( 'Worker execution failed because workflow could not be found in database.', { @@ -384,10 +381,10 @@ export class Worker extends Command { async (req: express.Request, res: express.Response) => { LoggerProxy.debug('Health check started!'); - const connection = getConnectionManager().get(); + const connection = Db.getConnection(); try { - if (!connection.isConnected) { + if (!connection.isInitialized) { // Connection is not active throw new Error('No active database connection!'); } diff --git a/packages/cli/src/credentials/credentials.controller.ts b/packages/cli/src/credentials/credentials.controller.ts index becdf92740854..aef54d10d1067 100644 --- a/packages/cli/src/credentials/credentials.controller.ts +++ b/packages/cli/src/credentials/credentials.controller.ts @@ -177,7 +177,7 @@ credentialsController.patch( const responseData = await CredentialsService.update(credentialId, newCredentialData); - if (responseData === undefined) { + if (responseData === null) { throw new ResponseHelper.NotFoundError( `Credential ID "${credentialId}" could not be found to be updated.`, ); diff --git a/packages/cli/src/credentials/credentials.service.ee.ts b/packages/cli/src/credentials/credentials.service.ee.ts index cb196a5a8b940..0d340f696fa15 100644 --- a/packages/cli/src/credentials/credentials.service.ee.ts +++ b/packages/cli/src/credentials/credentials.service.ee.ts @@ -1,5 +1,5 @@ /* eslint-disable no-param-reassign */ -import { DeleteResult, EntityManager, FindConditions, In, Not } from 'typeorm'; +import { DeleteResult, EntityManager, FindOptionsWhere, In, Not } from 'typeorm'; import * as Db from '@/Db'; import { RoleService } from '@/role/role.service'; import { CredentialsEntity } from '@db/entities/CredentialsEntity'; @@ -33,14 +33,14 @@ export class EECredentialsService extends CredentialsService { credentialId: string, relations: string[] = ['credentials'], { allowGlobalOwner } = { allowGlobalOwner: true }, - ): Promise { - const where: FindConditions = { credentialsId: credentialId }; + ): Promise { + const where: FindOptionsWhere = { credentialsId: credentialId }; // Omit user from where if the requesting user is the global // owner. This allows the global owner to view and delete // credentials they don't own. if (!allowGlobalOwner || user.globalRole.name !== 'owner') { - where.user = { id: user.id }; + where.userId = user.id; } return Db.collections.SharedCredentials.findOne({ @@ -65,7 +65,7 @@ export class EECredentialsService extends CredentialsService { credentialId: string, userIds: string[], ): Promise { - const conditions: FindConditions = { + const conditions: FindOptionsWhere = { credentialsId: credentialId, userId: Not(In(userIds)), }; @@ -86,9 +86,9 @@ export class EECredentialsService extends CredentialsService { .filter((user) => !user.isPending) .map((user) => Db.collections.SharedCredentials.create({ - credentials: credential, - user, - role, + credentialsId: credential.id, + userId: user.id, + roleId: role?.id, }), ); diff --git a/packages/cli/src/credentials/credentials.service.ts b/packages/cli/src/credentials/credentials.service.ts index 594e504ae96d5..c9749dcb4fe7c 100644 --- a/packages/cli/src/credentials/credentials.service.ts +++ b/packages/cli/src/credentials/credentials.service.ts @@ -10,7 +10,7 @@ import { LoggerProxy, NodeHelpers, } from 'n8n-workflow'; -import { FindConditions, FindManyOptions, In } from 'typeorm'; +import { FindManyOptions, FindOptionsWhere, In } from 'typeorm'; import * as Db from '@/Db'; import * as ResponseHelper from '@/ResponseHelper'; @@ -28,11 +28,12 @@ import { CredentialTypes } from '@/CredentialTypes'; export class CredentialsService { static async get( - credential: Partial, + where: FindOptionsWhere, options?: { relations: string[] }, - ): Promise { - return Db.collections.Credentials.findOne(credential, { + ): Promise { + return Db.collections.Credentials.findOne({ relations: options?.relations, + where, }); } @@ -88,8 +89,8 @@ export class CredentialsService { credentialId: string, relations: string[] = ['credentials'], { allowGlobalOwner } = { allowGlobalOwner: true }, - ): Promise { - const where: FindConditions = { credentialsId: credentialId }; + ): Promise { + const where: FindOptionsWhere = { credentialsId: credentialId }; // Omit user from where if the requesting user is the global // owner. This allows the global owner to view and delete @@ -204,7 +205,7 @@ export class CredentialsService { static async update( credentialId: string, newCredentialData: ICredentialsDb, - ): Promise { + ): Promise { await ExternalHooks().run('credentials.update', [newCredentialData]); // Update the credentials in DB @@ -212,7 +213,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 Db.collections.Credentials.findOne(credentialId); + return Db.collections.Credentials.findOneBy({ id: credentialId }); } static async save( @@ -226,7 +227,7 @@ export class CredentialsService { await ExternalHooks().run('credentials.create', [encryptedData]); - const role = await Db.collections.Role.findOneOrFail({ + const role = await Db.collections.Role.findOneByOrFail({ name: 'owner', scope: 'credential', }); diff --git a/packages/cli/src/databases/entities/Role.ts b/packages/cli/src/databases/entities/Role.ts index da9294a90a71e..b1943866d48d8 100644 --- a/packages/cli/src/databases/entities/Role.ts +++ b/packages/cli/src/databases/entities/Role.ts @@ -1,10 +1,11 @@ -import { Column, Entity, OneToMany, PrimaryGeneratedColumn, Unique } from 'typeorm'; +import { Column, Entity, OneToMany, PrimaryColumn, Unique } from 'typeorm'; import { IsString, Length } from 'class-validator'; import type { User } from './User'; import type { SharedWorkflow } from './SharedWorkflow'; import type { SharedCredentials } from './SharedCredentials'; import { AbstractEntity } from './AbstractEntity'; +import { idStringifier } from '../utils/transformers'; export type RoleNames = 'owner' | 'member' | 'user' | 'editor'; export type RoleScopes = 'global' | 'workflow' | 'credential'; @@ -12,8 +13,8 @@ export type RoleScopes = 'global' | 'workflow' | 'credential'; @Entity() @Unique(['scope', 'name']) export class Role extends AbstractEntity { - @PrimaryGeneratedColumn() - id: number; + @PrimaryColumn({ transformer: idStringifier }) + id: string; @Column({ length: 32 }) @IsString({ message: 'Role name must be of type string.' }) diff --git a/packages/cli/src/databases/entities/SharedCredentials.ts b/packages/cli/src/databases/entities/SharedCredentials.ts index c65ca623067b5..f46b4ad078306 100644 --- a/packages/cli/src/databases/entities/SharedCredentials.ts +++ b/packages/cli/src/databases/entities/SharedCredentials.ts @@ -1,4 +1,4 @@ -import { Entity, ManyToOne, PrimaryColumn, RelationId } from 'typeorm'; +import { Column, Entity, ManyToOne, PrimaryColumn } from 'typeorm'; import type { CredentialsEntity } from './CredentialsEntity'; import type { User } from './User'; import type { Role } from './Role'; @@ -10,20 +10,18 @@ export class SharedCredentials extends AbstractEntity { @ManyToOne('Role', 'sharedCredentials', { nullable: false }) role: Role; - @ManyToOne('User', 'sharedCredentials', { primary: true }) + @Column() + roleId: string; + + @ManyToOne('User', 'sharedCredentials') user: User; @PrimaryColumn() - @RelationId((sharedCredential: SharedCredentials) => sharedCredential.user) userId: string; - @ManyToOne('CredentialsEntity', 'shared', { - primary: true, - onDelete: 'CASCADE', - }) + @ManyToOne('CredentialsEntity', 'shared') credentials: CredentialsEntity; @PrimaryColumn({ transformer: idStringifier }) - @RelationId((sharedCredential: SharedCredentials) => sharedCredential.credentials) credentialsId: string; } diff --git a/packages/cli/src/databases/entities/SharedWorkflow.ts b/packages/cli/src/databases/entities/SharedWorkflow.ts index d24b234fc6928..3294b25cec084 100644 --- a/packages/cli/src/databases/entities/SharedWorkflow.ts +++ b/packages/cli/src/databases/entities/SharedWorkflow.ts @@ -1,4 +1,4 @@ -import { Entity, ManyToOne, PrimaryColumn, RelationId } from 'typeorm'; +import { Column, Entity, ManyToOne, PrimaryColumn } from 'typeorm'; import type { WorkflowEntity } from './WorkflowEntity'; import type { User } from './User'; import type { Role } from './Role'; @@ -10,20 +10,18 @@ export class SharedWorkflow extends AbstractEntity { @ManyToOne('Role', 'sharedWorkflows', { nullable: false }) role: Role; - @ManyToOne('User', 'sharedWorkflows', { primary: true }) + @Column() + roleId: string; + + @ManyToOne('User', 'sharedWorkflows') user: User; @PrimaryColumn() - @RelationId((sharedWorkflow: SharedWorkflow) => sharedWorkflow.user) userId: string; - @ManyToOne('WorkflowEntity', 'shared', { - primary: true, - onDelete: 'CASCADE', - }) + @ManyToOne('WorkflowEntity', 'shared') workflow: WorkflowEntity; @PrimaryColumn({ transformer: idStringifier }) - @RelationId((sharedWorkflow: SharedWorkflow) => sharedWorkflow.workflow) workflowId: string; } diff --git a/packages/cli/src/databases/entities/User.ts b/packages/cli/src/databases/entities/User.ts index e6867b17bfc2b..1664f1715d9b4 100644 --- a/packages/cli/src/databases/entities/User.ts +++ b/packages/cli/src/databases/entities/User.ts @@ -74,12 +74,12 @@ export class User extends AbstractEntity implements IUser { }) settings: IUserSettings | null; - @ManyToOne('Role', 'globalForUsers', { - cascade: true, - nullable: false, - }) + @ManyToOne('Role', 'globalForUsers', { nullable: false }) globalRole: Role; + @Column() + globalRoleId: string; + @OneToMany('SharedWorkflow', 'user') sharedWorkflows: SharedWorkflow[]; diff --git a/packages/cli/src/databases/entities/WorkflowStatistics.ts b/packages/cli/src/databases/entities/WorkflowStatistics.ts index 856175e87251e..6fad7b9b580a5 100644 --- a/packages/cli/src/databases/entities/WorkflowStatistics.ts +++ b/packages/cli/src/databases/entities/WorkflowStatistics.ts @@ -1,4 +1,4 @@ -import { Column, Entity, RelationId, ManyToOne, PrimaryColumn } from 'typeorm'; +import { Column, Entity, ManyToOne, PrimaryColumn } from 'typeorm'; import { idStringifier } from '../utils/transformers'; import { datetimeColumnType } from './AbstractEntity'; import type { WorkflowEntity } from './WorkflowEntity'; @@ -22,13 +22,9 @@ export class WorkflowStatistics { @PrimaryColumn({ length: 128 }) name: StatisticsNames; - @ManyToOne('WorkflowEntity', 'shared', { - primary: true, - onDelete: 'CASCADE', - }) + @ManyToOne('WorkflowEntity', 'shared') workflow: WorkflowEntity; @PrimaryColumn({ transformer: idStringifier }) - @RelationId((workflowStatistics: WorkflowStatistics) => workflowStatistics.workflow) workflowId: string; } diff --git a/packages/cli/src/eventbus/MessageEventBus/MessageEventBus.ts b/packages/cli/src/eventbus/MessageEventBus/MessageEventBus.ts index 2211f5846453f..ea773a3b34bd1 100644 --- a/packages/cli/src/eventbus/MessageEventBus/MessageEventBus.ts +++ b/packages/cli/src/eventbus/MessageEventBus/MessageEventBus.ts @@ -1,5 +1,5 @@ import { LoggerProxy, MessageEventBusDestinationOptions } from 'n8n-workflow'; -import { DeleteResult } from 'typeorm'; +import type { DeleteResult } from 'typeorm'; import { EventMessageTypes } from '../EventMessageClasses/'; import type { MessageEventBusDestination } from '../MessageEventBusDestination/MessageEventBusDestination.ee'; import { MessageEventBusLogWriter } from '../MessageEventBusWriter/MessageEventBusLogWriter'; diff --git a/packages/cli/src/eventbus/MessageEventBusDestination/MessageEventBusDestination.ee.ts b/packages/cli/src/eventbus/MessageEventBusDestination/MessageEventBusDestination.ee.ts index 5384075b3a7a2..f87141b7f527a 100644 --- a/packages/cli/src/eventbus/MessageEventBusDestination/MessageEventBusDestination.ee.ts +++ b/packages/cli/src/eventbus/MessageEventBusDestination/MessageEventBusDestination.ee.ts @@ -9,7 +9,7 @@ import * as Db from '@/Db'; import { AbstractEventMessage } from '../EventMessageClasses/AbstractEventMessage'; import { EventMessageTypes } from '../EventMessageClasses'; import { eventBus } from '..'; -import { DeleteResult, InsertResult } from 'typeorm'; +import type { DeleteResult, InsertResult } from 'typeorm'; export abstract class MessageEventBusDestination implements MessageEventBusDestinationOptions { // Since you can't have static abstract functions - this just serves as a reminder that you need to implement these. Please. diff --git a/packages/cli/src/executions/executions.service.ts b/packages/cli/src/executions/executions.service.ts index fe3a98060e5fd..98153bad6ac08 100644 --- a/packages/cli/src/executions/executions.service.ts +++ b/packages/cli/src/executions/executions.service.ts @@ -12,7 +12,7 @@ import { jsonParse, Workflow, } from 'n8n-workflow'; -import { FindConditions, FindOperator, In, IsNull, LessThanOrEqual, Not, Raw } from 'typeorm'; +import { FindOperator, FindOptionsWhere, In, IsNull, LessThanOrEqual, Not, Raw } from 'typeorm'; import * as ActiveExecutions from '@/ActiveExecutions'; import config from '@/config'; import type { User } from '@/databases/entities/User'; @@ -200,7 +200,7 @@ export class ExecutionsService { .map(({ id }) => id), ); - const findWhere: FindConditions = { workflowId: In(sharedWorkflowIds) }; + const findWhere: FindOptionsWhere = { workflowId: In(sharedWorkflowIds) }; const rangeQuery: string[] = []; const rangeQueryParams: { @@ -370,7 +370,9 @@ export class ExecutionsService { // Loads the currently saved workflow to execute instead of the // one saved at the time of the execution. const workflowId = fullExecutionData.workflowData.id as string; - const workflowData = (await Db.collections.Workflow.findOne(workflowId)) as IWorkflowBase; + const workflowData = (await Db.collections.Workflow.findOneBy({ + id: workflowId, + })) as IWorkflowBase; if (workflowData === undefined) { throw new Error( @@ -453,7 +455,7 @@ export class ExecutionsService { throw new Error('Either "deleteBefore" or "ids" must be present in the request body'); } - const where: FindConditions = { workflowId: In(sharedWorkflowIds) }; + const where: FindOptionsWhere = { workflowId: In(sharedWorkflowIds) }; if (deleteBefore) { // delete executions by date, if user may access the underlying workflows diff --git a/packages/cli/src/role/role.service.ts b/packages/cli/src/role/role.service.ts index 274aab0139537..6ba367e1b1565 100644 --- a/packages/cli/src/role/role.service.ts +++ b/packages/cli/src/role/role.service.ts @@ -1,14 +1,14 @@ -import { EntityManager } from 'typeorm'; +import type { EntityManager, FindOptionsWhere } from 'typeorm'; import * as Db from '@/Db'; import { Role } from '@db/entities/Role'; export class RoleService { - static async get(role: Partial): Promise { - return Db.collections.Role.findOne(role); + static async get(role: FindOptionsWhere): Promise { + return Db.collections.Role.findOneBy(role); } - static async trxGet(transaction: EntityManager, role: Partial) { - return transaction.findOne(Role, role); + static async trxGet(transaction: EntityManager, role: FindOptionsWhere) { + return transaction.findOneBy(Role, role); } static async getUserRoleForWorkflow(userId: string, workflowId: string) { diff --git a/packages/cli/src/user/user.service.ts b/packages/cli/src/user/user.service.ts index 86c5e853bdaf2..045a6c18ba5eb 100644 --- a/packages/cli/src/user/user.service.ts +++ b/packages/cli/src/user/user.service.ts @@ -1,15 +1,16 @@ -import { EntityManager, In } from 'typeorm'; +import { EntityManager, FindOptionsWhere, In } from 'typeorm'; import * as Db from '@/Db'; import { User } from '@db/entities/User'; export class UserService { - static async get(user: Partial): Promise { - return Db.collections.User.findOne(user, { + static async get(where: FindOptionsWhere): Promise { + return Db.collections.User.findOne({ relations: ['globalRole'], + where, }); } static async getByIds(transaction: EntityManager, ids: string[]) { - return transaction.find(User, { id: In(ids) }); + return transaction.find(User, { where: { id: In(ids) } }); } } diff --git a/packages/cli/src/workflows/workflows.controller.ee.ts b/packages/cli/src/workflows/workflows.controller.ee.ts index 9a980ec00f242..5253bf0745a9f 100644 --- a/packages/cli/src/workflows/workflows.controller.ee.ts +++ b/packages/cli/src/workflows/workflows.controller.ee.ts @@ -17,6 +17,7 @@ import * as TagHelpers from '@/TagHelpers'; import { EECredentialsService as EECredentials } from '../credentials/credentials.service.ee'; import { IExecutionPushResponse } from '@/Interfaces'; import * as GenericHelpers from '@/GenericHelpers'; +import { In } from 'typeorm'; // eslint-disable-next-line @typescript-eslint/naming-convention export const EEWorkflowController = express.Router(); @@ -130,8 +131,11 @@ EEWorkflowController.post( const { tags: tagIds } = req.body; if (tagIds?.length && !config.getEnv('workflowTagsDisabled')) { - newWorkflow.tags = await Db.collections.Tag.findByIds(tagIds, { + newWorkflow.tags = await Db.collections.Tag.find({ select: ['id', 'name'], + where: { + id: In(tagIds), + }, }); } @@ -157,7 +161,7 @@ EEWorkflowController.post( await Db.transaction(async (transactionManager) => { savedWorkflow = await transactionManager.save(newWorkflow); - const role = await Db.collections.Role.findOneOrFail({ + const role = await Db.collections.Role.findOneByOrFail({ name: 'owner', scope: 'workflow', }); diff --git a/packages/cli/src/workflows/workflows.controller.ts b/packages/cli/src/workflows/workflows.controller.ts index d85e9cd10b8d0..686f1f6b65ba8 100644 --- a/packages/cli/src/workflows/workflows.controller.ts +++ b/packages/cli/src/workflows/workflows.controller.ts @@ -23,6 +23,7 @@ import { isBelowOnboardingThreshold } from '@/WorkflowHelpers'; import { EEWorkflowController } from './workflows.controller.ee'; import { WorkflowsService } from './workflows.services'; import { whereClause } from '@/UserManagement/UserManagementHelper'; +import { In } from 'typeorm'; export const workflowsController = express.Router(); @@ -61,8 +62,11 @@ workflowsController.post( const { tags: tagIds } = req.body; if (tagIds?.length && !config.getEnv('workflowTagsDisabled')) { - newWorkflow.tags = await Db.collections.Tag.findByIds(tagIds, { + newWorkflow.tags = await Db.collections.Tag.find({ select: ['id', 'name'], + where: { + id: In(tagIds), + }, }); } @@ -75,7 +79,7 @@ workflowsController.post( await Db.transaction(async (transactionManager) => { savedWorkflow = await transactionManager.save(newWorkflow); - const role = await Db.collections.Role.findOneOrFail({ + const role = await Db.collections.Role.findOneByOrFail({ name: 'owner', scope: 'workflow', }); diff --git a/packages/cli/src/workflows/workflows.services.ee.ts b/packages/cli/src/workflows/workflows.services.ee.ts index 4f57b61f0636f..2b278b3a61a69 100644 --- a/packages/cli/src/workflows/workflows.services.ee.ts +++ b/packages/cli/src/workflows/workflows.services.ee.ts @@ -74,13 +74,12 @@ export class EEWorkflowsService extends WorkflowsService { if (user.isPending) { return acc; } - acc.push( - Db.collections.SharedWorkflow.create({ - workflow, - user, - role, - }), - ); + const entity: Partial = { + workflowId: workflow.id, + userId: user.id, + roleId: role?.id, + }; + acc.push(Db.collections.SharedWorkflow.create(entity)); return acc; }, []); diff --git a/packages/cli/src/workflows/workflows.services.ts b/packages/cli/src/workflows/workflows.services.ts index a798b2a51c6b1..6e214fadbcec9 100644 --- a/packages/cli/src/workflows/workflows.services.ts +++ b/packages/cli/src/workflows/workflows.services.ts @@ -1,6 +1,6 @@ import { validate as jsonSchemaValidate } from 'jsonschema'; import { INode, IPinData, JsonObject, jsonParse, LoggerProxy, Workflow } from 'n8n-workflow'; -import { FindConditions, In } from 'typeorm'; +import { FindOptionsWhere, In } from 'typeorm'; import pick from 'lodash.pick'; import { v4 as uuid } from 'uuid'; import * as ActiveWorkflowRunner from '@/ActiveWorkflowRunner'; @@ -48,8 +48,8 @@ export class WorkflowsService { workflowId: string, relations: string[] = ['workflow'], { allowGlobalOwner } = { allowGlobalOwner: true }, - ): Promise { - const where: FindConditions = { workflowId }; + ): Promise { + const where: FindOptionsWhere = { workflowId }; // Omit user from where if the requesting user is the global // owner. This allows the global owner to view and delete @@ -103,8 +103,8 @@ export class WorkflowsService { return pinnedTriggers.find((pt) => pt.name === checkNodeName) ?? null; // partial execution } - static async get(workflow: Partial, options?: { relations: string[] }) { - return Db.collections.Workflow.findOne(workflow, options); + static async get(workflow: FindOptionsWhere, options?: { relations: string[] }) { + return Db.collections.Workflow.findOne({ where: workflow, relations: options?.relations }); } // Warning: this function is overridden by EE to disregard role list. @@ -299,9 +299,12 @@ export class WorkflowsService { // We sadly get nothing back from "update". Neither if it updated a record // nor the new value. So query now the hopefully updated entry. - const updatedWorkflow = await Db.collections.Workflow.findOne(workflowId, { relations }); + const updatedWorkflow = await Db.collections.Workflow.findOne({ + where: { id: workflowId }, + relations, + }); - if (updatedWorkflow === undefined) { + if (updatedWorkflow === null) { throw new ResponseHelper.BadRequestError( `Workflow with ID "${workflowId}" could not be found to be updated.`, ); diff --git a/packages/cli/test/integration/audit/credentials.risk.test.ts b/packages/cli/test/integration/audit/credentials.risk.test.ts index cfd8f9e4167f4..47e0cf1eee2bb 100644 --- a/packages/cli/test/integration/audit/credentials.risk.test.ts +++ b/packages/cli/test/integration/audit/credentials.risk.test.ts @@ -6,19 +6,16 @@ import { CREDENTIALS_REPORT } from '@/audit/constants'; import { getRiskSection } from './utils'; import * as testDb from '../shared/testDb'; -let testDbName = ''; - beforeAll(async () => { - const initResult = await testDb.init(); - testDbName = initResult.testDbName; + await testDb.init(); }); beforeEach(async () => { - await testDb.truncate(['Workflow', 'Credentials', 'Execution'], testDbName); + await testDb.truncate(['Workflow', 'Credentials', 'Execution']); }); afterAll(async () => { - await testDb.terminate(testDbName); + await testDb.terminate(); }); test('should report credentials not in any use', async () => { diff --git a/packages/cli/test/integration/audit/database.risk.test.ts b/packages/cli/test/integration/audit/database.risk.test.ts index 037cd8d6c9245..7a9dae75a23ec 100644 --- a/packages/cli/test/integration/audit/database.risk.test.ts +++ b/packages/cli/test/integration/audit/database.risk.test.ts @@ -9,19 +9,16 @@ import { import { getRiskSection, saveManualTriggerWorkflow } from './utils'; import * as testDb from '../shared/testDb'; -let testDbName = ''; - beforeAll(async () => { - const initResult = await testDb.init(); - testDbName = initResult.testDbName; + await testDb.init(); }); beforeEach(async () => { - await testDb.truncate(['Workflow'], testDbName); + await testDb.truncate(['Workflow']); }); afterAll(async () => { - await testDb.terminate(testDbName); + await testDb.terminate(); }); test('should report expressions in queries', async () => { diff --git a/packages/cli/test/integration/audit/filesystem.risk.test.ts b/packages/cli/test/integration/audit/filesystem.risk.test.ts index 88a8115138604..57ebf38ad5f60 100644 --- a/packages/cli/test/integration/audit/filesystem.risk.test.ts +++ b/packages/cli/test/integration/audit/filesystem.risk.test.ts @@ -5,19 +5,16 @@ import { FILESYSTEM_INTERACTION_NODE_TYPES, FILESYSTEM_REPORT } from '@/audit/co import { getRiskSection, saveManualTriggerWorkflow } from './utils'; import * as testDb from '../shared/testDb'; -let testDbName = ''; - beforeAll(async () => { - const initResult = await testDb.init(); - testDbName = initResult.testDbName; + await testDb.init(); }); beforeEach(async () => { - await testDb.truncate(['Workflow'], testDbName); + await testDb.truncate(['Workflow']); }); afterAll(async () => { - await testDb.terminate(testDbName); + await testDb.terminate(); }); test('should report filesystem interaction nodes', async () => { diff --git a/packages/cli/test/integration/audit/instance.risk.test.ts b/packages/cli/test/integration/audit/instance.risk.test.ts index 2e00653e7125d..5080ff26229c7 100644 --- a/packages/cli/test/integration/audit/instance.risk.test.ts +++ b/packages/cli/test/integration/audit/instance.risk.test.ts @@ -13,21 +13,18 @@ import * as testDb from '../shared/testDb'; import { toReportTitle } from '@/audit/utils'; import config from '@/config'; -let testDbName = ''; - beforeAll(async () => { - const initResult = await testDb.init(); - testDbName = initResult.testDbName; + await testDb.init(); simulateUpToDateInstance(); }); beforeEach(async () => { - await testDb.truncate(['Workflow'], testDbName); + await testDb.truncate(['Workflow']); }); afterAll(async () => { - await testDb.terminate(testDbName); + await testDb.terminate(); }); test('should report webhook lacking authentication', async () => { diff --git a/packages/cli/test/integration/audit/nodes.risk.test.ts b/packages/cli/test/integration/audit/nodes.risk.test.ts index 7be91a2330e62..811bd11a35830 100644 --- a/packages/cli/test/integration/audit/nodes.risk.test.ts +++ b/packages/cli/test/integration/audit/nodes.risk.test.ts @@ -7,19 +7,16 @@ import { getRiskSection, MOCK_PACKAGE, saveManualTriggerWorkflow } from './utils import * as testDb from '../shared/testDb'; import { toReportTitle } from '@/audit/utils'; -let testDbName = ''; - beforeAll(async () => { - const initResult = await testDb.init(); - testDbName = initResult.testDbName; + await testDb.init(); }); beforeEach(async () => { - await testDb.truncate(['Workflow'], testDbName); + await testDb.truncate(['Workflow']); }); afterAll(async () => { - await testDb.terminate(testDbName); + await testDb.terminate(); }); test('should report risky official nodes', async () => { diff --git a/packages/cli/test/integration/auth.api.test.ts b/packages/cli/test/integration/auth.api.test.ts index 423338982ba6f..2cacbebe5a3ce 100644 --- a/packages/cli/test/integration/auth.api.test.ts +++ b/packages/cli/test/integration/auth.api.test.ts @@ -11,15 +11,13 @@ import type { AuthAgent } from './shared/types'; import * as utils from './shared/utils'; let app: express.Application; -let testDbName = ''; let globalOwnerRole: Role; let globalMemberRole: Role; let authAgent: AuthAgent; beforeAll(async () => { app = await utils.initTestServer({ endpointGroups: ['auth'], applyAuth: true }); - const initResult = await testDb.init(); - testDbName = initResult.testDbName; + await testDb.init(); globalOwnerRole = await testDb.getGlobalOwnerRole(); globalMemberRole = await testDb.getGlobalMemberRole(); @@ -31,7 +29,7 @@ beforeAll(async () => { }); beforeEach(async () => { - await testDb.truncate(['User'], testDbName); + await testDb.truncate(['User']); config.set('userManagement.isInstanceOwnerSetUp', true); @@ -42,7 +40,7 @@ beforeEach(async () => { }); afterAll(async () => { - await testDb.terminate(testDbName); + await testDb.terminate(); }); test('POST /login should log user in', async () => { diff --git a/packages/cli/test/integration/auth.mw.test.ts b/packages/cli/test/integration/auth.mw.test.ts index a365b3459926b..fafcb8c3348ea 100644 --- a/packages/cli/test/integration/auth.mw.test.ts +++ b/packages/cli/test/integration/auth.mw.test.ts @@ -12,7 +12,6 @@ import type { AuthAgent } from './shared/types'; import * as utils from './shared/utils'; let app: express.Application; -let testDbName = ''; let globalMemberRole: Role; let authAgent: AuthAgent; @@ -21,8 +20,7 @@ beforeAll(async () => { applyAuth: true, endpointGroups: ['me', 'auth', 'owner', 'users'], }); - const initResult = await testDb.init(); - testDbName = initResult.testDbName; + await testDb.init(); globalMemberRole = await testDb.getGlobalMemberRole(); @@ -33,7 +31,7 @@ beforeAll(async () => { }); afterAll(async () => { - await testDb.terminate(testDbName); + await testDb.terminate(); }); ROUTES_REQUIRING_AUTHENTICATION.concat(ROUTES_REQUIRING_AUTHORIZATION).forEach((route) => { diff --git a/packages/cli/test/integration/commands/reset.cmd.test.ts b/packages/cli/test/integration/commands/reset.cmd.test.ts index 81ea1eec2a240..179d4d413cf59 100644 --- a/packages/cli/test/integration/commands/reset.cmd.test.ts +++ b/packages/cli/test/integration/commands/reset.cmd.test.ts @@ -7,23 +7,21 @@ import * as utils from '../shared/utils'; import * as testDb from '../shared/testDb'; let app: express.Application; -let testDbName = ''; let globalOwnerRole: Role; beforeAll(async () => { app = await utils.initTestServer({ endpointGroups: ['owner'], applyAuth: true }); - const initResult = await testDb.init(); - testDbName = initResult.testDbName; + await testDb.init(); globalOwnerRole = await testDb.getGlobalOwnerRole(); }); beforeEach(async () => { - await testDb.truncate(['User'], testDbName); + await testDb.truncate(['User']); }); afterAll(async () => { - await testDb.terminate(testDbName); + await testDb.terminate(); }); test('user-management:reset should reset DB to default user state', async () => { @@ -31,7 +29,7 @@ test('user-management:reset should reset DB to default user state', async () => await Reset.run(); - const user = await Db.collections.User.findOne({ globalRole: globalOwnerRole }); + const user = await Db.collections.User.findOneBy({ globalRoleId: globalOwnerRole.id }); if (!user) { fail('No owner found after DB reset to default user state'); diff --git a/packages/cli/test/integration/credentials.ee.test.ts b/packages/cli/test/integration/credentials.ee.test.ts index a58d1e19d2c93..5dc70307e7857 100644 --- a/packages/cli/test/integration/credentials.ee.test.ts +++ b/packages/cli/test/integration/credentials.ee.test.ts @@ -14,7 +14,6 @@ import * as utils from './shared/utils'; import type { IUser } from 'n8n-workflow'; let app: express.Application; -let testDbName = ''; let globalOwnerRole: Role; let globalMemberRole: Role; let credentialOwnerRole: Role; @@ -27,8 +26,7 @@ beforeAll(async () => { endpointGroups: ['credentials'], applyAuth: true, }); - const initResult = await testDb.init(); - testDbName = initResult.testDbName; + await testDb.init(); utils.initConfigFile(); @@ -46,11 +44,11 @@ beforeAll(async () => { }); beforeEach(async () => { - await testDb.truncate(['User', 'SharedCredentials', 'Credentials'], testDbName); + await testDb.truncate(['User', 'SharedCredentials', 'Credentials']); }); afterAll(async () => { - await testDb.terminate(testDbName); + await testDb.terminate(); }); // ---------------------------------------- @@ -380,7 +378,7 @@ test('PUT /credentials/:id/share should share the credential with the provided u const sharedCredentials = await Db.collections.SharedCredentials.find({ relations: ['role'], - where: { credentials: savedCredential }, + where: { credentialsId: savedCredential.id }, }); // check that sharings have been removed/added correctly @@ -420,7 +418,7 @@ test('PUT /credentials/:id/share should share the credential with the provided u // check that sharings got correctly set in DB const sharedCredentials = await Db.collections.SharedCredentials.find({ relations: ['role'], - where: { credentials: savedCredential, user: { id: In([...memberIds]) } }, + where: { credentialsId: savedCredential.id, userId: In([...memberIds]) }, }); expect(sharedCredentials.length).toBe(memberIds.length); @@ -433,7 +431,7 @@ test('PUT /credentials/:id/share should share the credential with the provided u // check that owner still exists const ownerSharedCredential = await Db.collections.SharedCredentials.findOneOrFail({ relations: ['role'], - where: { credentials: savedCredential, user: owner }, + where: { credentialsId: savedCredential.id, userId: owner.id }, }); expect(ownerSharedCredential.role.name).toBe('owner'); @@ -475,7 +473,7 @@ test('PUT /credentials/:id/share should ignore pending sharee', async () => { expect(response.statusCode).toBe(200); const sharedCredentials = await Db.collections.SharedCredentials.find({ - where: { credentials: savedCredential }, + where: { credentialsId: savedCredential.id }, }); expect(sharedCredentials).toHaveLength(1); @@ -493,7 +491,7 @@ test('PUT /credentials/:id/share should ignore non-existing sharee', async () => expect(response.statusCode).toBe(200); const sharedCredentials = await Db.collections.SharedCredentials.find({ - where: { credentials: savedCredential }, + where: { credentialsId: savedCredential.id }, }); expect(sharedCredentials).toHaveLength(1); @@ -535,7 +533,7 @@ test('PUT /credentials/:id/share should unshare the credential', async () => { expect(response.statusCode).toBe(200); const sharedCredentials = await Db.collections.SharedCredentials.find({ - where: { credentials: savedCredential }, + where: { credentialsId: savedCredential.id }, }); expect(sharedCredentials).toHaveLength(1); diff --git a/packages/cli/test/integration/credentials.test.ts b/packages/cli/test/integration/credentials.test.ts index 09ceefb9de1e5..9baadd078c406 100644 --- a/packages/cli/test/integration/credentials.test.ts +++ b/packages/cli/test/integration/credentials.test.ts @@ -19,7 +19,6 @@ const mockIsCredentialsSharingEnabled = jest.spyOn(UserManagementHelpers, 'isSha mockIsCredentialsSharingEnabled.mockReturnValue(false); let app: express.Application; -let testDbName = ''; let globalOwnerRole: Role; let globalMemberRole: Role; let saveCredential: SaveCredentialFunction; @@ -30,8 +29,7 @@ beforeAll(async () => { endpointGroups: ['credentials'], applyAuth: true, }); - const initResult = await testDb.init(); - testDbName = initResult.testDbName; + await testDb.init(); utils.initConfigFile(); @@ -48,11 +46,11 @@ beforeAll(async () => { }); beforeEach(async () => { - await testDb.truncate(['User', 'SharedCredentials', 'Credentials'], testDbName); + await testDb.truncate(['User', 'SharedCredentials', 'Credentials']); }); afterAll(async () => { - await testDb.terminate(testDbName); + await testDb.terminate(); }); // ---------------------------------------- @@ -124,7 +122,7 @@ test('POST /credentials should create cred', async () => { expect(nodesAccess[0].nodeType).toBe(payload.nodesAccess[0].nodeType); expect(encryptedData).not.toBe(payload.data); - const credential = await Db.collections.Credentials.findOneOrFail(id); + const credential = await Db.collections.Credentials.findOneByOrFail({ id }); expect(credential.name).toBe(payload.name); expect(credential.type).toBe(payload.type); @@ -133,7 +131,7 @@ test('POST /credentials should create cred', async () => { const sharedCredential = await Db.collections.SharedCredentials.findOneOrFail({ relations: ['user', 'credentials'], - where: { credentials: credential }, + where: { credentialsId: credential.id }, }); expect(sharedCredential.user.id).toBe(ownerShell.id); @@ -191,13 +189,13 @@ test('DELETE /credentials/:id should delete owned cred for owner', async () => { expect(response.statusCode).toBe(200); expect(response.body).toEqual({ data: true }); - const deletedCredential = await Db.collections.Credentials.findOne(savedCredential.id); + const deletedCredential = await Db.collections.Credentials.findOneBy({ id: savedCredential.id }); - expect(deletedCredential).toBeUndefined(); // deleted + expect(deletedCredential).toBeNull(); // deleted - const deletedSharedCredential = await Db.collections.SharedCredentials.findOne(); + const deletedSharedCredential = await Db.collections.SharedCredentials.findOneBy({}); - expect(deletedSharedCredential).toBeUndefined(); // deleted + expect(deletedSharedCredential).toBeNull(); // deleted }); test('DELETE /credentials/:id should delete non-owned cred for owner', async () => { @@ -210,13 +208,13 @@ test('DELETE /credentials/:id should delete non-owned cred for owner', async () expect(response.statusCode).toBe(200); expect(response.body).toEqual({ data: true }); - const deletedCredential = await Db.collections.Credentials.findOne(savedCredential.id); + const deletedCredential = await Db.collections.Credentials.findOneBy({ id: savedCredential.id }); - expect(deletedCredential).toBeUndefined(); // deleted + expect(deletedCredential).toBeNull(); // deleted - const deletedSharedCredential = await Db.collections.SharedCredentials.findOne(); + const deletedSharedCredential = await Db.collections.SharedCredentials.findOneBy({}); - expect(deletedSharedCredential).toBeUndefined(); // deleted + expect(deletedSharedCredential).toBeNull(); // deleted }); test('DELETE /credentials/:id should delete owned cred for member', async () => { @@ -228,13 +226,13 @@ test('DELETE /credentials/:id should delete owned cred for member', async () => expect(response.statusCode).toBe(200); expect(response.body).toEqual({ data: true }); - const deletedCredential = await Db.collections.Credentials.findOne(savedCredential.id); + const deletedCredential = await Db.collections.Credentials.findOneBy({ id: savedCredential.id }); - expect(deletedCredential).toBeUndefined(); // deleted + expect(deletedCredential).toBeNull(); // deleted - const deletedSharedCredential = await Db.collections.SharedCredentials.findOne(); + const deletedSharedCredential = await Db.collections.SharedCredentials.findOneBy({}); - expect(deletedSharedCredential).toBeUndefined(); // deleted + expect(deletedSharedCredential).toBeNull(); // deleted }); test('DELETE /credentials/:id should not delete non-owned cred for member', async () => { @@ -246,11 +244,11 @@ test('DELETE /credentials/:id should not delete non-owned cred for member', asyn expect(response.statusCode).toBe(404); - const shellCredential = await Db.collections.Credentials.findOne(savedCredential.id); + const shellCredential = await Db.collections.Credentials.findOneBy({ id: savedCredential.id }); expect(shellCredential).toBeDefined(); // not deleted - const deletedSharedCredential = await Db.collections.SharedCredentials.findOne(); + const deletedSharedCredential = await Db.collections.SharedCredentials.findOneBy({}); expect(deletedSharedCredential).toBeDefined(); // not deleted }); @@ -285,7 +283,7 @@ test('PATCH /credentials/:id should update owned cred for owner', async () => { expect(encryptedData).not.toBe(patchPayload.data); - const credential = await Db.collections.Credentials.findOneOrFail(id); + const credential = await Db.collections.Credentials.findOneByOrFail({ id }); expect(credential.name).toBe(patchPayload.name); expect(credential.type).toBe(patchPayload.type); @@ -294,7 +292,7 @@ test('PATCH /credentials/:id should update owned cred for owner', async () => { const sharedCredential = await Db.collections.SharedCredentials.findOneOrFail({ relations: ['credentials'], - where: { credentials: credential }, + where: { credentialsId: credential.id }, }); expect(sharedCredential.credentials.name).toBe(patchPayload.name); // updated @@ -324,7 +322,7 @@ test('PATCH /credentials/:id should update non-owned cred for owner', async () = expect(encryptedData).not.toBe(patchPayload.data); - const credential = await Db.collections.Credentials.findOneOrFail(id); + const credential = await Db.collections.Credentials.findOneByOrFail({ id }); expect(credential.name).toBe(patchPayload.name); expect(credential.type).toBe(patchPayload.type); @@ -333,7 +331,7 @@ test('PATCH /credentials/:id should update non-owned cred for owner', async () = const sharedCredential = await Db.collections.SharedCredentials.findOneOrFail({ relations: ['credentials'], - where: { credentials: credential }, + where: { credentialsId: credential.id }, }); expect(sharedCredential.credentials.name).toBe(patchPayload.name); // updated @@ -362,7 +360,7 @@ test('PATCH /credentials/:id should update owned cred for member', async () => { expect(encryptedData).not.toBe(patchPayload.data); - const credential = await Db.collections.Credentials.findOneOrFail(id); + const credential = await Db.collections.Credentials.findOneByOrFail({ id }); expect(credential.name).toBe(patchPayload.name); expect(credential.type).toBe(patchPayload.type); @@ -371,7 +369,7 @@ test('PATCH /credentials/:id should update owned cred for member', async () => { const sharedCredential = await Db.collections.SharedCredentials.findOneOrFail({ relations: ['credentials'], - where: { credentials: credential }, + where: { credentialsId: credential.id }, }); expect(sharedCredential.credentials.name).toBe(patchPayload.name); // updated @@ -389,7 +387,9 @@ test('PATCH /credentials/:id should not update non-owned cred for member', async expect(response.statusCode).toBe(404); - const shellCredential = await Db.collections.Credentials.findOneOrFail(savedCredential.id); + const shellCredential = await Db.collections.Credentials.findOneByOrFail({ + id: savedCredential.id, + }); expect(shellCredential.name).not.toBe(patchPayload.name); // not updated }); diff --git a/packages/cli/test/integration/eventbus.test.ts b/packages/cli/test/integration/eventbus.test.ts index dfb8fbaa2e83a..b1349a5758293 100644 --- a/packages/cli/test/integration/eventbus.test.ts +++ b/packages/cli/test/integration/eventbus.test.ts @@ -16,11 +16,11 @@ import { } from 'n8n-workflow'; import { eventBus } from '@/eventbus'; import { SuperAgentTest } from 'supertest'; -import { EventMessageGeneric } from '../../src/eventbus/EventMessageClasses/EventMessageGeneric'; -import { MessageEventBusDestinationSyslog } from '../../src/eventbus/MessageEventBusDestination/MessageEventBusDestinationSyslog.ee'; -import { MessageEventBusDestinationWebhook } from '../../src/eventbus/MessageEventBusDestination/MessageEventBusDestinationWebhook.ee'; -import { MessageEventBusDestinationSentry } from '../../src/eventbus/MessageEventBusDestination/MessageEventBusDestinationSentry.ee'; -import { EventMessageAudit } from '../../src/eventbus/EventMessageClasses/EventMessageAudit'; +import { EventMessageGeneric } from '@/eventbus/EventMessageClasses/EventMessageGeneric'; +import { MessageEventBusDestinationSyslog } from '@/eventbus/MessageEventBusDestination/MessageEventBusDestinationSyslog.ee'; +import { MessageEventBusDestinationWebhook } from '@/eventbus/MessageEventBusDestination/MessageEventBusDestinationWebhook.ee'; +import { MessageEventBusDestinationSentry } from '@/eventbus/MessageEventBusDestination/MessageEventBusDestinationSentry.ee'; +import { EventMessageAudit } from '@/eventbus/EventMessageClasses/EventMessageAudit'; import { v4 as uuid } from 'uuid'; jest.unmock('@/eventbus/MessageEventBus/MessageEventBus'); @@ -30,7 +30,6 @@ jest.mock('syslog-client'); const mockedSyslog = syslog as jest.Mocked; let app: express.Application; -let testDbName = ''; let globalOwnerRole: Role; let owner: User; let unAuthOwnerAgent: SuperAgentTest; @@ -82,8 +81,7 @@ async function confirmIdSent(id: string) { } beforeAll(async () => { - const initResult = await testDb.init(); - testDbName = initResult.testDbName; + await testDb.init(); globalOwnerRole = await testDb.getGlobalOwnerRole(); owner = await testDb.createUser({ globalRole: globalOwnerRole }); @@ -121,7 +119,7 @@ beforeEach(async () => { afterAll(async () => { jest.mock('@/eventbus/MessageEventBus/MessageEventBus'); - await testDb.terminate(testDbName); + await testDb.terminate(); await eventBus.close(); }); diff --git a/packages/cli/test/integration/license.api.test.ts b/packages/cli/test/integration/license.api.test.ts index 70a8dbfdc1ce9..c3fa668034db9 100644 --- a/packages/cli/test/integration/license.api.test.ts +++ b/packages/cli/test/integration/license.api.test.ts @@ -13,7 +13,6 @@ const MOCK_RENEW_OFFSET = 259200; const MOCK_INSTANCE_ID = 'instance-id'; let app: express.Application; -let testDbName = ''; let globalOwnerRole: Role; let globalMemberRole: Role; let authAgent: AuthAgent; @@ -21,8 +20,7 @@ let license: License; beforeAll(async () => { app = await utils.initTestServer({ endpointGroups: ['license'], applyAuth: true }); - const initResult = await testDb.init(); - testDbName = initResult.testDbName; + await testDb.init(); globalOwnerRole = await testDb.getGlobalOwnerRole(); globalMemberRole = await testDb.getGlobalMemberRole(); @@ -43,11 +41,11 @@ beforeEach(async () => { }); afterEach(async () => { - await testDb.truncate(['Settings'], testDbName); + await testDb.truncate(['Settings']); }); afterAll(async () => { - await testDb.terminate(testDbName); + await testDb.terminate(); }); test('GET /license should return license information to the instance owner', async () => { diff --git a/packages/cli/test/integration/me.api.test.ts b/packages/cli/test/integration/me.api.test.ts index 7fd29a63297c7..e7b765511993c 100644 --- a/packages/cli/test/integration/me.api.test.ts +++ b/packages/cli/test/integration/me.api.test.ts @@ -18,15 +18,13 @@ import type { AuthAgent } from './shared/types'; import * as utils from './shared/utils'; let app: express.Application; -let testDbName = ''; let globalOwnerRole: Role; let globalMemberRole: Role; let authAgent: AuthAgent; beforeAll(async () => { app = await utils.initTestServer({ endpointGroups: ['me'], applyAuth: true }); - const initResult = await testDb.init(); - testDbName = initResult.testDbName; + await testDb.init(); globalOwnerRole = await testDb.getGlobalOwnerRole(); globalMemberRole = await testDb.getGlobalMemberRole(); @@ -38,12 +36,12 @@ beforeAll(async () => { }); afterAll(async () => { - await testDb.terminate(testDbName); + await testDb.terminate(); }); describe('Owner shell', () => { beforeEach(async () => { - await testDb.truncate(['User'], testDbName); + await testDb.truncate(['User']); }); test('GET /me should return sanitized owner shell', async () => { @@ -113,7 +111,7 @@ describe('Owner shell', () => { expect(globalRole.scope).toBe('global'); expect(apiKey).toBeUndefined(); - const storedOwnerShell = await Db.collections.User.findOneOrFail(id); + const storedOwnerShell = await Db.collections.User.findOneByOrFail({ id }); expect(storedOwnerShell.email).toBe(validPayload.email.toLowerCase()); expect(storedOwnerShell.firstName).toBe(validPayload.firstName); @@ -129,7 +127,7 @@ describe('Owner shell', () => { const response = await authOwnerShellAgent.patch('/me').send(invalidPayload); expect(response.statusCode).toBe(400); - const storedOwnerShell = await Db.collections.User.findOneOrFail(); + const storedOwnerShell = await Db.collections.User.findOneByOrFail({}); expect(storedOwnerShell.email).toBeNull(); expect(storedOwnerShell.firstName).toBeNull(); expect(storedOwnerShell.lastName).toBeNull(); @@ -152,7 +150,7 @@ describe('Owner shell', () => { const response = await authOwnerShellAgent.patch('/me/password').send(payload); expect([400, 500].includes(response.statusCode)).toBe(true); - const storedMember = await Db.collections.User.findOneOrFail(); + const storedMember = await Db.collections.User.findOneByOrFail({}); if (payload.newPassword) { expect(storedMember.password).not.toBe(payload.newPassword); @@ -164,7 +162,7 @@ describe('Owner shell', () => { }), ); - const storedOwnerShell = await Db.collections.User.findOneOrFail(); + const storedOwnerShell = await Db.collections.User.findOneByOrFail({}); expect(storedOwnerShell.password).toBeNull(); }); @@ -241,7 +239,7 @@ describe('Member', () => { }); afterEach(async () => { - await testDb.truncate(['User'], testDbName); + await testDb.truncate(['User']); }); test('GET /me should return sanitized member', async () => { @@ -311,7 +309,7 @@ describe('Member', () => { expect(globalRole.scope).toBe('global'); expect(apiKey).toBeUndefined(); - const storedMember = await Db.collections.User.findOneOrFail(id); + const storedMember = await Db.collections.User.findOneByOrFail({ id }); expect(storedMember.email).toBe(validPayload.email.toLowerCase()); expect(storedMember.firstName).toBe(validPayload.firstName); @@ -327,7 +325,7 @@ describe('Member', () => { const response = await authMemberAgent.patch('/me').send(invalidPayload); expect(response.statusCode).toBe(400); - const storedMember = await Db.collections.User.findOneOrFail(); + const storedMember = await Db.collections.User.findOneByOrFail({}); expect(storedMember.email).toBe(member.email); expect(storedMember.firstName).toBe(member.firstName); expect(storedMember.lastName).toBe(member.lastName); @@ -350,7 +348,7 @@ describe('Member', () => { expect(response.statusCode).toBe(200); expect(response.body).toEqual(SUCCESS_RESPONSE_BODY); - const storedMember = await Db.collections.User.findOneOrFail(); + const storedMember = await Db.collections.User.findOneByOrFail({}); expect(storedMember.password).not.toBe(member.password); expect(storedMember.password).not.toBe(validPayload.newPassword); }); @@ -363,7 +361,7 @@ describe('Member', () => { const response = await authMemberAgent.patch('/me/password').send(payload); expect([400, 500].includes(response.statusCode)).toBe(true); - const storedMember = await Db.collections.User.findOneOrFail(); + const storedMember = await Db.collections.User.findOneByOrFail({}); if (payload.newPassword) { expect(storedMember.password).not.toBe(payload.newPassword); @@ -385,7 +383,9 @@ describe('Member', () => { expect(response.statusCode).toBe(200); expect(response.body).toEqual(SUCCESS_RESPONSE_BODY); - const { personalizationAnswers: storedAnswers } = await Db.collections.User.findOneOrFail(); + const { personalizationAnswers: storedAnswers } = await Db.collections.User.findOneByOrFail( + {}, + ); expect(storedAnswers).toEqual(validPayload); } @@ -403,7 +403,7 @@ describe('Member', () => { expect(response.body.data.apiKey).toBeDefined(); expect(response.body.data.apiKey).not.toBeNull(); - const storedMember = await Db.collections.User.findOneOrFail(member.id); + const storedMember = await Db.collections.User.findOneByOrFail({ id: member.id }); expect(storedMember.apiKey).toEqual(response.body.data.apiKey); }); @@ -430,7 +430,7 @@ describe('Member', () => { expect(response.statusCode).toBe(200); - const storedMember = await Db.collections.User.findOneOrFail(member.id); + const storedMember = await Db.collections.User.findOneByOrFail({ id: member.id }); expect(storedMember.apiKey).toBeNull(); }); @@ -442,7 +442,7 @@ describe('Owner', () => { }); afterEach(async () => { - await testDb.truncate(['User'], testDbName); + await testDb.truncate(['User']); }); test('GET /me should return sanitized owner', async () => { @@ -512,7 +512,7 @@ describe('Owner', () => { expect(globalRole.scope).toBe('global'); expect(apiKey).toBeUndefined(); - const storedOwner = await Db.collections.User.findOneOrFail(id); + const storedOwner = await Db.collections.User.findOneByOrFail({ id }); expect(storedOwner.email).toBe(validPayload.email.toLowerCase()); expect(storedOwner.firstName).toBe(validPayload.firstName); diff --git a/packages/cli/test/integration/nodes.api.test.ts b/packages/cli/test/integration/nodes.api.test.ts index 0e9ba1e6f20fe..de09df8b80c7a 100644 --- a/packages/cli/test/integration/nodes.api.test.ts +++ b/packages/cli/test/integration/nodes.api.test.ts @@ -43,14 +43,12 @@ jest.mock('@/CommunityNodes/packageModel', () => { const mockedEmptyPackage = mocked(utils.emptyPackage); let app: express.Application; -let testDbName = ''; let globalOwnerRole: Role; let authAgent: AuthAgent; beforeAll(async () => { app = await utils.initTestServer({ endpointGroups: ['nodes'], applyAuth: true }); - const initResult = await testDb.init(); - testDbName = initResult.testDbName; + await testDb.init(); globalOwnerRole = await testDb.getGlobalOwnerRole(); @@ -62,14 +60,14 @@ beforeAll(async () => { }); beforeEach(async () => { - await testDb.truncate(['InstalledNodes', 'InstalledPackages', 'User'], testDbName); + await testDb.truncate(['InstalledNodes', 'InstalledPackages', 'User']); mocked(executeCommand).mockReset(); mocked(findInstalledPackage).mockReset(); }); afterAll(async () => { - await testDb.terminate(testDbName); + await testDb.terminate(); }); /** diff --git a/packages/cli/test/integration/owner.api.test.ts b/packages/cli/test/integration/owner.api.test.ts index 607bf1546a731..8ae033e16b5ac 100644 --- a/packages/cli/test/integration/owner.api.test.ts +++ b/packages/cli/test/integration/owner.api.test.ts @@ -15,14 +15,12 @@ import type { AuthAgent } from './shared/types'; import * as utils from './shared/utils'; let app: express.Application; -let testDbName = ''; let globalOwnerRole: Role; let authAgent: AuthAgent; beforeAll(async () => { app = await utils.initTestServer({ endpointGroups: ['owner'], applyAuth: true }); - const initResult = await testDb.init(); - testDbName = initResult.testDbName; + await testDb.init(); globalOwnerRole = await testDb.getGlobalOwnerRole(); @@ -37,11 +35,11 @@ beforeEach(async () => { }); afterEach(async () => { - await testDb.truncate(['User'], testDbName); + await testDb.truncate(['User']); }); afterAll(async () => { - await testDb.terminate(testDbName); + await testDb.terminate(); }); test('POST /owner should create owner and enable isInstanceOwnerSetUp', async () => { @@ -83,7 +81,7 @@ test('POST /owner should create owner and enable isInstanceOwnerSetUp', async () expect(globalRole.scope).toBe('global'); expect(apiKey).toBeUndefined(); - const storedOwner = await Db.collections.User.findOneOrFail(id); + const storedOwner = await Db.collections.User.findOneByOrFail({ id }); expect(storedOwner.password).not.toBe(newOwnerData.password); expect(storedOwner.email).toBe(newOwnerData.email); expect(storedOwner.firstName).toBe(newOwnerData.firstName); @@ -115,7 +113,7 @@ test('POST /owner should create owner with lowercased email', async () => { expect(id).toBe(ownerShell.id); expect(email).toBe(newOwnerData.email.toLowerCase()); - const storedOwner = await Db.collections.User.findOneOrFail(id); + const storedOwner = await Db.collections.User.findOneByOrFail({ id }); expect(storedOwner.email).toBe(newOwnerData.email.toLowerCase()); }); @@ -141,7 +139,7 @@ test('POST /owner/skip-setup should persist skipping setup to the DB', async () const skipConfig = config.getEnv('userManagement.skipInstanceOwnerSetup'); expect(skipConfig).toBe(true); - const { value } = await Db.collections.Settings.findOneOrFail({ + const { value } = await Db.collections.Settings.findOneByOrFail({ key: 'userManagement.skipInstanceOwnerSetup', }); expect(value).toBe('true'); diff --git a/packages/cli/test/integration/passwordReset.api.test.ts b/packages/cli/test/integration/passwordReset.api.test.ts index ac46048c49113..bc8ec8adb9def 100644 --- a/packages/cli/test/integration/passwordReset.api.test.ts +++ b/packages/cli/test/integration/passwordReset.api.test.ts @@ -17,14 +17,12 @@ import type { Role } from '@db/entities/Role'; jest.mock('@/UserManagement/email/NodeMailer'); let app: express.Application; -let testDbName = ''; let globalOwnerRole: Role; let globalMemberRole: Role; beforeAll(async () => { app = await utils.initTestServer({ endpointGroups: ['passwordReset'], applyAuth: true }); - const initResult = await testDb.init(); - testDbName = initResult.testDbName; + await testDb.init(); globalOwnerRole = await testDb.getGlobalOwnerRole(); globalMemberRole = await testDb.getGlobalMemberRole(); @@ -34,7 +32,7 @@ beforeAll(async () => { }); beforeEach(async () => { - await testDb.truncate(['User'], testDbName); + await testDb.truncate(['User']); jest.mock('@/config'); @@ -43,7 +41,7 @@ beforeEach(async () => { }); afterAll(async () => { - await testDb.terminate(testDbName); + await testDb.terminate(); }); test('POST /forgot-password should send password reset email', async () => { @@ -64,7 +62,7 @@ test('POST /forgot-password should send password reset email', async () => { expect(response.statusCode).toBe(200); expect(response.body).toEqual({}); - const user = await Db.collections.User.findOneOrFail({ email: payload.email }); + const user = await Db.collections.User.findOneByOrFail({ email: payload.email }); expect(user.resetPasswordToken).toBeDefined(); expect(user.resetPasswordTokenExpiration).toBeGreaterThan(Math.ceil(Date.now() / 1000)); }), @@ -80,7 +78,7 @@ test('POST /forgot-password should fail if emailing is not set up', async () => expect(response.statusCode).toBe(500); - const storedOwner = await Db.collections.User.findOneOrFail({ email: owner.email }); + const storedOwner = await Db.collections.User.findOneByOrFail({ email: owner.email }); expect(storedOwner.resetPasswordToken).toBeNull(); }); @@ -104,7 +102,7 @@ test('POST /forgot-password should fail with invalid inputs', async () => { const response = await authlessAgent.post('/forgot-password').send(invalidPayload); expect(response.statusCode).toBe(400); - const storedOwner = await Db.collections.User.findOneOrFail({ email: owner.email }); + const storedOwner = await Db.collections.User.findOneByOrFail({ email: owner.email }); expect(storedOwner.resetPasswordToken).toBeNull(); }), ); @@ -218,7 +216,7 @@ test('POST /change-password should succeed with valid inputs', async () => { const authToken = utils.getAuthToken(response); expect(authToken).toBeDefined(); - const { password: storedPassword } = await Db.collections.User.findOneOrFail(owner.id); + const { password: storedPassword } = await Db.collections.User.findOneByOrFail({ id: owner.id }); const comparisonResult = await compare(passwordToStore, storedPassword); expect(comparisonResult).toBe(true); @@ -262,7 +260,7 @@ test('POST /change-password should fail with invalid inputs', async () => { const response = await authlessAgent.post('/change-password').query(invalidPayload); expect(response.statusCode).toBe(400); - const { password: storedPassword } = await Db.collections.User.findOneOrFail(); + const { password: storedPassword } = await Db.collections.User.findOneByOrFail({}); expect(owner.password).toBe(storedPassword); }), ); diff --git a/packages/cli/test/integration/publicApi/credentials.test.ts b/packages/cli/test/integration/publicApi/credentials.test.ts index d34881e87e711..776eebe4d9fd1 100644 --- a/packages/cli/test/integration/publicApi/credentials.test.ts +++ b/packages/cli/test/integration/publicApi/credentials.test.ts @@ -11,7 +11,6 @@ import type { CredentialPayload, SaveCredentialFunction } from '../shared/types' import * as testDb from '../shared/testDb'; let app: express.Application; -let testDbName = ''; let globalOwnerRole: Role; let globalMemberRole: Role; let credentialOwnerRole: Role; @@ -20,8 +19,7 @@ let saveCredential: SaveCredentialFunction; beforeAll(async () => { app = await utils.initTestServer({ endpointGroups: ['publicApi'], applyAuth: false }); - const initResult = await testDb.init(); - testDbName = initResult.testDbName; + await testDb.init(); utils.initConfigFile(); @@ -40,11 +38,11 @@ beforeAll(async () => { }); beforeEach(async () => { - await testDb.truncate(['User', 'SharedCredentials', 'Credentials'], testDbName); + await testDb.truncate(['User', 'SharedCredentials', 'Credentials']); }); afterAll(async () => { - await testDb.terminate(testDbName); + await testDb.terminate(); }); test('POST /credentials should create credentials', async () => { @@ -76,15 +74,15 @@ test('POST /credentials should create credentials', async () => { expect(name).toBe(payload.name); expect(type).toBe(payload.type); - const credential = await Db.collections.Credentials!.findOneOrFail(id); + const credential = await Db.collections.Credentials.findOneByOrFail({ id }); expect(credential.name).toBe(payload.name); expect(credential.type).toBe(payload.type); expect(credential.data).not.toBe(payload.data); - const sharedCredential = await Db.collections.SharedCredentials!.findOneOrFail({ + const sharedCredential = await Db.collections.SharedCredentials.findOneOrFail({ relations: ['user', 'credentials', 'role'], - where: { credentials: credential, user: ownerShell }, + where: { credentialsId: credential.id, userId: ownerShell.id }, }); expect(sharedCredential.role).toEqual(credentialOwnerRole); @@ -153,13 +151,13 @@ test('DELETE /credentials/:id should delete owned cred for owner', async () => { expect(name).toBe(savedCredential.name); expect(type).toBe(savedCredential.type); - const deletedCredential = await Db.collections.Credentials!.findOne(savedCredential.id); + const deletedCredential = await Db.collections.Credentials.findOneBy({ id: savedCredential.id }); - expect(deletedCredential).toBeUndefined(); // deleted + expect(deletedCredential).toBeNull(); // deleted - const deletedSharedCredential = await Db.collections.SharedCredentials!.findOne(); + const deletedSharedCredential = await Db.collections.SharedCredentials.findOneBy({}); - expect(deletedSharedCredential).toBeUndefined(); // deleted + expect(deletedSharedCredential).toBeNull(); // deleted }); test('DELETE /credentials/:id should delete non-owned cred for owner', async () => { @@ -181,13 +179,13 @@ test('DELETE /credentials/:id should delete non-owned cred for owner', async () expect(response.statusCode).toBe(200); - const deletedCredential = await Db.collections.Credentials!.findOne(savedCredential.id); + const deletedCredential = await Db.collections.Credentials.findOneBy({ id: savedCredential.id }); - expect(deletedCredential).toBeUndefined(); // deleted + expect(deletedCredential).toBeNull(); // deleted - const deletedSharedCredential = await Db.collections.SharedCredentials!.findOne(); + const deletedSharedCredential = await Db.collections.SharedCredentials.findOneBy({}); - expect(deletedSharedCredential).toBeUndefined(); // deleted + expect(deletedSharedCredential).toBeNull(); // deleted }); test('DELETE /credentials/:id should delete owned cred for member', async () => { @@ -211,13 +209,13 @@ test('DELETE /credentials/:id should delete owned cred for member', async () => expect(name).toBe(savedCredential.name); expect(type).toBe(savedCredential.type); - const deletedCredential = await Db.collections.Credentials!.findOne(savedCredential.id); + const deletedCredential = await Db.collections.Credentials.findOneBy({ id: savedCredential.id }); - expect(deletedCredential).toBeUndefined(); // deleted + expect(deletedCredential).toBeNull(); // deleted - const deletedSharedCredential = await Db.collections.SharedCredentials!.findOne(); + const deletedSharedCredential = await Db.collections.SharedCredentials.findOneBy({}); - expect(deletedSharedCredential).toBeUndefined(); // deleted + expect(deletedSharedCredential).toBeNull(); // deleted }); test('DELETE /credentials/:id should delete owned cred for member but leave others untouched', async () => { @@ -244,27 +242,27 @@ test('DELETE /credentials/:id should delete owned cred for member but leave othe expect(name).toBe(savedCredential.name); expect(type).toBe(savedCredential.type); - const deletedCredential = await Db.collections.Credentials!.findOne(savedCredential.id); + const deletedCredential = await Db.collections.Credentials.findOneBy({ id: savedCredential.id }); - expect(deletedCredential).toBeUndefined(); // deleted + expect(deletedCredential).toBeNull(); // deleted - const deletedSharedCredential = await Db.collections.SharedCredentials!.findOne({ + const deletedSharedCredential = await Db.collections.SharedCredentials.findOne({ where: { - credentials: savedCredential, + credentialsId: savedCredential.id, }, }); - expect(deletedSharedCredential).toBeUndefined(); // deleted + expect(deletedSharedCredential).toBeNull(); // deleted await Promise.all( [notToBeChangedCredential, notToBeChangedCredential2].map(async (credential) => { - const untouchedCredential = await Db.collections.Credentials!.findOne(credential.id); + const untouchedCredential = await Db.collections.Credentials.findOneBy({ id: credential.id }); expect(untouchedCredential).toEqual(credential); // not deleted - const untouchedSharedCredential = await Db.collections.SharedCredentials!.findOne({ + const untouchedSharedCredential = await Db.collections.SharedCredentials.findOne({ where: { - credentials: credential, + credentialsId: credential.id, }, }); @@ -289,11 +287,11 @@ test('DELETE /credentials/:id should not delete non-owned cred for member', asyn expect(response.statusCode).toBe(404); - const shellCredential = await Db.collections.Credentials!.findOne(savedCredential.id); + const shellCredential = await Db.collections.Credentials.findOneBy({ id: savedCredential.id }); expect(shellCredential).toBeDefined(); // not deleted - const deletedSharedCredential = await Db.collections.SharedCredentials!.findOne(); + const deletedSharedCredential = await Db.collections.SharedCredentials.findOneBy({}); expect(deletedSharedCredential).toBeDefined(); // not deleted }); diff --git a/packages/cli/test/integration/publicApi/executions.test.ts b/packages/cli/test/integration/publicApi/executions.test.ts index 5b2a57b193439..24ff6f7badff9 100644 --- a/packages/cli/test/integration/publicApi/executions.test.ts +++ b/packages/cli/test/integration/publicApi/executions.test.ts @@ -9,14 +9,12 @@ import * as utils from '../shared/utils'; import * as testDb from '../shared/testDb'; let app: express.Application; -let testDbName = ''; let globalOwnerRole: Role; let workflowRunner: ActiveWorkflowRunner; beforeAll(async () => { app = await utils.initTestServer({ endpointGroups: ['publicApi'], applyAuth: false }); - const initResult = await testDb.init(); - testDbName = initResult.testDbName; + await testDb.init(); globalOwnerRole = await testDb.getGlobalOwnerRole(); @@ -30,18 +28,15 @@ beforeAll(async () => { }); beforeEach(async () => { - await testDb.truncate( - [ - 'SharedCredentials', - 'SharedWorkflow', - 'User', - 'Workflow', - 'Credentials', - 'Execution', - 'Settings', - ], - testDbName, - ); + await testDb.truncate([ + 'SharedCredentials', + 'SharedWorkflow', + 'User', + 'Workflow', + 'Credentials', + 'Execution', + 'Settings', + ]); config.set('userManagement.disabled', false); config.set('userManagement.isInstanceOwnerSetUp', true); @@ -52,7 +47,7 @@ afterEach(async () => { }); afterAll(async () => { - await testDb.terminate(testDbName); + await testDb.terminate(); }); test('GET /executions/:id should fail due to missing API Key', async () => { diff --git a/packages/cli/test/integration/publicApi/workflows.test.ts b/packages/cli/test/integration/publicApi/workflows.test.ts index 1bc4319e2adbb..125f215fa459f 100644 --- a/packages/cli/test/integration/publicApi/workflows.test.ts +++ b/packages/cli/test/integration/publicApi/workflows.test.ts @@ -11,7 +11,6 @@ import * as utils from '../shared/utils'; import * as testDb from '../shared/testDb'; let app: express.Application; -let testDbName = ''; let globalOwnerRole: Role; let globalMemberRole: Role; let workflowOwnerRole: Role; @@ -19,8 +18,7 @@ let workflowRunner: ActiveWorkflowRunner; beforeAll(async () => { app = await utils.initTestServer({ endpointGroups: ['publicApi'], applyAuth: false }); - const initResult = await testDb.init(); - testDbName = initResult.testDbName; + await testDb.init(); const [fetchedGlobalOwnerRole, fetchedGlobalMemberRole, fetchedWorkflowOwnerRole] = await testDb.getAllRoles(); @@ -37,10 +35,14 @@ beforeAll(async () => { }); beforeEach(async () => { - await testDb.truncate( - ['SharedCredentials', 'SharedWorkflow', 'Tag', 'User', 'Workflow', 'Credentials'], - testDbName, - ); + await testDb.truncate([ + 'SharedCredentials', + 'SharedWorkflow', + 'Tag', + 'User', + 'Workflow', + 'Credentials', + ]); config.set('userManagement.disabled', false); config.set('userManagement.isInstanceOwnerSetUp', true); @@ -51,7 +53,7 @@ afterEach(async () => { }); afterAll(async () => { - await testDb.terminate(testDbName); + await testDb.terminate(); }); test('GET /workflows should fail due to missing API Key', async () => { @@ -537,11 +539,11 @@ test('DELETE /workflows/:id should delete the workflow', async () => { expect(updatedAt).toEqual(workflow.updatedAt.toISOString()); // make sure the workflow actually deleted from the db - const sharedWorkflow = await Db.collections.SharedWorkflow.findOne({ - workflow, + const sharedWorkflow = await Db.collections.SharedWorkflow.findOneBy({ + workflowId: workflow.id, }); - expect(sharedWorkflow).toBeUndefined(); + expect(sharedWorkflow).toBeNull(); }); test('DELETE /workflows/:id should delete non-owned workflow when owner', async () => { @@ -576,11 +578,11 @@ test('DELETE /workflows/:id should delete non-owned workflow when owner', async expect(updatedAt).toEqual(workflow.updatedAt.toISOString()); // make sure the workflow actually deleted from the db - const sharedWorkflow = await Db.collections.SharedWorkflow.findOne({ - workflow, + const sharedWorkflow = await Db.collections.SharedWorkflow.findOneBy({ + workflowId: workflow.id, }); - expect(sharedWorkflow).toBeUndefined(); + expect(sharedWorkflow).toBeNull(); }); test('POST /workflows/:id/activate should fail due to missing API Key', async () => { @@ -679,8 +681,8 @@ test('POST /workflows/:id/activate should set workflow as active', async () => { // check whether the workflow is on the database const sharedWorkflow = await Db.collections.SharedWorkflow.findOne({ where: { - user: member, - workflow, + userId: member.id, + workflowId: workflow.id, }, relations: ['workflow'], }); @@ -724,17 +726,17 @@ test('POST /workflows/:id/activate should set non-owned workflow as active when // check whether the workflow is on the database const sharedOwnerWorkflow = await Db.collections.SharedWorkflow.findOne({ where: { - user: owner, - workflow, + userId: owner.id, + workflowId: workflow.id, }, }); - expect(sharedOwnerWorkflow).toBeUndefined(); + expect(sharedOwnerWorkflow).toBeNull(); const sharedWorkflow = await Db.collections.SharedWorkflow.findOne({ where: { - user: member, - workflow, + userId: member.id, + workflowId: workflow.id, }, relations: ['workflow'], }); @@ -824,8 +826,8 @@ test('POST /workflows/:id/deactivate should deactivate workflow', async () => { // get the workflow after it was deactivated const sharedWorkflow = await Db.collections.SharedWorkflow.findOne({ where: { - user: member, - workflow, + userId: member.id, + workflowId: workflow.id, }, relations: ['workflow'], }); @@ -869,17 +871,17 @@ test('POST /workflows/:id/deactivate should deactivate non-owned workflow when o // check whether the workflow is deactivated in the database const sharedOwnerWorkflow = await Db.collections.SharedWorkflow.findOne({ where: { - user: owner, - workflow, + userId: owner.id, + workflowId: workflow.id, }, }); - expect(sharedOwnerWorkflow).toBeUndefined(); + expect(sharedOwnerWorkflow).toBeNull(); const sharedWorkflow = await Db.collections.SharedWorkflow.findOne({ where: { - user: member, - workflow, + userId: member.id, + workflowId: workflow.id, }, relations: ['workflow'], }); @@ -990,8 +992,8 @@ test('POST /workflows should create workflow', async () => { // check if created workflow in DB const sharedWorkflow = await Db.collections.SharedWorkflow.findOne({ where: { - user: member, - workflow: response.body, + userId: member.id, + workflowId: response.body.id, }, relations: ['workflow', 'role'], }); @@ -1170,8 +1172,8 @@ test('PUT /workflows/:id should update workflow', async () => { // check updated workflow in DB const sharedWorkflow = await Db.collections.SharedWorkflow.findOne({ where: { - user: member, - workflow: response.body, + userId: member.id, + workflowId: response.body.id, }, relations: ['workflow'], }); @@ -1247,17 +1249,17 @@ test('PUT /workflows/:id should update non-owned workflow if owner', async () => // check updated workflow in DB const sharedOwnerWorkflow = await Db.collections.SharedWorkflow.findOne({ where: { - user: owner, - workflow: response.body, + userId: owner.id, + workflowId: response.body.id, }, }); - expect(sharedOwnerWorkflow).toBeUndefined(); + expect(sharedOwnerWorkflow).toBeNull(); const sharedWorkflow = await Db.collections.SharedWorkflow.findOne({ where: { - user: member, - workflow: response.body, + userId: member.id, + workflowId: response.body.id, }, relations: ['workflow', 'role'], }); diff --git a/packages/cli/test/integration/shared/augmentation.d.ts b/packages/cli/test/integration/shared/augmentation.d.ts index 43a74a2f34ef8..f685b42c60c54 100644 --- a/packages/cli/test/integration/shared/augmentation.d.ts +++ b/packages/cli/test/integration/shared/augmentation.d.ts @@ -1,5 +1,5 @@ import superagent = require('superagent'); -import { ObjectLiteral } from 'typeorm'; +import type { ObjectLiteral } from 'typeorm'; /** * Make `SuperTest` string-indexable. diff --git a/packages/cli/test/integration/shared/testDb.ts b/packages/cli/test/integration/shared/testDb.ts index 6eef3fcf31f65..0f7ad277cc820 100644 --- a/packages/cli/test/integration/shared/testDb.ts +++ b/packages/cli/test/integration/shared/testDb.ts @@ -1,5 +1,5 @@ import { UserSettings } from 'n8n-core'; -import { Connection, ConnectionOptions, createConnection, getConnection } from 'typeorm'; +import { DataSource as Connection, DataSourceOptions as ConnectionOptions } from 'typeorm'; import config from '@/config'; import * as Db from '@/Db'; @@ -50,7 +50,7 @@ export async function init() { // no bootstrap connection required const testDbName = `n8n_test_sqlite_${randomString(6, 10)}_${Date.now()}`; await Db.init(getSqliteOptions({ name: testDbName })); - await getConnection(testDbName).runMigrations({ transaction: 'none' }); + await Db.getConnection().runMigrations({ transaction: 'none' }); return { testDbName }; } @@ -60,7 +60,7 @@ export async function init() { const pgOptions = getBootstrapDBOptions('postgres'); try { - bootstrapPostgres = await createConnection(pgOptions); + bootstrapPostgres = await new Connection(pgOptions).initialize(); } catch (error) { const pgConfig = getPostgresSchemaSection(); @@ -82,15 +82,15 @@ export async function init() { const testDbName = `postgres_${randomString(6, 10)}_${Date.now()}_n8n_test`; await bootstrapPostgres.query(`CREATE DATABASE ${testDbName}`); - await bootstrapPostgres.close(); + await bootstrapPostgres.destroy(); const dbOptions = getDBOptions('postgres', testDbName); if (dbOptions.schema !== 'public') { const { schema, migrations, ...options } = dbOptions; - const connection = await createConnection(options); + const connection = await new Connection(options).initialize(); await connection.query(`CREATE SCHEMA IF NOT EXISTS "${schema}"`); - await connection.close(); + await connection.destroy(); } await Db.init(dbOptions); @@ -99,11 +99,11 @@ export async function init() { } if (dbType === 'mysqldb') { - const bootstrapMysql = await createConnection(getBootstrapDBOptions('mysql')); + const bootstrapMysql = await new Connection(getBootstrapDBOptions('mysql')).initialize(); const testDbName = `mysql_${randomString(6, 10)}_${Date.now()}_n8n_test`; await bootstrapMysql.query(`CREATE DATABASE ${testDbName}`); - await bootstrapMysql.close(); + await bootstrapMysql.destroy(); await Db.init(getDBOptions('mysql', testDbName)); @@ -116,8 +116,8 @@ export async function init() { /** * Drop test DB, closing bootstrap connection if existing. */ -export async function terminate(testDbName: string) { - await getConnection(testDbName).close(); +export async function terminate() { + await Db.getConnection().destroy(); } async function truncateMappingTables( @@ -171,9 +171,9 @@ async function truncateMappingTables( * @param collections Array of entity names whose tables to truncate. * @param testDbName Name of the test DB to truncate tables in. */ -export async function truncate(collections: Array, testDbName: string) { +export async function truncate(collections: Array) { const dbType = config.getEnv('database.type'); - const testDb = getConnection(testDbName); + const testDb = Db.getConnection(); if (dbType === 'sqlite') { await testDb.query('PRAGMA foreign_keys=OFF'); @@ -287,12 +287,12 @@ export async function saveCredential( } export async function shareCredentialWithUsers(credential: CredentialsEntity, users: User[]) { - const role = await Db.collections.Role.findOne({ scope: 'credential', name: 'user' }); + const role = await Db.collections.Role.findOneBy({ scope: 'credential', name: 'user' }); const newSharedCredentials = users.map((user) => Db.collections.SharedCredentials.create({ - user, - credentials: credential, - role, + userId: user.id, + credentialsId: credential.id, + roleId: role?.id, }), ); return Db.collections.SharedCredentials.save(newSharedCredentials); @@ -333,7 +333,7 @@ export function createUserShell(globalRole: Role): Promise { throw new Error(`Invalid role received: ${JSON.stringify(globalRole)}`); } - const shell: Partial = { globalRole }; + const shell: Partial = { globalRoleId: globalRole.id }; if (globalRole.name !== 'owner') { shell.email = randomEmail(); @@ -405,35 +405,35 @@ export function addApiKey(user: User): Promise { // ---------------------------------- export function getGlobalOwnerRole() { - return Db.collections.Role.findOneOrFail({ + return Db.collections.Role.findOneByOrFail({ name: 'owner', scope: 'global', }); } export function getGlobalMemberRole() { - return Db.collections.Role.findOneOrFail({ + return Db.collections.Role.findOneByOrFail({ name: 'member', scope: 'global', }); } export function getWorkflowOwnerRole() { - return Db.collections.Role.findOneOrFail({ + return Db.collections.Role.findOneByOrFail({ name: 'owner', scope: 'workflow', }); } export function getWorkflowEditorRole() { - return Db.collections.Role.findOneOrFail({ + return Db.collections.Role.findOneByOrFail({ name: 'editor', scope: 'workflow', }); } export function getCredentialOwnerRole() { - return Db.collections.Role.findOneOrFail({ + return Db.collections.Role.findOneByOrFail({ name: 'owner', scope: 'credential', }); @@ -641,10 +641,8 @@ export async function createWorkflowWithTrigger( // ---------------------------------- export async function getWorkflowSharing(workflow: WorkflowEntity) { - return Db.collections.SharedWorkflow.find({ - where: { - workflow, - }, + return Db.collections.SharedWorkflow.findBy({ + workflowId: workflow.id, }); } diff --git a/packages/cli/test/integration/shared/utils.ts b/packages/cli/test/integration/shared/utils.ts index 0745c33219fad..b43c7a31677d0 100644 --- a/packages/cli/test/integration/shared/utils.ts +++ b/packages/cli/test/integration/shared/utils.ts @@ -646,7 +646,7 @@ export function getAuthToken(response: request.Response, authCookieName = AUTH_C // ---------------------------------- export async function isInstanceOwnerSetUp() { - const { value } = await Db.collections.Settings.findOneOrFail({ + const { value } = await Db.collections.Settings.findOneByOrFail({ key: 'userManagement.isInstanceOwnerSetUp', }); diff --git a/packages/cli/test/integration/users.api.test.ts b/packages/cli/test/integration/users.api.test.ts index d2b978fcd0613..cc97105a175d4 100644 --- a/packages/cli/test/integration/users.api.test.ts +++ b/packages/cli/test/integration/users.api.test.ts @@ -25,7 +25,6 @@ import { NodeMailer } from '@/UserManagement/email/NodeMailer'; jest.mock('@/UserManagement/email/NodeMailer'); let app: express.Application; -let testDbName = ''; let globalMemberRole: Role; let globalOwnerRole: Role; let workflowOwnerRole: Role; @@ -34,8 +33,7 @@ let authAgent: AuthAgent; beforeAll(async () => { app = await utils.initTestServer({ endpointGroups: ['users'], applyAuth: true }); - const initResult = await testDb.init(); - testDbName = initResult.testDbName; + await testDb.init(); const [ fetchedGlobalOwnerRole, @@ -56,10 +54,7 @@ beforeAll(async () => { }); beforeEach(async () => { - await testDb.truncate( - ['User', 'SharedCredentials', 'SharedWorkflow', 'Workflow', 'Credentials'], - testDbName, - ); + await testDb.truncate(['User', 'SharedCredentials', 'SharedWorkflow', 'Workflow', 'Credentials']); jest.mock('@/config'); @@ -70,7 +65,7 @@ beforeEach(async () => { }); afterAll(async () => { - await testDb.terminate(testDbName); + await testDb.terminate(); }); test('GET /users should return all users', async () => { @@ -156,28 +151,28 @@ test('DELETE /users/:id should delete the user', async () => { expect(response.statusCode).toBe(200); expect(response.body).toEqual(SUCCESS_RESPONSE_BODY); - const user = await Db.collections.User.findOne(userToDelete.id); - expect(user).toBeUndefined(); // deleted + const user = await Db.collections.User.findOneBy({ id: userToDelete.id }); + expect(user).toBeNull(); // deleted const sharedWorkflow = await Db.collections.SharedWorkflow.findOne({ relations: ['user'], - where: { user: userToDelete, role: workflowOwnerRole }, + where: { userId: userToDelete.id, roleId: workflowOwnerRole.id }, }); - expect(sharedWorkflow).toBeUndefined(); // deleted + expect(sharedWorkflow).toBeNull(); // deleted const sharedCredential = await Db.collections.SharedCredentials.findOne({ relations: ['user'], - where: { user: userToDelete, role: credentialOwnerRole }, + where: { userId: userToDelete.id, roleId: credentialOwnerRole.id }, }); - expect(sharedCredential).toBeUndefined(); // deleted + expect(sharedCredential).toBeNull(); // deleted - const workflow = await Db.collections.Workflow.findOne(savedWorkflow.id); - expect(workflow).toBeUndefined(); // deleted + const workflow = await Db.collections.Workflow.findOneBy({ id: savedWorkflow.id }); + expect(workflow).toBeNull(); // deleted // TODO: Include active workflow and check whether webhook has been removed - const credential = await Db.collections.Credentials.findOne(savedCredential.id); - expect(credential).toBeUndefined(); // deleted + const credential = await Db.collections.Credentials.findOneBy({ id: savedCredential.id }); + expect(credential).toBeNull(); // deleted }); test('DELETE /users/:id should fail to delete self', async () => { @@ -187,7 +182,7 @@ test('DELETE /users/:id should fail to delete self', async () => { expect(response.statusCode).toBe(400); - const user = await Db.collections.User.findOne(owner.id); + const user = await Db.collections.User.findOneBy({ id: owner.id }); expect(user).toBeDefined(); }); @@ -202,7 +197,7 @@ test('DELETE /users/:id should fail if user to delete is transferee', async () = expect(response.statusCode).toBe(400); - const user = await Db.collections.User.findOne(idToDelete); + const user = await Db.collections.User.findOneBy({ id: idToDelete }); expect(user).toBeDefined(); }); @@ -226,7 +221,7 @@ test('DELETE /users/:id with transferId should perform transfer', async () => { const sharedWorkflow = await Db.collections.SharedWorkflow.findOneOrFail({ relations: ['workflow'], - where: { user: owner }, + where: { userId: owner.id }, }); expect(sharedWorkflow.workflow).toBeDefined(); @@ -234,15 +229,15 @@ test('DELETE /users/:id with transferId should perform transfer', async () => { const sharedCredential = await Db.collections.SharedCredentials.findOneOrFail({ relations: ['credentials'], - where: { user: owner }, + where: { userId: owner.id }, }); expect(sharedCredential.credentials).toBeDefined(); expect(sharedCredential.credentials.id).toBe(savedCredential.id); - const deletedUser = await Db.collections.User.findOne(userToDelete); + const deletedUser = await Db.collections.User.findOneBy({ id: userToDelete.id }); - expect(deletedUser).toBeUndefined(); + expect(deletedUser).toBeNull(); }); test('GET /resolve-signup-token should validate invite token', async () => { @@ -342,7 +337,7 @@ test('POST /users/:id should fill out a user shell', async () => { const authToken = utils.getAuthToken(response); expect(authToken).toBeDefined(); - const member = await Db.collections.User.findOneOrFail(memberShell.id); + const member = await Db.collections.User.findOneByOrFail({ id: memberShell.id }); expect(member.firstName).toBe(memberData.firstName); expect(member.lastName).toBe(memberData.lastName); expect(member.password).not.toBe(memberData.password); @@ -487,7 +482,7 @@ test('POST /users should email invites and create user shells but ignore existin expect(error).toBe('Email could not be sent'); } - const storedUser = await Db.collections.User.findOneOrFail(id); + const storedUser = await Db.collections.User.findOneByOrFail({ id }); const { firstName, lastName, personalizationAnswers, password, resetPasswordToken } = storedUser; diff --git a/packages/cli/test/integration/workflows.controller.ee.test.ts b/packages/cli/test/integration/workflows.controller.ee.test.ts index ffa658d97c34a..f4a9bb49715f9 100644 --- a/packages/cli/test/integration/workflows.controller.ee.test.ts +++ b/packages/cli/test/integration/workflows.controller.ee.test.ts @@ -14,7 +14,6 @@ import { randomCredentialPayload } from './shared/random'; import { ActiveWorkflowRunner } from '@/ActiveWorkflowRunner'; let app: express.Application; -let testDbName = ''; let globalOwnerRole: Role; let globalMemberRole: Role; let credentialOwnerRole: Role; @@ -29,8 +28,7 @@ beforeAll(async () => { endpointGroups: ['workflows'], applyAuth: true, }); - const initResult = await testDb.init(); - testDbName = initResult.testDbName; + await testDb.init(); globalOwnerRole = await testDb.getGlobalOwnerRole(); globalMemberRole = await testDb.getGlobalMemberRole(); @@ -53,11 +51,11 @@ beforeAll(async () => { }); beforeEach(async () => { - await testDb.truncate(['User', 'Workflow', 'SharedWorkflow'], testDbName); + await testDb.truncate(['User', 'Workflow', 'SharedWorkflow']); }); afterAll(async () => { - await testDb.terminate(testDbName); + await testDb.terminate(); }); test('Router should switch dynamically', async () => { diff --git a/packages/cli/test/integration/workflows.controller.test.ts b/packages/cli/test/integration/workflows.controller.test.ts index 6a99bea5b0ce8..9ea0b992950bf 100644 --- a/packages/cli/test/integration/workflows.controller.test.ts +++ b/packages/cli/test/integration/workflows.controller.test.ts @@ -9,7 +9,6 @@ import type { IPinData } from 'n8n-workflow'; import { makeWorkflow, MOCK_PINDATA } from './shared/utils'; let app: express.Application; -let testDbName = ''; let globalOwnerRole: Role; // mock whether sharing is enabled or not @@ -20,8 +19,7 @@ beforeAll(async () => { endpointGroups: ['workflows'], applyAuth: true, }); - const initResult = await testDb.init(); - testDbName = initResult.testDbName; + await testDb.init(); globalOwnerRole = await testDb.getGlobalOwnerRole(); @@ -30,11 +28,11 @@ beforeAll(async () => { }); beforeEach(async () => { - await testDb.truncate(['User', 'Workflow', 'SharedWorkflow'], testDbName); + await testDb.truncate(['User', 'Workflow', 'SharedWorkflow']); }); afterAll(async () => { - await testDb.terminate(testDbName); + await testDb.terminate(); }); test('POST /workflows should store pin data for node in workflow', async () => { diff --git a/packages/cli/test/teardown.ts b/packages/cli/test/teardown.ts index 07cebbab587d3..bfc2748b1c303 100644 --- a/packages/cli/test/teardown.ts +++ b/packages/cli/test/teardown.ts @@ -1,5 +1,5 @@ import 'tsconfig-paths/register'; -import { createConnection } from 'typeorm'; +import { DataSource as Connection } from 'typeorm'; import config from '@/config'; import { getBootstrapDBOptions } from './integration/shared/testDb'; @@ -7,7 +7,8 @@ export default async () => { const dbType = config.getEnv('database.type').replace(/db$/, ''); if (dbType !== 'postgres' && dbType !== 'mysql') return; - const connection = await createConnection(getBootstrapDBOptions(dbType)); + const connection = new Connection(getBootstrapDBOptions(dbType)); + await connection.initialize(); const query = dbType === 'postgres' ? 'SELECT datname as "Database" FROM pg_database' : 'SHOW DATABASES'; @@ -20,5 +21,5 @@ export default async () => { const promises = databases.map((dbName) => connection.query(`DROP DATABASE ${dbName};`)); await Promise.all(promises); - await connection.close(); + await connection.destroy(); }; diff --git a/packages/cli/test/unit/PermissionChecker.test.ts b/packages/cli/test/unit/PermissionChecker.test.ts index 56a1c23757852..2964210dcdafc 100644 --- a/packages/cli/test/unit/PermissionChecker.test.ts +++ b/packages/cli/test/unit/PermissionChecker.test.ts @@ -25,15 +25,13 @@ import type { SaveCredentialFunction } from '../integration/shared/types'; import { User } from '@/databases/entities/User'; import { SharedWorkflow } from '@/databases/entities/SharedWorkflow'; -let testDbName = ''; let mockNodeTypes: INodeTypes; let credentialOwnerRole: Role; let workflowOwnerRole: Role; let saveCredential: SaveCredentialFunction; beforeAll(async () => { - const initResult = await testDb.init(); - testDbName = initResult.testDbName; + await testDb.init(); mockNodeTypes = MockNodeTypes({ loaded: { @@ -51,12 +49,12 @@ beforeAll(async () => { }); beforeEach(async () => { - await testDb.truncate(['SharedWorkflow', 'SharedCredentials'], testDbName); - await testDb.truncate(['User', 'Workflow', 'Credentials'], testDbName); + await testDb.truncate(['SharedWorkflow', 'SharedCredentials']); + await testDb.truncate(['User', 'Workflow', 'Credentials']); }); afterAll(async () => { - await testDb.terminate(testDbName); + await testDb.terminate(); }); describe('PermissionChecker.check()', () => { diff --git a/packages/cli/test/unit/Telemetry.test.ts b/packages/cli/test/unit/Telemetry.test.ts index 061632bb15d45..8c671eff08a2c 100644 --- a/packages/cli/test/unit/Telemetry.test.ts +++ b/packages/cli/test/unit/Telemetry.test.ts @@ -39,7 +39,7 @@ describe('Telemetry', () => { beforeEach(() => { spyTrack.mockClear(); - telemetry = new Telemetry(instanceId, n8nVersion); + telemetry = new Telemetry(instanceId); (telemetry as any).rudderStack = { flush: () => {}, identify: () => {}, diff --git a/packages/cli/test/unit/WorkflowCredentials.test.ts b/packages/cli/test/unit/WorkflowCredentials.test.ts index 27963b282ecc2..ece7dd66351b9 100644 --- a/packages/cli/test/unit/WorkflowCredentials.test.ts +++ b/packages/cli/test/unit/WorkflowCredentials.test.ts @@ -3,7 +3,7 @@ import type { INode, IWorkflowCredentials } from 'n8n-workflow'; import * as Db from '@/Db'; import { WorkflowCredentials } from '@/WorkflowCredentials'; -// Define a function used to mock the findOne function +// Define a function used to mock the findOneBy function async function mockFind({ id, type, @@ -33,7 +33,7 @@ jest.mock('@/Db', () => { return { collections: { Credentials: { - findOne: jest.fn(mockFind), + findOneBy: jest.fn(mockFind), }, }, }; @@ -54,7 +54,7 @@ describe('WorkflowCredentials', () => { `Credentials with name "${credentials.name}" for type "test" miss an ID.`, ); expect(WorkflowCredentials([noIdNode])).rejects.toEqual(expectedError); - expect(mocked(Db.collections.Credentials.findOne)).toHaveBeenCalledTimes(0); + expect(mocked(Db.collections.Credentials.findOneBy)).toHaveBeenCalledTimes(0); }); test('Should return an error if credentials cannot be found in the DB', () => { @@ -63,7 +63,7 @@ describe('WorkflowCredentials', () => { `Could not find credentials for type "test" with ID "${credentials.id}".`, ); expect(WorkflowCredentials([notFoundNode])).rejects.toEqual(expectedError); - expect(mocked(Db.collections.Credentials.findOne)).toHaveBeenCalledTimes(1); + expect(mocked(Db.collections.Credentials.findOneBy)).toHaveBeenCalledTimes(1); }); test('Should ignore duplicates', async () => { diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 19b01777604f7..1e1a31795ad41 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -184,7 +184,7 @@ importers: lodash.uniqby: ^4.7.0 lodash.unset: ^4.5.2 luxon: ^3.1.0 - mysql2: ~2.3.0 + mysql2: ~2.3.3 n8n-core: ~0.151.0 n8n-editor-ui: ~0.177.0 n8n-nodes-base: ~0.209.0 @@ -199,7 +199,7 @@ importers: passport: ^0.6.0 passport-cookie: ^1.0.9 passport-jwt: ^4.0.0 - pg: ^8.3.0 + pg: ^8.8.0 picocolors: ^1.0.0 posthog-node: ^2.2.2 prom-client: ^13.1.0 @@ -209,7 +209,7 @@ importers: semver: ^7.3.8 shelljs: ^0.8.5 source-map-support: ^0.5.21 - sqlite3: ^5.1.2 + sqlite3: ^5.1.4 sse-channel: ^4.0.0 supertest: ^6.2.2 swagger-ui-express: ^4.3.0 @@ -218,7 +218,7 @@ importers: tsc-alias: ^1.7.0 tsconfig-paths: ^3.14.1 tslib: 1.14.1 - typeorm: 0.2.45 + typeorm: 0.3.11 uuid: ^8.3.2 validator: 13.7.0 winston: ^3.3.3 @@ -299,12 +299,12 @@ importers: semver: 7.3.8 shelljs: 0.8.5 source-map-support: 0.5.21 - sqlite3: 5.1.2 + sqlite3: 5.1.4 sse-channel: 4.0.0 swagger-ui-express: 4.5.0_express@4.18.2 syslog-client: 1.1.1 tslib: 1.14.1 - typeorm: 0.2.45_b2izk5tn6tm5xb65gvog337urq + typeorm: 0.3.11_a77gzgdqnod3rkvxniiwirlqsi uuid: 8.3.2 validator: 13.7.0 winston: 3.8.2 @@ -3481,6 +3481,7 @@ packages: /@npmcli/move-file/1.1.2: resolution: {integrity: sha512-1SUf/Cg2GzGDyaf15aR9St9TWlb+XvbZXWpDx8YKs7MLzMH/BCeopv+y9vzrzgkfykCGuWOlSu3mZhj2+FQcrg==} engines: {node: '>=10'} + deprecated: This functionality has been moved to @npmcli/fs dependencies: mkdirp: 1.0.4 rimraf: 3.0.2 @@ -6407,10 +6408,6 @@ packages: dev: true optional: true - /@types/zen-observable/0.8.3: - resolution: {integrity: sha512-fbF6oTd4sGGy0xjHPKAt+eS2CrxJ3+6gQ3FGcBoIJR2TLAyCkCyI8JqZNy+FeON0AhVgNJoUumVoZQjBFUqHkw==} - dev: false - /@typescript-eslint/eslint-plugin/5.45.0_psz44bhp76u27vmulntnlx26h4: resolution: {integrity: sha512-CXXHNlf0oL+Yg021cxgOdMHNTXD17rHkq7iW6RFHoybdFgQBjU3yIXhhcPpGwr1CjZlo6ET8C6tzX5juQoXeGA==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} @@ -7378,7 +7375,6 @@ packages: /arg/4.1.3: resolution: {integrity: sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==} - dev: true /argparse/1.0.10: resolution: {integrity: sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==} @@ -9707,7 +9703,6 @@ packages: /create-require/1.1.1: resolution: {integrity: sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==} - dev: true /crelt/1.0.5: resolution: {integrity: sha512-+BO9wPPi+DWTDcNYhr/W90myha8ptzftZT+LwcmUbbok0rcP/fequmFYCw8NMoH7pkAZQzU78b3kYrlua5a9eA==} @@ -9998,7 +9993,6 @@ packages: /date-fns/2.29.3: resolution: {integrity: sha512-dDCnyH2WnnKusqvZZ6+jA1O51Ibt8ZMRNkDZdyAyK4YfbDwa/cEmuztzG5pk6hqlp9aSBPYcjOlktquahGwGeA==} engines: {node: '>=0.11'} - dev: true /date-now/0.1.4: resolution: {integrity: sha512-AsElvov3LoNB7tf5k37H2jYSB+ZZPMT5sG2QjJCcdlV5chIv6htBUBUui2IKRjgtKAKtCBN7Zbwa+MtwLjSeNw==} @@ -10307,7 +10301,6 @@ packages: /diff/4.0.2: resolution: {integrity: sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==} engines: {node: '>=0.3.1'} - dev: true /diffie-hellman/5.0.3: resolution: {integrity: sha512-kqag/Nl+f3GwyK25fhUMYj81BUOrZ9IuJsjIcDE5icNM9FJHAVm3VcUDxdLPoQtTuUylWm6ZIknYJwwaPxsUzg==} @@ -10460,7 +10453,6 @@ packages: /dotenv/16.0.3: resolution: {integrity: sha512-7GO6HghkA5fYG9TYnNxi14/7K9f5occMlp3zXAuSxn7CKCxt9xbNWG7yF8hTCSUchlfWSe3uLmlPfigevRItzQ==} engines: {node: '>=12'} - dev: true /dotenv/8.6.0: resolution: {integrity: sha512-IrPdXQsk2BbzvCBGBOTmmSH5SodmqZNt4ERAZDmW4CT+tL8VtvinqywuANaFu4bOMWki16nqf0e4oC0QIaDr/g==} @@ -19609,8 +19601,8 @@ packages: resolution: {integrity: sha512-VE0SOVEHCk7Qc8ulkWw3ntAzXuqf7S2lvwQaDLRnUeIEaKNQJzV6BwmLKhOqT61aGhfUMrXeaBk+oDGCzvhcug==} dev: false - /sqlite3/5.1.2: - resolution: {integrity: sha512-D0Reg6pRWAFXFUnZKsszCI67tthFD8fGPewRddDCX6w4cYwz3MbvuwRICbL+YQjBAh9zbw+lJ/V9oC8nG5j6eg==} + /sqlite3/5.1.4: + resolution: {integrity: sha512-i0UlWAzPlzX3B5XP2cYuhWQJsTtlMD6obOa1PgeEQ4DHEXUuyJkgv50I3isqZAP5oFc2T8OFvakmDh2W6I+YpA==} requiresBuild: true peerDependenciesMeta: node-gyp: @@ -20732,7 +20724,6 @@ packages: source-map-support: 0.5.21 typescript: 4.8.4 yn: 3.1.1 - dev: true /ts-pnp/1.2.0_typescript@4.8.4: resolution: {integrity: sha512-csd+vJOb/gkzvcCHgTGSChYpy5f1/XKNsmvBGO4JXS+z1v2HobugDz4s1IeFXM3wZB44uczs+eazB5Q/ccdhQw==} @@ -20935,26 +20926,31 @@ packages: /typedarray/0.0.6: resolution: {integrity: sha512-/aCDEGatGvZ2BIk+HmLf4ifCJFwvKFNb9/JeZPMulfgFracn9QFcAf5GO8B/mweUjSoblS5In0cWhqpfs/5PQA==} - /typeorm/0.2.45_b2izk5tn6tm5xb65gvog337urq: - resolution: {integrity: sha512-c0rCO8VMJ3ER7JQ73xfk0zDnVv0WDjpsP6Q1m6CVKul7DB9iVdWLRjPzc8v2eaeBuomsbZ2+gTaYr8k1gm3bYA==} + /typeorm/0.3.11_a77gzgdqnod3rkvxniiwirlqsi: + resolution: {integrity: sha512-pzdOyWbVuz/z8Ww6gqvBW4nylsM0KLdUCDExr2gR20/x1khGSVxQkjNV/3YqliG90jrWzrknYbYscpk8yxFJVg==} + engines: {node: '>= 12.9.0'} hasBin: true peerDependencies: - '@sap/hana-client': ^2.11.14 - better-sqlite3: ^7.1.2 + '@google-cloud/spanner': ^5.18.0 + '@sap/hana-client': ^2.12.25 + better-sqlite3: ^7.1.2 || ^8.0.0 hdb-pool: ^0.1.6 - ioredis: ^4.28.3 + ioredis: ^5.0.4 mongodb: ^3.6.0 - mssql: ^6.3.1 + mssql: ^7.3.0 mysql2: ^2.2.5 oracledb: ^5.1.0 pg: ^8.5.1 pg-native: ^3.0.0 pg-query-stream: ^4.0.0 - redis: ^3.1.1 + redis: ^3.1.1 || ^4.0.0 sql.js: ^1.4.0 - sqlite3: ^5.0.2 + sqlite3: ^5.0.3 + ts-node: ^10.7.0 typeorm-aurora-data-api-driver: ^2.0.0 peerDependenciesMeta: + '@google-cloud/spanner': + optional: true '@sap/hana-client': optional: true better-sqlite3: @@ -20983,6 +20979,8 @@ packages: optional: true sqlite3: optional: true + ts-node: + optional: true typeorm-aurora-data-api-driver: optional: true dependencies: @@ -20991,8 +20989,9 @@ packages: buffer: 6.0.3 chalk: 4.1.2 cli-highlight: 2.1.11 + date-fns: 2.29.3 debug: 4.3.4 - dotenv: 8.6.0 + dotenv: 16.0.3 glob: 7.2.3 ioredis: 5.2.4 js-yaml: 4.1.0 @@ -21001,12 +21000,12 @@ packages: pg: 8.8.0 reflect-metadata: 0.1.13 sha.js: 2.4.11 - sqlite3: 5.1.2 + sqlite3: 5.1.4 + ts-node: 9.1.1_typescript@4.8.4 tslib: 2.4.0 uuid: 8.3.2 xml2js: 0.4.23 yargs: 17.6.0 - zen-observable-ts: 1.1.0 transitivePeerDependencies: - supports-color dev: false @@ -22715,7 +22714,6 @@ packages: /yn/3.1.1: resolution: {integrity: sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==} engines: {node: '>=6'} - dev: true /yocto-queue/0.1.0: resolution: {integrity: sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==} @@ -22747,17 +22745,6 @@ packages: commander: 2.20.3 dev: true - /zen-observable-ts/1.1.0: - resolution: {integrity: sha512-1h4zlLSqI2cRLPJUHJFL8bCWHhkpuXkF+dbGkRaWjgDIG26DmzyshUMrdV/rL3UnR+mhaX4fRq8LPouq0MYYIA==} - dependencies: - '@types/zen-observable': 0.8.3 - zen-observable: 0.8.15 - dev: false - - /zen-observable/0.8.15: - resolution: {integrity: sha512-PQ2PC7R9rslx84ndNBZB/Dkv8V8fZEpk83RLgXtYd0fwUgEjseMn1Dgajh2x6S8QbZAFa9p2qVCEuYZNgve0dQ==} - dev: false - /zwitch/1.0.5: resolution: {integrity: sha512-V50KMwwzqJV0NpZIZFwfOD5/lyny3WlSzRiXgA0G7VUnRlqttta1L6UQIHzd6EuBY/cHGfwTIck7w1yH6Q5zUw==} dev: true