From 1e7a309e63ba0a216d7836ea0c2e5380792c9fe1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Iv=C3=A1n=20Ovejero?= Date: Fri, 15 Dec 2023 12:59:56 +0100 Subject: [PATCH] refactor(core): Make workflow services injectable (no-changelog) (#8033) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Refactor static workflow service classes into DI-compatible classes Context: https://n8nio.slack.com/archives/C069HS026UF/p1702466571648889 Up next: - Inject dependencies into workflow services - Consolidate workflow controllers into one - Make workflow controller injectable - Inject dependencies into workflow controller --------- Co-authored-by: कारतोफ्फेलस्क्रिप्ट™ --- packages/cli/src/ActiveWorkflowRunner.ts | 18 ++--- .../handlers/workflows/workflows.handler.ts | 4 +- packages/cli/src/WebhookHelpers.ts | 4 +- .../cli/src/WorkflowExecuteAdditionalData.ts | 8 +-- .../src/executions/executions.service.ee.ts | 11 +-- ....services.ee.ts => workflow.service.ee.ts} | 25 +++---- ...kflows.services.ts => workflow.service.ts} | 31 +++++---- .../src/workflows/workflows.controller.ee.ts | 69 +++++++++++++------ .../cli/src/workflows/workflows.controller.ts | 14 ++-- .../integration/ActiveWorkflowRunner.test.ts | 2 + .../integration/publicApi/workflows.test.ts | 7 +- .../test/integration/workflow.service.test.ts | 7 +- .../workflows.controller.ee.test.ts | 5 ++ 13 files changed, 125 insertions(+), 80 deletions(-) rename packages/cli/src/workflows/{workflows.services.ee.ts => workflow.service.ee.ts} (90%) rename packages/cli/src/workflows/{workflows.services.ts => workflow.service.ts} (94%) diff --git a/packages/cli/src/ActiveWorkflowRunner.ts b/packages/cli/src/ActiveWorkflowRunner.ts index a665e794f3..72ec73da36 100644 --- a/packages/cli/src/ActiveWorkflowRunner.ts +++ b/packages/cli/src/ActiveWorkflowRunner.ts @@ -2,7 +2,7 @@ /* eslint-disable @typescript-eslint/no-unsafe-member-access */ /* eslint-disable @typescript-eslint/no-unsafe-assignment */ -import { Service } from 'typedi'; +import { Container, Service } from 'typedi'; import { ActiveWorkflows, NodeExecuteFunctions } from 'n8n-core'; import type { @@ -59,7 +59,7 @@ import { NodeTypes } from '@/NodeTypes'; import { WorkflowRunner } from '@/WorkflowRunner'; import { ExternalHooks } from '@/ExternalHooks'; import { whereClause } from './UserManagement/UserManagementHelper'; -import { WorkflowsService } from './workflows/workflows.services'; +import { WorkflowService } from './workflows/workflow.service'; import { webhookNotFoundErrorMessage } from './utils'; import { In } from 'typeorm'; import { WebhookService } from './services/webhook.service'; @@ -418,8 +418,8 @@ export class ActiveWorkflowRunner implements IWebhookManager { } } await this.webhookService.populateCache(); - // Save static data! - await WorkflowsService.saveStaticData(workflow); + + await Container.get(WorkflowService).saveStaticData(workflow); } /** @@ -458,7 +458,7 @@ export class ActiveWorkflowRunner implements IWebhookManager { await workflow.deleteWebhook(webhookData, NodeExecuteFunctions, mode, 'update', false); } - await WorkflowsService.saveStaticData(workflow); + await Container.get(WorkflowService).saveStaticData(workflow); await this.webhookService.deleteWorkflowWebhooks(workflowId); } @@ -531,7 +531,7 @@ export class ActiveWorkflowRunner implements IWebhookManager { donePromise?: IDeferredPromise, ): void => { this.logger.debug(`Received event to trigger execution for workflow "${workflow.name}"`); - void WorkflowsService.saveStaticData(workflow); + void Container.get(WorkflowService).saveStaticData(workflow); const executePromise = this.runWorkflow( workflowData, node, @@ -586,7 +586,7 @@ export class ActiveWorkflowRunner implements IWebhookManager { donePromise?: IDeferredPromise, ): void => { this.logger.debug(`Received trigger for workflow "${workflow.name}"`); - void WorkflowsService.saveStaticData(workflow); + void Container.get(WorkflowService).saveStaticData(workflow); const executePromise = this.runWorkflow( workflowData, @@ -821,7 +821,7 @@ export class ActiveWorkflowRunner implements IWebhookManager { await this.activationErrorsService.unset(workflowId); const triggerCount = this.countTriggers(workflow, additionalData); - await WorkflowsService.updateWorkflowTriggerCount(workflow.id, triggerCount); + await Container.get(WorkflowService).updateWorkflowTriggerCount(workflow.id, triggerCount); } catch (e) { const error = e instanceof Error ? e : new Error(`${e}`); await this.activationErrorsService.set(workflowId, error.message); @@ -831,7 +831,7 @@ export class ActiveWorkflowRunner implements IWebhookManager { // If for example webhooks get created it sometimes has to save the // id of them in the static data. So make sure that data gets persisted. - await WorkflowsService.saveStaticData(workflow); + await Container.get(WorkflowService).saveStaticData(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 c90d5ae8b3..542bd6a209 100644 --- a/packages/cli/src/PublicApi/v1/handlers/workflows/workflows.handler.ts +++ b/packages/cli/src/PublicApi/v1/handlers/workflows/workflows.handler.ts @@ -24,7 +24,7 @@ import { parseTagNames, getWorkflowsAndCount, } from './workflows.service'; -import { WorkflowsService } from '@/workflows/workflows.services'; +import { WorkflowService } from '@/workflows/workflow.service'; import { InternalHooks } from '@/InternalHooks'; import { RoleService } from '@/services/role.service'; import { WorkflowHistoryService } from '@/workflows/workflowHistory/workflowHistory.service.ee'; @@ -63,7 +63,7 @@ export = { async (req: WorkflowRequest.Get, res: express.Response): Promise => { const { id: workflowId } = req.params; - const workflow = await WorkflowsService.delete(req.user, workflowId); + const workflow = await Container.get(WorkflowService).delete(req.user, workflowId); if (!workflow) { // user trying to access a workflow they do not own // or workflow does not exist diff --git a/packages/cli/src/WebhookHelpers.ts b/packages/cli/src/WebhookHelpers.ts index 81cb48907c..0446d5a92b 100644 --- a/packages/cli/src/WebhookHelpers.ts +++ b/packages/cli/src/WebhookHelpers.ts @@ -60,7 +60,7 @@ import type { WorkflowEntity } from '@db/entities/WorkflowEntity'; import { EventsService } from '@/services/events.service'; import { OwnershipService } from './services/ownership.service'; import { parseBody } from './middlewares'; -import { WorkflowsService } from './workflows/workflows.services'; +import { WorkflowService } from './workflows/workflow.service'; import { Logger } from './Logger'; import { NotFoundError } from './errors/response-errors/not-found.error'; import { InternalServerError } from './errors/response-errors/internal-server.error'; @@ -387,7 +387,7 @@ export async function executeWebhook( } // Save static data if it changed - await WorkflowsService.saveStaticData(workflow); + await Container.get(WorkflowService).saveStaticData(workflow); const additionalKeys: IWorkflowDataProxyAdditionalKeys = { $executionId: executionId, diff --git a/packages/cli/src/WorkflowExecuteAdditionalData.ts b/packages/cli/src/WorkflowExecuteAdditionalData.ts index 4358be529e..1f6bcc627b 100644 --- a/packages/cli/src/WorkflowExecuteAdditionalData.ts +++ b/packages/cli/src/WorkflowExecuteAdditionalData.ts @@ -52,7 +52,7 @@ import * as WebhookHelpers from '@/WebhookHelpers'; import * as WorkflowHelpers from '@/WorkflowHelpers'; import { findSubworkflowStart, isWorkflowIdValid } from '@/utils'; import { PermissionChecker } from './UserManagement/PermissionChecker'; -import { WorkflowsService } from './workflows/workflows.services'; +import { WorkflowService } from './workflows/workflow.service'; import { InternalHooks } from '@/InternalHooks'; import { ExecutionRepository } from '@db/repositories/execution.repository'; import { EventsService } from '@/services/events.service'; @@ -418,7 +418,7 @@ function hookFunctionsSave(parentProcessMode?: string): IWorkflowExecuteHooks { if (!isManualMode && isWorkflowIdValid(this.workflowData.id) && newStaticData) { // Workflow is saved so update in database try { - await WorkflowsService.saveStaticDataById( + await Container.get(WorkflowService).saveStaticDataById( this.workflowData.id as string, newStaticData, ); @@ -564,7 +564,7 @@ function hookFunctionsSaveWorker(): IWorkflowExecuteHooks { if (isWorkflowIdValid(this.workflowData.id) && newStaticData) { // Workflow is saved so update in database try { - await WorkflowsService.saveStaticDataById( + await Container.get(WorkflowService).saveStaticDataById( this.workflowData.id as string, newStaticData, ); @@ -714,7 +714,7 @@ export async function getWorkflowData( if (workflowInfo.id !== undefined) { const relations = config.getEnv('workflowTagsDisabled') ? [] : ['tags']; - workflowData = await WorkflowsService.get({ id: workflowInfo.id }, { relations }); + workflowData = await Container.get(WorkflowService).get({ id: workflowInfo.id }, { relations }); if (workflowData === undefined || workflowData === null) { throw new ApplicationError('Workflow does not exist.', { diff --git a/packages/cli/src/executions/executions.service.ee.ts b/packages/cli/src/executions/executions.service.ee.ts index e46d873a42..87139b8e72 100644 --- a/packages/cli/src/executions/executions.service.ee.ts +++ b/packages/cli/src/executions/executions.service.ee.ts @@ -3,8 +3,9 @@ import { getSharedWorkflowIds } from '@/WorkflowHelpers'; import { ExecutionsService } from './executions.service'; import type { ExecutionRequest } from '@/requests'; import type { IExecutionResponse, IExecutionFlattedResponse } from '@/Interfaces'; -import { EEWorkflowsService as EEWorkflows } from '../workflows/workflows.services.ee'; +import { EnterpriseWorkflowService } from '../workflows/workflow.service.ee'; import type { WorkflowWithSharingsAndCredentials } from '@/workflows/workflows.types'; +import Container from 'typedi'; export class EEExecutionsService extends ExecutionsService { /** @@ -23,14 +24,16 @@ export class EEExecutionsService extends ExecutionsService { if (!execution) return; const relations = ['shared', 'shared.user', 'shared.role']; - const workflow = (await EEWorkflows.get( + const enterpriseWorkflowService = Container.get(EnterpriseWorkflowService); + + const workflow = (await enterpriseWorkflowService.get( { id: execution.workflowId }, { relations }, )) as WorkflowWithSharingsAndCredentials; if (!workflow) return; - EEWorkflows.addOwnerAndSharings(workflow); - await EEWorkflows.addCredentialsToWorkflow(workflow, req.user); + enterpriseWorkflowService.addOwnerAndSharings(workflow); + await enterpriseWorkflowService.addCredentialsToWorkflow(workflow, req.user); execution.workflowData = { ...execution.workflowData, diff --git a/packages/cli/src/workflows/workflows.services.ee.ts b/packages/cli/src/workflows/workflow.service.ee.ts similarity index 90% rename from packages/cli/src/workflows/workflows.services.ee.ts rename to packages/cli/src/workflows/workflow.service.ee.ts index ef348cb578..78893db624 100644 --- a/packages/cli/src/workflows/workflows.services.ee.ts +++ b/packages/cli/src/workflows/workflow.service.ee.ts @@ -5,7 +5,7 @@ import { SharedWorkflow } from '@db/entities/SharedWorkflow'; import type { User } from '@db/entities/User'; import { WorkflowEntity } from '@db/entities/WorkflowEntity'; import { UserService } from '@/services/user.service'; -import { WorkflowsService } from './workflows.services'; +import { WorkflowService } from './workflow.service'; import type { CredentialUsedByWorkflow, WorkflowWithSharingsAndCredentials, @@ -13,14 +13,15 @@ import type { import { CredentialsService } from '@/credentials/credentials.service'; import { ApplicationError, NodeOperationError } from 'n8n-workflow'; import { RoleService } from '@/services/role.service'; -import Container from 'typedi'; +import Container, { Service } from 'typedi'; import type { CredentialsEntity } from '@db/entities/CredentialsEntity'; import { SharedWorkflowRepository } from '@db/repositories/sharedWorkflow.repository'; import { BadRequestError } from '@/errors/response-errors/bad-request.error'; import { NotFoundError } from '@/errors/response-errors/not-found.error'; -export class EEWorkflowsService extends WorkflowsService { - static async isOwned( +@Service() +export class EnterpriseWorkflowService extends WorkflowService { + async isOwned( user: User, workflowId: string, ): Promise<{ ownsWorkflow: boolean; workflow?: WorkflowEntity }> { @@ -36,7 +37,7 @@ export class EEWorkflowsService extends WorkflowsService { return { ownsWorkflow: true, workflow }; } - static async getSharings( + async getSharings( transaction: EntityManager, workflowId: string, relations = ['shared'], @@ -48,7 +49,7 @@ export class EEWorkflowsService extends WorkflowsService { return workflow?.shared ?? []; } - static async pruneSharings( + async pruneSharings( transaction: EntityManager, workflowId: string, userIds: string[], @@ -59,7 +60,7 @@ export class EEWorkflowsService extends WorkflowsService { }); } - static async share( + async share( transaction: EntityManager, workflow: WorkflowEntity, shareWithIds: string[], @@ -83,7 +84,7 @@ export class EEWorkflowsService extends WorkflowsService { return transaction.save(newSharedWorkflows); } - static addOwnerAndSharings(workflow: WorkflowWithSharingsAndCredentials): void { + addOwnerAndSharings(workflow: WorkflowWithSharingsAndCredentials): void { workflow.ownedBy = null; workflow.sharedWith = []; if (!workflow.usedCredentials) { @@ -104,7 +105,7 @@ export class EEWorkflowsService extends WorkflowsService { delete workflow.shared; } - static async addCredentialsToWorkflow( + async addCredentialsToWorkflow( workflow: WorkflowWithSharingsAndCredentials, currentUser: User, ): Promise { @@ -150,7 +151,7 @@ export class EEWorkflowsService extends WorkflowsService { }); } - static validateCredentialPermissionsToUser( + validateCredentialPermissionsToUser( workflow: WorkflowEntity, allowedCredentials: CredentialsEntity[], ) { @@ -171,8 +172,8 @@ export class EEWorkflowsService extends WorkflowsService { }); } - static async preventTampering(workflow: WorkflowEntity, workflowId: string, user: User) { - const previousVersion = await EEWorkflowsService.get({ id: workflowId }); + async preventTampering(workflow: WorkflowEntity, workflowId: string, user: User) { + const previousVersion = await this.get({ id: workflowId }); if (!previousVersion) { throw new NotFoundError('Workflow not found'); diff --git a/packages/cli/src/workflows/workflows.services.ts b/packages/cli/src/workflows/workflow.service.ts similarity index 94% rename from packages/cli/src/workflows/workflows.services.ts rename to packages/cli/src/workflows/workflow.service.ts index 9dc153d62e..0bc86c514a 100644 --- a/packages/cli/src/workflows/workflows.services.ts +++ b/packages/cli/src/workflows/workflow.service.ts @@ -1,4 +1,4 @@ -import { Container } from 'typedi'; +import { Container, Service } from 'typedi'; import type { IDataObject, INode, IPinData } from 'n8n-workflow'; import { NodeApiError, ErrorReporterProxy as ErrorReporter, Workflow } from 'n8n-workflow'; import type { FindManyOptions, FindOptionsSelect, FindOptionsWhere, UpdateResult } from 'typeorm'; @@ -41,8 +41,9 @@ export type WorkflowsGetSharedOptions = | { allowGlobalScope: true; globalScope: Scope } | { allowGlobalScope: false }; -export class WorkflowsService { - static async getSharing( +@Service() +export class WorkflowService { + async getSharing( user: User, workflowId: string, options: WorkflowsGetSharedOptions, @@ -68,7 +69,7 @@ export class WorkflowsService { * - select the _first_ pinned trigger that leads to the executed node, * - else select the executed pinned trigger. */ - static findPinnedTrigger(workflow: IWorkflowDb, startNodes?: string[], pinData?: IPinData) { + findPinnedTrigger(workflow: IWorkflowDb, startNodes?: string[], pinData?: IPinData) { if (!pinData || !startNodes) return null; const isTrigger = (nodeTypeName: string) => @@ -102,14 +103,14 @@ export class WorkflowsService { return pinnedTriggers.find((pt) => pt.name === checkNodeName) ?? null; // partial execution } - static async get(workflow: FindOptionsWhere, options?: { relations: string[] }) { + async get(workflow: FindOptionsWhere, options?: { relations: string[] }) { return Container.get(WorkflowRepository).findOne({ where: workflow, relations: options?.relations, }); } - static async getMany(sharedWorkflowIds: string[], options?: ListQuery.Options) { + async getMany(sharedWorkflowIds: string[], options?: ListQuery.Options) { if (sharedWorkflowIds.length === 0) return { workflows: [], count: 0 }; const where: FindOptionsWhere = { @@ -188,7 +189,7 @@ export class WorkflowsService { : { workflows, count }; } - static async update( + async update( user: User, workflow: WorkflowEntity, workflowId: string, @@ -381,7 +382,7 @@ export class WorkflowsService { return updatedWorkflow; } - static async runManually( + async runManually( { workflowData, runData, @@ -395,7 +396,7 @@ export class WorkflowsService { const EXECUTION_MODE = 'manual'; const ACTIVATION_MODE = 'manual'; - const pinnedTrigger = WorkflowsService.findPinnedTrigger(workflowData, startNodes, pinData); + const pinnedTrigger = this.findPinnedTrigger(workflowData, startNodes, pinData); // If webhooks nodes exist and are active we have to wait for till we receive a call if ( @@ -463,7 +464,7 @@ export class WorkflowsService { }; } - static async delete(user: User, workflowId: string): Promise { + async delete(user: User, workflowId: string): Promise { await Container.get(ExternalHooks).run('workflow.delete', [workflowId]); const sharedWorkflow = await Container.get(SharedWorkflowRepository).findOne({ @@ -502,7 +503,7 @@ export class WorkflowsService { return sharedWorkflow.workflow; } - static async updateWorkflowTriggerCount(id: string, triggerCount: number): Promise { + async updateWorkflowTriggerCount(id: string, triggerCount: number): Promise { const qb = Container.get(WorkflowRepository).createQueryBuilder('workflow'); return qb .update() @@ -522,19 +523,19 @@ export class WorkflowsService { /** * Saves the static data if it changed */ - static async saveStaticData(workflow: Workflow): Promise { + async saveStaticData(workflow: Workflow): Promise { if (workflow.staticData.__dataChanged === true) { // Static data of workflow changed and so has to be saved if (isWorkflowIdValid(workflow.id)) { // Workflow is saved so update in database try { - await WorkflowsService.saveStaticDataById(workflow.id, workflow.staticData); + await this.saveStaticDataById(workflow.id, workflow.staticData); workflow.staticData.__dataChanged = false; } catch (error) { ErrorReporter.error(error); Container.get(Logger).error( // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access - `There was a problem saving the workflow with id "${workflow.id}" to save changed staticData: "${error.message}"`, + `There was a problem saving the workflow with id "${workflow.id}" to save changed Data: "${error.message}"`, { workflowId: workflow.id }, ); } @@ -548,7 +549,7 @@ export class WorkflowsService { * @param {(string)} workflowId The id of the workflow to save data on * @param {IDataObject} newStaticData The static data to save */ - static async saveStaticDataById(workflowId: string, newStaticData: IDataObject): Promise { + async saveStaticDataById(workflowId: string, newStaticData: IDataObject): Promise { await Container.get(WorkflowRepository).update(workflowId, { staticData: newStaticData, }); diff --git a/packages/cli/src/workflows/workflows.controller.ee.ts b/packages/cli/src/workflows/workflows.controller.ee.ts index 2f66a4ce6c..4838b0b024 100644 --- a/packages/cli/src/workflows/workflows.controller.ee.ts +++ b/packages/cli/src/workflows/workflows.controller.ee.ts @@ -8,7 +8,7 @@ import { WorkflowEntity } from '@db/entities/WorkflowEntity'; import { validateEntity } from '@/GenericHelpers'; import type { ListQuery, WorkflowRequest } from '@/requests'; import { isSharingEnabled, rightDiff } from '@/UserManagement/UserManagementHelper'; -import { EEWorkflowsService as EEWorkflows } from './workflows.services.ee'; +import { EnterpriseWorkflowService } from './workflow.service.ee'; import { ExternalHooks } from '@/ExternalHooks'; import { SharedWorkflow } from '@db/entities/SharedWorkflow'; import { CredentialsService } from '../credentials/credentials.service'; @@ -59,7 +59,7 @@ EEWorkflowController.put( throw new BadRequestError('Bad request'); } - const isOwnedRes = await EEWorkflows.isOwned(req.user, workflowId); + const isOwnedRes = await Container.get(EnterpriseWorkflowService).isOwned(req.user, workflowId); const { ownsWorkflow } = isOwnedRes; let { workflow } = isOwnedRes; @@ -67,10 +67,14 @@ EEWorkflowController.put( workflow = undefined; // Allow owners/admins to share if (await req.user.hasGlobalScope('workflow:share')) { - const sharedRes = await EEWorkflows.getSharing(req.user, workflowId, { - allowGlobalScope: true, - globalScope: 'workflow:share', - }); + const sharedRes = await Container.get(EnterpriseWorkflowService).getSharing( + req.user, + workflowId, + { + allowGlobalScope: true, + globalScope: 'workflow:share', + }, + ); workflow = sharedRes?.workflow; } if (!workflow) { @@ -79,10 +83,11 @@ EEWorkflowController.put( } const ownerIds = ( - await EEWorkflows.getSharings(Db.getConnection().createEntityManager(), workflowId, [ - 'shared', - 'shared.role', - ]) + await Container.get(EnterpriseWorkflowService).getSharings( + Db.getConnection().createEntityManager(), + workflowId, + ['shared', 'shared.role'], + ) ) .filter((e) => e.role.name === 'owner') .map((e) => e.userId); @@ -90,9 +95,12 @@ EEWorkflowController.put( let newShareeIds: string[] = []; await Db.transaction(async (trx) => { // remove all sharings that are not supposed to exist anymore - await EEWorkflows.pruneSharings(trx, workflowId, [...ownerIds, ...shareWithIds]); + await Container.get(EnterpriseWorkflowService).pruneSharings(trx, workflowId, [ + ...ownerIds, + ...shareWithIds, + ]); - const sharings = await EEWorkflows.getSharings(trx, workflowId); + const sharings = await Container.get(EnterpriseWorkflowService).getSharings(trx, workflowId); // extract the new sharings that need to be added newShareeIds = rightDiff( @@ -101,7 +109,7 @@ EEWorkflowController.put( ); if (newShareeIds.length) { - await EEWorkflows.share(trx, workflow!, newShareeIds); + await Container.get(EnterpriseWorkflowService).share(trx, workflow!, newShareeIds); } }); @@ -124,7 +132,9 @@ EEWorkflowController.get( relations.push('tags'); } - const workflow = await EEWorkflows.get({ id: workflowId }, { relations }); + const enterpriseWorkflowService = Container.get(EnterpriseWorkflowService); + + const workflow = await enterpriseWorkflowService.get({ id: workflowId }, { relations }); if (!workflow) { throw new NotFoundError(`Workflow with ID "${workflowId}" does not exist`); @@ -137,8 +147,8 @@ EEWorkflowController.get( ); } - EEWorkflows.addOwnerAndSharings(workflow); - await EEWorkflows.addCredentialsToWorkflow(workflow, req.user); + enterpriseWorkflowService.addOwnerAndSharings(workflow); + await enterpriseWorkflowService.addCredentialsToWorkflow(workflow, req.user); return workflow; }), ); @@ -179,7 +189,10 @@ EEWorkflowController.post( const allCredentials = await CredentialsService.getMany(req.user); try { - EEWorkflows.validateCredentialPermissionsToUser(newWorkflow, allCredentials); + Container.get(EnterpriseWorkflowService).validateCredentialPermissionsToUser( + newWorkflow, + allCredentials, + ); } catch (error) { throw new BadRequestError( 'The workflow you are trying to save contains credentials that are not shared with you', @@ -240,7 +253,7 @@ EEWorkflowController.get( try { const sharedWorkflowIds = await WorkflowHelpers.getSharedWorkflowIds(req.user); - const { workflows: data, count } = await EEWorkflows.getMany( + const { workflows: data, count } = await Container.get(EnterpriseWorkflowService).getMany( sharedWorkflowIds, req.listQueryOptions, ); @@ -264,9 +277,13 @@ EEWorkflowController.patch( const { tags, ...rest } = req.body; Object.assign(updateData, rest); - const safeWorkflow = await EEWorkflows.preventTampering(updateData, workflowId, req.user); + const safeWorkflow = await Container.get(EnterpriseWorkflowService).preventTampering( + updateData, + workflowId, + req.user, + ); - const updatedWorkflow = await EEWorkflows.update( + const updatedWorkflow = await Container.get(EnterpriseWorkflowService).update( req.user, safeWorkflow, workflowId, @@ -288,10 +305,18 @@ EEWorkflowController.post( Object.assign(workflow, req.body.workflowData); if (req.body.workflowData.id !== undefined) { - const safeWorkflow = await EEWorkflows.preventTampering(workflow, workflow.id, req.user); + const safeWorkflow = await Container.get(EnterpriseWorkflowService).preventTampering( + workflow, + workflow.id, + req.user, + ); req.body.workflowData.nodes = safeWorkflow.nodes; } - return EEWorkflows.runManually(req.body, req.user, GenericHelpers.getSessionId(req)); + return Container.get(EnterpriseWorkflowService).runManually( + req.body, + req.user, + GenericHelpers.getSessionId(req), + ); }), ); diff --git a/packages/cli/src/workflows/workflows.controller.ts b/packages/cli/src/workflows/workflows.controller.ts index f41e1f2900..de53ed1dfe 100644 --- a/packages/cli/src/workflows/workflows.controller.ts +++ b/packages/cli/src/workflows/workflows.controller.ts @@ -15,7 +15,7 @@ import { ExternalHooks } from '@/ExternalHooks'; import type { ListQuery, WorkflowRequest } from '@/requests'; import { isBelowOnboardingThreshold } from '@/WorkflowHelpers'; import { EEWorkflowController } from './workflows.controller.ee'; -import { WorkflowsService } from './workflows.services'; +import { WorkflowService } from './workflow.service'; import { whereClause } from '@/UserManagement/UserManagementHelper'; import { In } from 'typeorm'; import { Container } from 'typedi'; @@ -120,7 +120,7 @@ workflowsController.get( try { const sharedWorkflowIds = await WorkflowHelpers.getSharedWorkflowIds(req.user, ['owner']); - const { workflows: data, count } = await WorkflowsService.getMany( + const { workflows: data, count } = await Container.get(WorkflowService).getMany( sharedWorkflowIds, req.listQueryOptions, ); @@ -245,7 +245,7 @@ workflowsController.patch( const { tags, ...rest } = req.body; Object.assign(updateData, rest); - const updatedWorkflow = await WorkflowsService.update( + const updatedWorkflow = await Container.get(WorkflowService).update( req.user, updateData, workflowId, @@ -267,7 +267,7 @@ workflowsController.delete( ResponseHelper.send(async (req: WorkflowRequest.Delete) => { const { id: workflowId } = req.params; - const workflow = await WorkflowsService.delete(req.user, workflowId); + const workflow = await Container.get(WorkflowService).delete(req.user, workflowId); if (!workflow) { Container.get(Logger).verbose('User attempted to delete a workflow without permissions', { workflowId, @@ -288,6 +288,10 @@ workflowsController.delete( workflowsController.post( '/run', ResponseHelper.send(async (req: WorkflowRequest.ManualRun): Promise => { - return WorkflowsService.runManually(req.body, req.user, GenericHelpers.getSessionId(req)); + return Container.get(WorkflowService).runManually( + req.body, + req.user, + GenericHelpers.getSessionId(req), + ); }), ); diff --git a/packages/cli/test/integration/ActiveWorkflowRunner.test.ts b/packages/cli/test/integration/ActiveWorkflowRunner.test.ts index d70f896aa1..07a147a0eb 100644 --- a/packages/cli/test/integration/ActiveWorkflowRunner.test.ts +++ b/packages/cli/test/integration/ActiveWorkflowRunner.test.ts @@ -24,11 +24,13 @@ import { setSchedulerAsLoadedNode } from './shared/utils'; import * as testDb from './shared/testDb'; import { createOwner } from './shared/db/users'; import { createWorkflow } from './shared/db/workflows'; +import { WorkflowService } from '@/workflows/workflow.service'; mockInstance(ActiveExecutions); mockInstance(ActiveWorkflows); mockInstance(Push); mockInstance(SecretsHelper); +mockInstance(WorkflowService); const webhookService = mockInstance(WebhookService); const multiMainSetup = mockInstance(MultiMainSetup, { diff --git a/packages/cli/test/integration/publicApi/workflows.test.ts b/packages/cli/test/integration/publicApi/workflows.test.ts index f417d45cae..0783c76f7a 100644 --- a/packages/cli/test/integration/publicApi/workflows.test.ts +++ b/packages/cli/test/integration/publicApi/workflows.test.ts @@ -7,7 +7,7 @@ import type { TagEntity } from '@db/entities/TagEntity'; import type { User } from '@db/entities/User'; import { SharedWorkflowRepository } from '@db/repositories/sharedWorkflow.repository'; import { WorkflowHistoryRepository } from '@db/repositories/workflowHistory.repository'; -import type { ActiveWorkflowRunner } from '@/ActiveWorkflowRunner'; +import { ActiveWorkflowRunner } from '@/ActiveWorkflowRunner'; import { randomApiKey } from '../shared/random'; import * as utils from '../shared/utils/'; @@ -43,7 +43,10 @@ beforeAll(async () => { }); await utils.initNodeTypes(); - workflowRunner = await utils.initActiveWorkflowRunner(); + + workflowRunner = Container.get(ActiveWorkflowRunner); + + await workflowRunner.init(); }); beforeEach(async () => { diff --git a/packages/cli/test/integration/workflow.service.test.ts b/packages/cli/test/integration/workflow.service.test.ts index a421ea9196..b0c161897c 100644 --- a/packages/cli/test/integration/workflow.service.test.ts +++ b/packages/cli/test/integration/workflow.service.test.ts @@ -1,6 +1,7 @@ +import Container from 'typedi'; import { ActiveWorkflowRunner } from '@/ActiveWorkflowRunner'; import * as testDb from './shared/testDb'; -import { WorkflowsService } from '@/workflows/workflows.services'; +import { WorkflowService } from '@/workflows/workflow.service'; import { mockInstance } from '../shared/mocking'; import { Telemetry } from '@/telemetry'; import { createOwner } from './shared/db/users'; @@ -31,7 +32,7 @@ describe('update()', () => { const removeSpy = jest.spyOn(activeWorkflowRunner, 'remove'); const addSpy = jest.spyOn(activeWorkflowRunner, 'add'); - await WorkflowsService.update(owner, workflow, workflow.id); + await Container.get(WorkflowService).update(owner, workflow, workflow.id); expect(removeSpy).toHaveBeenCalledTimes(1); const [removedWorkflowId] = removeSpy.mock.calls[0]; @@ -51,7 +52,7 @@ describe('update()', () => { const addSpy = jest.spyOn(activeWorkflowRunner, 'add'); workflow.active = false; - await WorkflowsService.update(owner, workflow, workflow.id); + await Container.get(WorkflowService).update(owner, workflow, workflow.id); expect(removeSpy).toHaveBeenCalledTimes(1); const [removedWorkflowId] = removeSpy.mock.calls[0]; diff --git a/packages/cli/test/integration/workflows.controller.ee.test.ts b/packages/cli/test/integration/workflows.controller.ee.test.ts index fc71e75843..811a367ff0 100644 --- a/packages/cli/test/integration/workflows.controller.ee.test.ts +++ b/packages/cli/test/integration/workflows.controller.ee.test.ts @@ -20,6 +20,8 @@ import { getCredentialOwnerRole, getGlobalMemberRole, getGlobalOwnerRole } from import { createUser } from './shared/db/users'; import { createWorkflow, getWorkflowSharing, shareWorkflowWithUsers } from './shared/db/workflows'; import type { Role } from '@/databases/entities/Role'; +import { EnterpriseWorkflowService } from '@/workflows/workflow.service.ee'; +import { WorkflowService } from '@/workflows/workflow.service'; let globalMemberRole: Role; let owner: User; @@ -55,6 +57,9 @@ beforeAll(async () => { saveCredential = affixRoleToSaveCredential(credentialOwnerRole); await utils.initNodeTypes(); + + Container.set(WorkflowService, new WorkflowService()); + Container.set(EnterpriseWorkflowService, new EnterpriseWorkflowService()); }); beforeEach(async () => {