From d87ec5f2bbe0309c3c65a57d1a37dc57b6a906e4 Mon Sep 17 00:00:00 2001 From: Adam Chmara Date: Tue, 10 Dec 2024 10:03:45 +0100 Subject: [PATCH] feat(api): add full step data to workflow dto; refactor (#7235) --- .../mappers/notification-template-mapper.ts | 32 +--- .../build-step-data.usecase.ts | 17 +-- .../get-workflow/get-workflow.usecase.ts | 57 +++++++- .../sync-to-environment.usecase.ts | 82 +++-------- .../upsert-workflow.usecase.ts | 14 +- .../workflows-v2/workflow.controller.e2e.ts | 34 ++--- apps/dashboard/src/utils/types.ts | 6 +- .../create-workflow-deprecated.dto.ts | 2 +- .../dto/workflows/get-list-query-params.ts | 2 +- packages/shared/src/dto/workflows/index.ts | 6 +- .../workflows/preview-step-response.dto.ts | 1 - .../{step-data.dto.ts => step.dto.ts} | 43 +++++- .../update-workflow-deprecated.dto.ts | 2 +- .../dto/workflows/workflow-deprecated.dto.ts | 35 +++++ .../dto/workflows/workflow-response.dto.ts | 138 ------------------ .../shared/src/dto/workflows/workflow.dto.ts | 114 +++++++++++---- .../notification-template.interface.ts | 2 +- 17 files changed, 285 insertions(+), 302 deletions(-) rename packages/shared/src/dto/workflows/{step-data.dto.ts => step.dto.ts} (54%) create mode 100644 packages/shared/src/dto/workflows/workflow-deprecated.dto.ts delete mode 100644 packages/shared/src/dto/workflows/workflow-response.dto.ts diff --git a/apps/api/src/app/workflows-v2/mappers/notification-template-mapper.ts b/apps/api/src/app/workflows-v2/mappers/notification-template-mapper.ts index 3739e18e1ff..3977b3e151f 100644 --- a/apps/api/src/app/workflows-v2/mappers/notification-template-mapper.ts +++ b/apps/api/src/app/workflows-v2/mappers/notification-template-mapper.ts @@ -2,7 +2,7 @@ import { PreferencesResponseDto, RuntimeIssue, ShortIsPrefixEnum, - StepResponseDto, + StepDataDto, StepTypeEnum, WorkflowCreateAndUpdateKeys, WorkflowListResponseDto, @@ -15,7 +15,10 @@ import { NotificationStepEntity, NotificationTemplateEntity } from '@novu/dal'; import { WorkflowInternalResponseDto } from '@novu/application-generic'; import { buildSlug } from '../../shared/helpers/build-slug'; -export function toResponseWorkflowDto(workflow: WorkflowInternalResponseDto): WorkflowResponseDto { +export function toResponseWorkflowDto( + workflow: WorkflowInternalResponseDto, + steps: StepDataDto[] +): WorkflowResponseDto { const preferencesDto: PreferencesResponseDto = { user: workflow.userPreferences, default: workflow.defaultPreferences, @@ -30,7 +33,7 @@ export function toResponseWorkflowDto(workflow: WorkflowInternalResponseDto): Wo tags: workflow.tags, active: workflow.active, preferences: preferencesDto, - steps: getSteps(workflow), + steps, description: workflow.description, origin: computeOrigin(workflow), updatedAt: workflow.updatedAt || 'Missing Updated At', @@ -40,16 +43,6 @@ export function toResponseWorkflowDto(workflow: WorkflowInternalResponseDto): Wo }; } -function getSteps(template: NotificationTemplateEntity) { - const steps: StepResponseDto[] = []; - for (const step of template.steps) { - const stepResponseDto = toStepResponseDto(step); - steps.push(stepResponseDto); - } - - return steps; -} - function toMinifiedWorkflowDto(template: NotificationTemplateEntity): WorkflowListResponseDto { const workflowName = template.name || 'Missing Name'; @@ -71,19 +64,6 @@ export function toWorkflowsMinifiedDtos(templates: NotificationTemplateEntity[]) return templates.map(toMinifiedWorkflowDto); } -function toStepResponseDto(persistedStep: NotificationStepEntity): StepResponseDto { - const stepName = persistedStep.name || 'Missing Name'; - - return { - _id: persistedStep._templateId, - slug: buildSlug(stepName, ShortIsPrefixEnum.STEP, persistedStep._templateId), - name: stepName, - stepId: persistedStep.stepId || 'Missing Step Id', - type: persistedStep.template?.type || StepTypeEnum.EMAIL, - issues: persistedStep.issues, - } satisfies StepResponseDto; -} - function buildStepTypeOverview(step: NotificationStepEntity): StepTypeEnum | undefined { return step.template?.type; } diff --git a/apps/api/src/app/workflows-v2/usecases/build-step-data/build-step-data.usecase.ts b/apps/api/src/app/workflows-v2/usecases/build-step-data/build-step-data.usecase.ts index 4d0c49b8808..60b6ffa7480 100644 --- a/apps/api/src/app/workflows-v2/usecases/build-step-data/build-step-data.usecase.ts +++ b/apps/api/src/app/workflows-v2/usecases/build-step-data/build-step-data.usecase.ts @@ -1,10 +1,11 @@ import { BadRequestException, Injectable } from '@nestjs/common'; -import { ControlValuesLevelEnum, StepDataDto, WorkflowOriginEnum } from '@novu/shared'; +import { ControlValuesLevelEnum, ShortIsPrefixEnum, StepDataDto, WorkflowOriginEnum } from '@novu/shared'; import { ControlValuesRepository, NotificationStepEntity, NotificationTemplateEntity } from '@novu/dal'; import { GetWorkflowByIdsUseCase, Instrument, InstrumentUsecase } from '@novu/application-generic'; import { BuildStepDataCommand } from './build-step-data.command'; import { InvalidStepException } from '../../exceptions/invalid-step.exception'; import { BuildAvailableVariableSchemaUsecase } from '../build-variable-schema'; +import { buildSlug } from '../../../shared/helpers/build-slug'; @Injectable() export class BuildStepDataUsecase { @@ -19,15 +20,12 @@ export class BuildStepDataUsecase { const workflow = await this.fetchWorkflow(command); const { currentStep } = await this.loadStepsFromDb(command, workflow); - if ( - currentStep.name === undefined || - !currentStep._templateId || - currentStep.stepId === undefined || - !currentStep.template?.type - ) { + + if (!currentStep._templateId || !currentStep.template?.type) { throw new InvalidStepException(currentStep); } const controlValues = await this.getValues(command, currentStep, workflow._id); + const stepName = currentStep.name || 'Missing Step Name'; return { controls: { @@ -42,9 +40,10 @@ export class BuildStepDataUsecase { stepInternalId: currentStep._templateId, workflow, }), - name: currentStep.name, + name: stepName, + slug: buildSlug(stepName, ShortIsPrefixEnum.STEP, currentStep._templateId), _id: currentStep._templateId, - stepId: currentStep.stepId, + stepId: currentStep.stepId || 'Missing Step Id', type: currentStep.template?.type, origin: workflow.origin || WorkflowOriginEnum.EXTERNAL, workflowId: workflow.triggers[0].identifier, diff --git a/apps/api/src/app/workflows-v2/usecases/get-workflow/get-workflow.usecase.ts b/apps/api/src/app/workflows-v2/usecases/get-workflow/get-workflow.usecase.ts index 3e43fc42204..47fbf3efa4f 100644 --- a/apps/api/src/app/workflows-v2/usecases/get-workflow/get-workflow.usecase.ts +++ b/apps/api/src/app/workflows-v2/usecases/get-workflow/get-workflow.usecase.ts @@ -1,14 +1,25 @@ -import { Injectable } from '@nestjs/common'; +import { Injectable, InternalServerErrorException } from '@nestjs/common'; -import { WorkflowResponseDto } from '@novu/shared'; -import { GetWorkflowByIdsCommand, GetWorkflowByIdsUseCase, InstrumentUsecase } from '@novu/application-generic'; +import { StepDataDto, UserSessionData, WorkflowResponseDto } from '@novu/shared'; +import { + GetWorkflowByIdsCommand, + GetWorkflowByIdsUseCase, + InstrumentUsecase, + WorkflowInternalResponseDto, +} from '@novu/application-generic'; +import { NotificationStepEntity } from '@novu/dal'; import { GetWorkflowCommand } from './get-workflow.command'; import { toResponseWorkflowDto } from '../../mappers/notification-template-mapper'; +import { BuildStepDataUsecase } from '../build-step-data/build-step-data.usecase'; +import { BuildStepDataCommand } from '../build-step-data/build-step-data.command'; @Injectable() export class GetWorkflowUseCase { - constructor(private getWorkflowByIdsUseCase: GetWorkflowByIdsUseCase) {} + constructor( + private getWorkflowByIdsUseCase: GetWorkflowByIdsUseCase, + private buildStepDataUsecase: BuildStepDataUsecase + ) {} @InstrumentUsecase() async execute(command: GetWorkflowCommand): Promise { @@ -21,6 +32,42 @@ export class GetWorkflowUseCase { }) ); - return toResponseWorkflowDto(workflowEntity); + const fullSteps = await this.getFullWorkflowSteps(workflowEntity, command.user); + + return toResponseWorkflowDto(workflowEntity, fullSteps); + } + + private async getFullWorkflowSteps( + workflow: WorkflowInternalResponseDto, + user: UserSessionData + ): Promise { + const stepPromises = workflow.steps.map((step: NotificationStepEntity & { _id: string }) => + this.buildStepForWorkflow(workflow, step, user) + ); + + return Promise.all(stepPromises); + } + + private async buildStepForWorkflow( + workflow: WorkflowInternalResponseDto, + step: NotificationStepEntity & { _id: string }, + user: UserSessionData + ): Promise { + try { + return await this.buildStepDataUsecase.execute( + BuildStepDataCommand.create({ + workflowIdOrInternalId: workflow._id, + stepIdOrInternalId: step._id, + user, + }) + ); + } catch (error) { + throw new InternalServerErrorException({ + message: 'Failed to build workflow step', + workflowId: workflow._id, + stepId: step._id, + error: error.message, + }); + } } } diff --git a/apps/api/src/app/workflows-v2/usecases/sync-to-environment/sync-to-environment.usecase.ts b/apps/api/src/app/workflows-v2/usecases/sync-to-environment/sync-to-environment.usecase.ts index ea95a7b9c80..39013c8ea04 100644 --- a/apps/api/src/app/workflows-v2/usecases/sync-to-environment/sync-to-environment.usecase.ts +++ b/apps/api/src/app/workflows-v2/usecases/sync-to-environment/sync-to-environment.usecase.ts @@ -4,7 +4,6 @@ import { PreferencesTypeEnum, StepCreateDto, StepDataDto, - StepResponseDto, StepUpdateDto, UpdateWorkflowDto, WorkflowCreationSourceEnum, @@ -16,7 +15,6 @@ import { Instrument, InstrumentUsecase } from '@novu/application-generic'; import { SyncToEnvironmentCommand } from './sync-to-environment.command'; import { GetWorkflowCommand, GetWorkflowUseCase } from '../get-workflow'; import { UpsertWorkflowCommand, UpsertWorkflowUseCase } from '../upsert-workflow'; -import { BuildStepDataUsecase } from '../build-step-data'; /** * This usecase is used to sync a workflow from one environment to another. @@ -32,8 +30,7 @@ export class SyncToEnvironmentUseCase { constructor( private getWorkflowUseCase: GetWorkflowUseCase, private preferencesRepository: PreferencesRepository, - private upsertWorkflowUseCase: UpsertWorkflowUseCase, - private buildStepDataUsecase: BuildStepDataUsecase + private upsertWorkflowUseCase: UpsertWorkflowUseCase ) {} @InstrumentUsecase() @@ -46,7 +43,7 @@ export class SyncToEnvironmentUseCase { const preferencesToClone = await this.getWorkflowPreferences(originWorkflow._id, command.user.environmentId); const externalId = originWorkflow.workflowId; const targetWorkflow = await this.findWorkflowInTargetEnvironment(command, externalId); - const workflowDto = await this.buildRequestDto(originWorkflow, preferencesToClone, command, targetWorkflow); + const workflowDto = await this.buildRequestDto(originWorkflow, preferencesToClone, targetWorkflow); return await this.upsertWorkflowUseCase.execute( UpsertWorkflowCommand.create({ @@ -61,14 +58,13 @@ export class SyncToEnvironmentUseCase { private async buildRequestDto( originWorkflow: WorkflowResponseDto, preferencesToClone: PreferencesEntity[], - command: SyncToEnvironmentCommand, targetWorkflow?: WorkflowResponseDto ) { if (targetWorkflow) { - return await this.mapWorkflowToUpdateWorkflowDto(originWorkflow, targetWorkflow, preferencesToClone, command); + return await this.mapWorkflowToUpdateWorkflowDto(originWorkflow, targetWorkflow, preferencesToClone); } - return await this.mapWorkflowToCreateWorkflowDto(originWorkflow, preferencesToClone, command); + return await this.mapWorkflowToCreateWorkflowDto(originWorkflow, preferencesToClone); } @Instrument() @@ -101,8 +97,7 @@ export class SyncToEnvironmentUseCase { @Instrument() private async mapWorkflowToCreateWorkflowDto( originWorkflow: WorkflowResponseDto, - preferences: PreferencesEntity[], - command: SyncToEnvironmentCommand + preferences: PreferencesEntity[] ): Promise { return { workflowId: originWorkflow.workflowId, @@ -111,7 +106,7 @@ export class SyncToEnvironmentUseCase { tags: originWorkflow.tags, description: originWorkflow.description, __source: WorkflowCreationSourceEnum.DASHBOARD, - steps: await this.mapStepsToDto(originWorkflow.steps, command), + steps: await this.mapStepsToCreateOrUpdateDto(originWorkflow.steps), preferences: this.mapPreferences(preferences), }; } @@ -119,9 +114,8 @@ export class SyncToEnvironmentUseCase { @Instrument() private async mapWorkflowToUpdateWorkflowDto( originWorkflow: WorkflowResponseDto, - existingWorkflowInProd: WorkflowResponseDto | undefined, - preferencesToClone: PreferencesEntity[], - command: SyncToEnvironmentCommand + existingTargetEnvWorkflow: WorkflowResponseDto | undefined, + preferencesToClone: PreferencesEntity[] ): Promise { return { workflowId: originWorkflow.workflowId, @@ -129,65 +123,35 @@ export class SyncToEnvironmentUseCase { active: originWorkflow.active, tags: originWorkflow.tags, description: originWorkflow.description, - steps: await this.mapStepsToDto(originWorkflow.steps, command, existingWorkflowInProd?.steps), + steps: await this.mapStepsToCreateOrUpdateDto(originWorkflow.steps, existingTargetEnvWorkflow?.steps), preferences: this.mapPreferences(preferencesToClone), }; } @Instrument() - private async mapStepsToDto( - originSteps: StepResponseDto[], - command: SyncToEnvironmentCommand, - targetWorkflowSteps?: StepResponseDto[] + private async mapStepsToCreateOrUpdateDto( + originSteps: StepDataDto[], + targetEnvSteps?: StepDataDto[] ): Promise<(StepUpdateDto | StepCreateDto)[]> { - const augmentedSteps: (StepUpdateDto | StepCreateDto)[] = []; - for (const originStep of originSteps) { - const idAsOptionalObject = this.prodDbIdAsOptionalObject(originStep, targetWorkflowSteps); - const stepDataDto = await this.buildStepDataUsecase.execute({ - workflowIdOrInternalId: command.workflowIdOrInternalId, - stepIdOrInternalId: originStep._id || originStep.stepId, - user: command.user, - }); - - augmentedSteps.push(this.buildSingleStepRequest(idAsOptionalObject, originStep, stepDataDto)); - } + return originSteps.map((sourceStep) => { + // if we find matching step in target environment, we are updating + const targetEnvStepInternalId = targetEnvSteps?.find( + (targetStep) => targetStep.stepId === sourceStep.stepId + )?._id; - return augmentedSteps; + return this.buildStepCreateOrUpdateDto(sourceStep, targetEnvStepInternalId); + }); } - /* - * If we are updating an existing workflow, we need to map the updated steps to the existing steps - * (!) 'existingWorkflowSteps' are from a different environment than 'steps' - the only thing that doesn't change - * in steps across environments is the stepId (TODO) - */ - private buildSingleStepRequest( - idAsOptionalObject: { _id: string } | {}, - step: StepResponseDto, - stepDataDto: StepDataDto - ): StepUpdateDto | StepCreateDto { + + private buildStepCreateOrUpdateDto(step: StepDataDto, existingInternalId?: string): StepUpdateDto | StepCreateDto { return { - ...idAsOptionalObject, + ...(existingInternalId && { _id: existingInternalId }), name: step.name ?? '', type: step.type, - controlValues: stepDataDto.controls.values ?? {}, + controlValues: step.controls.values ?? {}, }; } - private prodDbIdAsOptionalObject(originStep: StepResponseDto, targetWorkflowSteps?: StepResponseDto[]) { - const prodDatabaseId = this.findDatabaseIdInProdByExternalId(originStep, targetWorkflowSteps); - - if (prodDatabaseId) { - return { - _id: prodDatabaseId, - }; - } else { - return {}; - } - } - - private findDatabaseIdInProdByExternalId(originStep: StepResponseDto, targetWorkflowSteps?: StepResponseDto[]) { - return targetWorkflowSteps?.find((targetStep) => targetStep.stepId === originStep.stepId)?._id; - } - private mapPreferences(preferences: PreferencesEntity[]): { user: WorkflowPreferences | null; workflow: WorkflowPreferences | null; diff --git a/apps/api/src/app/workflows-v2/usecases/upsert-workflow/upsert-workflow.usecase.ts b/apps/api/src/app/workflows-v2/usecases/upsert-workflow/upsert-workflow.usecase.ts index 85c9d9207b8..cf7f109de5f 100644 --- a/apps/api/src/app/workflows-v2/usecases/upsert-workflow/upsert-workflow.usecase.ts +++ b/apps/api/src/app/workflows-v2/usecases/upsert-workflow/upsert-workflow.usecase.ts @@ -28,10 +28,11 @@ import { } from '@novu/application-generic'; import { BadRequestException, Injectable } from '@nestjs/common'; import { UpsertWorkflowCommand } from './upsert-workflow.command'; -import { toResponseWorkflowDto } from '../../mappers/notification-template-mapper'; import { stepTypeToDefaultDashboardControlSchema } from '../../shared'; import { PatchStepUsecase } from '../patch-step-data'; import { PostProcessWorkflowUpdate } from '../post-process-workflow-update'; +import { GetWorkflowUseCase } from '../get-workflow/get-workflow.usecase'; +import { GetWorkflowCommand } from '../get-workflow/get-workflow.command'; @Injectable() export class UpsertWorkflowUseCase { @@ -41,6 +42,7 @@ export class UpsertWorkflowUseCase { private notificationGroupRepository: NotificationGroupRepository, private workflowUpdatePostProcess: PostProcessWorkflowUpdate, private getWorkflowByIdsUseCase: GetWorkflowByIdsUseCase, + private getWorkflowUseCase: GetWorkflowUseCase, private patchStepDataUsecase: PatchStepUsecase ) {} @@ -54,9 +56,15 @@ export class UpsertWorkflowUseCase { workflow: persistedWorkflow, }); await this.persistWorkflow(validatedWorkflowWithIssues); - persistedWorkflow = await this.getWorkflow(validatedWorkflowWithIssues._id, command); - return toResponseWorkflowDto(persistedWorkflow); + const workflow = await this.getWorkflowUseCase.execute( + GetWorkflowCommand.create({ + workflowIdOrInternalId: validatedWorkflowWithIssues._id, + user: command.user, + }) + ); + + return workflow; } @Instrument() diff --git a/apps/api/src/app/workflows-v2/workflow.controller.e2e.ts b/apps/api/src/app/workflows-v2/workflow.controller.e2e.ts index 0f7fad4977f..a9499158120 100644 --- a/apps/api/src/app/workflows-v2/workflow.controller.e2e.ts +++ b/apps/api/src/app/workflows-v2/workflow.controller.e2e.ts @@ -15,8 +15,8 @@ import { slugify, StepContentIssueEnum, StepCreateDto, + StepDataDto, StepIssueEnum, - StepResponseDto, StepTypeEnum, StepUpdateDto, UpdateStepBody, @@ -479,10 +479,11 @@ describe('Workflow Controller E2E API Testing', () => { describe('Promote Workflow Permutations', () => { it('should promote by creating a new workflow in production environment with the same properties', async () => { // Create a workflow in the development environment - const devWorkflow = await createWorkflowAndValidate('-promote-workflow'); + let devWorkflow = await createWorkflowAndValidate('-promote-workflow'); await workflowsClient.patchWorkflowStepData(devWorkflow._id, devWorkflow.steps[0]._id, { controlValues: { vinyl: 'vinyl', color: 'red', band: 'beatles' }, }); + devWorkflow = await getWorkflowRest(devWorkflow._id); // Switch to production environment and get its ID const devEnvironmentId = session.environment._id; @@ -512,10 +513,7 @@ describe('Workflow Controller E2E API Testing', () => { * TODO: this is not true yet, but some ID will remain the same across environments * expect(prodStep.stepId).to.equal(devStep.stepId, 'Step ID should be the same'); */ - const prodValues = await getWorkflowStepControlValues(prodWorkflow, prodStep, prodEnvironmentId); - - const devValues = await getWorkflowStepControlValues(devWorkflow, devStep, devEnvironmentId); - expect(prodValues).to.deep.equal(devValues, 'Step controlValues should match'); + expect(prodStep.controls.values).to.deep.equal(devStep.controls.values, 'Step controlValues should match'); expect(prodStep.name).to.equal(devStep.name, 'Step name should match'); expect(prodStep.type).to.equal(devStep.type, 'Step type should match'); } @@ -889,7 +887,7 @@ describe('Workflow Controller E2E API Testing', () => { return Object.keys(controlValues).length === 0 ? undefined : controlValues; } - function prepareStepsForUpdateWithNewValues(steps: StepResponseDto[]): StepUpdateDto[] { + function prepareStepsForUpdateWithNewValues(steps: StepDataDto[]): StepUpdateDto[] { const newSteps: StepUpdateDto[] = []; for (const step of steps) { const newStep: StepUpdateDto = { @@ -938,11 +936,6 @@ describe('Workflow Controller E2E API Testing', () => { return value; } - async function getWorkflowStepControlValues(workflow: WorkflowResponseDto, step: StepResponseDto, envId: string) { - const value = await getStepData(workflow._id, step._id, envId); - - return value.controls.values; - } async function updateWorkflowAndValidate( workflowRequestId: string, expectedPastUpdatedAt: string, @@ -965,18 +958,15 @@ describe('Workflow Controller E2E API Testing', () => { } async function assertValuesInSteps(workflowCreated: WorkflowResponseDto) { for (const step of workflowCreated.steps) { - const stepDataDto = await getStepData(workflowCreated._id, step._id); - expect(stepDataDto).to.be.ok; - expect(stepDataDto.controls).to.be.ok; - if (stepDataDto.controls) { - expect(stepDataDto.controls.values).to.be.ok; - expect(stepDataDto.controls.dataSchema).to.be.ok; - expect(Object.keys(stepDataDto.controls.dataSchema?.properties || {}).length).to.deep.equal( + expect(step).to.be.ok; + expect(step.controls).to.be.ok; + if (step.controls) { + expect(step.controls.values).to.be.ok; + expect(step.controls.dataSchema).to.be.ok; + expect(Object.keys(step.controls.dataSchema?.properties || {}).length).to.deep.equal( Object.keys(stepTypeToDefaultDashboardControlSchema[step.type].schema.properties).length ); - expect(stepDataDto.controls.uiSchema).to.deep.equal( - stepTypeToDefaultDashboardControlSchema[step.type].uiSchema - ); + expect(step.controls.uiSchema).to.deep.equal(stepTypeToDefaultDashboardControlSchema[step.type].uiSchema); } } } diff --git a/apps/dashboard/src/utils/types.ts b/apps/dashboard/src/utils/types.ts index 8bec79059ea..e3129591f3c 100644 --- a/apps/dashboard/src/utils/types.ts +++ b/apps/dashboard/src/utils/types.ts @@ -1,4 +1,4 @@ -import type { StepResponseDto } from '@novu/shared'; +import type { StepDataDto } from '@novu/shared'; export enum ConnectionStatus { CONNECTED = 'connected', @@ -20,9 +20,7 @@ export type RuntimeIssue = { message: string; }; -export type Step = Pick & { - slug: string; -}; +export type Step = Pick; /** * Omit the `environment` field from the parameters of a function. diff --git a/packages/shared/src/dto/workflows/create-workflow-deprecated.dto.ts b/packages/shared/src/dto/workflows/create-workflow-deprecated.dto.ts index 89a7dd22630..0905914c576 100644 --- a/packages/shared/src/dto/workflows/create-workflow-deprecated.dto.ts +++ b/packages/shared/src/dto/workflows/create-workflow-deprecated.dto.ts @@ -1,4 +1,4 @@ -import { NotificationStepDto } from './workflow.dto'; +import { NotificationStepDto } from './workflow-deprecated.dto'; import { CustomDataType } from '../../types'; interface IPreferenceChannelsDto { diff --git a/packages/shared/src/dto/workflows/get-list-query-params.ts b/packages/shared/src/dto/workflows/get-list-query-params.ts index 0667ad5b6d6..d6b6434bedd 100644 --- a/packages/shared/src/dto/workflows/get-list-query-params.ts +++ b/packages/shared/src/dto/workflows/get-list-query-params.ts @@ -1,4 +1,4 @@ -import { WorkflowResponseDto } from './workflow-response.dto'; +import { WorkflowResponseDto } from './workflow.dto'; import { LimitOffsetPaginationDto } from '../../types'; export class GetListQueryParams extends LimitOffsetPaginationDto { diff --git a/packages/shared/src/dto/workflows/index.ts b/packages/shared/src/dto/workflows/index.ts index ef091691922..8c91d5414b7 100644 --- a/packages/shared/src/dto/workflows/index.ts +++ b/packages/shared/src/dto/workflows/index.ts @@ -6,9 +6,9 @@ export * from './json-schema-dto'; export * from './preview-step-response.dto'; export * from './promote-workflow-dto'; export * from './step-content-issue.enum'; -export * from './step-data.dto'; +export * from './step.dto'; +export * from './workflow.dto'; export * from './update-workflow-deprecated.dto'; -export * from './workflow-response.dto'; +export * from './workflow-deprecated.dto'; export * from './workflow-status-enum'; export * from './workflow-test-data-response-dto'; -export * from './workflow.dto'; diff --git a/packages/shared/src/dto/workflows/preview-step-response.dto.ts b/packages/shared/src/dto/workflows/preview-step-response.dto.ts index dc22f0e7ade..bd25043fd1f 100644 --- a/packages/shared/src/dto/workflows/preview-step-response.dto.ts +++ b/packages/shared/src/dto/workflows/preview-step-response.dto.ts @@ -1,6 +1,5 @@ import { ActionTypeEnum, ChannelTypeEnum } from '../../types'; import { SubscriberDto } from '../subscriber'; -import { ContentIssue } from './workflow-response.dto'; export class RenderOutput {} diff --git a/packages/shared/src/dto/workflows/step-data.dto.ts b/packages/shared/src/dto/workflows/step.dto.ts similarity index 54% rename from packages/shared/src/dto/workflows/step-data.dto.ts rename to packages/shared/src/dto/workflows/step.dto.ts index 21a7e804c4d..5a97fdabdb8 100644 --- a/packages/shared/src/dto/workflows/step-data.dto.ts +++ b/packages/shared/src/dto/workflows/step.dto.ts @@ -1,6 +1,6 @@ import type { JSONSchemaDto } from './json-schema-dto'; -import { StepTypeEnum, WorkflowOriginEnum } from '../../types'; -import { StepIssuesDto } from './workflow-response.dto'; +import { Slug, StepTypeEnum, WorkflowOriginEnum } from '../../types'; +import { StepContentIssueEnum, StepIssueEnum } from './step-content-issue.enum'; export type StepDataDto = { controls: ControlsMetadata; @@ -8,6 +8,7 @@ export type StepDataDto = { stepId: string; _id: string; name: string; + slug: Slug; type: StepTypeEnum; origin: WorkflowOriginEnum; workflowId: string; @@ -15,6 +16,44 @@ export type StepDataDto = { issues?: StepIssuesDto; }; +export type StepUpdateDto = StepCreateDto & { + _id: string; +}; + +export type StepCreateDto = StepDto & { + controlValues?: Record; +}; + +export type PatchStepDataDto = { + name?: string; + controlValues?: Record; +}; + +export type StepDto = { + name: string; + type: StepTypeEnum; +}; + +// eslint-disable-next-line @typescript-eslint/naming-convention +interface Issue { + issueType: T; + variableName?: string; + message: string; +} + +export class StepIssuesDto { + body?: Record; + controls?: Record; +} + +export type StepCreateAndUpdateKeys = keyof StepCreateDto | keyof StepUpdateDto; + +// eslint-disable-next-line @typescript-eslint/naming-convention +export interface ContentIssue extends Issue {} + +// eslint-disable-next-line @typescript-eslint/naming-convention +export interface StepIssue extends Issue {} + export enum UiSchemaGroupEnum { IN_APP = 'IN_APP', EMAIL = 'EMAIL', diff --git a/packages/shared/src/dto/workflows/update-workflow-deprecated.dto.ts b/packages/shared/src/dto/workflows/update-workflow-deprecated.dto.ts index b7afba6add4..ff13f778c22 100644 --- a/packages/shared/src/dto/workflows/update-workflow-deprecated.dto.ts +++ b/packages/shared/src/dto/workflows/update-workflow-deprecated.dto.ts @@ -1,4 +1,4 @@ -import { NotificationStepDto } from './workflow.dto'; +import { NotificationStepDto } from './workflow-deprecated.dto'; import { CustomDataType } from '../../types'; /** diff --git a/packages/shared/src/dto/workflows/workflow-deprecated.dto.ts b/packages/shared/src/dto/workflows/workflow-deprecated.dto.ts new file mode 100644 index 00000000000..7be61ed15e8 --- /dev/null +++ b/packages/shared/src/dto/workflows/workflow-deprecated.dto.ts @@ -0,0 +1,35 @@ +import { IWorkflowStepMetadata } from '../../entities/step'; +import { BuilderFieldType, BuilderGroupValues, FilterParts } from '../../types'; +import { MessageTemplateDto } from '../message-template'; + +/** + * @deprecated use DTOs from step.dto.ts + */ +export class StepVariantDto { + id?: string; + _id?: string; + name?: string; + uuid?: string; + _templateId?: string; + template?: MessageTemplateDto; + filters?: { + isNegated?: boolean; + type?: BuilderFieldType; + value?: BuilderGroupValues; + children?: FilterParts[]; + }[]; + active?: boolean; + shouldStopOnFail?: boolean; + replyCallback?: { + active: boolean; + url?: string; + }; + metadata?: IWorkflowStepMetadata; +} + +/** + * @deprecated use DTOs from step.dto.ts + */ +export class NotificationStepDto extends StepVariantDto { + variants?: StepVariantDto[]; +} diff --git a/packages/shared/src/dto/workflows/workflow-response.dto.ts b/packages/shared/src/dto/workflows/workflow-response.dto.ts deleted file mode 100644 index a530411a99d..00000000000 --- a/packages/shared/src/dto/workflows/workflow-response.dto.ts +++ /dev/null @@ -1,138 +0,0 @@ -import type { JSONSchemaDto } from './json-schema-dto'; -import { Slug } from '../../types/utils'; -import { WorkflowCreationSourceEnum, StepTypeEnum, WorkflowOriginEnum, WorkflowPreferences } from '../../types'; -import { WorkflowStatusEnum } from './workflow-status-enum'; -import { StepContentIssueEnum, StepIssueEnum } from './step-content-issue.enum'; - -export class ControlsSchema { - schema: JSONSchemaDto; -} -export type StepCreateAndUpdateKeys = keyof StepCreateDto | keyof StepUpdateDto; - -export class StepIssuesDto { - body?: Record; - controls?: Record; -} -// eslint-disable-next-line @typescript-eslint/naming-convention -interface Issue { - issueType: T; - variableName?: string; - message: string; -} - -// eslint-disable-next-line @typescript-eslint/naming-convention -export interface ContentIssue extends Issue {} - -// eslint-disable-next-line @typescript-eslint/naming-convention -export interface StepIssue extends Issue {} - -export type PatchStepDataDto = { - name?: string; - controlValues?: Record; -}; - -export type PatchWorkflowDto = { - active?: boolean; - name?: string; - description?: string; - tags?: string[]; -}; - -export type StepResponseDto = StepDto & { - _id: string; - slug: Slug; - stepId: string; - issues?: StepIssuesDto; -}; - -export type StepUpdateDto = StepCreateDto & { - _id: string; -}; - -export type StepCreateDto = StepDto & { - /** - * @deprecated This field is deprecated and will be removed in future versions, use the patch step data. - */ - controlValues?: Record; -}; - -export type ListWorkflowResponse = { - workflows: WorkflowListResponseDto[]; - totalCount: number; -}; - -export type WorkflowListResponseDto = Pick< - WorkflowResponseDto, - 'name' | 'tags' | 'updatedAt' | 'createdAt' | '_id' | 'workflowId' | 'slug' | 'status' | 'origin' -> & { - stepTypeOverviews: StepTypeEnum[]; -}; - -export type StepDto = { - name: string; - type: StepTypeEnum; -}; - -export type WorkflowCommonsFields = { - name: string; - description?: string; - tags?: string[]; - active?: boolean; -}; - -export type PreferencesResponseDto = { - user: WorkflowPreferences | null; - default: WorkflowPreferences; -}; - -export type PreferencesRequestDto = { - user: WorkflowPreferences | null; - workflow?: WorkflowPreferences | null; -}; - -export type WorkflowResponseDto = WorkflowCommonsFields & { - _id: string; - workflowId: string; - slug: Slug; - updatedAt: string; - createdAt: string; - steps: StepResponseDto[]; - origin: WorkflowOriginEnum; - preferences: PreferencesResponseDto; - status: WorkflowStatusEnum; - issues?: Record; -}; -export type WorkflowCreateAndUpdateKeys = keyof CreateWorkflowDto | keyof UpdateWorkflowDto; -export class RuntimeIssue { - issueType: WorkflowIssueTypeEnum; - variableName?: string; - message: string; -} -export enum WorkflowIssueTypeEnum { - MISSING_VALUE = 'MISSING_VALUE', - MAX_LENGTH_ACCESSED = 'MAX_LENGTH_ACCESSED', - WORKFLOW_ID_ALREADY_EXISTS = 'WORKFLOW_ID_ALREADY_EXISTS', - DUPLICATED_VALUE = 'DUPLICATED_VALUE', - LIMIT_REACHED = 'LIMIT_REACHED', -} - -export type CreateWorkflowDto = WorkflowCommonsFields & { - workflowId: string; - - steps: StepCreateDto[]; - - __source: WorkflowCreationSourceEnum; - - preferences?: PreferencesRequestDto; -}; - -export type UpdateWorkflowDto = WorkflowCommonsFields & { - /** - * We allow to update workflow id only for code first workflows - */ - workflowId?: string; - - steps: (StepCreateDto | StepUpdateDto)[]; - - preferences: PreferencesRequestDto; -}; diff --git a/packages/shared/src/dto/workflows/workflow.dto.ts b/packages/shared/src/dto/workflows/workflow.dto.ts index 68c48c372d6..d71cff367f5 100644 --- a/packages/shared/src/dto/workflows/workflow.dto.ts +++ b/packages/shared/src/dto/workflows/workflow.dto.ts @@ -1,34 +1,96 @@ -import { IWorkflowStepMetadata } from '../../entities/step'; -import { BuilderFieldType, BuilderGroupValues, FilterParts } from '../../types'; -import { MessageTemplateDto } from '../message-template'; -import { UpdateWorkflowDto, StepCreateDto, StepUpdateDto } from './workflow-response.dto'; - -export class StepVariantDto { - id?: string; - _id?: string; +import type { JSONSchemaDto } from './json-schema-dto'; +import { Slug } from '../../types/utils'; +import { WorkflowCreationSourceEnum, StepTypeEnum, WorkflowOriginEnum, WorkflowPreferences } from '../../types'; +import { WorkflowStatusEnum } from './workflow-status-enum'; +import { StepCreateDto, StepUpdateDto, StepDataDto } from './step.dto'; + +export class ControlsSchema { + schema: JSONSchemaDto; +} + +export type PatchWorkflowDto = { + active?: boolean; name?: string; - uuid?: string; - _templateId?: string; - template?: MessageTemplateDto; - filters?: { - isNegated?: boolean; - type?: BuilderFieldType; - value?: BuilderGroupValues; - children?: FilterParts[]; - }[]; + description?: string; + tags?: string[]; +}; + +export type ListWorkflowResponse = { + workflows: WorkflowListResponseDto[]; + totalCount: number; +}; + +export type WorkflowListResponseDto = Pick< + WorkflowResponseDto, + 'name' | 'tags' | 'updatedAt' | 'createdAt' | '_id' | 'workflowId' | 'slug' | 'status' | 'origin' +> & { + stepTypeOverviews: StepTypeEnum[]; +}; + +export type WorkflowCommonsFields = { + name: string; + description?: string; + tags?: string[]; active?: boolean; - shouldStopOnFail?: boolean; - replyCallback?: { - active: boolean; - url?: string; - }; - metadata?: IWorkflowStepMetadata; -} +}; + +export type PreferencesResponseDto = { + user: WorkflowPreferences | null; + default: WorkflowPreferences; +}; -export class NotificationStepDto extends StepVariantDto { - variants?: StepVariantDto[]; +export type PreferencesRequestDto = { + user: WorkflowPreferences | null; + workflow?: WorkflowPreferences | null; +}; + +export type WorkflowResponseDto = WorkflowCommonsFields & { + _id: string; + workflowId: string; + slug: Slug; + updatedAt: string; + createdAt: string; + steps: StepDataDto[]; + origin: WorkflowOriginEnum; + preferences: PreferencesResponseDto; + status: WorkflowStatusEnum; + issues?: Record; +}; +export type WorkflowCreateAndUpdateKeys = keyof CreateWorkflowDto | keyof UpdateWorkflowDto; +export class RuntimeIssue { + issueType: WorkflowIssueTypeEnum; + variableName?: string; + message: string; +} +export enum WorkflowIssueTypeEnum { + MISSING_VALUE = 'MISSING_VALUE', + MAX_LENGTH_ACCESSED = 'MAX_LENGTH_ACCESSED', + WORKFLOW_ID_ALREADY_EXISTS = 'WORKFLOW_ID_ALREADY_EXISTS', + DUPLICATED_VALUE = 'DUPLICATED_VALUE', + LIMIT_REACHED = 'LIMIT_REACHED', } +export type CreateWorkflowDto = WorkflowCommonsFields & { + workflowId: string; + + steps: StepCreateDto[]; + + __source: WorkflowCreationSourceEnum; + + preferences?: PreferencesRequestDto; +}; + +export type UpdateWorkflowDto = WorkflowCommonsFields & { + /** + * We allow to update workflow id only for code first workflows + */ + workflowId?: string; + + steps: (StepCreateDto | StepUpdateDto)[]; + + preferences: PreferencesRequestDto; +}; + export type UpsertWorkflowBody = Omit & { steps: UpsertStepBody[]; }; diff --git a/packages/shared/src/entities/notification-template/notification-template.interface.ts b/packages/shared/src/entities/notification-template/notification-template.interface.ts index 26e1362f335..52d7cf656d0 100644 --- a/packages/shared/src/entities/notification-template/notification-template.interface.ts +++ b/packages/shared/src/entities/notification-template/notification-template.interface.ts @@ -1,6 +1,6 @@ import type { BuilderFieldType, BuilderGroupValues, CustomDataType, FilterParts, WorkflowTypeEnum } from '../../types'; import { JSONSchemaDto } from '../../dto/workflows'; -import type { ContentIssue, StepIssue } from '../../dto/workflows/workflow-response.dto'; +import type { ContentIssue, StepIssue } from '../../dto/workflows/step.dto'; import { ControlSchemas, IMessageTemplate } from '../message-template'; import { INotificationGroup } from '../notification-group'; import { INotificationBridgeTrigger, INotificationTrigger } from '../notification-trigger';