Skip to content

Commit

Permalink
feat(api): refactor
Browse files Browse the repository at this point in the history
  • Loading branch information
djabarovgeorge committed Nov 19, 2024
1 parent 3fd1c58 commit 4a3d9da
Show file tree
Hide file tree
Showing 6 changed files with 17 additions and 111 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -56,9 +56,7 @@ export class GeneratePreviewUsecase {
controlDataSchema: stepData.controls.dataSchema,
variableSchema: stepData.variables,
previewPayloadFromDto: dto.previewPayload,
environmentId: user.environmentId,
organizationId: user.organizationId,
userId: user._id,
user,
});
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -53,9 +53,7 @@ export class OverloadContentDataOnWorkflowUseCase {
controlDataSchema: controls.schema,
controlValues,
variableSchema: jsonSchemaDto,
environmentId: user.environmentId,
organizationId: user.organizationId,
userId: user._id,
user,
});
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,6 @@ export class PostProcessWorkflowUpdate {
constructor(
private notificationTemplateRepository: NotificationTemplateRepository,
private controlValuesRepository: ControlValuesRepository,
private organizationRepository: OrganizationRepository,
private overloadContentDataOnWorkflowUseCase: OverloadContentDataOnWorkflowUseCase
) {}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import { EnvironmentWithUserCommand } from '@novu/application-generic';
import { EnvironmentWithUserObjectCommand } from '@novu/application-generic';
import { JSONSchemaDto, PreviewPayload, StepTypeEnum } from '@novu/shared';

// eslint-disable-next-line @typescript-eslint/naming-convention
export interface PrepareAndValidateContentCommand extends EnvironmentWithUserCommand {
export interface PrepareAndValidateContentCommand extends EnvironmentWithUserObjectCommand {
controlValues: Record<string, unknown>;
controlDataSchema: JSONSchemaDto;
variableSchema: JSONSchemaDto;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,15 +1,13 @@
import { Injectable } from '@nestjs/common';
import {
ApiServiceLevelEnum,
ContentIssue,
DigestUnitEnum,
JSONSchemaDto,
PreviewPayload,
StepContentIssueEnum,
StepTypeEnum,
UserSessionData,
} from '@novu/shared';
import { merge } from 'lodash';
import { OrganizationEntity, OrganizationRepository } from '@novu/dal';
import { PrepareAndValidateContentCommand } from './prepare-and-validate-content.command';
import { mergeObjects } from '../../../util/jsonUtils';
import { findMissingKeys } from '../../../util/utils';
Expand All @@ -18,9 +16,7 @@ import { ValidatedPlaceholderAggregation, ValidatePlaceholderUsecase } from '../
import { CollectPlaceholderWithDefaultsUsecase, PlaceholderAggregation } from '../collect-placeholders';
import { ExtractDefaultValuesFromSchemaUsecase } from '../../extract-default-values-from-schema';
import { ValidatedContentResponse } from './validated-content.response';

const MAX_DELAY_DAYS_FREE_TIER = 30;
const MAX_DELAY_DAYS_BUSINESS_TIER = 90;
import { ValidateControlByTierUsecase } from '../validateControlByTier/validate-control-by-tier.usecase';

/**
* Validates and prepares workflow step content by collecting placeholders,
Expand All @@ -36,7 +32,7 @@ export class PrepareAndValidateContentUsecase {
private validatePlaceholdersUseCase: ValidatePlaceholderUsecase,
private collectPlaceholderWithDefaultsUsecase: CollectPlaceholderWithDefaultsUsecase,
private extractDefaultsFromSchemaUseCase: ExtractDefaultValuesFromSchemaUsecase,
private organizationRepository: OrganizationRepository
private validateControlByTierUsecase: ValidateControlByTierUsecase
) {}

async execute(command: PrepareAndValidateContentCommand): Promise<ValidatedContentResponse> {
Expand All @@ -58,7 +54,7 @@ export class PrepareAndValidateContentUsecase {
defaultControlValues,
command.controlValues,
controlValueToValidPlaceholders,
command.organizationId,
command.user,
command.stepType
);

Expand Down Expand Up @@ -150,17 +146,19 @@ export class PrepareAndValidateContentUsecase {
defaultControlValues: Record<string, unknown>,
userProvidedValues: Record<string, unknown>,
valueToPlaceholders: Record<string, ValidatedPlaceholderAggregation>,
organizationId: string,
user: UserSessionData,
stepType?: StepTypeEnum
): Promise<Record<string, ContentIssue[]>> {
let finalIssues: Record<string, ContentIssue[]> = {};
finalIssues = mergeObjects(finalIssues, this.computeIllegalVariablesIssues(valueToPlaceholders));
finalIssues = mergeObjects(finalIssues, this.getMissingInPayload(providedPayload, valueToPlaceholders, payload));
finalIssues = mergeObjects(finalIssues, this.computeMissingControlValue(defaultControlValues, userProvidedValues));
finalIssues = mergeObjects(
finalIssues,
await this.computeTierLimitExceeded(defaultControlValues, organizationId, stepType)
);
const tierIssues = await this.validateControlByTierUsecase.execute({
controlValues: defaultControlValues,
user,
stepType,
});
finalIssues = mergeObjects(finalIssues, tierIssues);

return finalIssues;
}
Expand Down Expand Up @@ -233,93 +231,4 @@ export class PrepareAndValidateContentUsecase {

return result;
}

private async computeTierLimitExceeded(
defaultControlValues: Record<string, unknown>,
organizationId: string,
stepType?: StepTypeEnum
) {
const issues: Record<string, ContentIssue[]> = {};
let organization: OrganizationEntity | null = null;
const controlValues = defaultControlValues;
if (
controlValues.unit &&
controlValues.amount &&
this.isNumber(controlValues.amount) &&
this.isValidDigestUnit(controlValues.unit)
) {
organization = await this.getOrganization(organization, organizationId);

const delayInDays = this.calculateDaysFromUnit(controlValues.amount, controlValues.unit);

const tier = organization?.apiServiceLevel;
if (tier === undefined || tier === ApiServiceLevelEnum.BUSINESS || tier === ApiServiceLevelEnum.ENTERPRISE) {
if (delayInDays > MAX_DELAY_DAYS_BUSINESS_TIER) {
issues.tier = [
...(issues.tier || []),
{
issueType: StepContentIssueEnum.TIER_LIMIT_EXCEEDED,
message:
`The maximum delay allowed is ${MAX_DELAY_DAYS_BUSINESS_TIER} days.` +
'Please contact our support team to discuss extending this limit for your use case.',
},
];
}
}

if (tier === ApiServiceLevelEnum.FREE) {
if (delayInDays > MAX_DELAY_DAYS_FREE_TIER) {
issues.tier = [
...(issues.tier || []),
{
issueType: StepContentIssueEnum.TIER_LIMIT_EXCEEDED,
message:
`The maximum delay allowed is ${MAX_DELAY_DAYS_FREE_TIER} days.` +
'Please contact our support team to discuss extending this limit for your use case.',
},
];
}
}
}

return issues;
}

private isValidDigestUnit(unit: unknown): unit is DigestUnitEnum {
return Object.values(DigestUnitEnum).includes(unit as DigestUnitEnum);
}

private isNumber(value: unknown): value is number {
return !Number.isNaN(Number(value));
}

private calculateDaysFromUnit(amount: number, unit: DigestUnitEnum): number {
switch (unit) {
case DigestUnitEnum.SECONDS:
return amount / (24 * 60 * 60);
case DigestUnitEnum.MINUTES:
return amount / (24 * 60);
case DigestUnitEnum.HOURS:
return amount / 24;
case DigestUnitEnum.DAYS:
return amount;
case DigestUnitEnum.WEEKS:
return amount * 7;
case DigestUnitEnum.MONTHS:
return amount * 30; // Using 30 days as an approximation for a month
default:
return 0;
}
}

private async getOrganization(
organization: OrganizationEntity | null,
organizationId: string
): Promise<OrganizationEntity | null> {
if (organization === null) {
return await this.organizationRepository.findById(organizationId);
}

return organization;
}
}
2 changes: 2 additions & 0 deletions apps/api/src/app/workflows-v2/workflow.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ import { HydrateEmailSchemaUseCase } from '../environments-v1/usecases/output-re
import { OverloadContentDataOnWorkflowUseCase } from './usecases/overload-content-data';
import { PatchWorkflowUsecase } from './usecases/patch-workflow';
import { PatchStepUsecase } from './usecases/patch-step-data/patch-step.usecase';
import { ValidateControlByTierUsecase } from './usecases/validate-content/validateControlByTier/validate-control-by-tier.usecase';

@Module({
imports: [SharedModule, MessageTemplateModule, ChangeModule, AuthModule, BridgeModule, IntegrationModule],
Expand Down Expand Up @@ -67,6 +68,7 @@ import { PatchStepUsecase } from './usecases/patch-step-data/patch-step.usecase'
PostProcessWorkflowUpdate,
OverloadContentDataOnWorkflowUseCase,
PatchWorkflowUsecase,
ValidateControlByTierUsecase,
],
})
export class WorkflowModule implements NestModule {
Expand Down

0 comments on commit 4a3d9da

Please sign in to comment.