|
1 | | -import { Schema, Prop, SchemaFactory } from '@nestjs/mongoose'; |
| 1 | +import { Prop, Schema, SchemaFactory } from '@nestjs/mongoose'; |
2 | 2 | import { Document } from 'mongoose'; |
| 3 | +import * as Joi from 'joi'; |
| 4 | +import { NotificationChannel } from '../../../../config/notification.config'; |
| 5 | +import { NotificationType } from '../../../../config/notification.config'; |
| 6 | + |
| 7 | +export type NotificationDocument = Notification & Document; |
3 | 8 |
|
4 | 9 | @Schema({ timestamps: true }) |
5 | | -export class NotificationDocument extends Document { |
6 | | - @Prop({ required: true, minlength: 1, maxlength: 100 }) |
| 10 | +export class Notification { |
| 11 | + @Prop({ required: true }) |
7 | 12 | recipient: string; |
8 | 13 |
|
9 | | - @Prop({ required: true, minlength: 1, maxlength: 100 }) |
| 14 | + @Prop({ required: true }) |
10 | 15 | subject: string; |
11 | 16 |
|
12 | | - @Prop({ required: true, minlength: 1, maxlength: 1000 }) |
| 17 | + @Prop({ required: true }) |
13 | 18 | body: string; |
14 | 19 |
|
15 | | - @Prop({ required: true }) |
16 | | - mediaType: string; |
| 20 | + @Prop({ required: true, enum: NotificationChannel }) |
| 21 | + mediaType: NotificationChannel; |
17 | 22 |
|
18 | | - @Prop() |
19 | | - sentAt?: Date; |
| 23 | + @Prop({ required: true, enum: NotificationType }) |
| 24 | + notificationType: NotificationType; |
| 25 | + |
| 26 | + @Prop({ default: Date.now }) |
| 27 | + sentAt: Date; |
20 | 28 | } |
21 | | -export const NotificationSchema = |
22 | | - SchemaFactory.createForClass(NotificationDocument); |
| 29 | + |
| 30 | +export const NotificationSchema = SchemaFactory.createForClass(Notification); |
| 31 | + |
| 32 | +const NotificationJoiSchema = Joi.object({ |
| 33 | + recipient: Joi.string().required().messages({ |
| 34 | + 'string.empty': 'Recipient is required', |
| 35 | + }), |
| 36 | + subject: Joi.string().min(3).max(100).required().messages({ |
| 37 | + 'string.min': 'Subject must be at least 3 characters', |
| 38 | + 'string.max': 'Subject must not exceed 100 characters', |
| 39 | + 'string.empty': 'Subject is required', |
| 40 | + }), |
| 41 | + body: Joi.string().min(10).max(1000).required().messages({ |
| 42 | + 'string.min': 'Body must be at least 10 characters', |
| 43 | + 'string.max': 'Body must not exceed 1000 characters', |
| 44 | + 'string.empty': 'Body is required', |
| 45 | + }), |
| 46 | + mediaType: Joi.string() |
| 47 | + .valid(...Object.values(NotificationChannel)) |
| 48 | + .required() |
| 49 | + .messages({ |
| 50 | + 'any.only': 'Invalid mediaType, must be one of: EMAIL, SMS', |
| 51 | + 'string.empty': 'mediaType is required', |
| 52 | + }), |
| 53 | + notificationType: Joi.string() |
| 54 | + .valid(...Object.values(NotificationType)) |
| 55 | + .required() |
| 56 | + .messages({ |
| 57 | + 'any.only': 'Invalid notificationType', |
| 58 | + 'string.empty': 'notificationType is required', |
| 59 | + }), |
| 60 | + sentAt: Joi.date().default(Date.now), |
| 61 | +}).custom( |
| 62 | + (value: { recipient: string; mediaType: NotificationChannel }, helpers) => { |
| 63 | + if ( |
| 64 | + value.mediaType === NotificationChannel.EMAIL && |
| 65 | + !Joi.string().email().validate(value.recipient).value |
| 66 | + ) { |
| 67 | + return helpers.message({ |
| 68 | + custom: 'Recipient must be a valid email for EMAIL channel', |
| 69 | + }); |
| 70 | + } |
| 71 | + if ( |
| 72 | + value.mediaType === NotificationChannel.SMS && |
| 73 | + !Joi.string() |
| 74 | + .pattern(/^\+?[1-9]\d{1,14}$/) |
| 75 | + .validate(value.recipient).value |
| 76 | + ) { |
| 77 | + return helpers.message({ |
| 78 | + custom: 'Recipient must be a valid phone number for SMS channel', |
| 79 | + }); |
| 80 | + } |
| 81 | + return value; |
| 82 | + }, |
| 83 | +); |
| 84 | + |
| 85 | +NotificationSchema.pre('save', function (next) { |
| 86 | + const { error } = NotificationJoiSchema.validate(this.toObject(), { |
| 87 | + abortEarly: false, |
| 88 | + }); |
| 89 | + if (error) { |
| 90 | + return next( |
| 91 | + new Error( |
| 92 | + 'Validation failed: ' + error.details.map((d) => d.message).join(', '), |
| 93 | + ), |
| 94 | + ); |
| 95 | + } |
| 96 | + next(); |
| 97 | +}); |
0 commit comments