Skip to content

Commit

Permalink
fix(api-service): Enhance Feed DTO (#7501)
Browse files Browse the repository at this point in the history
  • Loading branch information
tatarco authored Jan 15, 2025
1 parent fe98e77 commit c31f2c0
Show file tree
Hide file tree
Showing 14 changed files with 654 additions and 289 deletions.
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
import { forwardRef, Inject, Injectable, Logger, NotFoundException } from '@nestjs/common';
import {
ChangeRepository,
NotificationTemplateEntity,
NotificationTemplateRepository,
EnvironmentRepository,
MessageTemplateRepository,
NotificationStepEntity,
NotificationGroupRepository,
StepVariantEntity,
EnvironmentRepository,
NotificationStepData,
NotificationStepEntity,
NotificationTemplateEntity,
NotificationTemplateRepository,
} from '@novu/dal';
import {
buildWorkflowPreferencesFromPreferenceChannels,
Expand All @@ -20,9 +20,9 @@ import {
buildGroupedBlueprintsKey,
buildNotificationTemplateIdentifierKey,
buildNotificationTemplateKey,
InvalidateCacheService,
DeletePreferencesUseCase,
DeletePreferencesCommand,
DeletePreferencesUseCase,
InvalidateCacheService,
UpsertPreferences,
UpsertUserWorkflowPreferencesCommand,
UpsertWorkflowPreferencesCommand,
Expand Down Expand Up @@ -85,7 +85,7 @@ export class PromoteNotificationTemplateChange {
// eslint-disable-next-line no-param-reassign
step.variants = step.variants
?.map(mapNewVariantItem)
.filter((variant): variant is StepVariantEntity => variant !== undefined);
.filter((variant): variant is NotificationStepData => variant !== undefined);
}

if (!oldMessage) {
Expand All @@ -102,7 +102,7 @@ export class PromoteNotificationTemplateChange {
return step;
};

const mapNewVariantItem = (step: StepVariantEntity) => {
const mapNewVariantItem = (step: NotificationStepData) => {
const oldMessage = messages.find((message) => {
return message._parentId === step._templateId;
});
Expand Down
196 changes: 171 additions & 25 deletions apps/api/src/app/notifications/dtos/activities-response.dto.ts
Original file line number Diff line number Diff line change
@@ -1,20 +1,74 @@
import { ApiProperty, ApiPropertyOptional } from '@nestjs/swagger';
import {
DaysEnum,
DigestTypeEnum,
DigestUnitEnum,
ExecutionDetailsSourceEnum,
ExecutionDetailsStatusEnum,
MessageTemplateDto,
MonthlyTypeEnum,
OrdinalEnum,
OrdinalValueEnum,
ProvidersIdEnum,
ProvidersIdEnumConst,
StepTypeEnum,
TriggerTypeEnum,
} from '@novu/shared';
import { StepFilter } from '@novu/dal';
import { IsEnum, IsString } from 'class-validator';
import { IsArray, IsEnum, IsNumber, IsOptional, IsString } from 'class-validator';
import { StepFilterDto } from '../../shared/dtos/step-filter-dto';

// Base DTO for Digest Metadata
export class DigestBaseMetadataDto {
export class DigestTimedConfigDto {
@ApiPropertyOptional({ description: 'Time at which the digest is triggered' })
@IsOptional()
@IsString()
atTime?: string;

@ApiPropertyOptional({ description: 'Days of the week for the digest', type: [String], enum: DaysEnum })
@IsOptional()
@IsArray()
@IsEnum(DaysEnum, { each: true })
weekDays?: DaysEnum[];

@ApiPropertyOptional({ description: 'Specific days of the month for the digest', type: [Number] })
@IsOptional()
@IsArray()
@IsNumber({}, { each: true })
monthDays?: number[];

@ApiPropertyOptional({
description: 'Ordinal position for the digest',
enum: [...Object.values(OrdinalEnum)],
enumName: 'OrdinalEnum',
})
@IsOptional()
@IsEnum(OrdinalEnum)
ordinal?: OrdinalEnum;

@ApiPropertyOptional({
description: 'Value of the ordinal',
enum: [...Object.values(OrdinalValueEnum)],
enumName: 'OrdinalValueEnum',
})
@IsOptional()
@IsEnum(OrdinalValueEnum)
ordinalValue?: OrdinalValueEnum;

@ApiPropertyOptional({
description: 'Type of monthly schedule',
enum: [...Object.values(MonthlyTypeEnum)],
enumName: 'MonthlyTypeEnum',
})
@IsOptional()
@IsEnum(MonthlyTypeEnum)
monthlyType?: MonthlyTypeEnum;

@ApiPropertyOptional({ description: 'Cron expression for scheduling' })
@IsOptional()
@IsString()
cronExpression?: string;
}

export class DigestMetadataDto {
@ApiPropertyOptional({ description: 'Optional key for the digest' })
digestKey?: string;

Expand All @@ -23,10 +77,15 @@ export class DigestBaseMetadataDto {

@ApiPropertyOptional({ description: 'Unit of the digest', enum: DigestUnitEnum })
unit?: DigestUnitEnum;
}

// DTO for Digest including events
export class DigestWithEventsDto extends DigestBaseMetadataDto {
@ApiProperty({
enum: [...Object.values(DigestTypeEnum)],
enumName: 'DigestTypeEnum',
description: 'The Digest Type',
type: String,
})
type: DigestTypeEnum;

@ApiPropertyOptional({
type: 'array',
items: {
Expand All @@ -35,24 +94,73 @@ export class DigestWithEventsDto extends DigestBaseMetadataDto {
},
description: 'Optional array of events associated with the digest, represented as key-value pairs',
})
events?: Record<string, unknown>[]; // Optional array of events as key-value pairs
events?: Record<string, unknown>[];

// Properties for Regular Digest
@ApiPropertyOptional({
description: 'Regular digest: Indicates if backoff is enabled for the regular digest',
type: Boolean,
})
backoff?: boolean;

@ApiPropertyOptional({ description: 'Regular digest: Amount for backoff', type: Number })
backoffAmount?: number;

@ApiPropertyOptional({
description: 'Regular digest: Unit for backoff',
enum: [...Object.values(DigestUnitEnum)],
enumName: 'DigestUnitEnum',
})
backoffUnit?: DigestUnitEnum;

@ApiPropertyOptional({ description: 'Regular digest: Indicates if the digest should update', type: Boolean })
updateMode?: boolean;

// Properties for Timed Digest
@ApiPropertyOptional({ description: 'Configuration for timed digest', type: () => DigestTimedConfigDto })
timed?: DigestTimedConfigDto;
}

// Activity Notification Step Response DTO
export class ActivityNotificationStepResponseDto {
@ApiProperty({ description: 'Unique identifier of the step', type: String })
_id: string;

@ApiProperty({ description: 'Whether the step is active or not', type: Boolean })
active: boolean;

@ApiProperty({ description: 'Filter criteria for the step', isArray: true, type: StepFilter })
@ApiPropertyOptional({ description: 'Reply callback settings', type: Object })
replyCallback?: {
active: boolean;
url: string;
};

@ApiPropertyOptional({ description: 'Control variables', type: Object })
controlVariables?: Record<string, unknown>;

@ApiPropertyOptional({ description: 'Metadata for the workflow step', type: Object })
metadata?: any; // Adjust the type based on your actual metadata structure

@ApiPropertyOptional({ description: 'Step issues', type: Object })
issues?: any; // Adjust the type based on your actual issues structure

@ApiProperty({ description: 'Filter criteria for the step', isArray: true, type: StepFilterDto })
filters: StepFilterDto[];

@ApiPropertyOptional({ description: 'Optional template for the step', type: MessageTemplateDto })
template?: MessageTemplateDto;
}

@ApiPropertyOptional({ description: 'Variants of the step', type: [ActivityNotificationStepResponseDto] })
variants?: ActivityNotificationStepResponseDto[]; // Assuming variants are the same type

@ApiProperty({ description: 'The identifier for the template associated with this step', type: String })
_templateId: string;

@ApiPropertyOptional({ description: 'The name of the step', type: String })
name?: string;

@ApiPropertyOptional({ description: 'The unique identifier for the parent step', type: String })
_parentId?: string | null;
}
// Activity Notification Execution Detail Response DTO
export class ActivityNotificationExecutionDetailResponseDto {
@ApiProperty({ description: 'Unique identifier of the execution detail', type: String })
Expand All @@ -62,9 +170,10 @@ export class ActivityNotificationExecutionDetailResponseDto {
createdAt?: string;

@ApiProperty({
enum: ExecutionDetailsStatusEnum,
enum: [...Object.values(ExecutionDetailsStatusEnum)],
enumName: 'ExecutionDetailsStatusEnum',
description: 'Status of the execution detail',
type: String, // Explicit type reference for enum
type: String,
})
status: ExecutionDetailsStatusEnum;

Expand All @@ -91,10 +200,13 @@ export class ActivityNotificationExecutionDetailResponseDto {
raw?: string;

@ApiProperty({
enum: ExecutionDetailsSourceEnum,
enum: [...Object.values(ExecutionDetailsSourceEnum)],
enumName: 'ExecutionDetailsSourceEnum',
description: 'Source of the execution detail',
type: String, // Explicit type reference for enum
type: String,
})
@IsString()
@IsEnum(ExecutionDetailsSourceEnum)
source: ExecutionDetailsSourceEnum;
}

Expand All @@ -108,9 +220,9 @@ export class ActivityNotificationJobResponseDto {

@ApiPropertyOptional({
description: 'Optional digest for the job, including metadata and events',
type: DigestWithEventsDto,
type: DigestMetadataDto,
})
digest?: DigestWithEventsDto; // Use the new DTO
digest?: DigestMetadataDto;

@ApiProperty({
description: 'Execution details of the job',
Expand All @@ -124,7 +236,7 @@ export class ActivityNotificationJobResponseDto {
})
step: ActivityNotificationStepResponseDto;

@ApiPropertyOptional({ description: 'Optional payload for the job', type: Object }) // Use Object for unknown structure
@ApiPropertyOptional({ description: 'Optional payload for the job', type: Object })
payload?: Record<string, unknown>;

@ApiProperty({
Expand Down Expand Up @@ -166,8 +278,7 @@ export class NotificationTriggerVariable {
name: string;
}

// Notification Trigger DTO
export class NotificationTrigger {
export class NotificationTriggerDto {
@ApiProperty({
enum: TriggerTypeEnum,
description: 'Type of the trigger',
Expand All @@ -192,7 +303,7 @@ export class NotificationTrigger {
}

// Activity Notification Template Response DTO
class ActivityNotificationTemplateResponseDto {
export class ActivityNotificationTemplateResponseDto {
@ApiPropertyOptional({ description: 'Unique identifier of the template', type: String })
_id?: string;

Expand All @@ -201,9 +312,9 @@ class ActivityNotificationTemplateResponseDto {

@ApiProperty({
description: 'Triggers of the template',
type: [NotificationTrigger],
type: [NotificationTriggerDto],
})
triggers: NotificationTrigger[];
triggers: NotificationTriggerDto[];
}

// Activity Notification Response DTO
Expand All @@ -217,18 +328,30 @@ export class ActivityNotificationResponseDto {
@ApiProperty({ description: 'Organization ID of the notification', type: String })
_organizationId: string;

@ApiProperty({ description: 'Subscriber ID of the notification', type: String })
_subscriberId: string; // Added to align with NotificationEntity

@ApiProperty({ description: 'Transaction ID of the notification', type: String })
transactionId: string;

@ApiPropertyOptional({ description: 'Template ID of the notification', type: String })
_templateId?: string; // Added to align with NotificationEntity

@ApiPropertyOptional({ description: 'Digested Notification ID', type: String })
_digestedNotificationId?: string; // Added to align with NotificationEntity

@ApiPropertyOptional({ description: 'Creation time of the notification', type: String })
createdAt?: string;

@ApiPropertyOptional({ description: 'Last updated time of the notification', type: String })
updatedAt?: string; // Added to align with NotificationEntity

@ApiPropertyOptional({
description: 'Channels of the notification',
enum: [...Object.values(StepTypeEnum)],
enumName: 'StepTypeEnum',
isArray: true,
type: String, // Explicit type reference for enum
type: String,
})
channels?: StepTypeEnum[];

Expand All @@ -249,8 +372,31 @@ export class ActivityNotificationResponseDto {
type: [ActivityNotificationJobResponseDto],
})
jobs?: ActivityNotificationJobResponseDto[];
}

@ApiPropertyOptional({
description: 'Payload of the notification',
type: Object, // Adjust type as necessary
})
payload?: any; // Added to align with NotificationEntity

@ApiPropertyOptional({
description: 'Tags associated with the notification',
type: [String],
})
tags?: string[]; // Added to align with NotificationEntity

@ApiPropertyOptional({
description: 'Controls associated with the notification',
type: Object, // Adjust type as necessary
})
controls?: any; // Added to align with NotificationEntity

@ApiPropertyOptional({
description: 'To field for subscriber definition',
type: Object, // Adjust type as necessary
})
to?: any; // Added to align with NotificationEntity
}
// Activities Response DTO
export class ActivitiesResponseDto {
@ApiProperty({ description: 'Indicates if there are more activities in the result set', type: Boolean })
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@ describe('Get activity feed - /notifications (GET) #novu-v2', async () => {
expect(activity.template.name).to.equal(template.name);
expect(activity.template.id).to.equal(template._id);
expect(activity.subscriber.firstName).to.equal('Test');
expect(activity.channels).to.be.ok;
expect(activity.channels).to.include.oneOf(Object.keys(ChannelTypeEnum).map((i) => ChannelTypeEnum[i]));
});

Expand Down
Loading

0 comments on commit c31f2c0

Please sign in to comment.