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 eab6db6d3..958e70895 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 1ca41f569..15481cc8a 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 000000000..c19093777 --- /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 e9e97b864..26c35e74a 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 5b9386aec..37058e995 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 3b74a39cd..82e4e1cfa 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 26905f9e4..0bce86bfe 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 f6a4b41c4..7bc33a261 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 3994425bf..419fa84db 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 04f1e7552..a98e6f1e8 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 c1e0142c3..a41f3514a 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 000000000..c3a4e0087 --- /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 8c2a89731..bde6bad8e 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 000000000..494e21bab --- /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 17afc7570..a881c3850 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 000000000..48de4d2e6 --- /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 000000000..945326a53 --- /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 78c275847..81832319a 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 30130a63b..227cddea9 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',