From 83930551d830a2194bc83bd593e48e151d3fd59f Mon Sep 17 00:00:00 2001 From: Thomas Trompette Date: Thu, 27 Feb 2025 10:39:48 +0100 Subject: [PATCH] Move workflow versions and steps building to workflow-builder folder (#10523) We are starting to put too many services in common folder. Version and step building should be separated into different services and go to the builder folder. Today builder folder only manage schema. We should: - keep services responsible for only one action - keep modules based on the actual action these provide rather than having common module This PR: - creates a service for workflow version builder - moves version and step builders to workflow builder folder rather than commun - creates separated folders for schema, version and steps No logic has been added. Only modules created and functions moved. --- .../resolvers/workflow-builder.resolver.ts | 8 +- .../workflow-version-step.resolver.ts | 28 +- .../resolvers/workflow-version.resolver.ts | 38 ++ .../workflow/workflow-api.module.ts | 16 +- .../dtos/serverless-function.dto.ts | 2 +- .../serverless-function.entity.ts | 2 +- .../workflow/common/workflow-common.module.ts | 22 +- .../workflow-builder.module.ts | 21 +- .../types/input-schema.type.ts | 0 .../types/output-schema.type.ts | 2 +- .../generate-fake-object-record-event.ts | 4 +- .../utils/generate-fake-object-record.ts | 4 +- .../utils/should-generate-field-fake-value.ts | 0 .../workflow-schema/workflow-schema.module.ts | 12 + .../workflow-schema.workspace-service.ts} | 12 +- .../workflow-version-step.module.ts | 19 + ...workflow-version-step.workspace-service.ts | 521 +++++++----------- .../workflow-version.module.ts | 28 + .../workflow-version.workspace-service.ts | 151 +++++ .../types/workflow-action-settings.type.ts | 2 +- .../types/workflow-trigger.type.ts | 2 +- 21 files changed, 495 insertions(+), 399 deletions(-) create mode 100644 packages/twenty-server/src/engine/core-modules/workflow/resolvers/workflow-version.resolver.ts rename packages/twenty-server/src/modules/workflow/workflow-builder/{ => workflow-schema}/types/input-schema.type.ts (100%) rename packages/twenty-server/src/modules/workflow/workflow-builder/{ => workflow-schema}/types/output-schema.type.ts (93%) rename packages/twenty-server/src/modules/workflow/workflow-builder/{ => workflow-schema}/utils/generate-fake-object-record-event.ts (95%) rename packages/twenty-server/src/modules/workflow/workflow-builder/{ => workflow-schema}/utils/generate-fake-object-record.ts (92%) rename packages/twenty-server/src/modules/workflow/workflow-builder/{ => workflow-schema}/utils/should-generate-field-fake-value.ts (100%) create mode 100644 packages/twenty-server/src/modules/workflow/workflow-builder/workflow-schema/workflow-schema.module.ts rename packages/twenty-server/src/modules/workflow/workflow-builder/{workflow-builder.workspace-service.ts => workflow-schema/workflow-schema.workspace-service.ts} (93%) create mode 100644 packages/twenty-server/src/modules/workflow/workflow-builder/workflow-version/workflow-step/workflow-version-step.module.ts rename packages/twenty-server/src/modules/workflow/{common/workspace-services => workflow-builder/workflow-version/workflow-step}/workflow-version-step.workspace-service.ts (74%) create mode 100644 packages/twenty-server/src/modules/workflow/workflow-builder/workflow-version/workflow-version.module.ts create mode 100644 packages/twenty-server/src/modules/workflow/workflow-builder/workflow-version/workflow-version.workspace-service.ts diff --git a/packages/twenty-server/src/engine/core-modules/workflow/resolvers/workflow-builder.resolver.ts b/packages/twenty-server/src/engine/core-modules/workflow/resolvers/workflow-builder.resolver.ts index eab6db6d342..958e7089573 100644 --- a/packages/twenty-server/src/engine/core-modules/workflow/resolvers/workflow-builder.resolver.ts +++ b/packages/twenty-server/src/engine/core-modules/workflow/resolvers/workflow-builder.resolver.ts @@ -9,15 +9,15 @@ import { Workspace } from 'src/engine/core-modules/workspace/workspace.entity'; import { AuthWorkspace } from 'src/engine/decorators/auth/auth-workspace.decorator'; import { UserAuthGuard } from 'src/engine/guards/user-auth.guard'; import { WorkspaceAuthGuard } from 'src/engine/guards/workspace-auth.guard'; -import { WorkflowBuilderWorkspaceService } from 'src/modules/workflow/workflow-builder/workflow-builder.workspace-service'; -import { OutputSchema } from 'src/modules/workflow/workflow-builder/types/output-schema.type'; +import { OutputSchema } from 'src/modules/workflow/workflow-builder/workflow-schema/types/output-schema.type'; +import { WorkflowSchemaWorkspaceService } from 'src/modules/workflow/workflow-builder/workflow-schema/workflow-schema.workspace-service'; @Resolver() @UseGuards(WorkspaceAuthGuard, UserAuthGuard) @UseFilters(WorkflowTriggerGraphqlApiExceptionFilter) export class WorkflowBuilderResolver { constructor( - private readonly workflowBuilderWorkspaceService: WorkflowBuilderWorkspaceService, + private readonly workflowSchemaWorkspaceService: WorkflowSchemaWorkspaceService, ) {} @Mutation(() => graphqlTypeJson) @@ -25,7 +25,7 @@ export class WorkflowBuilderResolver { @AuthWorkspace() { id: workspaceId }: Workspace, @Args('input') { step }: ComputeStepOutputSchemaInput, ): Promise { - return this.workflowBuilderWorkspaceService.computeStepOutputSchema({ + return this.workflowSchemaWorkspaceService.computeStepOutputSchema({ step, workspaceId, }); diff --git a/packages/twenty-server/src/engine/core-modules/workflow/resolvers/workflow-version-step.resolver.ts b/packages/twenty-server/src/engine/core-modules/workflow/resolvers/workflow-version-step.resolver.ts index 1ca41f56939..15481cc8a80 100644 --- a/packages/twenty-server/src/engine/core-modules/workflow/resolvers/workflow-version-step.resolver.ts +++ b/packages/twenty-server/src/engine/core-modules/workflow/resolvers/workflow-version-step.resolver.ts @@ -1,22 +1,18 @@ -import { UseFilters, UseGuards } from '@nestjs/common'; +import { UseGuards } from '@nestjs/common'; import { Args, Mutation, Resolver } from '@nestjs/graphql'; -import { CreateDraftFromWorkflowVersionInput } from 'src/engine/core-modules/workflow/dtos/create-draft-from-workflow-version-input'; import { CreateWorkflowVersionStepInput } from 'src/engine/core-modules/workflow/dtos/create-workflow-version-step-input.dto'; import { DeleteWorkflowVersionStepInput } from 'src/engine/core-modules/workflow/dtos/delete-workflow-version-step-input.dto'; import { UpdateWorkflowVersionStepInput } from 'src/engine/core-modules/workflow/dtos/update-workflow-version-step-input.dto'; import { WorkflowActionDTO } from 'src/engine/core-modules/workflow/dtos/workflow-step.dto'; -import { WorkflowTriggerGraphqlApiExceptionFilter } from 'src/engine/core-modules/workflow/filters/workflow-trigger-graphql-api-exception.filter'; import { Workspace } from 'src/engine/core-modules/workspace/workspace.entity'; import { AuthWorkspace } from 'src/engine/decorators/auth/auth-workspace.decorator'; import { UserAuthGuard } from 'src/engine/guards/user-auth.guard'; import { WorkspaceAuthGuard } from 'src/engine/guards/workspace-auth.guard'; -import { WorkflowVersionStepWorkspaceService } from 'src/modules/workflow/common/workspace-services/workflow-version-step.workspace-service'; -import { WorkflowVersionDTO } from 'src/engine/core-modules/workflow/dtos/workflow-version.dto'; +import { WorkflowVersionStepWorkspaceService } from 'src/modules/workflow/workflow-builder/workflow-version/workflow-step/workflow-version-step.workspace-service'; @Resolver() @UseGuards(WorkspaceAuthGuard, UserAuthGuard) -@UseFilters(WorkflowTriggerGraphqlApiExceptionFilter) export class WorkflowVersionStepResolver { constructor( private readonly workflowVersionStepWorkspaceService: WorkflowVersionStepWorkspaceService, @@ -60,24 +56,4 @@ export class WorkflowVersionStepResolver { stepId, }); } - - @Mutation(() => WorkflowVersionDTO) - async createDraftFromWorkflowVersion( - @AuthWorkspace() { id: workspaceId }: Workspace, - @Args('input') - { - workflowId, - workflowVersionIdToCopy, - }: CreateDraftFromWorkflowVersionInput, - ): Promise { - return { - id: await this.workflowVersionStepWorkspaceService.createDraftFromWorkflowVersion( - { - workspaceId, - workflowId, - workflowVersionIdToCopy, - }, - ), - }; - } } diff --git a/packages/twenty-server/src/engine/core-modules/workflow/resolvers/workflow-version.resolver.ts b/packages/twenty-server/src/engine/core-modules/workflow/resolvers/workflow-version.resolver.ts new file mode 100644 index 00000000000..c1909377731 --- /dev/null +++ b/packages/twenty-server/src/engine/core-modules/workflow/resolvers/workflow-version.resolver.ts @@ -0,0 +1,38 @@ +import { UseGuards } from '@nestjs/common'; +import { Args, Mutation, Resolver } from '@nestjs/graphql'; + +import { CreateDraftFromWorkflowVersionInput } from 'src/engine/core-modules/workflow/dtos/create-draft-from-workflow-version-input'; +import { WorkflowVersionDTO } from 'src/engine/core-modules/workflow/dtos/workflow-version.dto'; +import { Workspace } from 'src/engine/core-modules/workspace/workspace.entity'; +import { AuthWorkspace } from 'src/engine/decorators/auth/auth-workspace.decorator'; +import { UserAuthGuard } from 'src/engine/guards/user-auth.guard'; +import { WorkspaceAuthGuard } from 'src/engine/guards/workspace-auth.guard'; +import { WorkflowVersionWorkspaceService } from 'src/modules/workflow/workflow-builder/workflow-version/workflow-version.workspace-service'; + +@Resolver() +@UseGuards(WorkspaceAuthGuard, UserAuthGuard) +export class WorkflowVersionResolver { + constructor( + private readonly workflowVersionWorkspaceService: WorkflowVersionWorkspaceService, + ) {} + + @Mutation(() => WorkflowVersionDTO) + async createDraftFromWorkflowVersion( + @AuthWorkspace() { id: workspaceId }: Workspace, + @Args('input') + { + workflowId, + workflowVersionIdToCopy, + }: CreateDraftFromWorkflowVersionInput, + ): Promise { + return { + id: await this.workflowVersionWorkspaceService.createDraftFromWorkflowVersion( + { + workspaceId, + workflowId, + workflowVersionIdToCopy, + }, + ), + }; + } +} 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 e9e97b864ee..26c35e74a92 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 @@ -1,18 +1,26 @@ import { Module } from '@nestjs/common'; -import { WorkflowTriggerResolver } from 'src/engine/core-modules/workflow/resolvers/workflow-trigger.resolver'; import { WorkflowBuilderResolver } from 'src/engine/core-modules/workflow/resolvers/workflow-builder.resolver'; -import { WorkflowTriggerModule } from 'src/modules/workflow/workflow-trigger/workflow-trigger.module'; +import { WorkflowTriggerResolver } from 'src/engine/core-modules/workflow/resolvers/workflow-trigger.resolver'; import { WorkflowVersionStepResolver } from 'src/engine/core-modules/workflow/resolvers/workflow-version-step.resolver'; -import { WorkflowBuilderModule } from 'src/modules/workflow/workflow-builder/workflow-builder.module'; +import { WorkflowVersionResolver } from 'src/engine/core-modules/workflow/resolvers/workflow-version.resolver'; import { WorkflowCommonModule } from 'src/modules/workflow/common/workflow-common.module'; +import { WorkflowBuilderModule } from 'src/modules/workflow/workflow-builder/workflow-builder.module'; +import { WorkflowVersionModule } from 'src/modules/workflow/workflow-builder/workflow-version/workflow-version.module'; +import { WorkflowTriggerModule } from 'src/modules/workflow/workflow-trigger/workflow-trigger.module'; @Module({ - imports: [WorkflowTriggerModule, WorkflowBuilderModule, WorkflowCommonModule], + imports: [ + WorkflowTriggerModule, + WorkflowBuilderModule, + WorkflowCommonModule, + WorkflowVersionModule, + ], providers: [ WorkflowTriggerResolver, WorkflowBuilderResolver, WorkflowVersionStepResolver, + WorkflowVersionResolver, ], }) export class WorkflowApiModule {} diff --git a/packages/twenty-server/src/engine/metadata-modules/serverless-function/dtos/serverless-function.dto.ts b/packages/twenty-server/src/engine/metadata-modules/serverless-function/dtos/serverless-function.dto.ts index 5b9386aec1a..37058e99587 100644 --- a/packages/twenty-server/src/engine/metadata-modules/serverless-function/dtos/serverless-function.dto.ts +++ b/packages/twenty-server/src/engine/metadata-modules/serverless-function/dtos/serverless-function.dto.ts @@ -23,7 +23,7 @@ import GraphQLJSON from 'graphql-type-json'; import { UUIDScalarType } from 'src/engine/api/graphql/workspace-schema-builder/graphql-types/scalars'; import { ServerlessFunctionSyncStatus } from 'src/engine/metadata-modules/serverless-function/serverless-function.entity'; -import { InputSchema } from 'src/modules/workflow/workflow-builder/types/input-schema.type'; +import { InputSchema } from 'src/modules/workflow/workflow-builder/workflow-schema/types/input-schema.type'; registerEnumType(ServerlessFunctionSyncStatus, { name: 'ServerlessFunctionSyncStatus', diff --git a/packages/twenty-server/src/engine/metadata-modules/serverless-function/serverless-function.entity.ts b/packages/twenty-server/src/engine/metadata-modules/serverless-function/serverless-function.entity.ts index 3b74a39cdd8..82e4e1cfa66 100644 --- a/packages/twenty-server/src/engine/metadata-modules/serverless-function/serverless-function.entity.ts +++ b/packages/twenty-server/src/engine/metadata-modules/serverless-function/serverless-function.entity.ts @@ -7,7 +7,7 @@ import { UpdateDateColumn, } from 'typeorm'; -import { InputSchema } from 'src/modules/workflow/workflow-builder/types/input-schema.type'; +import { InputSchema } from 'src/modules/workflow/workflow-builder/workflow-schema/types/input-schema.type'; const DEFAULT_SERVERLESS_TIMEOUT_SECONDS = 300; // 5 minutes diff --git a/packages/twenty-server/src/modules/workflow/common/workflow-common.module.ts b/packages/twenty-server/src/modules/workflow/common/workflow-common.module.ts index 26905f9e482..0bce86bfe26 100644 --- a/packages/twenty-server/src/modules/workflow/common/workflow-common.module.ts +++ b/packages/twenty-server/src/modules/workflow/common/workflow-common.module.ts @@ -1,28 +1,12 @@ import { Module } from '@nestjs/common'; -import { NestjsQueryTypeOrmModule } from '@ptc-org/nestjs-query-typeorm'; - -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 { WorkflowQueryHookModule } from 'src/modules/workflow/common/query-hooks/workflow-query-hook.module'; import { WorkflowCommonWorkspaceService } from 'src/modules/workflow/common/workspace-services/workflow-common.workspace-service'; -import { WorkflowVersionStepWorkspaceService } from 'src/modules/workflow/common/workspace-services/workflow-version-step.workspace-service'; -import { WorkflowBuilderModule } from 'src/modules/workflow/workflow-builder/workflow-builder.module'; @Module({ - imports: [ - WorkflowQueryHookModule, - WorkflowBuilderModule, - ServerlessFunctionModule, - NestjsQueryTypeOrmModule.forFeature([ObjectMetadataEntity], 'metadata'), - ], - providers: [ - WorkflowCommonWorkspaceService, - WorkflowVersionStepWorkspaceService, - ], - exports: [ - WorkflowCommonWorkspaceService, - WorkflowVersionStepWorkspaceService, - ], + imports: [WorkflowQueryHookModule, ServerlessFunctionModule], + providers: [WorkflowCommonWorkspaceService], + exports: [WorkflowCommonWorkspaceService], }) export class WorkflowCommonModule {} 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 f6a4b41c482..7bc33a261f1 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 @@ -1,16 +1,23 @@ import { Module } from '@nestjs/common'; -import { TypeOrmModule } from '@nestjs/typeorm'; + +import { NestjsQueryTypeOrmModule } from '@ptc-org/nestjs-query-typeorm'; 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 { WorkflowBuilderWorkspaceService } from 'src/modules/workflow/workflow-builder/workflow-builder.workspace-service'; +import { WorkflowSchemaModule } from 'src/modules/workflow/workflow-builder/workflow-schema/workflow-schema.module'; +import { WorkflowVersionStepModule } from 'src/modules/workflow/workflow-builder/workflow-version/workflow-step/workflow-version-step.module'; +import { WorkflowVersionModule } from 'src/modules/workflow/workflow-builder/workflow-version/workflow-version.module'; @Module({ imports: [ - TypeOrmModule.forFeature([ObjectMetadataEntity], 'metadata'), - ServerlessFunctionModule, + WorkflowSchemaModule, + WorkflowVersionModule, + WorkflowVersionStepModule, + NestjsQueryTypeOrmModule.forFeature([ObjectMetadataEntity], 'metadata'), + ], + exports: [ + WorkflowSchemaModule, + WorkflowVersionModule, + WorkflowVersionStepModule, ], - providers: [WorkflowBuilderWorkspaceService], - exports: [WorkflowBuilderWorkspaceService], }) export class WorkflowBuilderModule {} diff --git a/packages/twenty-server/src/modules/workflow/workflow-builder/types/input-schema.type.ts b/packages/twenty-server/src/modules/workflow/workflow-builder/workflow-schema/types/input-schema.type.ts similarity index 100% rename from packages/twenty-server/src/modules/workflow/workflow-builder/types/input-schema.type.ts rename to packages/twenty-server/src/modules/workflow/workflow-builder/workflow-schema/types/input-schema.type.ts diff --git a/packages/twenty-server/src/modules/workflow/workflow-builder/types/output-schema.type.ts b/packages/twenty-server/src/modules/workflow/workflow-builder/workflow-schema/types/output-schema.type.ts similarity index 93% rename from packages/twenty-server/src/modules/workflow/workflow-builder/types/output-schema.type.ts rename to packages/twenty-server/src/modules/workflow/workflow-builder/workflow-schema/types/output-schema.type.ts index 3994425bf70..419fa84dbd5 100644 --- a/packages/twenty-server/src/modules/workflow/workflow-builder/types/output-schema.type.ts +++ b/packages/twenty-server/src/modules/workflow/workflow-builder/workflow-schema/types/output-schema.type.ts @@ -1,4 +1,4 @@ -import { InputSchemaPropertyType } from 'src/modules/workflow/workflow-builder/types/input-schema.type'; +import { InputSchemaPropertyType } from 'src/modules/workflow/workflow-builder/workflow-schema/types/input-schema.type'; export type Leaf = { isLeaf: true; diff --git a/packages/twenty-server/src/modules/workflow/workflow-builder/utils/generate-fake-object-record-event.ts b/packages/twenty-server/src/modules/workflow/workflow-builder/workflow-schema/utils/generate-fake-object-record-event.ts similarity index 95% rename from packages/twenty-server/src/modules/workflow/workflow-builder/utils/generate-fake-object-record-event.ts rename to packages/twenty-server/src/modules/workflow/workflow-builder/workflow-schema/utils/generate-fake-object-record-event.ts index 04f1e7552b9..a98e6f1e8e9 100644 --- a/packages/twenty-server/src/modules/workflow/workflow-builder/utils/generate-fake-object-record-event.ts +++ b/packages/twenty-server/src/modules/workflow/workflow-builder/workflow-schema/utils/generate-fake-object-record-event.ts @@ -2,8 +2,8 @@ import { v4 } from 'uuid'; import { DatabaseEventAction } from 'src/engine/api/graphql/graphql-query-runner/enums/database-event-action'; import { ObjectMetadataEntity } from 'src/engine/metadata-modules/object-metadata/object-metadata.entity'; -import { BaseOutputSchema } from 'src/modules/workflow/workflow-builder/types/output-schema.type'; -import { generateFakeObjectRecord } from 'src/modules/workflow/workflow-builder/utils/generate-fake-object-record'; +import { BaseOutputSchema } from 'src/modules/workflow/workflow-builder/workflow-schema/types/output-schema.type'; +import { generateFakeObjectRecord } from 'src/modules/workflow/workflow-builder/workflow-schema/utils/generate-fake-object-record'; import { camelToTitleCase } from 'src/utils/camel-to-title-case'; export const generateFakeObjectRecordEvent = ( diff --git a/packages/twenty-server/src/modules/workflow/workflow-builder/utils/generate-fake-object-record.ts b/packages/twenty-server/src/modules/workflow/workflow-builder/workflow-schema/utils/generate-fake-object-record.ts similarity index 92% rename from packages/twenty-server/src/modules/workflow/workflow-builder/utils/generate-fake-object-record.ts rename to packages/twenty-server/src/modules/workflow/workflow-builder/workflow-schema/utils/generate-fake-object-record.ts index c1e0142c356..a41f3514a32 100644 --- a/packages/twenty-server/src/modules/workflow/workflow-builder/utils/generate-fake-object-record.ts +++ b/packages/twenty-server/src/modules/workflow/workflow-builder/workflow-schema/utils/generate-fake-object-record.ts @@ -5,8 +5,8 @@ import { Leaf, Node, RecordOutputSchema, -} from 'src/modules/workflow/workflow-builder/types/output-schema.type'; -import { shouldGenerateFieldFakeValue } from 'src/modules/workflow/workflow-builder/utils/should-generate-field-fake-value'; +} from 'src/modules/workflow/workflow-builder/workflow-schema/types/output-schema.type'; +import { shouldGenerateFieldFakeValue } from 'src/modules/workflow/workflow-builder/workflow-schema/utils/should-generate-field-fake-value'; import { camelToTitleCase } from 'src/utils/camel-to-title-case'; const generateObjectRecordFields = ( diff --git a/packages/twenty-server/src/modules/workflow/workflow-builder/utils/should-generate-field-fake-value.ts b/packages/twenty-server/src/modules/workflow/workflow-builder/workflow-schema/utils/should-generate-field-fake-value.ts similarity index 100% rename from packages/twenty-server/src/modules/workflow/workflow-builder/utils/should-generate-field-fake-value.ts rename to packages/twenty-server/src/modules/workflow/workflow-builder/workflow-schema/utils/should-generate-field-fake-value.ts diff --git a/packages/twenty-server/src/modules/workflow/workflow-builder/workflow-schema/workflow-schema.module.ts b/packages/twenty-server/src/modules/workflow/workflow-builder/workflow-schema/workflow-schema.module.ts new file mode 100644 index 00000000000..c3a4e0087da --- /dev/null +++ b/packages/twenty-server/src/modules/workflow/workflow-builder/workflow-schema/workflow-schema.module.ts @@ -0,0 +1,12 @@ +import { Module } from '@nestjs/common'; +import { TypeOrmModule } from '@nestjs/typeorm'; + +import { ObjectMetadataEntity } from 'src/engine/metadata-modules/object-metadata/object-metadata.entity'; +import { WorkflowSchemaWorkspaceService } from 'src/modules/workflow/workflow-builder/workflow-schema/workflow-schema.workspace-service'; + +@Module({ + imports: [TypeOrmModule.forFeature([ObjectMetadataEntity], 'metadata')], + providers: [WorkflowSchemaWorkspaceService], + exports: [WorkflowSchemaWorkspaceService], +}) +export class WorkflowSchemaModule {} diff --git a/packages/twenty-server/src/modules/workflow/workflow-builder/workflow-builder.workspace-service.ts b/packages/twenty-server/src/modules/workflow/workflow-builder/workflow-schema/workflow-schema.workspace-service.ts similarity index 93% rename from packages/twenty-server/src/modules/workflow/workflow-builder/workflow-builder.workspace-service.ts rename to packages/twenty-server/src/modules/workflow/workflow-builder/workflow-schema/workflow-schema.workspace-service.ts index 8c2a8973113..bde6bad8ec0 100644 --- a/packages/twenty-server/src/modules/workflow/workflow-builder/workflow-builder.workspace-service.ts +++ b/packages/twenty-server/src/modules/workflow/workflow-builder/workflow-schema/workflow-schema.workspace-service.ts @@ -1,17 +1,16 @@ import { Injectable } from '@nestjs/common'; import { InjectRepository } from '@nestjs/typeorm'; -import { Repository } from 'typeorm'; import { isDefined } from 'twenty-shared'; +import { Repository } from 'typeorm'; import { DatabaseEventAction } from 'src/engine/api/graphql/graphql-query-runner/enums/database-event-action'; import { checkStringIsDatabaseEventAction } from 'src/engine/api/graphql/graphql-query-runner/utils/check-string-is-database-event-action'; 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 { generateFakeValue } from 'src/engine/utils/generate-fake-value'; -import { OutputSchema } from 'src/modules/workflow/workflow-builder/types/output-schema.type'; -import { generateFakeObjectRecord } from 'src/modules/workflow/workflow-builder/utils/generate-fake-object-record'; -import { generateFakeObjectRecordEvent } from 'src/modules/workflow/workflow-builder/utils/generate-fake-object-record-event'; +import { OutputSchema } from 'src/modules/workflow/workflow-builder/workflow-schema/types/output-schema.type'; +import { generateFakeObjectRecord } from 'src/modules/workflow/workflow-builder/workflow-schema/utils/generate-fake-object-record'; +import { generateFakeObjectRecordEvent } from 'src/modules/workflow/workflow-builder/workflow-schema/utils/generate-fake-object-record-event'; import { WorkflowAction, WorkflowActionType, @@ -22,9 +21,8 @@ import { } from 'src/modules/workflow/workflow-trigger/types/workflow-trigger.type'; @Injectable() -export class WorkflowBuilderWorkspaceService { +export class WorkflowSchemaWorkspaceService { constructor( - private readonly serverlessFunctionService: ServerlessFunctionService, @InjectRepository(ObjectMetadataEntity, 'metadata') private readonly objectMetadataRepository: Repository, ) {} diff --git a/packages/twenty-server/src/modules/workflow/workflow-builder/workflow-version/workflow-step/workflow-version-step.module.ts b/packages/twenty-server/src/modules/workflow/workflow-builder/workflow-version/workflow-step/workflow-version-step.module.ts new file mode 100644 index 00000000000..494e21babcb --- /dev/null +++ b/packages/twenty-server/src/modules/workflow/workflow-builder/workflow-version/workflow-step/workflow-version-step.module.ts @@ -0,0 +1,19 @@ +import { Module } from '@nestjs/common'; + +import { NestjsQueryTypeOrmModule } from '@ptc-org/nestjs-query-typeorm'; + +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 { WorkflowVersionStepWorkspaceService } from 'src/modules/workflow/workflow-builder/workflow-version/workflow-step/workflow-version-step.workspace-service'; + +@Module({ + imports: [ + WorkflowSchemaModule, + ServerlessFunctionModule, + NestjsQueryTypeOrmModule.forFeature([ObjectMetadataEntity], 'metadata'), + ], + providers: [WorkflowVersionStepWorkspaceService], + exports: [WorkflowVersionStepWorkspaceService], +}) +export class WorkflowVersionStepModule {} diff --git a/packages/twenty-server/src/modules/workflow/common/workspace-services/workflow-version-step.workspace-service.ts b/packages/twenty-server/src/modules/workflow/workflow-builder/workflow-version/workflow-step/workflow-version-step.workspace-service.ts similarity index 74% rename from packages/twenty-server/src/modules/workflow/common/workspace-services/workflow-version-step.workspace-service.ts rename to packages/twenty-server/src/modules/workflow/workflow-builder/workflow-version/workflow-step/workflow-version-step.workspace-service.ts index 17afc7570f0..a881c3850f6 100644 --- a/packages/twenty-server/src/modules/workflow/common/workspace-services/workflow-version-step.workspace-service.ts +++ b/packages/twenty-server/src/modules/workflow/workflow-builder/workflow-version/workflow-step/workflow-version-step.workspace-service.ts @@ -5,25 +5,17 @@ import { isDefined } from 'twenty-shared'; import { Repository } from 'typeorm'; import { v4 } from 'uuid'; -import { DatabaseEventAction } from 'src/engine/api/graphql/graphql-query-runner/enums/database-event-action'; import { BASE_TYPESCRIPT_PROJECT_INPUT_SCHEMA } from 'src/engine/core-modules/serverless/drivers/constants/base-typescript-project-input-schema'; import { WorkflowActionDTO } from 'src/engine/core-modules/workflow/dtos/workflow-step.dto'; 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 { TwentyORMManager } from 'src/engine/twenty-orm/twenty-orm.manager'; -import { WorkspaceEventEmitter } from 'src/engine/workspace-event-emitter/workspace-event-emitter'; import { WorkflowVersionStepException, WorkflowVersionStepExceptionCode, } from 'src/modules/workflow/common/exceptions/workflow-version-step.exception'; -import { - WorkflowVersionStatus, - WorkflowVersionWorkspaceEntity, -} from 'src/modules/workflow/common/standard-objects/workflow-version.workspace-entity'; -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 { WorkflowBuilderWorkspaceService } from 'src/modules/workflow/workflow-builder/workflow-builder.workspace-service'; +import { WorkflowVersionWorkspaceEntity } from 'src/modules/workflow/common/standard-objects/workflow-version.workspace-entity'; +import { WorkflowSchemaWorkspaceService } from 'src/modules/workflow/workflow-builder/workflow-schema/workflow-schema.workspace-service'; import { BaseWorkflowActionSettings } from 'src/modules/workflow/workflow-executor/workflow-actions/types/workflow-action-settings.type'; import { WorkflowAction, @@ -48,230 +40,12 @@ const BASE_STEP_DEFINITION: BaseWorkflowActionSettings = { export class WorkflowVersionStepWorkspaceService { constructor( private readonly twentyORMManager: TwentyORMManager, - private readonly workflowBuilderWorkspaceService: WorkflowBuilderWorkspaceService, + private readonly workflowSchemaWorkspaceService: WorkflowSchemaWorkspaceService, private readonly serverlessFunctionService: ServerlessFunctionService, @InjectRepository(ObjectMetadataEntity, 'metadata') private readonly objectMetadataRepository: Repository, - private readonly workspaceEventEmitter: WorkspaceEventEmitter, ) {} - private async getStepDefaultDefinition({ - type, - workspaceId, - }: { - type: WorkflowActionType; - workspaceId: string; - }): Promise { - const newStepId = v4(); - - switch (type) { - case WorkflowActionType.CODE: { - const newServerlessFunction = - await this.serverlessFunctionService.createOneServerlessFunction( - { - name: 'A Serverless Function Code Workflow Step', - description: '', - }, - workspaceId, - ); - - if (!isDefined(newServerlessFunction)) { - throw new WorkflowVersionStepException( - 'Fail to create Code Step', - WorkflowVersionStepExceptionCode.FAILURE, - ); - } - - return { - id: newStepId, - name: 'Code - Serverless Function', - type: WorkflowActionType.CODE, - valid: false, - settings: { - ...BASE_STEP_DEFINITION, - outputSchema: { - link: { - isLeaf: true, - icon: 'IconVariable', - tab: 'test', - label: 'Generate Function Output', - }, - _outputSchemaType: 'LINK', - }, - input: { - serverlessFunctionId: newServerlessFunction.id, - serverlessFunctionVersion: 'draft', - serverlessFunctionInput: BASE_TYPESCRIPT_PROJECT_INPUT_SCHEMA, - }, - }, - }; - } - case WorkflowActionType.SEND_EMAIL: { - return { - id: newStepId, - name: 'Send Email', - type: WorkflowActionType.SEND_EMAIL, - valid: false, - settings: { - ...BASE_STEP_DEFINITION, - input: { - connectedAccountId: '', - email: '', - subject: '', - body: '', - }, - }, - }; - } - case WorkflowActionType.CREATE_RECORD: { - const activeObjectMetadataItem = - await this.objectMetadataRepository.findOne({ - where: { workspaceId, isActive: true, isSystem: false }, - }); - - return { - id: newStepId, - name: 'Create Record', - type: WorkflowActionType.CREATE_RECORD, - valid: false, - settings: { - ...BASE_STEP_DEFINITION, - input: { - objectName: activeObjectMetadataItem?.nameSingular || '', - objectRecord: {}, - }, - }, - }; - } - case WorkflowActionType.UPDATE_RECORD: { - const activeObjectMetadataItem = - await this.objectMetadataRepository.findOne({ - where: { workspaceId, isActive: true, isSystem: false }, - }); - - return { - id: newStepId, - name: 'Update Record', - type: WorkflowActionType.UPDATE_RECORD, - valid: false, - settings: { - ...BASE_STEP_DEFINITION, - input: { - objectName: activeObjectMetadataItem?.nameSingular || '', - objectRecord: {}, - objectRecordId: '', - fieldsToUpdate: [], - }, - }, - }; - } - case WorkflowActionType.DELETE_RECORD: { - const activeObjectMetadataItem = - await this.objectMetadataRepository.findOne({ - where: { workspaceId, isActive: true, isSystem: false }, - }); - - return { - id: newStepId, - name: 'Delete Record', - type: WorkflowActionType.DELETE_RECORD, - valid: false, - settings: { - ...BASE_STEP_DEFINITION, - input: { - objectName: activeObjectMetadataItem?.nameSingular || '', - objectRecordId: '', - }, - }, - }; - } - case WorkflowActionType.FIND_RECORDS: { - const activeObjectMetadataItem = - await this.objectMetadataRepository.findOne({ - where: { workspaceId, isActive: true, isSystem: false }, - }); - - return { - id: newStepId, - name: 'Search Records', - type: WorkflowActionType.FIND_RECORDS, - valid: false, - settings: { - ...BASE_STEP_DEFINITION, - input: { - objectName: activeObjectMetadataItem?.nameSingular || '', - limit: 1, - }, - }, - }; - } - default: - throw new WorkflowVersionStepException( - `WorkflowActionType '${type}' unknown`, - WorkflowVersionStepExceptionCode.UNKNOWN, - ); - } - } - - private async duplicateStep({ - step, - workspaceId, - }: { - step: WorkflowAction; - workspaceId: string; - }): Promise { - switch (step.type) { - case WorkflowActionType.CODE: { - await this.serverlessFunctionService.usePublishedVersionAsDraft({ - id: step.settings.input.serverlessFunctionId, - version: step.settings.input.serverlessFunctionVersion, - workspaceId, - }); - - return { - ...step, - settings: { - ...step.settings, - input: { - ...step.settings.input, - serverlessFunctionVersion: 'draft', - }, - }, - }; - } - default: { - return step; - } - } - } - - private async enrichOutputSchema({ - step, - workspaceId, - }: { - step: WorkflowAction; - workspaceId: string; - }): Promise { - // We don't enrich on the fly for code workflow action. OutputSchema is computed and updated when testing the serverless function - if (step.type === WorkflowActionType.CODE) { - return step; - } - - const result = { ...step }; - const outputSchema = - await this.workflowBuilderWorkspaceService.computeStepOutputSchema({ - step, - workspaceId, - }); - - result.settings = { - ...result.settings, - outputSchema: outputSchema || {}, - }; - - return result; - } - async createWorkflowVersionStep({ workspaceId, workflowVersionId, @@ -431,83 +205,63 @@ export class WorkflowVersionStepWorkspaceService { return stepToDelete; } - async createDraftFromWorkflowVersion({ + async duplicateStep({ + step, workspaceId, - workflowId, - workflowVersionIdToCopy, }: { + step: WorkflowAction; workspaceId: string; - workflowId: string; - workflowVersionIdToCopy: string; - }) { - const workflowVersionRepository = - await this.twentyORMManager.getRepository( - 'workflowVersion', - ); + }): Promise { + switch (step.type) { + case WorkflowActionType.CODE: { + await this.serverlessFunctionService.usePublishedVersionAsDraft({ + id: step.settings.input.serverlessFunctionId, + version: step.settings.input.serverlessFunctionVersion, + workspaceId, + }); - const workflowVersionToCopy = await workflowVersionRepository.findOne({ - where: { - id: workflowVersionIdToCopy, - workflowId, - }, - }); + return { + ...step, + settings: { + ...step.settings, + input: { + ...step.settings.input, + serverlessFunctionVersion: 'draft', + }, + }, + }; + } + default: { + return step; + } + } + } - if (!isDefined(workflowVersionToCopy)) { - throw new WorkflowVersionStepException( - 'WorkflowVersion to copy not found', - WorkflowVersionStepExceptionCode.NOT_FOUND, - ); + private async enrichOutputSchema({ + step, + workspaceId, + }: { + step: WorkflowAction; + workspaceId: string; + }): Promise { + // We don't enrich on the fly for code workflow action. OutputSchema is computed and updated when testing the serverless function + if (step.type === WorkflowActionType.CODE) { + return step; } - assertWorkflowVersionTriggerIsDefined(workflowVersionToCopy); - assertWorkflowVersionHasSteps(workflowVersionToCopy); - - let draftWorkflowVersion = await workflowVersionRepository.findOne({ - where: { - workflowId, - status: WorkflowVersionStatus.DRAFT, - }, - }); - - if (!isDefined(draftWorkflowVersion)) { - const workflowVersionsCount = await workflowVersionRepository.count({ - where: { - workflowId, - }, - }); - - draftWorkflowVersion = await workflowVersionRepository.save({ - workflowId, - name: `v${workflowVersionsCount + 1}`, - status: WorkflowVersionStatus.DRAFT, - }); - - await this.emitWorkflowVersionCreationEvent({ - workflowVersion: draftWorkflowVersion, - workspaceId, - }); - } - - assertWorkflowVersionIsDraft(draftWorkflowVersion); - - const newWorkflowVersionTrigger = workflowVersionToCopy.trigger; - const newWorkflowVersionSteps: WorkflowAction[] = []; - - for (const step of workflowVersionToCopy.steps) { - const duplicatedStep = await this.duplicateStep({ + const result = { ...step }; + const outputSchema = + await this.workflowSchemaWorkspaceService.computeStepOutputSchema({ step, workspaceId, }); - newWorkflowVersionSteps.push(duplicatedStep); - } + result.settings = { + ...result.settings, + outputSchema: outputSchema || {}, + }; - await workflowVersionRepository.update(draftWorkflowVersion.id, { - steps: newWorkflowVersionSteps, - trigger: newWorkflowVersionTrigger, - }); - - return draftWorkflowVersion.id; + return result; } private async runWorkflowVersionStepDeletionSideEffects({ @@ -534,40 +288,161 @@ export class WorkflowVersionStepWorkspaceService { } } - private async emitWorkflowVersionCreationEvent({ - workflowVersion, + private async getStepDefaultDefinition({ + type, workspaceId, }: { - workflowVersion: WorkflowVersionWorkspaceEntity; + type: WorkflowActionType; workspaceId: string; - }) { - const objectMetadata = await this.objectMetadataRepository.findOne({ - where: { - nameSingular: 'workflowVersion', - workspaceId, - }, - }); + }): Promise { + const newStepId = v4(); - if (!objectMetadata) { - throw new WorkflowVersionStepException( - 'Object metadata not found', - WorkflowVersionStepExceptionCode.FAILURE, - ); - } + switch (type) { + case WorkflowActionType.CODE: { + const newServerlessFunction = + await this.serverlessFunctionService.createOneServerlessFunction( + { + name: 'A Serverless Function Code Workflow Step', + description: '', + }, + workspaceId, + ); - this.workspaceEventEmitter.emitDatabaseBatchEvent({ - objectMetadataNameSingular: 'workflowVersion', - action: DatabaseEventAction.CREATED, - events: [ - { - recordId: workflowVersion.id, - objectMetadata, - properties: { - after: workflowVersion, + if (!isDefined(newServerlessFunction)) { + throw new WorkflowVersionStepException( + 'Fail to create Code Step', + WorkflowVersionStepExceptionCode.FAILURE, + ); + } + + return { + id: newStepId, + name: 'Code - Serverless Function', + type: WorkflowActionType.CODE, + valid: false, + settings: { + ...BASE_STEP_DEFINITION, + outputSchema: { + link: { + isLeaf: true, + icon: 'IconVariable', + tab: 'test', + label: 'Generate Function Output', + }, + _outputSchemaType: 'LINK', + }, + input: { + serverlessFunctionId: newServerlessFunction.id, + serverlessFunctionVersion: 'draft', + serverlessFunctionInput: BASE_TYPESCRIPT_PROJECT_INPUT_SCHEMA, + }, }, - }, - ], - workspaceId, - }); + }; + } + case WorkflowActionType.SEND_EMAIL: { + return { + id: newStepId, + name: 'Send Email', + type: WorkflowActionType.SEND_EMAIL, + valid: false, + settings: { + ...BASE_STEP_DEFINITION, + input: { + connectedAccountId: '', + email: '', + subject: '', + body: '', + }, + }, + }; + } + case WorkflowActionType.CREATE_RECORD: { + const activeObjectMetadataItem = + await this.objectMetadataRepository.findOne({ + where: { workspaceId, isActive: true, isSystem: false }, + }); + + return { + id: newStepId, + name: 'Create Record', + type: WorkflowActionType.CREATE_RECORD, + valid: false, + settings: { + ...BASE_STEP_DEFINITION, + input: { + objectName: activeObjectMetadataItem?.nameSingular || '', + objectRecord: {}, + }, + }, + }; + } + case WorkflowActionType.UPDATE_RECORD: { + const activeObjectMetadataItem = + await this.objectMetadataRepository.findOne({ + where: { workspaceId, isActive: true, isSystem: false }, + }); + + return { + id: newStepId, + name: 'Update Record', + type: WorkflowActionType.UPDATE_RECORD, + valid: false, + settings: { + ...BASE_STEP_DEFINITION, + input: { + objectName: activeObjectMetadataItem?.nameSingular || '', + objectRecord: {}, + objectRecordId: '', + fieldsToUpdate: [], + }, + }, + }; + } + case WorkflowActionType.DELETE_RECORD: { + const activeObjectMetadataItem = + await this.objectMetadataRepository.findOne({ + where: { workspaceId, isActive: true, isSystem: false }, + }); + + return { + id: newStepId, + name: 'Delete Record', + type: WorkflowActionType.DELETE_RECORD, + valid: false, + settings: { + ...BASE_STEP_DEFINITION, + input: { + objectName: activeObjectMetadataItem?.nameSingular || '', + objectRecordId: '', + }, + }, + }; + } + case WorkflowActionType.FIND_RECORDS: { + const activeObjectMetadataItem = + await this.objectMetadataRepository.findOne({ + where: { workspaceId, isActive: true, isSystem: false }, + }); + + return { + id: newStepId, + name: 'Search Records', + type: WorkflowActionType.FIND_RECORDS, + valid: false, + settings: { + ...BASE_STEP_DEFINITION, + input: { + objectName: activeObjectMetadataItem?.nameSingular || '', + limit: 1, + }, + }, + }; + } + default: + throw new WorkflowVersionStepException( + `WorkflowActionType '${type}' unknown`, + WorkflowVersionStepExceptionCode.UNKNOWN, + ); + } } } 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 new file mode 100644 index 00000000000..48de4d2e6c2 --- /dev/null +++ b/packages/twenty-server/src/modules/workflow/workflow-builder/workflow-version/workflow-version.module.ts @@ -0,0 +1,28 @@ +import { Module } from '@nestjs/common'; + +import { NestjsQueryTypeOrmModule } from '@ptc-org/nestjs-query-typeorm'; + +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-version/workflow-step/workflow-version-step.module'; +import { WorkflowVersionStepWorkspaceService } from 'src/modules/workflow/workflow-builder/workflow-version/workflow-step/workflow-version-step.workspace-service'; +import { WorkflowVersionWorkspaceService } from 'src/modules/workflow/workflow-builder/workflow-version/workflow-version.workspace-service'; + +@Module({ + imports: [ + WorkflowVersionStepModule, + WorkflowSchemaModule, + ServerlessFunctionModule, + NestjsQueryTypeOrmModule.forFeature([ObjectMetadataEntity], 'metadata'), + ], + providers: [ + WorkflowVersionWorkspaceService, + WorkflowVersionStepWorkspaceService, + ], + exports: [ + WorkflowVersionWorkspaceService, + WorkflowVersionStepWorkspaceService, + ], +}) +export class WorkflowVersionModule {} 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 new file mode 100644 index 00000000000..945326a53f9 --- /dev/null +++ b/packages/twenty-server/src/modules/workflow/workflow-builder/workflow-version/workflow-version.workspace-service.ts @@ -0,0 +1,151 @@ +import { Injectable } from '@nestjs/common'; +import { InjectRepository } from '@nestjs/typeorm'; + +import { isDefined } from 'twenty-shared'; +import { Repository } from 'typeorm'; + +import { DatabaseEventAction } from 'src/engine/api/graphql/graphql-query-runner/enums/database-event-action'; +import { ObjectMetadataEntity } from 'src/engine/metadata-modules/object-metadata/object-metadata.entity'; +import { TwentyORMManager } from 'src/engine/twenty-orm/twenty-orm.manager'; +import { WorkspaceEventEmitter } from 'src/engine/workspace-event-emitter/workspace-event-emitter'; +import { + WorkflowVersionStepException, + WorkflowVersionStepExceptionCode, +} from 'src/modules/workflow/common/exceptions/workflow-version-step.exception'; +import { + WorkflowVersionStatus, + WorkflowVersionWorkspaceEntity, +} from 'src/modules/workflow/common/standard-objects/workflow-version.workspace-entity'; +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-version/workflow-step/workflow-version-step.workspace-service'; +import { WorkflowAction } from 'src/modules/workflow/workflow-executor/workflow-actions/types/workflow-action.type'; + +@Injectable() +export class WorkflowVersionWorkspaceService { + constructor( + private readonly twentyORMManager: TwentyORMManager, + private readonly workflowVersionStepWorkspaceService: WorkflowVersionStepWorkspaceService, + @InjectRepository(ObjectMetadataEntity, 'metadata') + private readonly objectMetadataRepository: Repository, + private readonly workspaceEventEmitter: WorkspaceEventEmitter, + ) {} + + async createDraftFromWorkflowVersion({ + workspaceId, + workflowId, + workflowVersionIdToCopy, + }: { + workspaceId: string; + workflowId: string; + workflowVersionIdToCopy: string; + }) { + const workflowVersionRepository = + await this.twentyORMManager.getRepository( + 'workflowVersion', + ); + + const workflowVersionToCopy = await workflowVersionRepository.findOne({ + where: { + id: workflowVersionIdToCopy, + workflowId, + }, + }); + + if (!isDefined(workflowVersionToCopy)) { + throw new WorkflowVersionStepException( + 'WorkflowVersion to copy not found', + WorkflowVersionStepExceptionCode.NOT_FOUND, + ); + } + + assertWorkflowVersionTriggerIsDefined(workflowVersionToCopy); + assertWorkflowVersionHasSteps(workflowVersionToCopy); + + let draftWorkflowVersion = await workflowVersionRepository.findOne({ + where: { + workflowId, + status: WorkflowVersionStatus.DRAFT, + }, + }); + + if (!isDefined(draftWorkflowVersion)) { + const workflowVersionsCount = await workflowVersionRepository.count({ + where: { + workflowId, + }, + }); + + draftWorkflowVersion = await workflowVersionRepository.save({ + workflowId, + name: `v${workflowVersionsCount + 1}`, + status: WorkflowVersionStatus.DRAFT, + }); + + await this.emitWorkflowVersionCreationEvent({ + workflowVersion: draftWorkflowVersion, + workspaceId, + }); + } + + assertWorkflowVersionIsDraft(draftWorkflowVersion); + + const newWorkflowVersionTrigger = workflowVersionToCopy.trigger; + const newWorkflowVersionSteps: WorkflowAction[] = []; + + for (const step of workflowVersionToCopy.steps) { + const duplicatedStep = + await this.workflowVersionStepWorkspaceService.duplicateStep({ + step, + workspaceId, + }); + + newWorkflowVersionSteps.push(duplicatedStep); + } + + await workflowVersionRepository.update(draftWorkflowVersion.id, { + steps: newWorkflowVersionSteps, + trigger: newWorkflowVersionTrigger, + }); + + return draftWorkflowVersion.id; + } + + private async emitWorkflowVersionCreationEvent({ + workflowVersion, + workspaceId, + }: { + workflowVersion: WorkflowVersionWorkspaceEntity; + workspaceId: string; + }) { + const objectMetadata = await this.objectMetadataRepository.findOne({ + where: { + nameSingular: 'workflowVersion', + workspaceId, + }, + }); + + if (!objectMetadata) { + throw new WorkflowVersionStepException( + 'Object metadata not found', + WorkflowVersionStepExceptionCode.FAILURE, + ); + } + + this.workspaceEventEmitter.emitDatabaseBatchEvent({ + objectMetadataNameSingular: 'workflowVersion', + action: DatabaseEventAction.CREATED, + events: [ + { + recordId: workflowVersion.id, + objectMetadata, + properties: { + after: workflowVersion, + }, + }, + ], + workspaceId, + }); + } +} diff --git a/packages/twenty-server/src/modules/workflow/workflow-executor/workflow-actions/types/workflow-action-settings.type.ts b/packages/twenty-server/src/modules/workflow/workflow-executor/workflow-actions/types/workflow-action-settings.type.ts index 78c2758472a..81832319a42 100644 --- a/packages/twenty-server/src/modules/workflow/workflow-executor/workflow-actions/types/workflow-action-settings.type.ts +++ b/packages/twenty-server/src/modules/workflow/workflow-executor/workflow-actions/types/workflow-action-settings.type.ts @@ -1,4 +1,4 @@ -import { OutputSchema } from 'src/modules/workflow/workflow-builder/types/output-schema.type'; +import { OutputSchema } from 'src/modules/workflow/workflow-builder/workflow-schema/types/output-schema.type'; import { WorkflowCodeActionSettings } from 'src/modules/workflow/workflow-executor/workflow-actions/code/types/workflow-code-action-settings.type'; import { WorkflowFormActionSettings } from 'src/modules/workflow/workflow-executor/workflow-actions/form/types/workflow-form-action-settings.type'; import { WorkflowSendEmailActionSettings } from 'src/modules/workflow/workflow-executor/workflow-actions/mail-sender/types/workflow-send-email-action-settings.type'; diff --git a/packages/twenty-server/src/modules/workflow/workflow-trigger/types/workflow-trigger.type.ts b/packages/twenty-server/src/modules/workflow/workflow-trigger/types/workflow-trigger.type.ts index 30130a63bb9..227cddea9b8 100644 --- a/packages/twenty-server/src/modules/workflow/workflow-trigger/types/workflow-trigger.type.ts +++ b/packages/twenty-server/src/modules/workflow/workflow-trigger/types/workflow-trigger.type.ts @@ -1,4 +1,4 @@ -import { OutputSchema } from 'src/modules/workflow/workflow-builder/types/output-schema.type'; +import { OutputSchema } from 'src/modules/workflow/workflow-builder/workflow-schema/types/output-schema.type'; export enum WorkflowTriggerType { DATABASE_EVENT = 'DATABASE_EVENT',