Skip to content

Commit

Permalink
feat(api): add tags issues (#6957)
Browse files Browse the repository at this point in the history
  • Loading branch information
djabarovgeorge authored Nov 13, 2024
1 parent bc9a0d3 commit 8590fa2
Show file tree
Hide file tree
Showing 6 changed files with 65 additions and 20 deletions.
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { EnvironmentWithUserObjectCommand, GetPreferencesResponseDto } from '@novu/application-generic';
import { ControlValuesEntity, NotificationTemplateEntity } from '@novu/dal';

export class ValidateWorkflowCommand extends EnvironmentWithUserObjectCommand {
export class ProcessWorkflowIssuesCommand extends EnvironmentWithUserObjectCommand {
workflow: NotificationTemplateEntity;
preferences?: GetPreferencesResponseDto;
stepIdToControlValuesMap: { [p: string]: ControlValuesEntity };
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,18 +15,18 @@ import {
NotificationTemplateRepository,
} from '@novu/dal';
import { Injectable } from '@nestjs/common';
import { ValidateWorkflowCommand } from './validate-workflow.command';
import { WorkflowNotFoundException } from '../../exceptions/workflow-not-found-exception';
import { ProcessWorkflowIssuesCommand } from './process-workflow-issues.command';
import { ValidateControlValuesAndConstructPassableStructureUsecase } from '../validate-control-values/build-default-control-values-usecase.service';
import { WorkflowNotFoundException } from '../../exceptions/workflow-not-found-exception';

@Injectable()
export class ValidateAndPersistWorkflowIssuesUsecase {
export class ProcessWorkflowIssuesUsecase {
constructor(
private notificationTemplateRepository: NotificationTemplateRepository,
private buildDefaultControlValuesUsecase: ValidateControlValuesAndConstructPassableStructureUsecase
) {}

async execute(command: ValidateWorkflowCommand): Promise<NotificationTemplateEntity> {
async execute(command: ProcessWorkflowIssuesCommand): Promise<NotificationTemplateEntity> {
const workflowIssues = await this.validateWorkflow(command);
const stepIssues = this.validateSteps(command.workflow.steps, command.stepIdToControlValuesMap);
const workflowWithIssues = this.updateIssuesOnWorkflow(command.workflow, workflowIssues, stepIssues);
Expand All @@ -35,7 +35,7 @@ export class ValidateAndPersistWorkflowIssuesUsecase {
return await this.getWorkflow(command);
}

private async persistWorkflow(command: ValidateWorkflowCommand, workflowWithIssues: NotificationTemplateEntity) {
private async persistWorkflow(command: ProcessWorkflowIssuesCommand, workflowWithIssues: NotificationTemplateEntity) {
const isWorkflowCompleteAndValid = this.isWorkflowCompleteAndValid(workflowWithIssues);
const status = this.calculateStatus(isWorkflowCompleteAndValid, workflowWithIssues);
await this.notificationTemplateRepository.update(
Expand Down Expand Up @@ -81,7 +81,7 @@ export class ValidateAndPersistWorkflowIssuesUsecase {
return issue.body && Object.keys(issue.body).length > 0;
}

private async getWorkflow(command: ValidateWorkflowCommand) {
private async getWorkflow(command: ProcessWorkflowIssuesCommand) {
const entity = await this.notificationTemplateRepository.findById(command.workflow._id, command.user.environmentId);
if (entity == null) {
throw new WorkflowNotFoundException(command.workflow._id);
Expand Down Expand Up @@ -123,28 +123,70 @@ export class ValidateAndPersistWorkflowIssuesUsecase {
}
}
private async validateWorkflow(
command: ValidateWorkflowCommand
command: ProcessWorkflowIssuesCommand
): Promise<Record<keyof WorkflowResponseDto, RuntimeIssue[]>> {
// @ts-ignore
const issues: Record<keyof WorkflowResponseDto, RuntimeIssue[]> = {};
await this.addTriggerIdentifierNotUniqueIfApplicable(command, issues);
this.addNameMissingIfApplicable(command, issues);
this.addDescriptionTooLongIfApplicable(command, issues);
this.addTagsIssues(command, issues);

return issues;
}

private addNameMissingIfApplicable(
command: ValidateWorkflowCommand,
command: ProcessWorkflowIssuesCommand,
issues: Record<keyof WorkflowResponseDto, RuntimeIssue[]>
) {
if (!command.workflow.name || command.workflow.name.trim() === '') {
// eslint-disable-next-line no-param-reassign
issues.name = [{ issueType: WorkflowIssueTypeEnum.MISSING_VALUE, message: 'Name is missing' }];
}
}

private addTagsIssues(
command: ProcessWorkflowIssuesCommand,
issues: Record<keyof WorkflowResponseDto, RuntimeIssue[]>
) {
const tags = command.workflow.tags?.map((tag) => tag.trim());

if (!tags.length) {
return;
}

const tagsIssues: RuntimeIssue[] = [];

const duplicatedTags = tags.filter((tag, index) => tags.indexOf(tag) !== index);
const hasDuplications = duplicatedTags.length > 0;
if (hasDuplications) {
tagsIssues.push({
issueType: WorkflowIssueTypeEnum.DUPLICATED_VALUE,
message: `Duplicated tags: ${duplicatedTags.join(', ')}`,
});
}

const hasEmptyTags = tags?.some((tag) => !tag || tag === '');
if (hasEmptyTags) {
tagsIssues.push({ issueType: WorkflowIssueTypeEnum.MISSING_VALUE, message: 'Empty tag value' });
}

const exceedsMaxLength = tags?.some((tag) => tag.length > 8);
if (exceedsMaxLength) {
tagsIssues.push({
issueType: WorkflowIssueTypeEnum.LIMIT_REACHED,
message: 'Exceeded the 8 tag limit',
});
}

if (tagsIssues.length > 0) {
// eslint-disable-next-line no-param-reassign
issues.tags = tagsIssues;
}
}

private addDescriptionTooLongIfApplicable(
command: ValidateWorkflowCommand,
command: ProcessWorkflowIssuesCommand,
issues: Record<keyof WorkflowResponseDto, RuntimeIssue[]>
) {
if (command.workflow.description && command.workflow.description.length > 160) {
Expand All @@ -154,8 +196,9 @@ export class ValidateAndPersistWorkflowIssuesUsecase {
];
}
}

private async addTriggerIdentifierNotUniqueIfApplicable(
command: ValidateWorkflowCommand,
command: ProcessWorkflowIssuesCommand,
issues: Record<keyof WorkflowResponseDto, RuntimeIssue[]>
) {
const findAllByTriggerIdentifier = await this.notificationTemplateRepository.findAllByTriggerIdentifier(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -44,8 +44,8 @@ import { UpsertWorkflowCommand } from './upsert-workflow.command';
import { StepUpsertMechanismFailedMissingIdException } from '../../exceptions/step-upsert-mechanism-failed-missing-id.exception';
import { toResponseWorkflowDto } from '../../mappers/notification-template-mapper';
import { stepTypeToDefaultDashboardControlSchema } from '../../shared';
import { ValidateAndPersistWorkflowIssuesUsecase } from './validate-and-persist-workflow-issues.usecase';
import { ValidateWorkflowCommand } from './validate-workflow.command';
import { ProcessWorkflowIssuesUsecase } from '../process-workflow-issues/process-workflow-issues.usecase';
import { ProcessWorkflowIssuesCommand } from '../process-workflow-issues/process-workflow-issues.command';

function buildUpsertControlValuesCommand(
command: UpsertWorkflowCommand,
Expand All @@ -70,7 +70,7 @@ export class UpsertWorkflowUseCase {
private notificationGroupRepository: NotificationGroupRepository,
private upsertPreferencesUsecase: UpsertPreferences,
private upsertControlValuesUseCase: UpsertControlValuesUseCase,
private validateWorkflowUsecase: ValidateAndPersistWorkflowIssuesUsecase,
private processWorkflowIssuesUsecase: ProcessWorkflowIssuesUsecase,
private getWorkflowByIdsUseCase: GetWorkflowByIdsUseCase,
private getPreferencesUseCase: GetPreferences
) {}
Expand All @@ -80,16 +80,16 @@ export class UpsertWorkflowUseCase {
const workflow = await this.createOrUpdateWorkflow(workflowForUpdate, command);
const stepIdToControlValuesMap = await this.upsertControlValues(workflow, command);
const preferences = await this.upsertPreference(command, workflow);
const validatedWorkflowWithIssues = await this.validateWorkflowUsecase.execute(
ValidateWorkflowCommand.create({
const workflowIssues = await this.processWorkflowIssuesUsecase.execute(
ProcessWorkflowIssuesCommand.create({
user: command.user,
workflow,
preferences,
stepIdToControlValuesMap,
})
);

return toResponseWorkflowDto(validatedWorkflowWithIssues, preferences);
return toResponseWorkflowDto(workflowIssues, preferences);
}

private async queryWorkflow(command: UpsertWorkflowCommand): Promise<NotificationTemplateEntity | null> {
Expand Down
4 changes: 2 additions & 2 deletions apps/api/src/app/workflows-v2/workflow.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ import { ExtractDefaultsUsecase } from './usecases/get-default-values-from-schem
import { HydrateEmailSchemaUseCase } from '../environments-v1/usecases/output-renderers';
import { WorkflowTestDataUseCase } from './usecases/test-data/test-data.usecase';
import { GetStepDataUsecase } from './usecases/get-step-schema/get-step-data.usecase';
import { ValidateAndPersistWorkflowIssuesUsecase } from './usecases/upsert-workflow/validate-and-persist-workflow-issues.usecase';
import { ProcessWorkflowIssuesUsecase } from './usecases/process-workflow-issues/process-workflow-issues.usecase';
import { BuildPayloadNestedStructureUsecase } from './usecases/placeholder-enrichment/buildPayloadNestedStructureUsecase';
import { GetWorkflowUseCase } from './usecases/get-workflow/get-workflow.usecase';
import { BuildDefaultPayloadUseCase } from './usecases/build-payload-from-placeholder';
Expand Down Expand Up @@ -54,7 +54,7 @@ import { BuildAvailableVariableSchemaUsecase } from './usecases/get-step-schema/
WorkflowTestDataUseCase,
GetWorkflowUseCase,
HydrateEmailSchemaUseCase,
ValidateAndPersistWorkflowIssuesUsecase,
ProcessWorkflowIssuesUsecase,
BuildDefaultPayloadUseCase,
ValidateControlValuesAndConstructPassableStructureUsecase,
BuildAvailableVariableSchemaUsecase,
Expand Down
2 changes: 1 addition & 1 deletion apps/dashboard/src/components/workflow-row.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -95,7 +95,7 @@ export const WorkflowRow = ({ workflow }: WorkflowRowProps) => {
</TableCell>
</TooltipTrigger>
<TooltipPortal>
<TooltipContent>{new Date(workflow.updatedAt).toUTCString()}</TooltipContent>
<TooltipContent align="start">{new Date(workflow.updatedAt).toUTCString()}</TooltipContent>
</TooltipPortal>
</Tooltip>
</TooltipProvider>
Expand Down
2 changes: 2 additions & 0 deletions packages/shared/src/dto/workflows/workflow-response-dto.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,4 +26,6 @@ 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',
}

0 comments on commit 8590fa2

Please sign in to comment.