From 44f5600300f00975d055e37bfdafb024dae347d2 Mon Sep 17 00:00:00 2001 From: martmull Date: Tue, 5 Aug 2025 16:10:11 +0200 Subject: [PATCH] Split workspace service (#13642) Splits workflow-version-step workspace-service into multiple once --- .../workflow-version-edge.resolver.ts | 60 ++++ ...r.ts => workflow-version-step.resolver.ts} | 33 +- .../workflow/workflow-api.module.ts | 6 +- .../workflow-version-edge.exception.ts | 8 + ...workflow-version-step-changes.util.spec.ts | 2 +- ...pute-workflow-version-step-updates.util.ts | 0 .../workflow-builder.module.ts | 5 +- ...ow-version-edge.workspace-service.spec.ts} | 188 +----------- .../workflow-version-edge.module.ts | 9 + ...workflow-version-edge.workspace-service.ts | 258 ++++++++++++++++ ...low-version-step.workspace-service.spec.ts | 283 ++++++++++++++++++ .../utils/__tests__/insert-step.spec.ts | 2 +- .../utils/__tests__/remove-step.spec.ts | 2 +- .../utils/insert-step.ts | 0 .../utils/remove-step.ts | 0 .../workflow-version-step.module.ts | 8 +- ...workflow-version-step.workspace-service.ts | 246 +-------------- .../workflow-version.module.ts | 2 +- .../workflow-version.workspace-service.ts | 2 +- 19 files changed, 643 insertions(+), 471 deletions(-) create mode 100644 packages/twenty-server/src/engine/core-modules/workflow/resolvers/workflow-version-edge.resolver.ts rename packages/twenty-server/src/engine/core-modules/workflow/resolvers/{workflow-step.resolver.ts => workflow-version-step.resolver.ts} (82%) create mode 100644 packages/twenty-server/src/modules/workflow/common/exceptions/workflow-version-edge.exception.ts rename packages/twenty-server/src/modules/workflow/workflow-builder/{workflow-step => }/utils/__tests__/compute-workflow-version-step-changes.util.spec.ts (90%) rename packages/twenty-server/src/modules/workflow/workflow-builder/{workflow-step => }/utils/compute-workflow-version-step-updates.util.ts (100%) rename packages/twenty-server/src/modules/workflow/workflow-builder/{workflow-step/__tests__/workflow-version-step.workspace-service.spec.ts => workflow-version-edge/__tests__/workflow-version-edge.workspace-service.spec.ts} (62%) create mode 100644 packages/twenty-server/src/modules/workflow/workflow-builder/workflow-version-edge/workflow-version-edge.module.ts create mode 100644 packages/twenty-server/src/modules/workflow/workflow-builder/workflow-version-edge/workflow-version-edge.workspace-service.ts create mode 100644 packages/twenty-server/src/modules/workflow/workflow-builder/workflow-version-step/__tests__/workflow-version-step.workspace-service.spec.ts rename packages/twenty-server/src/modules/workflow/workflow-builder/{workflow-step => workflow-version-step}/utils/__tests__/insert-step.spec.ts (99%) rename packages/twenty-server/src/modules/workflow/workflow-builder/{workflow-step => workflow-version-step}/utils/__tests__/remove-step.spec.ts (98%) rename packages/twenty-server/src/modules/workflow/workflow-builder/{workflow-step => workflow-version-step}/utils/insert-step.ts (100%) rename packages/twenty-server/src/modules/workflow/workflow-builder/{workflow-step => workflow-version-step}/utils/remove-step.ts (100%) rename packages/twenty-server/src/modules/workflow/workflow-builder/{workflow-step => workflow-version-step}/workflow-version-step.module.ts (80%) rename packages/twenty-server/src/modules/workflow/workflow-builder/{workflow-step => workflow-version-step}/workflow-version-step.workspace-service.ts (76%) diff --git a/packages/twenty-server/src/engine/core-modules/workflow/resolvers/workflow-version-edge.resolver.ts b/packages/twenty-server/src/engine/core-modules/workflow/resolvers/workflow-version-edge.resolver.ts new file mode 100644 index 00000000000..ae9bc9b5881 --- /dev/null +++ b/packages/twenty-server/src/engine/core-modules/workflow/resolvers/workflow-version-edge.resolver.ts @@ -0,0 +1,60 @@ +import { UseFilters, UseGuards, UsePipes } from '@nestjs/common'; +import { Args, Mutation, Resolver } from '@nestjs/graphql'; + +import { PreventNestToAutoLogGraphqlErrorsFilter } from 'src/engine/core-modules/graphql/filters/prevent-nest-to-auto-log-graphql-errors.filter'; +import { ResolverValidationPipe } from 'src/engine/core-modules/graphql/pipes/resolver-validation.pipe'; +import { Workspace } from 'src/engine/core-modules/workspace/workspace.entity'; +import { AuthWorkspace } from 'src/engine/decorators/auth/auth-workspace.decorator'; +import { SettingsPermissionsGuard } from 'src/engine/guards/settings-permissions.guard'; +import { UserAuthGuard } from 'src/engine/guards/user-auth.guard'; +import { WorkspaceAuthGuard } from 'src/engine/guards/workspace-auth.guard'; +import { PermissionFlagType } from 'src/engine/metadata-modules/permissions/constants/permission-flag-type.constants'; +import { PermissionsGraphqlApiExceptionFilter } from 'src/engine/metadata-modules/permissions/utils/permissions-graphql-api-exception.filter'; +import { CreateWorkflowVersionEdgeInput } from 'src/engine/core-modules/workflow/dtos/create-workflow-version-edge-input.dto'; +import { WorkflowVersionStepChangesDTO } from 'src/engine/core-modules/workflow/dtos/workflow-version-step-changes.dto'; +import { WorkflowVersionEdgeWorkspaceService } from 'src/modules/workflow/workflow-builder/workflow-version-edge/workflow-version-edge.workspace-service'; + +@Resolver() +@UsePipes(ResolverValidationPipe) +@UseGuards( + WorkspaceAuthGuard, + UserAuthGuard, + SettingsPermissionsGuard(PermissionFlagType.WORKFLOWS), +) +@UseFilters( + PermissionsGraphqlApiExceptionFilter, + PreventNestToAutoLogGraphqlErrorsFilter, +) +export class WorkflowVersionEdgeResolver { + constructor( + private readonly workflowVersionEdgeWorkspaceService: WorkflowVersionEdgeWorkspaceService, + ) {} + + @Mutation(() => WorkflowVersionStepChangesDTO) + async createWorkflowVersionEdge( + @AuthWorkspace() { id: workspaceId }: Workspace, + @Args('input') + { source, target, workflowVersionId }: CreateWorkflowVersionEdgeInput, + ): Promise { + return this.workflowVersionEdgeWorkspaceService.createWorkflowVersionEdge({ + source, + target, + workflowVersionId, + workspaceId, + }); + } + + @Mutation(() => WorkflowVersionStepChangesDTO) + async deleteWorkflowVersionEdge( + @AuthWorkspace() { id: workspaceId }: Workspace, + @Args('input') + { source, target, workflowVersionId }: CreateWorkflowVersionEdgeInput, + ): Promise { + return this.workflowVersionEdgeWorkspaceService.deleteWorkflowVersionEdge({ + source, + target, + workflowVersionId, + workspaceId, + }); + } +} diff --git a/packages/twenty-server/src/engine/core-modules/workflow/resolvers/workflow-step.resolver.ts b/packages/twenty-server/src/engine/core-modules/workflow/resolvers/workflow-version-step.resolver.ts similarity index 82% rename from packages/twenty-server/src/engine/core-modules/workflow/resolvers/workflow-step.resolver.ts rename to packages/twenty-server/src/engine/core-modules/workflow/resolvers/workflow-version-step.resolver.ts index 0935d98e0c4..4011187f71c 100644 --- a/packages/twenty-server/src/engine/core-modules/workflow/resolvers/workflow-step.resolver.ts +++ b/packages/twenty-server/src/engine/core-modules/workflow/resolvers/workflow-version-step.resolver.ts @@ -18,10 +18,9 @@ import { UserAuthGuard } from 'src/engine/guards/user-auth.guard'; import { WorkspaceAuthGuard } from 'src/engine/guards/workspace-auth.guard'; import { PermissionFlagType } from 'src/engine/metadata-modules/permissions/constants/permission-flag-type.constants'; import { PermissionsGraphqlApiExceptionFilter } from 'src/engine/metadata-modules/permissions/utils/permissions-graphql-api-exception.filter'; -import { WorkflowVersionStepWorkspaceService } from 'src/modules/workflow/workflow-builder/workflow-step/workflow-version-step.workspace-service'; +import { WorkflowVersionStepWorkspaceService } from 'src/modules/workflow/workflow-builder/workflow-version-step/workflow-version-step.workspace-service'; import { WorkflowActionType } from 'src/modules/workflow/workflow-executor/workflow-actions/types/workflow-action.type'; import { WorkflowRunWorkspaceService } from 'src/modules/workflow/workflow-runner/workflow-run/workflow-run.workspace-service'; -import { CreateWorkflowVersionEdgeInput } from 'src/engine/core-modules/workflow/dtos/create-workflow-version-edge-input.dto'; import { WorkflowVersionStepChangesDTO } from 'src/engine/core-modules/workflow/dtos/workflow-version-step-changes.dto'; @Resolver() @@ -35,7 +34,7 @@ import { WorkflowVersionStepChangesDTO } from 'src/engine/core-modules/workflow/ PermissionsGraphqlApiExceptionFilter, PreventNestToAutoLogGraphqlErrorsFilter, ) -export class WorkflowStepResolver { +export class WorkflowVersionStepResolver { constructor( private readonly workflowVersionStepWorkspaceService: WorkflowVersionStepWorkspaceService, private readonly workflowRunWorkspaceService: WorkflowRunWorkspaceService, @@ -123,32 +122,4 @@ export class WorkflowStepResolver { return step; } - - @Mutation(() => WorkflowVersionStepChangesDTO) - async createWorkflowVersionEdge( - @AuthWorkspace() { id: workspaceId }: Workspace, - @Args('input') - { source, target, workflowVersionId }: CreateWorkflowVersionEdgeInput, - ): Promise { - return this.workflowVersionStepWorkspaceService.createWorkflowVersionEdge({ - source, - target, - workflowVersionId, - workspaceId, - }); - } - - @Mutation(() => WorkflowVersionStepChangesDTO) - async deleteWorkflowVersionEdge( - @AuthWorkspace() { id: workspaceId }: Workspace, - @Args('input') - { source, target, workflowVersionId }: CreateWorkflowVersionEdgeInput, - ): Promise { - return this.workflowVersionStepWorkspaceService.deleteWorkflowVersionEdge({ - source, - target, - workflowVersionId, - workspaceId, - }); - } } diff --git a/packages/twenty-server/src/engine/core-modules/workflow/workflow-api.module.ts b/packages/twenty-server/src/engine/core-modules/workflow/workflow-api.module.ts index aab34aaf898..bb2834eaaf4 100644 --- a/packages/twenty-server/src/engine/core-modules/workflow/workflow-api.module.ts +++ b/packages/twenty-server/src/engine/core-modules/workflow/workflow-api.module.ts @@ -3,7 +3,7 @@ import { Module } from '@nestjs/common'; import { FeatureFlagModule } from 'src/engine/core-modules/feature-flag/feature-flag.module'; import { WorkflowTriggerController } from 'src/engine/core-modules/workflow/controllers/workflow-trigger.controller'; import { WorkflowBuilderResolver } from 'src/engine/core-modules/workflow/resolvers/workflow-builder.resolver'; -import { WorkflowStepResolver } from 'src/engine/core-modules/workflow/resolvers/workflow-step.resolver'; +import { WorkflowVersionStepResolver } from 'src/engine/core-modules/workflow/resolvers/workflow-version-step.resolver'; import { WorkflowTriggerResolver } from 'src/engine/core-modules/workflow/resolvers/workflow-trigger.resolver'; import { WorkflowVersionResolver } from 'src/engine/core-modules/workflow/resolvers/workflow-version.resolver'; import { PermissionsModule } from 'src/engine/metadata-modules/permissions/permissions.module'; @@ -12,6 +12,7 @@ import { WorkflowBuilderModule } from 'src/modules/workflow/workflow-builder/wor import { WorkflowVersionModule } from 'src/modules/workflow/workflow-builder/workflow-version/workflow-version.module'; import { WorkflowRunModule } from 'src/modules/workflow/workflow-runner/workflow-run/workflow-run.module'; import { WorkflowTriggerModule } from 'src/modules/workflow/workflow-trigger/workflow-trigger.module'; +import { WorkflowVersionEdgeResolver } from 'src/engine/core-modules/workflow/resolvers/workflow-version-edge.resolver'; @Module({ imports: [ @@ -27,7 +28,8 @@ import { WorkflowTriggerModule } from 'src/modules/workflow/workflow-trigger/wor providers: [ WorkflowTriggerResolver, WorkflowBuilderResolver, - WorkflowStepResolver, + WorkflowVersionStepResolver, + WorkflowVersionEdgeResolver, WorkflowVersionResolver, ], }) diff --git a/packages/twenty-server/src/modules/workflow/common/exceptions/workflow-version-edge.exception.ts b/packages/twenty-server/src/modules/workflow/common/exceptions/workflow-version-edge.exception.ts new file mode 100644 index 00000000000..f0367a32aaf --- /dev/null +++ b/packages/twenty-server/src/modules/workflow/common/exceptions/workflow-version-edge.exception.ts @@ -0,0 +1,8 @@ +import { CustomException } from 'src/utils/custom-exception'; + +export class WorkflowVersionEdgeException extends CustomException {} + +export enum WorkflowVersionEdgeExceptionCode { + UNKNOWN = 'UNKNOWN', + NOT_FOUND = 'NOT_FOUND', +} diff --git a/packages/twenty-server/src/modules/workflow/workflow-builder/workflow-step/utils/__tests__/compute-workflow-version-step-changes.util.spec.ts b/packages/twenty-server/src/modules/workflow/workflow-builder/utils/__tests__/compute-workflow-version-step-changes.util.spec.ts similarity index 90% rename from packages/twenty-server/src/modules/workflow/workflow-builder/workflow-step/utils/__tests__/compute-workflow-version-step-changes.util.spec.ts rename to packages/twenty-server/src/modules/workflow/workflow-builder/utils/__tests__/compute-workflow-version-step-changes.util.spec.ts index 0c99c321ad1..7d194b08112 100644 --- a/packages/twenty-server/src/modules/workflow/workflow-builder/workflow-step/utils/__tests__/compute-workflow-version-step-changes.util.spec.ts +++ b/packages/twenty-server/src/modules/workflow/workflow-builder/utils/__tests__/compute-workflow-version-step-changes.util.spec.ts @@ -1,4 +1,4 @@ -import { computeWorkflowVersionStepChanges } from 'src/modules/workflow/workflow-builder/workflow-step/utils/compute-workflow-version-step-updates.util'; +import { computeWorkflowVersionStepChanges } from 'src/modules/workflow/workflow-builder/utils/compute-workflow-version-step-updates.util'; import { WorkflowTrigger } from 'src/modules/workflow/workflow-trigger/types/workflow-trigger.type'; import { WorkflowAction } from 'src/modules/workflow/workflow-executor/workflow-actions/types/workflow-action.type'; diff --git a/packages/twenty-server/src/modules/workflow/workflow-builder/workflow-step/utils/compute-workflow-version-step-updates.util.ts b/packages/twenty-server/src/modules/workflow/workflow-builder/utils/compute-workflow-version-step-updates.util.ts similarity index 100% rename from packages/twenty-server/src/modules/workflow/workflow-builder/workflow-step/utils/compute-workflow-version-step-updates.util.ts rename to packages/twenty-server/src/modules/workflow/workflow-builder/utils/compute-workflow-version-step-updates.util.ts diff --git a/packages/twenty-server/src/modules/workflow/workflow-builder/workflow-builder.module.ts b/packages/twenty-server/src/modules/workflow/workflow-builder/workflow-builder.module.ts index 333f02b0e8e..dbc477ac95f 100644 --- a/packages/twenty-server/src/modules/workflow/workflow-builder/workflow-builder.module.ts +++ b/packages/twenty-server/src/modules/workflow/workflow-builder/workflow-builder.module.ts @@ -4,20 +4,23 @@ import { NestjsQueryTypeOrmModule } from '@ptc-org/nestjs-query-typeorm'; import { ObjectMetadataEntity } from 'src/engine/metadata-modules/object-metadata/object-metadata.entity'; import { WorkflowSchemaModule } from 'src/modules/workflow/workflow-builder/workflow-schema/workflow-schema.module'; -import { WorkflowVersionStepModule } from 'src/modules/workflow/workflow-builder/workflow-step/workflow-version-step.module'; +import { WorkflowVersionStepModule } from 'src/modules/workflow/workflow-builder/workflow-version-step/workflow-version-step.module'; import { WorkflowVersionModule } from 'src/modules/workflow/workflow-builder/workflow-version/workflow-version.module'; +import { WorkflowVersionEdgeModule } from 'src/modules/workflow/workflow-builder/workflow-version-edge/workflow-version-edge.module'; @Module({ imports: [ WorkflowSchemaModule, WorkflowVersionModule, WorkflowVersionStepModule, + WorkflowVersionEdgeModule, NestjsQueryTypeOrmModule.forFeature([ObjectMetadataEntity], 'core'), ], exports: [ WorkflowSchemaModule, WorkflowVersionModule, WorkflowVersionStepModule, + WorkflowVersionEdgeModule, ], }) export class WorkflowBuilderModule {} diff --git a/packages/twenty-server/src/modules/workflow/workflow-builder/workflow-step/__tests__/workflow-version-step.workspace-service.spec.ts b/packages/twenty-server/src/modules/workflow/workflow-builder/workflow-version-edge/__tests__/workflow-version-edge.workspace-service.spec.ts similarity index 62% rename from packages/twenty-server/src/modules/workflow/workflow-builder/workflow-step/__tests__/workflow-version-step.workspace-service.spec.ts rename to packages/twenty-server/src/modules/workflow/workflow-builder/workflow-version-edge/__tests__/workflow-version-edge.workspace-service.spec.ts index c50544e5ec4..1802778aafb 100644 --- a/packages/twenty-server/src/modules/workflow/workflow-builder/workflow-step/__tests__/workflow-version-step.workspace-service.spec.ts +++ b/packages/twenty-server/src/modules/workflow/workflow-builder/workflow-version-edge/__tests__/workflow-version-edge.workspace-service.spec.ts @@ -1,16 +1,6 @@ import { Test, TestingModule } from '@nestjs/testing'; -import { getRepositoryToken } from '@nestjs/typeorm'; -import { WorkflowVersionStepWorkspaceService } from 'src/modules/workflow/workflow-builder/workflow-step/workflow-version-step.workspace-service'; import { TwentyORMGlobalManager } from 'src/engine/twenty-orm/twenty-orm-global.manager'; -import { WorkflowSchemaWorkspaceService } from 'src/modules/workflow/workflow-builder/workflow-schema/workflow-schema.workspace-service'; -import { ServerlessFunctionService } from 'src/engine/metadata-modules/serverless-function/serverless-function.service'; -import { AgentService } from 'src/engine/metadata-modules/agent/agent.service'; -import { WorkflowRunWorkspaceService } from 'src/modules/workflow/workflow-runner/workflow-run/workflow-run.workspace-service'; -import { WorkflowRunnerWorkspaceService } from 'src/modules/workflow/workflow-runner/workspace-services/workflow-runner.workspace-service'; -import { WorkflowCommonWorkspaceService } from 'src/modules/workflow/common/workspace-services/workflow-common.workspace-service'; -import { ScopedWorkspaceContextFactory } from 'src/engine/twenty-orm/factories/scoped-workspace-context.factory'; -import { ObjectMetadataEntity } from 'src/engine/metadata-modules/object-metadata/object-metadata.entity'; import { WorkflowVersionWorkspaceEntity } from 'src/modules/workflow/common/standard-objects/workflow-version.workspace-entity'; import { WorkspaceRepository } from 'src/engine/twenty-orm/repository/workspace.repository'; import { @@ -18,6 +8,7 @@ import { WorkflowActionType, } from 'src/modules/workflow/workflow-executor/workflow-actions/types/workflow-action.type'; import { WorkflowTriggerType } from 'src/modules/workflow/workflow-trigger/types/workflow-trigger.type'; +import { WorkflowVersionEdgeWorkspaceService } from 'src/modules/workflow/workflow-builder/workflow-version-edge/workflow-version-edge.workspace-service'; type MockWorkspaceRepository = Partial< WorkspaceRepository @@ -78,9 +69,9 @@ const mockWorkflowVersion = { status: 'DRAFT', } as WorkflowVersionWorkspaceEntity; -describe('WorkflowVersionStepWorkspaceService', () => { +describe('WorkflowVersionEdgeWorkspaceService', () => { let twentyORMGlobalManager: jest.Mocked; - let service: WorkflowVersionStepWorkspaceService; + let service: WorkflowVersionEdgeWorkspaceService; let mockWorkflowVersionWorkspaceRepository: MockWorkspaceRepository; beforeEach(async () => { @@ -101,135 +92,15 @@ describe('WorkflowVersionStepWorkspaceService', () => { const module: TestingModule = await Test.createTestingModule({ providers: [ - WorkflowVersionStepWorkspaceService, + WorkflowVersionEdgeWorkspaceService, { provide: TwentyORMGlobalManager, useValue: twentyORMGlobalManager, }, - { - provide: WorkflowSchemaWorkspaceService, - useValue: { - computeStepOutputSchema: jest.fn(), - }, - }, - { provide: ServerlessFunctionService, useValue: {} }, - { provide: AgentService, useValue: {} }, - { - provide: getRepositoryToken(ObjectMetadataEntity, 'core'), - useValue: { - findOne: jest.fn(), - }, - }, - { provide: WorkflowRunWorkspaceService, useValue: {} }, - { provide: WorkflowRunnerWorkspaceService, useValue: {} }, - { provide: WorkflowCommonWorkspaceService, useValue: {} }, - { provide: ScopedWorkspaceContextFactory, useValue: {} }, ], }).compile(); - service = module.get(WorkflowVersionStepWorkspaceService); - }); - - describe('createWorkflowVersionStep', () => { - it('should create a step linked to trigger', async () => { - const result = await service.createWorkflowVersionStep({ - input: { - stepType: WorkflowActionType.FORM, - parentStepId: 'trigger', - nextStepId: undefined, - workflowVersionId: mockWorkflowVersionId, - }, - workspaceId: mockWorkspaceId, - }); - - expect(mockWorkflowVersionWorkspaceRepository.update).toHaveBeenCalled(); - - expect(result.createdStep).toBeDefined(); - - const createdStepId = result.createdStep?.id; - - expect(result.triggerNextStepIds).toEqual(['step-1', createdStepId]); - expect(result.stepsNextStepIds).toEqual({ - 'step-1': ['step-2'], - 'step-2': [], - 'step-3': [], - }); - }); - - it('should create a step between a trigger and a step', async () => { - const result = await service.createWorkflowVersionStep({ - input: { - stepType: WorkflowActionType.FORM, - parentStepId: 'trigger', - nextStepId: 'step-1', - workflowVersionId: mockWorkflowVersionId, - }, - workspaceId: mockWorkspaceId, - }); - - expect(mockWorkflowVersionWorkspaceRepository.update).toHaveBeenCalled(); - - expect(result.createdStep).toBeDefined(); - - const createdStepId = result.createdStep?.id as string; - - expect(result.triggerNextStepIds).toEqual([createdStepId]); - expect(result.stepsNextStepIds).toEqual({ - [createdStepId]: ['step-1'], - 'step-1': ['step-2'], - 'step-2': [], - 'step-3': [], - }); - }); - - it('should create a step between two steps', async () => { - const result = await service.createWorkflowVersionStep({ - input: { - stepType: WorkflowActionType.FORM, - parentStepId: 'step-1', - nextStepId: 'step-2', - workflowVersionId: mockWorkflowVersionId, - }, - workspaceId: mockWorkspaceId, - }); - - expect(mockWorkflowVersionWorkspaceRepository.update).toHaveBeenCalled(); - - expect(result.createdStep).toBeDefined(); - - const createdStepId = result.createdStep?.id as string; - - expect(result.triggerNextStepIds).toEqual(['step-1']); - expect(result.stepsNextStepIds).toEqual({ - 'step-1': [createdStepId], - [createdStepId]: ['step-2'], - 'step-2': [], - 'step-3': [], - }); - }); - - it('should create a step without parent or children', async () => { - const result = await service.createWorkflowVersionStep({ - input: { - stepType: WorkflowActionType.FORM, - parentStepId: undefined, - nextStepId: undefined, - workflowVersionId: mockWorkflowVersionId, - }, - workspaceId: mockWorkspaceId, - }); - - expect(mockWorkflowVersionWorkspaceRepository.update).toHaveBeenCalled(); - - expect(result.createdStep).toBeDefined(); - - expect(result.triggerNextStepIds).toEqual(['step-1']); - expect(result.stepsNextStepIds).toEqual({ - 'step-1': ['step-2'], - 'step-2': [], - 'step-3': [], - }); - }); + service = module.get(WorkflowVersionEdgeWorkspaceService); }); describe('createWorkflowVersionEdge', () => { @@ -507,53 +378,4 @@ describe('WorkflowVersionStepWorkspaceService', () => { }); }); }); - - describe('deleteWorkflowVersionStep', () => { - it('should delete step linked to trigger', async () => { - const result = await service.deleteWorkflowVersionStep({ - stepIdToDelete: 'step-1', - workflowVersionId: mockWorkflowVersionId, - workspaceId: mockWorkspaceId, - }); - - expect( - mockWorkflowVersionWorkspaceRepository.update, - ).toHaveBeenCalledWith(mockWorkflowVersionId, { - trigger: { ...mockTrigger, nextStepIds: ['step-2'] }, - steps: mockSteps.filter((step) => step.id !== 'step-1'), - }); - - expect(result).toEqual({ - triggerNextStepIds: ['step-2'], - stepsNextStepIds: { - 'step-2': [], - 'step-3': [], - }, - deletedStepId: 'step-1', - }); - }); - - it('should delete trigger', async () => { - const result = await service.deleteWorkflowVersionStep({ - stepIdToDelete: 'trigger', - workflowVersionId: mockWorkflowVersionId, - workspaceId: mockWorkspaceId, - }); - - expect( - mockWorkflowVersionWorkspaceRepository.update, - ).toHaveBeenCalledWith(mockWorkflowVersionId, { - trigger: null, - }); - - expect(result).toEqual({ - stepsNextStepIds: { - 'step-1': ['step-2'], - 'step-2': [], - 'step-3': [], - }, - deletedStepId: 'trigger', - }); - }); - }); }); diff --git a/packages/twenty-server/src/modules/workflow/workflow-builder/workflow-version-edge/workflow-version-edge.module.ts b/packages/twenty-server/src/modules/workflow/workflow-builder/workflow-version-edge/workflow-version-edge.module.ts new file mode 100644 index 00000000000..7aa64acd6cd --- /dev/null +++ b/packages/twenty-server/src/modules/workflow/workflow-builder/workflow-version-edge/workflow-version-edge.module.ts @@ -0,0 +1,9 @@ +import { Module } from '@nestjs/common'; + +import { WorkflowVersionEdgeWorkspaceService } from 'src/modules/workflow/workflow-builder/workflow-version-edge/workflow-version-edge.workspace-service'; + +@Module({ + providers: [WorkflowVersionEdgeWorkspaceService], + exports: [WorkflowVersionEdgeWorkspaceService], +}) +export class WorkflowVersionEdgeModule {} diff --git a/packages/twenty-server/src/modules/workflow/workflow-builder/workflow-version-edge/workflow-version-edge.workspace-service.ts b/packages/twenty-server/src/modules/workflow/workflow-builder/workflow-version-edge/workflow-version-edge.workspace-service.ts new file mode 100644 index 00000000000..92d4503606e --- /dev/null +++ b/packages/twenty-server/src/modules/workflow/workflow-builder/workflow-version-edge/workflow-version-edge.workspace-service.ts @@ -0,0 +1,258 @@ +import { Injectable } from '@nestjs/common'; + +import { isDefined } from 'twenty-shared/utils'; + +import { WorkflowVersionStepChangesDTO } from 'src/engine/core-modules/workflow/dtos/workflow-version-step-changes.dto'; +import { WorkflowVersionWorkspaceEntity } from 'src/modules/workflow/common/standard-objects/workflow-version.workspace-entity'; +import { + WorkflowVersionEdgeException, + WorkflowVersionEdgeExceptionCode, +} from 'src/modules/workflow/common/exceptions/workflow-version-edge.exception'; +import { assertWorkflowVersionIsDraft } from 'src/modules/workflow/common/utils/assert-workflow-version-is-draft.util'; +import { computeWorkflowVersionStepChanges } from 'src/modules/workflow/workflow-builder/utils/compute-workflow-version-step-updates.util'; +import { TwentyORMGlobalManager } from 'src/engine/twenty-orm/twenty-orm-global.manager'; + +@Injectable() +export class WorkflowVersionEdgeWorkspaceService { + constructor( + private readonly twentyORMGlobalManager: TwentyORMGlobalManager, + ) {} + + async createWorkflowVersionEdge({ + source, + target, + workflowVersionId, + workspaceId, + }: { + source: string; + target: string; + workflowVersionId: string; + workspaceId: string; + }): Promise { + const workflowVersionRepository = + await this.twentyORMGlobalManager.getRepositoryForWorkspace( + workspaceId, + 'workflowVersion', + { shouldBypassPermissionChecks: true }, + ); + + const workflowVersion = await workflowVersionRepository.findOne({ + where: { + id: workflowVersionId, + }, + }); + + if (!isDefined(workflowVersion)) { + throw new WorkflowVersionEdgeException( + 'WorkflowVersion not found', + WorkflowVersionEdgeExceptionCode.NOT_FOUND, + ); + } + + assertWorkflowVersionIsDraft(workflowVersion); + + const steps = workflowVersion.steps || []; + + const trigger = workflowVersion.trigger; + + const isSourceTrigger = source === 'trigger'; + + const targetStep = steps.find((step) => step.id === target); + + if (!isDefined(targetStep)) { + throw new WorkflowVersionEdgeException( + `Target step '${target}' not found in workflowVersion '${workflowVersionId}'`, + WorkflowVersionEdgeExceptionCode.NOT_FOUND, + ); + } + + if (isSourceTrigger) { + if (!isDefined(trigger)) { + throw new WorkflowVersionEdgeException( + `Trigger not found in workflowVersion '${workflowVersionId}'`, + WorkflowVersionEdgeExceptionCode.NOT_FOUND, + ); + } + + if (trigger.nextStepIds?.includes(target)) { + return computeWorkflowVersionStepChanges({ + trigger, + steps, + }); + } + + const updatedTrigger = { + ...trigger, + nextStepIds: [...(trigger.nextStepIds ?? []), target], + }; + + await workflowVersionRepository.update(workflowVersion.id, { + trigger: updatedTrigger, + }); + + return computeWorkflowVersionStepChanges({ + trigger: updatedTrigger, + steps, + }); + } + + const sourceStep = steps.find((step) => step.id === source); + + if (!isDefined(sourceStep)) { + throw new WorkflowVersionEdgeException( + `Source step '${source}' not found in workflowVersion '${workflowVersionId}'`, + WorkflowVersionEdgeExceptionCode.NOT_FOUND, + ); + } + + if (sourceStep.nextStepIds?.includes(target)) { + return computeWorkflowVersionStepChanges({ + trigger, + steps, + }); + } + + const updatedSourceStep = { + ...sourceStep, + nextStepIds: [...(sourceStep.nextStepIds ?? []), target], + }; + + const updatedSteps = steps.map((step) => { + if (step.id === source) { + return updatedSourceStep; + } + + return step; + }); + + await workflowVersionRepository.update(workflowVersion.id, { + steps: updatedSteps, + }); + + return computeWorkflowVersionStepChanges({ + trigger, + steps: updatedSteps, + }); + } + + async deleteWorkflowVersionEdge({ + source, + target, + workflowVersionId, + workspaceId, + }: { + source: string; + target: string; + workflowVersionId: string; + workspaceId: string; + }): Promise { + const workflowVersionRepository = + await this.twentyORMGlobalManager.getRepositoryForWorkspace( + workspaceId, + 'workflowVersion', + { shouldBypassPermissionChecks: true }, + ); + + const workflowVersion = await workflowVersionRepository.findOne({ + where: { + id: workflowVersionId, + }, + }); + + if (!isDefined(workflowVersion)) { + throw new WorkflowVersionEdgeException( + 'WorkflowVersion not found', + WorkflowVersionEdgeExceptionCode.NOT_FOUND, + ); + } + + assertWorkflowVersionIsDraft(workflowVersion); + + const steps = workflowVersion.steps || []; + + const trigger = workflowVersion.trigger; + + const isSourceTrigger = source === 'trigger'; + + const targetStep = steps.find((step) => step.id === target); + + if (!isDefined(targetStep)) { + throw new WorkflowVersionEdgeException( + `Target step '${target}' not found in workflowVersion '${workflowVersionId}'`, + WorkflowVersionEdgeExceptionCode.NOT_FOUND, + ); + } + + if (isSourceTrigger) { + if (!isDefined(trigger)) { + throw new WorkflowVersionEdgeException( + `Trigger not found in workflowVersion '${workflowVersionId}'`, + WorkflowVersionEdgeExceptionCode.NOT_FOUND, + ); + } + + if (!trigger.nextStepIds?.includes(target)) { + return computeWorkflowVersionStepChanges({ + trigger, + steps, + }); + } + + const updatedTrigger = { + ...trigger, + nextStepIds: trigger.nextStepIds?.filter( + (nextStepId) => nextStepId !== target, + ), + }; + + await workflowVersionRepository.update(workflowVersion.id, { + trigger: updatedTrigger, + }); + + return computeWorkflowVersionStepChanges({ + trigger: updatedTrigger, + steps, + }); + } + + const sourceStep = steps.find((step) => step.id === source); + + if (!isDefined(sourceStep)) { + throw new WorkflowVersionEdgeException( + `Source step '${source}' not found in workflowVersion '${workflowVersionId}'`, + WorkflowVersionEdgeExceptionCode.NOT_FOUND, + ); + } + + if (!sourceStep.nextStepIds?.includes(target)) { + return computeWorkflowVersionStepChanges({ + trigger, + steps, + }); + } + + const updatedSourceStep = { + ...sourceStep, + nextStepIds: sourceStep.nextStepIds?.filter( + (nextStepId) => nextStepId !== target, + ), + }; + + const updatedSteps = steps.map((step) => { + if (step.id === source) { + return updatedSourceStep; + } + + return step; + }); + + await workflowVersionRepository.update(workflowVersion.id, { + steps: updatedSteps, + }); + + return computeWorkflowVersionStepChanges({ + trigger, + steps: updatedSteps, + }); + } +} diff --git a/packages/twenty-server/src/modules/workflow/workflow-builder/workflow-version-step/__tests__/workflow-version-step.workspace-service.spec.ts b/packages/twenty-server/src/modules/workflow/workflow-builder/workflow-version-step/__tests__/workflow-version-step.workspace-service.spec.ts new file mode 100644 index 00000000000..056dbb02b8a --- /dev/null +++ b/packages/twenty-server/src/modules/workflow/workflow-builder/workflow-version-step/__tests__/workflow-version-step.workspace-service.spec.ts @@ -0,0 +1,283 @@ +import { Test, TestingModule } from '@nestjs/testing'; +import { getRepositoryToken } from '@nestjs/typeorm'; + +import { WorkflowVersionStepWorkspaceService } from 'src/modules/workflow/workflow-builder/workflow-version-step/workflow-version-step.workspace-service'; +import { TwentyORMGlobalManager } from 'src/engine/twenty-orm/twenty-orm-global.manager'; +import { WorkflowSchemaWorkspaceService } from 'src/modules/workflow/workflow-builder/workflow-schema/workflow-schema.workspace-service'; +import { ServerlessFunctionService } from 'src/engine/metadata-modules/serverless-function/serverless-function.service'; +import { AgentService } from 'src/engine/metadata-modules/agent/agent.service'; +import { WorkflowRunWorkspaceService } from 'src/modules/workflow/workflow-runner/workflow-run/workflow-run.workspace-service'; +import { WorkflowRunnerWorkspaceService } from 'src/modules/workflow/workflow-runner/workspace-services/workflow-runner.workspace-service'; +import { WorkflowCommonWorkspaceService } from 'src/modules/workflow/common/workspace-services/workflow-common.workspace-service'; +import { ScopedWorkspaceContextFactory } from 'src/engine/twenty-orm/factories/scoped-workspace-context.factory'; +import { ObjectMetadataEntity } from 'src/engine/metadata-modules/object-metadata/object-metadata.entity'; +import { WorkflowVersionWorkspaceEntity } from 'src/modules/workflow/common/standard-objects/workflow-version.workspace-entity'; +import { WorkspaceRepository } from 'src/engine/twenty-orm/repository/workspace.repository'; +import { + WorkflowAction, + WorkflowActionType, +} from 'src/modules/workflow/workflow-executor/workflow-actions/types/workflow-action.type'; +import { WorkflowTriggerType } from 'src/modules/workflow/workflow-trigger/types/workflow-trigger.type'; + +type MockWorkspaceRepository = Partial< + WorkspaceRepository +> & { + findOne: jest.Mock; + update: jest.Mock; +}; + +const mockWorkflowVersionId = 'workflow-version-id'; +const mockWorkspaceId = 'workspace-id'; + +const mockSteps = [ + { + id: 'step-1', + type: WorkflowActionType.FORM, + settings: { + errorHandlingOptions: { + continueOnFailure: { value: false }, + retryOnFailure: { value: false }, + }, + }, + nextStepIds: ['step-2'], + }, + { + id: 'step-2', + type: WorkflowActionType.SEND_EMAIL, + settings: { + errorHandlingOptions: { + continueOnFailure: { value: false }, + retryOnFailure: { value: false }, + }, + }, + nextStepIds: [], + }, + { + id: 'step-3', + type: WorkflowActionType.SEND_EMAIL, + settings: { + errorHandlingOptions: { + continueOnFailure: { value: false }, + retryOnFailure: { value: false }, + }, + }, + nextStepIds: [], + }, +] as WorkflowAction[]; + +const mockTrigger = { + type: WorkflowTriggerType.MANUAL, + settings: {}, + nextStepIds: ['step-1'], +}; + +const mockWorkflowVersion = { + id: mockWorkflowVersionId, + trigger: mockTrigger, + steps: mockSteps, + status: 'DRAFT', +} as WorkflowVersionWorkspaceEntity; + +describe('WorkflowVersionStepWorkspaceService', () => { + let twentyORMGlobalManager: jest.Mocked; + let service: WorkflowVersionStepWorkspaceService; + let mockWorkflowVersionWorkspaceRepository: MockWorkspaceRepository; + + beforeEach(async () => { + mockWorkflowVersionWorkspaceRepository = { + findOne: jest.fn(), + update: jest.fn(), + }; + + mockWorkflowVersionWorkspaceRepository.findOne.mockResolvedValue( + mockWorkflowVersion, + ); + + twentyORMGlobalManager = { + getRepositoryForWorkspace: jest + .fn() + .mockResolvedValue(mockWorkflowVersionWorkspaceRepository), + } as unknown as jest.Mocked; + + const module: TestingModule = await Test.createTestingModule({ + providers: [ + WorkflowVersionStepWorkspaceService, + { + provide: TwentyORMGlobalManager, + useValue: twentyORMGlobalManager, + }, + { + provide: WorkflowSchemaWorkspaceService, + useValue: { + computeStepOutputSchema: jest.fn(), + }, + }, + { provide: ServerlessFunctionService, useValue: {} }, + { provide: AgentService, useValue: {} }, + { + provide: getRepositoryToken(ObjectMetadataEntity, 'core'), + useValue: { + findOne: jest.fn(), + }, + }, + { provide: WorkflowRunWorkspaceService, useValue: {} }, + { provide: WorkflowRunnerWorkspaceService, useValue: {} }, + { provide: WorkflowCommonWorkspaceService, useValue: {} }, + { provide: ScopedWorkspaceContextFactory, useValue: {} }, + ], + }).compile(); + + service = module.get(WorkflowVersionStepWorkspaceService); + }); + + describe('createWorkflowVersionStep', () => { + it('should create a step linked to trigger', async () => { + const result = await service.createWorkflowVersionStep({ + input: { + stepType: WorkflowActionType.FORM, + parentStepId: 'trigger', + nextStepId: undefined, + workflowVersionId: mockWorkflowVersionId, + }, + workspaceId: mockWorkspaceId, + }); + + expect(mockWorkflowVersionWorkspaceRepository.update).toHaveBeenCalled(); + + expect(result.createdStep).toBeDefined(); + + const createdStepId = result.createdStep?.id; + + expect(result.triggerNextStepIds).toEqual(['step-1', createdStepId]); + expect(result.stepsNextStepIds).toEqual({ + 'step-1': ['step-2'], + 'step-2': [], + 'step-3': [], + }); + }); + + it('should create a step between a trigger and a step', async () => { + const result = await service.createWorkflowVersionStep({ + input: { + stepType: WorkflowActionType.FORM, + parentStepId: 'trigger', + nextStepId: 'step-1', + workflowVersionId: mockWorkflowVersionId, + }, + workspaceId: mockWorkspaceId, + }); + + expect(mockWorkflowVersionWorkspaceRepository.update).toHaveBeenCalled(); + + expect(result.createdStep).toBeDefined(); + + const createdStepId = result.createdStep?.id as string; + + expect(result.triggerNextStepIds).toEqual([createdStepId]); + expect(result.stepsNextStepIds).toEqual({ + [createdStepId]: ['step-1'], + 'step-1': ['step-2'], + 'step-2': [], + 'step-3': [], + }); + }); + + it('should create a step between two steps', async () => { + const result = await service.createWorkflowVersionStep({ + input: { + stepType: WorkflowActionType.FORM, + parentStepId: 'step-1', + nextStepId: 'step-2', + workflowVersionId: mockWorkflowVersionId, + }, + workspaceId: mockWorkspaceId, + }); + + expect(mockWorkflowVersionWorkspaceRepository.update).toHaveBeenCalled(); + + expect(result.createdStep).toBeDefined(); + + const createdStepId = result.createdStep?.id as string; + + expect(result.triggerNextStepIds).toEqual(['step-1']); + expect(result.stepsNextStepIds).toEqual({ + 'step-1': [createdStepId], + [createdStepId]: ['step-2'], + 'step-2': [], + 'step-3': [], + }); + }); + + it('should create a step without parent or children', async () => { + const result = await service.createWorkflowVersionStep({ + input: { + stepType: WorkflowActionType.FORM, + parentStepId: undefined, + nextStepId: undefined, + workflowVersionId: mockWorkflowVersionId, + }, + workspaceId: mockWorkspaceId, + }); + + expect(mockWorkflowVersionWorkspaceRepository.update).toHaveBeenCalled(); + + expect(result.createdStep).toBeDefined(); + + expect(result.triggerNextStepIds).toEqual(['step-1']); + expect(result.stepsNextStepIds).toEqual({ + 'step-1': ['step-2'], + 'step-2': [], + 'step-3': [], + }); + }); + }); + + describe('deleteWorkflowVersionStep', () => { + it('should delete step linked to trigger', async () => { + const result = await service.deleteWorkflowVersionStep({ + stepIdToDelete: 'step-1', + workflowVersionId: mockWorkflowVersionId, + workspaceId: mockWorkspaceId, + }); + + expect( + mockWorkflowVersionWorkspaceRepository.update, + ).toHaveBeenCalledWith(mockWorkflowVersionId, { + trigger: { ...mockTrigger, nextStepIds: ['step-2'] }, + steps: mockSteps.filter((step) => step.id !== 'step-1'), + }); + + expect(result).toEqual({ + triggerNextStepIds: ['step-2'], + stepsNextStepIds: { + 'step-2': [], + 'step-3': [], + }, + deletedStepId: 'step-1', + }); + }); + + it('should delete trigger', async () => { + const result = await service.deleteWorkflowVersionStep({ + stepIdToDelete: 'trigger', + workflowVersionId: mockWorkflowVersionId, + workspaceId: mockWorkspaceId, + }); + + expect( + mockWorkflowVersionWorkspaceRepository.update, + ).toHaveBeenCalledWith(mockWorkflowVersionId, { + trigger: null, + }); + + expect(result).toEqual({ + stepsNextStepIds: { + 'step-1': ['step-2'], + 'step-2': [], + 'step-3': [], + }, + deletedStepId: 'trigger', + }); + }); + }); +}); diff --git a/packages/twenty-server/src/modules/workflow/workflow-builder/workflow-step/utils/__tests__/insert-step.spec.ts b/packages/twenty-server/src/modules/workflow/workflow-builder/workflow-version-step/utils/__tests__/insert-step.spec.ts similarity index 99% rename from packages/twenty-server/src/modules/workflow/workflow-builder/workflow-step/utils/__tests__/insert-step.spec.ts rename to packages/twenty-server/src/modules/workflow/workflow-builder/workflow-version-step/utils/__tests__/insert-step.spec.ts index ad8f4fa593f..54cb11e5ce9 100644 --- a/packages/twenty-server/src/modules/workflow/workflow-builder/workflow-step/utils/__tests__/insert-step.spec.ts +++ b/packages/twenty-server/src/modules/workflow/workflow-builder/workflow-version-step/utils/__tests__/insert-step.spec.ts @@ -1,4 +1,4 @@ -import { insertStep } from 'src/modules/workflow/workflow-builder/workflow-step/utils/insert-step'; +import { insertStep } from 'src/modules/workflow/workflow-builder/workflow-version-step/utils/insert-step'; import { WorkflowAction, WorkflowActionType, diff --git a/packages/twenty-server/src/modules/workflow/workflow-builder/workflow-step/utils/__tests__/remove-step.spec.ts b/packages/twenty-server/src/modules/workflow/workflow-builder/workflow-version-step/utils/__tests__/remove-step.spec.ts similarity index 98% rename from packages/twenty-server/src/modules/workflow/workflow-builder/workflow-step/utils/__tests__/remove-step.spec.ts rename to packages/twenty-server/src/modules/workflow/workflow-builder/workflow-version-step/utils/__tests__/remove-step.spec.ts index 64954a1e2eb..116598e0940 100644 --- a/packages/twenty-server/src/modules/workflow/workflow-builder/workflow-step/utils/__tests__/remove-step.spec.ts +++ b/packages/twenty-server/src/modules/workflow/workflow-builder/workflow-version-step/utils/__tests__/remove-step.spec.ts @@ -1,4 +1,4 @@ -import { removeStep } from 'src/modules/workflow/workflow-builder/workflow-step/utils/remove-step'; +import { removeStep } from 'src/modules/workflow/workflow-builder/workflow-version-step/utils/remove-step'; import { WorkflowAction, WorkflowActionType, diff --git a/packages/twenty-server/src/modules/workflow/workflow-builder/workflow-step/utils/insert-step.ts b/packages/twenty-server/src/modules/workflow/workflow-builder/workflow-version-step/utils/insert-step.ts similarity index 100% rename from packages/twenty-server/src/modules/workflow/workflow-builder/workflow-step/utils/insert-step.ts rename to packages/twenty-server/src/modules/workflow/workflow-builder/workflow-version-step/utils/insert-step.ts diff --git a/packages/twenty-server/src/modules/workflow/workflow-builder/workflow-step/utils/remove-step.ts b/packages/twenty-server/src/modules/workflow/workflow-builder/workflow-version-step/utils/remove-step.ts similarity index 100% rename from packages/twenty-server/src/modules/workflow/workflow-builder/workflow-step/utils/remove-step.ts rename to packages/twenty-server/src/modules/workflow/workflow-builder/workflow-version-step/utils/remove-step.ts diff --git a/packages/twenty-server/src/modules/workflow/workflow-builder/workflow-step/workflow-version-step.module.ts b/packages/twenty-server/src/modules/workflow/workflow-builder/workflow-version-step/workflow-version-step.module.ts similarity index 80% rename from packages/twenty-server/src/modules/workflow/workflow-builder/workflow-step/workflow-version-step.module.ts rename to packages/twenty-server/src/modules/workflow/workflow-builder/workflow-version-step/workflow-version-step.module.ts index 9d9e8c13079..cc63c70fb3a 100644 --- a/packages/twenty-server/src/modules/workflow/workflow-builder/workflow-step/workflow-version-step.module.ts +++ b/packages/twenty-server/src/modules/workflow/workflow-builder/workflow-version-step/workflow-version-step.module.ts @@ -5,10 +5,9 @@ import { NestjsQueryTypeOrmModule } from '@ptc-org/nestjs-query-typeorm'; import { AgentModule } from 'src/engine/metadata-modules/agent/agent.module'; import { ObjectMetadataEntity } from 'src/engine/metadata-modules/object-metadata/object-metadata.entity'; import { ServerlessFunctionModule } from 'src/engine/metadata-modules/serverless-function/serverless-function.module'; -import { ScopedWorkspaceContextFactory } from 'src/engine/twenty-orm/factories/scoped-workspace-context.factory'; import { WorkflowCommonModule } from 'src/modules/workflow/common/workflow-common.module'; import { WorkflowSchemaModule } from 'src/modules/workflow/workflow-builder/workflow-schema/workflow-schema.module'; -import { WorkflowVersionStepWorkspaceService } from 'src/modules/workflow/workflow-builder/workflow-step/workflow-version-step.workspace-service'; +import { WorkflowVersionStepWorkspaceService } from 'src/modules/workflow/workflow-builder/workflow-version-step/workflow-version-step.workspace-service'; import { WorkflowRunModule } from 'src/modules/workflow/workflow-runner/workflow-run/workflow-run.module'; import { WorkflowRunnerModule } from 'src/modules/workflow/workflow-runner/workflow-runner.module'; @@ -22,10 +21,7 @@ import { WorkflowRunnerModule } from 'src/modules/workflow/workflow-runner/workf WorkflowCommonModule, NestjsQueryTypeOrmModule.forFeature([ObjectMetadataEntity], 'core'), ], - providers: [ - WorkflowVersionStepWorkspaceService, - ScopedWorkspaceContextFactory, - ], + providers: [WorkflowVersionStepWorkspaceService], exports: [WorkflowVersionStepWorkspaceService], }) export class WorkflowVersionStepModule {} diff --git a/packages/twenty-server/src/modules/workflow/workflow-builder/workflow-step/workflow-version-step.workspace-service.ts b/packages/twenty-server/src/modules/workflow/workflow-builder/workflow-version-step/workflow-version-step.workspace-service.ts similarity index 76% rename from packages/twenty-server/src/modules/workflow/workflow-builder/workflow-step/workflow-version-step.workspace-service.ts rename to packages/twenty-server/src/modules/workflow/workflow-builder/workflow-version-step/workflow-version-step.workspace-service.ts index cc15c7be2f1..2768253f497 100644 --- a/packages/twenty-server/src/modules/workflow/workflow-builder/workflow-step/workflow-version-step.workspace-service.ts +++ b/packages/twenty-server/src/modules/workflow/workflow-builder/workflow-version-step/workflow-version-step.workspace-service.ts @@ -13,7 +13,6 @@ import { CreateWorkflowVersionStepInput } from 'src/engine/core-modules/workflow import { AgentService } from 'src/engine/metadata-modules/agent/agent.service'; import { ObjectMetadataEntity } from 'src/engine/metadata-modules/object-metadata/object-metadata.entity'; import { ServerlessFunctionService } from 'src/engine/metadata-modules/serverless-function/serverless-function.service'; -import { ScopedWorkspaceContextFactory } from 'src/engine/twenty-orm/factories/scoped-workspace-context.factory'; import { TwentyORMGlobalManager } from 'src/engine/twenty-orm/twenty-orm-global.manager'; import { WorkflowVersionStepException, @@ -23,8 +22,8 @@ import { WorkflowVersionWorkspaceEntity } from 'src/modules/workflow/common/stan import { assertWorkflowVersionIsDraft } from 'src/modules/workflow/common/utils/assert-workflow-version-is-draft.util'; import { WorkflowCommonWorkspaceService } from 'src/modules/workflow/common/workspace-services/workflow-common.workspace-service'; import { WorkflowSchemaWorkspaceService } from 'src/modules/workflow/workflow-builder/workflow-schema/workflow-schema.workspace-service'; -import { insertStep } from 'src/modules/workflow/workflow-builder/workflow-step/utils/insert-step'; -import { removeStep } from 'src/modules/workflow/workflow-builder/workflow-step/utils/remove-step'; +import { insertStep } from 'src/modules/workflow/workflow-builder/workflow-version-step/utils/insert-step'; +import { removeStep } from 'src/modules/workflow/workflow-builder/workflow-version-step/utils/remove-step'; import { BaseWorkflowActionSettings } from 'src/modules/workflow/workflow-executor/workflow-actions/types/workflow-action-settings.type'; import { WorkflowAction, @@ -35,7 +34,7 @@ import { WorkflowRunWorkspaceService } from 'src/modules/workflow/workflow-runne import { WorkflowRunnerWorkspaceService } from 'src/modules/workflow/workflow-runner/workspace-services/workflow-runner.workspace-service'; import { WorkflowStepPositionInput } from 'src/engine/core-modules/workflow/dtos/update-workflow-step-position-input.dto'; import { WorkflowVersionStepChangesDTO } from 'src/engine/core-modules/workflow/dtos/workflow-version-step-changes.dto'; -import { computeWorkflowVersionStepChanges } from 'src/modules/workflow/workflow-builder/workflow-step/utils/compute-workflow-version-step-updates.util'; +import { computeWorkflowVersionStepChanges } from 'src/modules/workflow/workflow-builder/utils/compute-workflow-version-step-updates.util'; const BASE_STEP_DEFINITION: BaseWorkflowActionSettings = { outputSchema: {}, @@ -61,7 +60,6 @@ export class WorkflowVersionStepWorkspaceService { private readonly workflowRunWorkspaceService: WorkflowRunWorkspaceService, private readonly workflowRunnerWorkspaceService: WorkflowRunnerWorkspaceService, private readonly workflowCommonWorkspaceService: WorkflowCommonWorkspaceService, - private readonly scopedWorkspaceContextFactory: ScopedWorkspaceContextFactory, ) {} async createWorkflowVersionStep({ @@ -368,244 +366,6 @@ export class WorkflowVersionStepWorkspaceService { }); } - async createWorkflowVersionEdge({ - source, - target, - workflowVersionId, - workspaceId, - }: { - source: string; - target: string; - workflowVersionId: string; - workspaceId: string; - }): Promise { - const workflowVersionRepository = - await this.twentyORMGlobalManager.getRepositoryForWorkspace( - workspaceId, - 'workflowVersion', - { shouldBypassPermissionChecks: true }, - ); - - const workflowVersion = await workflowVersionRepository.findOne({ - where: { - id: workflowVersionId, - }, - }); - - if (!isDefined(workflowVersion)) { - throw new WorkflowVersionStepException( - 'WorkflowVersion not found', - WorkflowVersionStepExceptionCode.NOT_FOUND, - ); - } - - assertWorkflowVersionIsDraft(workflowVersion); - - const steps = workflowVersion.steps || []; - - const trigger = workflowVersion.trigger; - - const isSourceTrigger = source === 'trigger'; - - const targetStep = steps.find((step) => step.id === target); - - if (!isDefined(targetStep)) { - throw new WorkflowVersionStepException( - `Target step '${target}' not found in workflowVersion '${workflowVersionId}'`, - WorkflowVersionStepExceptionCode.NOT_FOUND, - ); - } - - if (isSourceTrigger) { - if (!isDefined(trigger)) { - throw new WorkflowVersionStepException( - `Trigger not found in workflowVersion '${workflowVersionId}'`, - WorkflowVersionStepExceptionCode.NOT_FOUND, - ); - } - - if (trigger.nextStepIds?.includes(target)) { - return computeWorkflowVersionStepChanges({ - trigger, - steps, - }); - } - - const updatedTrigger = { - ...trigger, - nextStepIds: [...(trigger.nextStepIds ?? []), target], - }; - - await workflowVersionRepository.update(workflowVersion.id, { - trigger: updatedTrigger, - }); - - return computeWorkflowVersionStepChanges({ - trigger: updatedTrigger, - steps, - }); - } - - const sourceStep = steps.find((step) => step.id === source); - - if (!isDefined(sourceStep)) { - throw new WorkflowVersionStepException( - `Source step '${source}' not found in workflowVersion '${workflowVersionId}'`, - WorkflowVersionStepExceptionCode.NOT_FOUND, - ); - } - - if (sourceStep.nextStepIds?.includes(target)) { - return computeWorkflowVersionStepChanges({ - trigger, - steps, - }); - } - - const updatedSourceStep = { - ...sourceStep, - nextStepIds: [...(sourceStep.nextStepIds ?? []), target], - }; - - const updatedSteps = steps.map((step) => { - if (step.id === source) { - return updatedSourceStep; - } - - return step; - }); - - await workflowVersionRepository.update(workflowVersion.id, { - steps: updatedSteps, - }); - - return computeWorkflowVersionStepChanges({ - trigger, - steps: updatedSteps, - }); - } - - async deleteWorkflowVersionEdge({ - source, - target, - workflowVersionId, - workspaceId, - }: { - source: string; - target: string; - workflowVersionId: string; - workspaceId: string; - }): Promise { - const workflowVersionRepository = - await this.twentyORMGlobalManager.getRepositoryForWorkspace( - workspaceId, - 'workflowVersion', - { shouldBypassPermissionChecks: true }, - ); - - const workflowVersion = await workflowVersionRepository.findOne({ - where: { - id: workflowVersionId, - }, - }); - - if (!isDefined(workflowVersion)) { - throw new WorkflowVersionStepException( - 'WorkflowVersion not found', - WorkflowVersionStepExceptionCode.NOT_FOUND, - ); - } - - assertWorkflowVersionIsDraft(workflowVersion); - - const steps = workflowVersion.steps || []; - - const trigger = workflowVersion.trigger; - - const isSourceTrigger = source === 'trigger'; - - const targetStep = steps.find((step) => step.id === target); - - if (!isDefined(targetStep)) { - throw new WorkflowVersionStepException( - `Target step '${target}' not found in workflowVersion '${workflowVersionId}'`, - WorkflowVersionStepExceptionCode.NOT_FOUND, - ); - } - - if (isSourceTrigger) { - if (!isDefined(trigger)) { - throw new WorkflowVersionStepException( - `Trigger not found in workflowVersion '${workflowVersionId}'`, - WorkflowVersionStepExceptionCode.NOT_FOUND, - ); - } - - if (!trigger.nextStepIds?.includes(target)) { - return computeWorkflowVersionStepChanges({ - trigger, - steps, - }); - } - - const updatedTrigger = { - ...trigger, - nextStepIds: trigger.nextStepIds?.filter( - (nextStepId) => nextStepId !== target, - ), - }; - - await workflowVersionRepository.update(workflowVersion.id, { - trigger: updatedTrigger, - }); - - return computeWorkflowVersionStepChanges({ - trigger: updatedTrigger, - steps, - }); - } - - const sourceStep = steps.find((step) => step.id === source); - - if (!isDefined(sourceStep)) { - throw new WorkflowVersionStepException( - `Source step '${source}' not found in workflowVersion '${workflowVersionId}'`, - WorkflowVersionStepExceptionCode.NOT_FOUND, - ); - } - - if (!sourceStep.nextStepIds?.includes(target)) { - return computeWorkflowVersionStepChanges({ - trigger, - steps, - }); - } - - const updatedSourceStep = { - ...sourceStep, - nextStepIds: sourceStep.nextStepIds?.filter( - (nextStepId) => nextStepId !== target, - ), - }; - - const updatedSteps = steps.map((step) => { - if (step.id === source) { - return updatedSourceStep; - } - - return step; - }); - - await workflowVersionRepository.update(workflowVersion.id, { - steps: updatedSteps, - }); - - return computeWorkflowVersionStepChanges({ - trigger, - steps: updatedSteps, - }); - } - private async enrichOutputSchema({ step, workspaceId, diff --git a/packages/twenty-server/src/modules/workflow/workflow-builder/workflow-version/workflow-version.module.ts b/packages/twenty-server/src/modules/workflow/workflow-builder/workflow-version/workflow-version.module.ts index 5222f092968..52619b7cd4f 100644 --- a/packages/twenty-server/src/modules/workflow/workflow-builder/workflow-version/workflow-version.module.ts +++ b/packages/twenty-server/src/modules/workflow/workflow-builder/workflow-version/workflow-version.module.ts @@ -6,7 +6,7 @@ import { RecordPositionModule } from 'src/engine/core-modules/record-position/re import { ObjectMetadataEntity } from 'src/engine/metadata-modules/object-metadata/object-metadata.entity'; import { ServerlessFunctionModule } from 'src/engine/metadata-modules/serverless-function/serverless-function.module'; import { WorkflowSchemaModule } from 'src/modules/workflow/workflow-builder/workflow-schema/workflow-schema.module'; -import { WorkflowVersionStepModule } from 'src/modules/workflow/workflow-builder/workflow-step/workflow-version-step.module'; +import { WorkflowVersionStepModule } from 'src/modules/workflow/workflow-builder/workflow-version-step/workflow-version-step.module'; import { WorkflowVersionWorkspaceService } from 'src/modules/workflow/workflow-builder/workflow-version/workflow-version.workspace-service'; @Module({ diff --git a/packages/twenty-server/src/modules/workflow/workflow-builder/workflow-version/workflow-version.workspace-service.ts b/packages/twenty-server/src/modules/workflow/workflow-builder/workflow-version/workflow-version.workspace-service.ts index e05b01dbc2c..5fa5ad402a7 100644 --- a/packages/twenty-server/src/modules/workflow/workflow-builder/workflow-version/workflow-version.workspace-service.ts +++ b/packages/twenty-server/src/modules/workflow/workflow-builder/workflow-version/workflow-version.workspace-service.ts @@ -15,7 +15,7 @@ import { import { assertWorkflowVersionHasSteps } from 'src/modules/workflow/common/utils/assert-workflow-version-has-steps'; import { assertWorkflowVersionIsDraft } from 'src/modules/workflow/common/utils/assert-workflow-version-is-draft.util'; import { assertWorkflowVersionTriggerIsDefined } from 'src/modules/workflow/common/utils/assert-workflow-version-trigger-is-defined.util'; -import { WorkflowVersionStepWorkspaceService } from 'src/modules/workflow/workflow-builder/workflow-step/workflow-version-step.workspace-service'; +import { WorkflowVersionStepWorkspaceService } from 'src/modules/workflow/workflow-builder/workflow-version-step/workflow-version-step.workspace-service'; import { WorkflowAction } from 'src/modules/workflow/workflow-executor/workflow-actions/types/workflow-action.type'; import { WorkflowStepPositionUpdateInput } from 'src/engine/core-modules/workflow/dtos/update-workflow-step-position-update-input.dto';