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 eab6db6d34..958e708957 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 1ca41f5693..15481cc8a8 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 0000000000..c190937773 --- /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 e9e97b864e..26c35e74a9 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 5b9386aec1..37058e9958 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 3b74a39cdd..82e4e1cfa6 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 26905f9e48..0bce86bfe2 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 f6a4b41c48..7bc33a261f 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 3994425bf7..419fa84dbd 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 04f1e7552b..a98e6f1e8e 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 c1e0142c35..a41f3514a3 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 0000000000..c3a4e0087d --- /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 8c2a897311..bde6bad8ec 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 0000000000..494e21babc --- /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 17afc7570f..a881c3850f 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 0000000000..48de4d2e6c --- /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 0000000000..945326a53f --- /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 78c2758472..81832319a4 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 30130a63bb..227cddea9b 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',