From 9bd7529193e4b75471bd692aaa69651dd9fbf9d3 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: Thu, 16 Mar 2023 15:34:13 +0100 Subject: [PATCH] refactor(core): Use an IoC container to manage singleton classes [Part-2] (no-changelog) (#5690) * use typedi for UserManagementMailer * use typedi for SamlService * fix typos * use typedi for Queue * use typedi for License * convert some more code to use typedi --- packages/cli/src/AbstractServer.ts | 2 +- packages/cli/src/ActiveExecutions.ts | 7 ++--- packages/cli/src/ActiveWorkflowRunner.ts | 21 ++++++++------- packages/cli/src/Ldap/helpers.ts | 6 ++--- packages/cli/src/License.ts | 12 ++------- packages/cli/src/PublicApi/types.d.ts | 6 ++--- .../shared/middlewares/global.middleware.ts | 4 +-- packages/cli/src/Queue.ts | 14 ++-------- packages/cli/src/Server.ts | 27 +++++++++++++------ .../UserManagement/UserManagementHelper.ts | 5 ++-- .../src/UserManagement/email/Interfaces.ts | 6 ----- .../src/UserManagement/email/NodeMailer.ts | 4 +-- .../email/UserManagementMailer.ts | 20 +++----------- .../cli/src/UserManagement/email/index.ts | 4 +-- packages/cli/src/WaitTracker.ts | 7 +---- packages/cli/src/WaitingWebhooks.ts | 9 ++++--- .../cli/src/WorkflowExecuteAdditionalData.ts | 14 ++++------ packages/cli/src/WorkflowHelpers.ts | 12 ++------- packages/cli/src/WorkflowRunner.ts | 17 ++++++------ packages/cli/src/WorkflowRunnerProcess.ts | 8 +++--- packages/cli/src/commands/execute.ts | 5 ++-- packages/cli/src/commands/start.ts | 4 +-- packages/cli/src/commands/worker.ts | 15 ++++++----- .../controllers/passwordReset.controller.ts | 10 ++++--- .../MessageEventBus/MessageEventBusHelper.ts | 5 ++-- .../cli/src/executions/executionHelpers.ts | 5 ++-- .../cli/src/executions/executions.service.ts | 4 +-- packages/cli/src/index.ts | 2 -- packages/cli/src/license/License.service.ts | 5 ++-- .../cli/src/license/license.controller.ts | 6 ++--- packages/cli/src/requests.ts | 4 +-- packages/cli/src/sso/saml/saml.service.ee.ts | 11 ++------ packages/cli/src/sso/saml/samlHelpers.ts | 13 ++++----- packages/cli/src/telemetry/index.ts | 8 +++--- packages/cli/src/utils.ts | 7 +++++ packages/cli/test/integration/shared/utils.ts | 5 ++-- .../cli/test/integration/users.api.test.ts | 6 ++--- .../test/unit/ActiveWorkflowRunner.test.ts | 9 ++++++- packages/cli/test/unit/Telemetry.test.ts | 3 ++- 39 files changed, 154 insertions(+), 178 deletions(-) diff --git a/packages/cli/src/AbstractServer.ts b/packages/cli/src/AbstractServer.ts index 1ce52759aa..cde2ff3fdd 100644 --- a/packages/cli/src/AbstractServer.ts +++ b/packages/cli/src/AbstractServer.ts @@ -302,7 +302,7 @@ export abstract class AbstractServer { // ---------------------------------------- protected setupWaitingWebhookEndpoint() { const endpoint = this.endpointWebhookWaiting; - const waitingWebhooks = new WaitingWebhooks(); + const waitingWebhooks = Container.get(WaitingWebhooks); // Register all webhook-waiting requests this.app.all(`/${endpoint}/*`, async (req, res) => { diff --git a/packages/cli/src/ActiveExecutions.ts b/packages/cli/src/ActiveExecutions.ts index d73f6255ba..590cba36cc 100644 --- a/packages/cli/src/ActiveExecutions.ts +++ b/packages/cli/src/ActiveExecutions.ts @@ -1,7 +1,4 @@ -/* eslint-disable prefer-template */ -/* eslint-disable @typescript-eslint/restrict-plus-operands */ /* eslint-disable @typescript-eslint/no-unnecessary-type-assertion */ -/* eslint-disable no-param-reassign */ /* eslint-disable @typescript-eslint/no-unsafe-call */ /* eslint-disable @typescript-eslint/no-non-null-assertion */ /* eslint-disable @typescript-eslint/no-unsafe-assignment */ @@ -25,7 +22,7 @@ import type { IWorkflowExecutionDataProcess, } from '@/Interfaces'; import * as ResponseHelper from '@/ResponseHelper'; -import * as WorkflowHelpers from '@/WorkflowHelpers'; +import { isWorkflowIdValid } from '@/utils'; import { Service } from 'typedi'; @Service() @@ -60,7 +57,7 @@ export class ActiveExecutions { } const workflowId = executionData.workflowData.id; - if (workflowId !== undefined && WorkflowHelpers.isWorkflowIdValid(workflowId)) { + if (workflowId !== undefined && isWorkflowIdValid(workflowId)) { fullExecutionData.workflowId = workflowId; } diff --git a/packages/cli/src/ActiveWorkflowRunner.ts b/packages/cli/src/ActiveWorkflowRunner.ts index b632a104f3..2cd770291e 100644 --- a/packages/cli/src/ActiveWorkflowRunner.ts +++ b/packages/cli/src/ActiveWorkflowRunner.ts @@ -9,7 +9,7 @@ /* eslint-disable @typescript-eslint/no-unsafe-member-access */ /* eslint-disable @typescript-eslint/no-unsafe-assignment */ -import { Container, Service } from 'typedi'; +import { Service } from 'typedi'; import { ActiveWorkflows, NodeExecuteFunctions } from 'n8n-core'; import type { @@ -82,7 +82,11 @@ export class ActiveWorkflowRunner { [key: string]: IQueuedWorkflowActivations; } = {}; - constructor(private externalHooks: ExternalHooks) {} + constructor( + private activeExecutions: ActiveExecutions, + private externalHooks: ExternalHooks, + private nodeTypes: NodeTypes, + ) {} // eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types async init() { @@ -271,14 +275,13 @@ export class ActiveWorkflowRunner { ); } - const nodeTypes = Container.get(NodeTypes); const workflow = new Workflow({ id: webhook.workflowId, name: workflowData.name, nodes: workflowData.nodes, connections: workflowData.connections, active: workflowData.active, - nodeTypes, + nodeTypes: this.nodeTypes, staticData: workflowData.staticData, settings: workflowData.settings, }); @@ -514,14 +517,13 @@ export class ActiveWorkflowRunner { throw new Error(`Could not find workflow with id "${workflowId}"`); } - const nodeTypes = Container.get(NodeTypes); const workflow = new Workflow({ id: workflowId, name: workflowData.name, nodes: workflowData.nodes, connections: workflowData.connections, active: workflowData.active, - nodeTypes, + nodeTypes: this.nodeTypes, staticData: workflowData.staticData, settings: workflowData.settings, }); @@ -638,7 +640,7 @@ export class ActiveWorkflowRunner { if (donePromise) { executePromise.then((executionId) => { - Container.get(ActiveExecutions) + this.activeExecutions .getPostExecutePromise(executionId) .then(donePromise.resolve) .catch(donePromise.reject); @@ -695,7 +697,7 @@ export class ActiveWorkflowRunner { if (donePromise) { executePromise.then((executionId) => { - Container.get(ActiveExecutions) + this.activeExecutions .getPostExecutePromise(executionId) .then(donePromise.resolve) .catch(donePromise.reject); @@ -782,14 +784,13 @@ export class ActiveWorkflowRunner { if (!workflowData) { throw new Error(`Could not find workflow with id "${workflowId}".`); } - const nodeTypes = Container.get(NodeTypes); workflowInstance = new Workflow({ id: workflowId, name: workflowData.name, nodes: workflowData.nodes, connections: workflowData.connections, active: workflowData.active, - nodeTypes, + nodeTypes: this.nodeTypes, staticData: workflowData.staticData, settings: workflowData.settings, }); diff --git a/packages/cli/src/Ldap/helpers.ts b/packages/cli/src/Ldap/helpers.ts index 654734abcc..fd7fb1450e 100644 --- a/packages/cli/src/Ldap/helpers.ts +++ b/packages/cli/src/Ldap/helpers.ts @@ -2,6 +2,7 @@ import { AES, enc } from 'crypto-js'; import type { Entry as LdapUser } from 'ldapts'; import { Filter } from 'ldapts/filters/Filter'; +import { Container } from 'typedi'; import { UserSettings } from 'n8n-core'; import { validate } from 'jsonschema'; import * as Db from '@/Db'; @@ -23,15 +24,14 @@ import { } from './constants'; import type { ConnectionSecurity, LdapConfig } from './types'; import { jsonParse, LoggerProxy as Logger } from 'n8n-workflow'; -import { getLicense } from '@/License'; -import { Container } from 'typedi'; +import { License } from '@/License'; import { InternalHooks } from '@/InternalHooks'; /** * Check whether the LDAP feature is disabled in the instance */ export const isLdapEnabled = (): boolean => { - const license = getLicense(); + const license = Container.get(License); return isUserManagementEnabled() && (config.getEnv(LDAP_ENABLED) || license.isLdapEnabled()); }; diff --git a/packages/cli/src/License.ts b/packages/cli/src/License.ts index 35ed836060..9dc68d86a5 100644 --- a/packages/cli/src/License.ts +++ b/packages/cli/src/License.ts @@ -5,6 +5,7 @@ import { getLogger } from './Logger'; import config from '@/config'; import * as Db from '@/Db'; import { LICENSE_FEATURES, N8N_VERSION, SETTINGS_LICENSE_CERT_KEY } from './constants'; +import { Service } from 'typedi'; async function loadCertStr(): Promise { const databaseSettings = await Db.collections.Settings.findOne({ @@ -27,6 +28,7 @@ async function saveCertStr(value: TLicenseContainerStr): Promise { ); } +@Service() export class License { private logger: ILogger; @@ -160,13 +162,3 @@ export class License { return (this.getFeatureValue('planName') ?? 'Community') as string; } } - -let licenseInstance: License | undefined; - -export function getLicense(): License { - if (licenseInstance === undefined) { - licenseInstance = new License(); - } - - return licenseInstance; -} diff --git a/packages/cli/src/PublicApi/types.d.ts b/packages/cli/src/PublicApi/types.d.ts index b908f07b85..b1bd59d6dc 100644 --- a/packages/cli/src/PublicApi/types.d.ts +++ b/packages/cli/src/PublicApi/types.d.ts @@ -7,7 +7,7 @@ import type { Role } from '@db/entities/Role'; import type { WorkflowEntity } from '@db/entities/WorkflowEntity'; -import type * as UserManagementMailer from '@/UserManagement/email/UserManagementMailer'; +import type { UserManagementMailer } from '@/UserManagement/email'; import type { Risk } from '@/audit/types'; @@ -26,10 +26,10 @@ export type AuthenticatedRequest< > = express.Request & { user: User; globalMemberRole?: Role; - mailer?: UserManagementMailer.UserManagementMailer; + mailer?: UserManagementMailer; }; -export type PaginatatedRequest = AuthenticatedRequest< +export type PaginatedRequest = AuthenticatedRequest< {}, {}, {}, diff --git a/packages/cli/src/PublicApi/v1/shared/middlewares/global.middleware.ts b/packages/cli/src/PublicApi/v1/shared/middlewares/global.middleware.ts index 5007c6ef99..1fe122e8f6 100644 --- a/packages/cli/src/PublicApi/v1/shared/middlewares/global.middleware.ts +++ b/packages/cli/src/PublicApi/v1/shared/middlewares/global.middleware.ts @@ -2,7 +2,7 @@ import type express from 'express'; -import type { AuthenticatedRequest, PaginatatedRequest } from '../../../types'; +import type { AuthenticatedRequest, PaginatedRequest } from '../../../types'; import { decodeCursor } from '../services/pagination.service'; export const authorize = @@ -22,7 +22,7 @@ export const authorize = }; export const validCursor = ( - req: PaginatatedRequest, + req: PaginatedRequest, res: express.Response, next: express.NextFunction, ): express.Response | void => { diff --git a/packages/cli/src/Queue.ts b/packages/cli/src/Queue.ts index fb494ba8d3..1bd7fb5fcf 100644 --- a/packages/cli/src/Queue.ts +++ b/packages/cli/src/Queue.ts @@ -1,10 +1,10 @@ import type Bull from 'bull'; import type { RedisOptions } from 'ioredis'; +import { Service } from 'typedi'; import type { IExecuteResponsePromiseData } from 'n8n-workflow'; import config from '@/config'; import { ActiveExecutions } from '@/ActiveExecutions'; import * as WebhookHelpers from '@/WebhookHelpers'; -import { Container } from 'typedi'; export type JobId = Bull.JobId; export type Job = Bull.Job; @@ -24,6 +24,7 @@ export interface WebhookResponse { response: IExecuteResponsePromiseData; } +@Service() export class Queue { private jobQueue: JobQueue; @@ -91,14 +92,3 @@ export class Queue { return false; } } - -let activeQueueInstance: Queue | undefined; - -export async function getInstance(): Promise { - if (activeQueueInstance === undefined) { - activeQueueInstance = new Queue(Container.get(ActiveExecutions)); - await activeQueueInstance.init(); - } - - return activeQueueInstance; -} diff --git a/packages/cli/src/Server.ts b/packages/cli/src/Server.ts index b29636ab99..92a7632b2e 100644 --- a/packages/cli/src/Server.ts +++ b/packages/cli/src/Server.ts @@ -56,7 +56,7 @@ import timezones from 'google-timezones-json'; import history from 'connect-history-api-fallback'; import config from '@/config'; -import * as Queue from '@/Queue'; +import { Queue } from '@/Queue'; import { getSharedWorkflowIds } from '@/WorkflowHelpers'; import { workflowsController } from '@/workflows/workflows.controller'; @@ -103,7 +103,7 @@ import { isUserManagementEnabled, whereClause, } from '@/UserManagement/UserManagementHelper'; -import { getInstance as getMailerInstance } from '@/UserManagement/email'; +import { UserManagementMailer } from '@/UserManagement/email'; import * as Db from '@/Db'; import type { ICredentialsDb, @@ -367,16 +367,23 @@ class Server extends AbstractServer { const logger = LoggerProxy; const internalHooks = Container.get(InternalHooks); - const mailer = getMailerInstance(); + const mailer = Container.get(UserManagementMailer); const postHog = this.postHog; - const samlService = SamlService.getInstance(); + const samlService = Container.get(SamlService); const controllers: object[] = [ new AuthController({ config, internalHooks, repositories, logger, postHog }), new OwnerController({ config, internalHooks, repositories, logger }), new MeController({ externalHooks, internalHooks, repositories, logger }), new NodeTypesController({ config, nodeTypes }), - new PasswordResetController({ config, externalHooks, internalHooks, repositories, logger }), + new PasswordResetController({ + config, + externalHooks, + internalHooks, + mailer, + repositories, + logger, + }), new TagsController({ config, repositories, externalHooks }), new TranslationController(config, this.credentialTypes), new UsersController({ @@ -480,6 +487,10 @@ class Server extends AbstractServer { }), ); + if (config.getEnv('executions.mode') === 'queue') { + await Container.get(Queue).init(); + } + await handleLdapInit(); this.registerControllers(ignoredEndpoints); @@ -509,7 +520,7 @@ class Server extends AbstractServer { // set up the initial environment if (isSamlLicensed()) { try { - await SamlService.getInstance().init(); + await Container.get(SamlService).init(); } catch (error) { LoggerProxy.error(`SAML initialization failed: ${error.message}`); } @@ -953,7 +964,7 @@ class Server extends AbstractServer { ResponseHelper.send( async (req: ExecutionRequest.GetAllCurrent): Promise => { if (config.getEnv('executions.mode') === 'queue') { - const queue = await Queue.getInstance(); + const queue = Container.get(Queue); const currentJobs = await queue.getJobs(['active', 'waiting']); const currentlyRunningQueueIds = currentJobs.map((job) => job.data.executionId); @@ -1096,7 +1107,7 @@ class Server extends AbstractServer { } as IExecutionsStopData; } - const queue = await Queue.getInstance(); + const queue = Container.get(Queue); const currentJobs = await queue.getJobs(['active', 'waiting']); const job = currentJobs.find((job) => job.data.executionId === req.params.id); diff --git a/packages/cli/src/UserManagement/UserManagementHelper.ts b/packages/cli/src/UserManagement/UserManagementHelper.ts index bd1c2d3881..4c34a57b31 100644 --- a/packages/cli/src/UserManagement/UserManagementHelper.ts +++ b/packages/cli/src/UserManagement/UserManagementHelper.ts @@ -3,6 +3,7 @@ import { In } from 'typeorm'; import type express from 'express'; import { compare, genSaltSync, hash } from 'bcryptjs'; +import Container from 'typedi'; import * as Db from '@/Db'; import * as ResponseHelper from '@/ResponseHelper'; @@ -13,7 +14,7 @@ import type { Role } from '@db/entities/Role'; import type { AuthenticatedRequest } from '@/requests'; import config from '@/config'; import { getWebhookBaseUrl } from '@/WebhookHelpers'; -import { getLicense } from '@/License'; +import { License } from '@/License'; import { RoleService } from '@/role/role.service'; import type { PostHogClient } from '@/posthog'; @@ -55,7 +56,7 @@ export function isUserManagementEnabled(): boolean { } export function isSharingEnabled(): boolean { - const license = getLicense(); + const license = Container.get(License); return ( isUserManagementEnabled() && (config.getEnv('enterprise.features.sharing') || license.isSharingEnabled()) diff --git a/packages/cli/src/UserManagement/email/Interfaces.ts b/packages/cli/src/UserManagement/email/Interfaces.ts index 0bbeba781b..5116434998 100644 --- a/packages/cli/src/UserManagement/email/Interfaces.ts +++ b/packages/cli/src/UserManagement/email/Interfaces.ts @@ -1,9 +1,3 @@ -export interface UserManagementMailerImplementation { - init: () => Promise; - sendMail: (mailData: MailData) => Promise; - verifyConnection: () => Promise; -} - export type InviteEmailData = { email: string; firstName?: string; diff --git a/packages/cli/src/UserManagement/email/NodeMailer.ts b/packages/cli/src/UserManagement/email/NodeMailer.ts index 98da46effe..32ee1b0238 100644 --- a/packages/cli/src/UserManagement/email/NodeMailer.ts +++ b/packages/cli/src/UserManagement/email/NodeMailer.ts @@ -3,9 +3,9 @@ import type { Transporter } from 'nodemailer'; import { createTransport } from 'nodemailer'; import { ErrorReporterProxy as ErrorReporter, LoggerProxy as Logger } from 'n8n-workflow'; import config from '@/config'; -import type { MailData, SendEmailResult, UserManagementMailerImplementation } from './Interfaces'; +import type { MailData, SendEmailResult } from './Interfaces'; -export class NodeMailer implements UserManagementMailerImplementation { +export class NodeMailer { private transport?: Transporter; async init(): Promise { diff --git a/packages/cli/src/UserManagement/email/UserManagementMailer.ts b/packages/cli/src/UserManagement/email/UserManagementMailer.ts index 2c9ef67198..4a140184cf 100644 --- a/packages/cli/src/UserManagement/email/UserManagementMailer.ts +++ b/packages/cli/src/UserManagement/email/UserManagementMailer.ts @@ -2,13 +2,9 @@ import { existsSync } from 'fs'; import { readFile } from 'fs/promises'; import Handlebars from 'handlebars'; import { join as pathJoin } from 'path'; +import { Service } from 'typedi'; import config from '@/config'; -import type { - InviteEmailData, - PasswordResetData, - SendEmailResult, - UserManagementMailerImplementation, -} from './Interfaces'; +import type { InviteEmailData, PasswordResetData, SendEmailResult } from './Interfaces'; import { NodeMailer } from './NodeMailer'; type Template = HandlebarsTemplateDelegate; @@ -36,8 +32,9 @@ async function getTemplate( return template; } +@Service() export class UserManagementMailer { - private mailer: UserManagementMailerImplementation | undefined; + private mailer: NodeMailer | undefined; constructor() { // Other implementations can be used in the future. @@ -81,12 +78,3 @@ export class UserManagementMailer { return result ?? { emailSent: false }; } } - -let mailerInstance: UserManagementMailer | undefined; - -export function getInstance(): UserManagementMailer { - if (mailerInstance === undefined) { - mailerInstance = new UserManagementMailer(); - } - return mailerInstance; -} diff --git a/packages/cli/src/UserManagement/email/index.ts b/packages/cli/src/UserManagement/email/index.ts index e577648f8e..8c94805eb6 100644 --- a/packages/cli/src/UserManagement/email/index.ts +++ b/packages/cli/src/UserManagement/email/index.ts @@ -1,3 +1,3 @@ -import { getInstance, UserManagementMailer } from './UserManagementMailer'; +import { UserManagementMailer } from './UserManagementMailer'; -export { getInstance, UserManagementMailer }; +export { UserManagementMailer }; diff --git a/packages/cli/src/WaitTracker.ts b/packages/cli/src/WaitTracker.ts index b36d49856f..4441cb8e31 100644 --- a/packages/cli/src/WaitTracker.ts +++ b/packages/cli/src/WaitTracker.ts @@ -10,6 +10,7 @@ import { LoggerProxy as Logger, WorkflowOperationError, } from 'n8n-workflow'; +import { Service } from 'typedi'; import type { FindManyOptions, ObjectLiteral } from 'typeorm'; import { Not, LessThanOrEqual } from 'typeorm'; import { DateUtils } from 'typeorm/util/DateUtils'; @@ -17,7 +18,6 @@ import { DateUtils } from 'typeorm/util/DateUtils'; import config from '@/config'; import * as Db from '@/Db'; import * as ResponseHelper from '@/ResponseHelper'; -import { ActiveExecutions } from '@/ActiveExecutions'; import type { IExecutionFlattedDb, IExecutionsStopData, @@ -25,12 +25,9 @@ import type { } from '@/Interfaces'; import { WorkflowRunner } from '@/WorkflowRunner'; import { getWorkflowOwner } from '@/UserManagement/UserManagementHelper'; -import { Container, Service } from 'typedi'; @Service() export class WaitTracker { - activeExecutionsInstance: ActiveExecutions; - private waitingExecutions: { [key: string]: { executionId: string; @@ -41,8 +38,6 @@ export class WaitTracker { mainTimer: NodeJS.Timeout; constructor() { - this.activeExecutionsInstance = Container.get(ActiveExecutions); - // Poll every 60 seconds a list of upcoming executions this.mainTimer = setInterval(() => { this.getWaitingExecutions(); diff --git a/packages/cli/src/WaitingWebhooks.ts b/packages/cli/src/WaitingWebhooks.ts index 262ff28963..f5e9ac6344 100644 --- a/packages/cli/src/WaitingWebhooks.ts +++ b/packages/cli/src/WaitingWebhooks.ts @@ -3,7 +3,7 @@ /* eslint-disable no-param-reassign */ import type { INode, WebhookHttpMethod } from 'n8n-workflow'; import { NodeHelpers, Workflow, LoggerProxy as Logger } from 'n8n-workflow'; - +import { Service } from 'typedi'; import type express from 'express'; import * as Db from '@/Db'; @@ -13,9 +13,11 @@ import { NodeTypes } from '@/NodeTypes'; import type { IExecutionResponse, IResponseCallbackData, IWorkflowDb } from '@/Interfaces'; import * as WorkflowExecuteAdditionalData from '@/WorkflowExecuteAdditionalData'; import { getWorkflowOwner } from '@/UserManagement/UserManagementHelper'; -import { Container } from 'typedi'; +@Service() export class WaitingWebhooks { + constructor(private nodeTypes: NodeTypes) {} + async executeWebhook( httpMethod: WebhookHttpMethod, fullPath: string, @@ -79,14 +81,13 @@ export class WaitingWebhooks { const { workflowData } = fullExecutionData; - const nodeTypes = Container.get(NodeTypes); const workflow = new Workflow({ id: workflowData.id!.toString(), name: workflowData.name, nodes: workflowData.nodes, connections: workflowData.connections, active: workflowData.active, - nodeTypes, + nodeTypes: this.nodeTypes, staticData: workflowData.staticData, settings: workflowData.settings, }); diff --git a/packages/cli/src/WorkflowExecuteAdditionalData.ts b/packages/cli/src/WorkflowExecuteAdditionalData.ts index 4488af126c..6c8cb06db4 100644 --- a/packages/cli/src/WorkflowExecuteAdditionalData.ts +++ b/packages/cli/src/WorkflowExecuteAdditionalData.ts @@ -66,7 +66,7 @@ import * as ResponseHelper from '@/ResponseHelper'; import * as WebhookHelpers from '@/WebhookHelpers'; import * as WorkflowHelpers from '@/WorkflowHelpers'; import { getWorkflowOwner } from '@/UserManagement/UserManagementHelper'; -import { findSubworkflowStart } from '@/utils'; +import { findSubworkflowStart, isWorkflowIdValid } from '@/utils'; import { PermissionChecker } from './UserManagement/PermissionChecker'; import { WorkflowsService } from './workflows/workflows.services'; import { Container } from 'typedi'; @@ -530,11 +530,7 @@ function hookFunctionsSave(parentProcessMode?: string): IWorkflowExecuteHooks { const isManualMode = [this.mode, parentProcessMode].includes('manual'); try { - if ( - !isManualMode && - WorkflowHelpers.isWorkflowIdValid(this.workflowData.id) && - newStaticData - ) { + if (!isManualMode && isWorkflowIdValid(this.workflowData.id) && newStaticData) { // Workflow is saved so update in database try { await WorkflowHelpers.saveStaticDataById( @@ -641,7 +637,7 @@ function hookFunctionsSave(parentProcessMode?: string): IWorkflowExecuteHooks { } const workflowId = this.workflowData.id; - if (WorkflowHelpers.isWorkflowIdValid(workflowId)) { + if (isWorkflowIdValid(workflowId)) { fullExecutionData.workflowId = workflowId; } @@ -729,7 +725,7 @@ function hookFunctionsSaveWorker(): IWorkflowExecuteHooks { newStaticData: IDataObject, ): Promise { try { - if (WorkflowHelpers.isWorkflowIdValid(this.workflowData.id) && newStaticData) { + if (isWorkflowIdValid(this.workflowData.id) && newStaticData) { // Workflow is saved so update in database try { await WorkflowHelpers.saveStaticDataById( @@ -776,7 +772,7 @@ function hookFunctionsSaveWorker(): IWorkflowExecuteHooks { } const workflowId = this.workflowData.id; - if (WorkflowHelpers.isWorkflowIdValid(workflowId)) { + if (isWorkflowIdValid(workflowId)) { fullExecutionData.workflowId = workflowId; } diff --git a/packages/cli/src/WorkflowHelpers.ts b/packages/cli/src/WorkflowHelpers.ts index b2d01cd0ea..f201abd40c 100644 --- a/packages/cli/src/WorkflowHelpers.ts +++ b/packages/cli/src/WorkflowHelpers.ts @@ -1,4 +1,5 @@ import { In } from 'typeorm'; +import { Container } from 'typedi'; import type { IDataObject, IExecuteData, @@ -32,7 +33,7 @@ import type { User } from '@db/entities/User'; import { whereClause } from '@/UserManagement/UserManagementHelper'; import omit from 'lodash.omit'; import { PermissionChecker } from './UserManagement/PermissionChecker'; -import { Container } from 'typedi'; +import { isWorkflowIdValid } from './utils'; const ERROR_TRIGGER_TYPE = config.getEnv('nodes.errorTriggerType'); @@ -74,15 +75,6 @@ export function getDataLastExecutedNodeData(inputData: IRun): ITaskData | undefi return lastNodeRunData; } -/** - * Returns if the given id is a valid workflow id - * - * @param {(string | null | undefined)} id The id to check - */ -export function isWorkflowIdValid(id: string | null | undefined): boolean { - return !(typeof id === 'string' && isNaN(parseInt(id, 10))); -} - /** * Executes the error workflow * diff --git a/packages/cli/src/WorkflowRunner.ts b/packages/cli/src/WorkflowRunner.ts index 53b2a566a4..2d67afd65a 100644 --- a/packages/cli/src/WorkflowRunner.ts +++ b/packages/cli/src/WorkflowRunner.ts @@ -44,7 +44,8 @@ import type { IWorkflowExecutionDataProcessWithExecution, } from '@/Interfaces'; import { NodeTypes } from '@/NodeTypes'; -import * as Queue from '@/Queue'; +import type { Job, JobData, JobQueue, JobResponse } from '@/Queue'; +import { Queue } from '@/Queue'; import * as ResponseHelper from '@/ResponseHelper'; import * as WebhookHelpers from '@/WebhookHelpers'; import * as WorkflowHelpers from '@/WorkflowHelpers'; @@ -63,7 +64,7 @@ export class WorkflowRunner { push: Push; - jobQueue: Queue.JobQueue; + jobQueue: JobQueue; constructor() { this.push = Container.get(Push); @@ -167,7 +168,7 @@ export class WorkflowRunner { await initErrorHandling(); if (executionsMode === 'queue') { - const queue = await Queue.getInstance(); + const queue = Container.get(Queue); this.jobQueue = queue.getBullObjectInstance(); } @@ -434,7 +435,7 @@ export class WorkflowRunner { this.activeExecutions.attachResponsePromise(executionId, responsePromise); } - const jobData: Queue.JobData = { + const jobData: JobData = { executionId, loadStaticData: !!loadStaticData, }; @@ -451,7 +452,7 @@ export class WorkflowRunner { removeOnComplete: true, removeOnFail: true, }; - let job: Queue.Job; + let job: Job; let hooks: WorkflowHooks; try { job = await this.jobQueue.add(jobData, jobOptions); @@ -485,7 +486,7 @@ export class WorkflowRunner { async (resolve, reject, onCancel) => { onCancel.shouldReject = false; onCancel(async () => { - const queue = await Queue.getInstance(); + const queue = Container.get(Queue); await queue.stopJob(job); // We use "getWorkflowHooksWorkerExecuter" as "getWorkflowHooksWorkerMain" does not contain the @@ -503,11 +504,11 @@ export class WorkflowRunner { reject(error); }); - const jobData: Promise = job.finished(); + const jobData: Promise = job.finished(); const queueRecoveryInterval = config.getEnv('queue.bull.queueRecoveryInterval'); - const racingPromises: Array> = [jobData]; + const racingPromises: Array> = [jobData]; let clearWatchdogInterval; if (queueRecoveryInterval > 0) { diff --git a/packages/cli/src/WorkflowRunnerProcess.ts b/packages/cli/src/WorkflowRunnerProcess.ts index 60c9bc1cf0..e3716e41fa 100644 --- a/packages/cli/src/WorkflowRunnerProcess.ts +++ b/packages/cli/src/WorkflowRunnerProcess.ts @@ -54,9 +54,9 @@ import config from '@/config'; import { generateFailedExecutionFromError } from '@/WorkflowHelpers'; import { initErrorHandling } from '@/ErrorReporting'; import { PermissionChecker } from '@/UserManagement/PermissionChecker'; -import { getLicense } from './License'; -import { InternalHooks } from './InternalHooks'; -import { PostHogClient } from './posthog'; +import { License } from '@/License'; +import { InternalHooks } from '@/InternalHooks'; +import { PostHogClient } from '@/posthog'; class WorkflowRunnerProcess { data: IWorkflowExecutionDataProcessWithExecution | undefined; @@ -127,7 +127,7 @@ class WorkflowRunnerProcess { // Init db since we need to read the license. await Db.init(); - const license = getLicense(); + const license = Container.get(License); await license.init(instanceId); // Start timeout for the execution diff --git a/packages/cli/src/commands/execute.ts b/packages/cli/src/commands/execute.ts index 63bbb863ed..16631a962e 100644 --- a/packages/cli/src/commands/execute.ts +++ b/packages/cli/src/commands/execute.ts @@ -6,11 +6,10 @@ import { ExecutionBaseError } from 'n8n-workflow'; import { ActiveExecutions } from '@/ActiveExecutions'; import * as Db from '@/Db'; -import * as WorkflowHelpers from '@/WorkflowHelpers'; import { WorkflowRunner } from '@/WorkflowRunner'; import type { IWorkflowExecutionDataProcess } from '@/Interfaces'; import { getInstanceOwner } from '@/UserManagement/UserManagementHelper'; -import { findCliWorkflowStart } from '@/utils'; +import { findCliWorkflowStart, isWorkflowIdValid } from '@/utils'; import { initEvents } from '@/events'; import { BaseCommand } from './BaseCommand'; import { Container } from 'typedi'; @@ -101,7 +100,7 @@ export class Execute extends BaseCommand { throw new Error('Failed to retrieve workflow data for requested workflow'); } - if (!WorkflowHelpers.isWorkflowIdValid(workflowId)) { + if (!isWorkflowIdValid(workflowId)) { workflowId = undefined; } diff --git a/packages/cli/src/commands/start.ts b/packages/cli/src/commands/start.ts index 1363870a20..6abd162ad8 100644 --- a/packages/cli/src/commands/start.ts +++ b/packages/cli/src/commands/start.ts @@ -28,7 +28,7 @@ import { EDITOR_UI_DIST_DIR, GENERATED_STATIC_DIR } from '@/constants'; import { eventBus } from '@/eventbus'; import { BaseCommand } from './BaseCommand'; import { InternalHooks } from '@/InternalHooks'; -import { getLicense } from '@/License'; +import { License } from '@/License'; // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment, @typescript-eslint/no-var-requires const open = require('open'); @@ -183,7 +183,7 @@ export class Start extends BaseCommand { } async initLicense(): Promise { - const license = getLicense(); + const license = Container.get(License); await license.init(this.instanceId); const activationKey = config.getEnv('license.activationKey'); diff --git a/packages/cli/src/commands/worker.ts b/packages/cli/src/commands/worker.ts index 87d7b55bff..c701d71186 100644 --- a/packages/cli/src/commands/worker.ts +++ b/packages/cli/src/commands/worker.ts @@ -1,6 +1,7 @@ import express from 'express'; import http from 'http'; import type PCancelable from 'p-cancelable'; +import { Container } from 'typedi'; import { flags } from '@oclif/command'; import { WorkflowExecute } from 'n8n-core'; @@ -15,7 +16,8 @@ import * as WorkflowExecuteAdditionalData from '@/WorkflowExecuteAdditionalData' import { PermissionChecker } from '@/UserManagement/PermissionChecker'; import config from '@/config'; -import * as Queue from '@/Queue'; +import type { Job, JobId, JobQueue, JobResponse, WebhookResponse } from '@/Queue'; +import { Queue } from '@/Queue'; import { getWorkflowOwner } from '@/UserManagement/UserManagementHelper'; import { generateFailedExecutionFromError } from '@/WorkflowHelpers'; import { N8N_VERSION } from '@/constants'; @@ -38,7 +40,7 @@ export class Worker extends BaseCommand { [key: string]: PCancelable; } = {}; - static jobQueue: Queue.JobQueue; + static jobQueue: JobQueue; /** * Stop n8n in a graceful way. @@ -86,7 +88,7 @@ export class Worker extends BaseCommand { await this.exitSuccessFully(); } - async runJob(job: Queue.Job, nodeTypes: INodeTypes): Promise { + async runJob(job: Job, nodeTypes: INodeTypes): Promise { const { executionId, loadStaticData } = job.data; const executionDb = await Db.collections.Execution.findOneBy({ id: executionId }); @@ -179,7 +181,7 @@ export class Worker extends BaseCommand { additionalData.hooks.hookFunctions.sendResponse = [ async (response: IExecuteResponsePromiseData): Promise => { - const progress: Queue.WebhookResponse = { + const progress: WebhookResponse = { executionId, response: WebhookHelpers.encodeWebhookResponse(response), }; @@ -238,7 +240,8 @@ export class Worker extends BaseCommand { // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment const redisConnectionTimeoutLimit = config.getEnv('queue.bull.redis.timeoutThreshold'); - const queue = await Queue.getInstance(); + const queue = Container.get(Queue); + await queue.init(); Worker.jobQueue = queue.getBullObjectInstance(); // eslint-disable-next-line @typescript-eslint/no-floating-promises Worker.jobQueue.process(flags.concurrency, async (job) => this.runJob(job, this.nodeTypes)); @@ -248,7 +251,7 @@ export class Worker extends BaseCommand { this.logger.info(` * Concurrency: ${flags.concurrency}`); this.logger.info(''); - Worker.jobQueue.on('global:progress', (jobId: Queue.JobId, progress) => { + Worker.jobQueue.on('global:progress', (jobId: JobId, progress) => { // Progress of a job got updated which does get used // to communicate that a job got canceled. diff --git a/packages/cli/src/controllers/passwordReset.controller.ts b/packages/cli/src/controllers/passwordReset.controller.ts index b78a57620d..4b0ec6c9df 100644 --- a/packages/cli/src/controllers/passwordReset.controller.ts +++ b/packages/cli/src/controllers/passwordReset.controller.ts @@ -14,7 +14,7 @@ import { hashPassword, validatePassword, } from '@/UserManagement/UserManagementHelper'; -import * as UserManagementMailer from '@/UserManagement/email'; +import type { UserManagementMailer } from '@/UserManagement/email'; import { Response } from 'express'; import type { ILogger } from 'n8n-workflow'; @@ -35,6 +35,8 @@ export class PasswordResetController { private readonly internalHooks: IInternalHooksClass; + private readonly mailer: UserManagementMailer; + private readonly userRepository: Repository; constructor({ @@ -42,18 +44,21 @@ export class PasswordResetController { logger, externalHooks, internalHooks, + mailer, repositories, }: { config: Config; logger: ILogger; externalHooks: IExternalHooksClass; internalHooks: IInternalHooksClass; + mailer: UserManagementMailer; repositories: Pick; }) { this.config = config; this.logger = logger; this.externalHooks = externalHooks; this.internalHooks = internalHooks; + this.mailer = mailer; this.userRepository = repositories.User; } @@ -126,8 +131,7 @@ export class PasswordResetController { url.searchParams.append('token', resetPasswordToken); try { - const mailer = UserManagementMailer.getInstance(); - await mailer.passwordReset({ + await this.mailer.passwordReset({ email, firstName, lastName, diff --git a/packages/cli/src/eventbus/MessageEventBus/MessageEventBusHelper.ts b/packages/cli/src/eventbus/MessageEventBus/MessageEventBusHelper.ts index 5ba935b1e3..7dfc68e564 100644 --- a/packages/cli/src/eventbus/MessageEventBus/MessageEventBusHelper.ts +++ b/packages/cli/src/eventbus/MessageEventBus/MessageEventBusHelper.ts @@ -1,7 +1,8 @@ import config from '@/config'; -import { getLicense } from '@/License'; +import { License } from '@/License'; +import { Container } from 'typedi'; export function isLogStreamingEnabled(): boolean { - const license = getLicense(); + const license = Container.get(License); return config.getEnv('enterprise.features.logStreaming') || license.isLogStreamingEnabled(); } diff --git a/packages/cli/src/executions/executionHelpers.ts b/packages/cli/src/executions/executionHelpers.ts index b6fe81e1f1..de27577e22 100644 --- a/packages/cli/src/executions/executionHelpers.ts +++ b/packages/cli/src/executions/executionHelpers.ts @@ -1,6 +1,7 @@ +import { Container } from 'typedi'; import type { IExecutionFlattedDb } from '@/Interfaces'; import type { ExecutionStatus } from 'n8n-workflow'; -import { getLicense } from '@/License'; +import { License } from '@/License'; import config from '@/config'; export function getStatusUsingPreviousExecutionStatusMethod( @@ -20,7 +21,7 @@ export function getStatusUsingPreviousExecutionStatusMethod( } export function isAdvancedExecutionFiltersEnabled(): boolean { - const license = getLicense(); + const license = Container.get(License); return ( config.getEnv('enterprise.features.advancedExecutionFilters') || license.isAdvancedExecutionFiltersEnabled() diff --git a/packages/cli/src/executions/executions.service.ts b/packages/cli/src/executions/executions.service.ts index a4f9d05dcd..c6cceb1bf6 100644 --- a/packages/cli/src/executions/executions.service.ts +++ b/packages/cli/src/executions/executions.service.ts @@ -26,7 +26,7 @@ import type { IWorkflowExecutionDataProcess, } from '@/Interfaces'; import { NodeTypes } from '@/NodeTypes'; -import * as Queue from '@/Queue'; +import { Queue } from '@/Queue'; import type { ExecutionRequest } from '@/requests'; import * as ResponseHelper from '@/ResponseHelper'; import { getSharedWorkflowIds } from '@/WorkflowHelpers'; @@ -197,7 +197,7 @@ export class ExecutionsService { const executingWorkflowIds: string[] = []; if (config.getEnv('executions.mode') === 'queue') { - const queue = await Queue.getInstance(); + const queue = Container.get(Queue); const currentJobs = await queue.getJobs(['active', 'waiting']); executingWorkflowIds.push(...currentJobs.map(({ data }) => data.executionId)); } diff --git a/packages/cli/src/index.ts b/packages/cli/src/index.ts index af889d15f9..6b336cf807 100644 --- a/packages/cli/src/index.ts +++ b/packages/cli/src/index.ts @@ -7,7 +7,6 @@ export * from './WaitingWebhooks'; export * from './WorkflowCredentials'; export * from './WorkflowRunner'; -import { ActiveExecutions } from './ActiveExecutions'; import * as Db from './Db'; import * as GenericHelpers from './GenericHelpers'; import * as ResponseHelper from './ResponseHelper'; @@ -19,7 +18,6 @@ import * as WorkflowExecuteAdditionalData from './WorkflowExecuteAdditionalData' import * as WorkflowHelpers from './WorkflowHelpers'; export { - ActiveExecutions, Db, GenericHelpers, ResponseHelper, diff --git a/packages/cli/src/license/License.service.ts b/packages/cli/src/license/License.service.ts index 5f7f1ff41c..adba06ca9c 100644 --- a/packages/cli/src/license/License.service.ts +++ b/packages/cli/src/license/License.service.ts @@ -1,4 +1,5 @@ -import { getLicense } from '@/License'; +import { Container } from 'typedi'; +import { License } from '@/License'; import type { ILicenseReadResponse } from '@/Interfaces'; import * as Db from '@/Db'; @@ -11,7 +12,7 @@ export class LicenseService { // Helper for getting the basic license data that we want to return static async getLicenseData(): Promise { const triggerCount = await LicenseService.getActiveTriggerCount(); - const license = getLicense(); + const license = Container.get(License); const mainPlan = license.getMainPlan(); return { diff --git a/packages/cli/src/license/license.controller.ts b/packages/cli/src/license/license.controller.ts index 71a28336f7..75d8252411 100644 --- a/packages/cli/src/license/license.controller.ts +++ b/packages/cli/src/license/license.controller.ts @@ -7,7 +7,7 @@ import { getLogger } from '@/Logger'; import * as ResponseHelper from '@/ResponseHelper'; import type { ILicensePostResponse, ILicenseReadResponse } from '@/Interfaces'; import { LicenseService } from './License.service'; -import { getLicense } from '@/License'; +import { License } from '@/License'; import type { AuthenticatedRequest, LicenseRequest } from '@/requests'; import { isInstanceOwner } from '@/PublicApi/v1/handlers/users/users.service'; import { Container } from 'typedi'; @@ -69,7 +69,7 @@ licenseController.post( '/activate', ResponseHelper.send(async (req: LicenseRequest.Activate): Promise => { // Call the license manager activate function and tell it to throw an error - const license = getLicense(); + const license = Container.get(License); try { await license.activate(req.body.activationKey); } catch (e) { @@ -111,7 +111,7 @@ licenseController.post( '/renew', ResponseHelper.send(async (): Promise => { // Call the license manager activate function and tell it to throw an error - const license = getLicense(); + const license = Container.get(License); try { await license.renew(); } catch (e) { diff --git a/packages/cli/src/requests.ts b/packages/cli/src/requests.ts index 5c520407e0..a90ec2b021 100644 --- a/packages/cli/src/requests.ts +++ b/packages/cli/src/requests.ts @@ -15,7 +15,7 @@ import { NoXss } from '@db/utils/customValidators'; import type { PublicUser, IExecutionDeleteFilter, IWorkflowDb } from '@/Interfaces'; import type { Role } from '@db/entities/Role'; import type { User } from '@db/entities/User'; -import type * as UserManagementMailer from '@/UserManagement/email/UserManagementMailer'; +import type { UserManagementMailer } from '@/UserManagement/email'; export class UserUpdatePayload implements Pick { @IsEmail() @@ -46,7 +46,7 @@ export type AuthenticatedRequest< RequestQuery = {}, > = Omit, 'user'> & { user: User; - mailer?: UserManagementMailer.UserManagementMailer; + mailer?: UserManagementMailer; globalMemberRole?: Role; }; diff --git a/packages/cli/src/sso/saml/saml.service.ee.ts b/packages/cli/src/sso/saml/saml.service.ee.ts index d3d1188210..69c4db0bdb 100644 --- a/packages/cli/src/sso/saml/saml.service.ee.ts +++ b/packages/cli/src/sso/saml/saml.service.ee.ts @@ -1,4 +1,5 @@ import type express from 'express'; +import { Service } from 'typedi'; import * as Db from '@/Db'; import type { User } from '@/databases/entities/User'; import { jsonParse, LoggerProxy } from 'n8n-workflow'; @@ -26,9 +27,8 @@ import type { SamlLoginBinding } from './types'; import type { BindingContext, PostBindingContext } from 'samlify/types/src/entity'; import { validateMetadata, validateResponse } from './samlValidator'; +@Service() export class SamlService { - private static instance: SamlService; - private identityProviderInstance: IdentityProviderInstance | undefined; private _samlPreferences: SamlPreferences = { @@ -65,13 +65,6 @@ export class SamlService { }; } - static getInstance(): SamlService { - if (!SamlService.instance) { - SamlService.instance = new SamlService(); - } - return SamlService.instance; - } - async init(): Promise { await this.loadFromDbAndApplySamlPreferences(); setSchemaValidator({ diff --git a/packages/cli/src/sso/saml/samlHelpers.ts b/packages/cli/src/sso/saml/samlHelpers.ts index 841cea5719..ed2e4f3746 100644 --- a/packages/cli/src/sso/saml/samlHelpers.ts +++ b/packages/cli/src/sso/saml/samlHelpers.ts @@ -1,10 +1,11 @@ +import { Container } from 'typedi'; import config from '@/config'; import * as Db from '@/Db'; -import { AuthIdentity } from '../../databases/entities/AuthIdentity'; -import { User } from '../../databases/entities/User'; -import { getLicense } from '../../License'; -import { AuthError } from '../../ResponseHelper'; -import { hashPassword, isUserManagementEnabled } from '../../UserManagement/UserManagementHelper'; +import { AuthIdentity } from '@db/entities/AuthIdentity'; +import { User } from '@db/entities/User'; +import { License } from '@/License'; +import { AuthError } from '@/ResponseHelper'; +import { hashPassword, isUserManagementEnabled } from '@/UserManagement/UserManagementHelper'; import type { SamlPreferences } from './types/samlPreferences'; import type { SamlUserAttributes } from './types/samlUserAttributes'; import type { FlowResult } from 'samlify/types/src/flow'; @@ -44,7 +45,7 @@ export function setSamlLoginLabel(label: string): void { } export function isSamlLicensed(): boolean { - const license = getLicense(); + const license = Container.get(License); return ( isUserManagementEnabled() && (license.isSamlEnabled() || config.getEnv(SAML_ENTERPRISE_FEATURE_ENABLED)) diff --git a/packages/cli/src/telemetry/index.ts b/packages/cli/src/telemetry/index.ts index 516c76f653..cdc7b96f8b 100644 --- a/packages/cli/src/telemetry/index.ts +++ b/packages/cli/src/telemetry/index.ts @@ -7,7 +7,7 @@ import { LoggerProxy } from 'n8n-workflow'; import config from '@/config'; import type { IExecutionTrackProperties } from '@/Interfaces'; import { getLogger } from '@/Logger'; -import { getLicense } from '@/License'; +import { License } from '@/License'; import { LicenseService } from '@/license/License.service'; import { N8N_VERSION } from '@/constants'; import { Service } from 'typedi'; @@ -39,7 +39,7 @@ export class Telemetry { private executionCountsBuffer: IExecutionsBuffer = {}; - constructor(private postHog: PostHogClient) {} + constructor(private postHog: PostHogClient, private license: License) {} setInstanceId(instanceId: string) { this.instanceId = instanceId; @@ -97,8 +97,8 @@ export class Telemetry { // License info const pulsePacket = { - plan_name_current: getLicense().getPlanName(), - quota: getLicense().getTriggerLimit(), + plan_name_current: this.license.getPlanName(), + quota: this.license.getTriggerLimit(), usage: await LicenseService.getActiveTriggerCount(), }; allPromises.push(this.track('pulse', pulsePacket)); diff --git a/packages/cli/src/utils.ts b/packages/cli/src/utils.ts index 7ec8afb253..c99ef97f50 100644 --- a/packages/cli/src/utils.ts +++ b/packages/cli/src/utils.ts @@ -3,6 +3,13 @@ import { CliWorkflowOperationError, SubworkflowOperationError } from 'n8n-workfl import type { INode } from 'n8n-workflow'; import { START_NODES } from './constants'; +/** + * Returns if the given id is a valid workflow id + */ +export function isWorkflowIdValid(id: string | null | undefined): boolean { + return !(typeof id === 'string' && isNaN(parseInt(id, 10))); +} + function findWorkflowStart(executionMode: 'integrated' | 'cli') { return function (nodes: INode[]) { const executeWorkflowTriggerNode = nodes.find( diff --git a/packages/cli/test/integration/shared/utils.ts b/packages/cli/test/integration/shared/utils.ts index 6aeded0866..bef914b73a 100644 --- a/packages/cli/test/integration/shared/utils.ts +++ b/packages/cli/test/integration/shared/utils.ts @@ -41,7 +41,7 @@ import type { User } from '@db/entities/User'; import { getLogger } from '@/Logger'; import { loadPublicApiVersions } from '@/PublicApi/'; import { issueJWT } from '@/auth/jwt'; -import * as UserManagementMailer from '@/UserManagement/email/UserManagementMailer'; +import { UserManagementMailer } from '@/UserManagement/email/UserManagementMailer'; import { AUTHLESS_ENDPOINTS, COMMUNITY_NODE_VERSION, @@ -177,7 +177,7 @@ export async function initTestServer({ if (functionEndpoints.length) { const externalHooks = Container.get(ExternalHooks); const internalHooks = Container.get(InternalHooks); - const mailer = UserManagementMailer.getInstance(); + const mailer = Container.get(UserManagementMailer); const repositories = Db.collections; for (const group of functionEndpoints) { @@ -226,6 +226,7 @@ export async function initTestServer({ logger, externalHooks, internalHooks, + mailer, repositories, }), ); diff --git a/packages/cli/test/integration/users.api.test.ts b/packages/cli/test/integration/users.api.test.ts index 28f36c4364..a5c480a375 100644 --- a/packages/cli/test/integration/users.api.test.ts +++ b/packages/cli/test/integration/users.api.test.ts @@ -20,7 +20,7 @@ import * as testDb from './shared/testDb'; import type { AuthAgent } from './shared/types'; import * as utils from './shared/utils'; -import * as UserManagementMailer from '@/UserManagement/email/UserManagementMailer'; +import { UserManagementMailer } from '@/UserManagement/email/UserManagementMailer'; import { NodeMailer } from '@/UserManagement/email/NodeMailer'; jest.mock('@/UserManagement/email/NodeMailer'); @@ -512,7 +512,7 @@ test('UserManagementMailer expect NodeMailer.verifyConnection not be called when const mockVerifyConnection = jest.spyOn(NodeMailer.prototype, 'verifyConnection'); mockVerifyConnection.mockImplementation(async () => {}); - const userManagementMailer = UserManagementMailer.getInstance(); + const userManagementMailer = new UserManagementMailer(); // NodeMailer.verifyConnection gets called only explicitly expect(async () => await userManagementMailer.verifyConnection()).rejects.toThrow(); @@ -531,7 +531,7 @@ test('UserManagementMailer expect NodeMailer.verifyConnection to be called when config.set('userManagement.emails.smtp.host', 'host'); config.set('userManagement.emails.mode', 'smtp'); - const userManagementMailer = new UserManagementMailer.UserManagementMailer(); + const userManagementMailer = new UserManagementMailer(); // NodeMailer.verifyConnection gets called only explicitly expect(async () => await userManagementMailer.verifyConnection()).not.toThrow(); diff --git a/packages/cli/test/unit/ActiveWorkflowRunner.test.ts b/packages/cli/test/unit/ActiveWorkflowRunner.test.ts index 395b85d377..557391d1be 100644 --- a/packages/cli/test/unit/ActiveWorkflowRunner.test.ts +++ b/packages/cli/test/unit/ActiveWorkflowRunner.test.ts @@ -27,6 +27,8 @@ import { Container } from 'typedi'; import { LoadNodesAndCredentials } from '@/LoadNodesAndCredentials'; import { mockInstance } from '../integration/shared/utils'; import { Push } from '@/push'; +import { ActiveExecutions } from '@/ActiveExecutions'; +import { NodeTypes } from '@/NodeTypes'; /** * TODO: @@ -157,12 +159,17 @@ describe('ActiveWorkflowRunner', () => { beforeEach(() => { externalHooks = mock(); - activeWorkflowRunner = new ActiveWorkflowRunner(externalHooks); + activeWorkflowRunner = new ActiveWorkflowRunner( + new ActiveExecutions(), + externalHooks, + Container.get(NodeTypes), + ); }); afterEach(async () => { await activeWorkflowRunner.removeAll(); databaseActiveWorkflowsCount = 0; + databaseActiveWorkflowsList = []; jest.clearAllMocks(); }); diff --git a/packages/cli/test/unit/Telemetry.test.ts b/packages/cli/test/unit/Telemetry.test.ts index 5c5b4019d0..4b58caf994 100644 --- a/packages/cli/test/unit/Telemetry.test.ts +++ b/packages/cli/test/unit/Telemetry.test.ts @@ -2,6 +2,7 @@ import { Telemetry } from '@/telemetry'; import config from '@/config'; import { flushPromises } from './Helpers'; import { PostHogClient } from '@/posthog'; +import { mock } from 'jest-mock-extended'; jest.unmock('@/telemetry'); jest.mock('@/license/License.service', () => { @@ -45,7 +46,7 @@ describe('Telemetry', () => { const postHog = new PostHogClient(); postHog.init(instanceId); - telemetry = new Telemetry(postHog); + telemetry = new Telemetry(postHog, mock()); telemetry.setInstanceId(instanceId); (telemetry as any).rudderStack = { flush: () => {},