Skip to content

Commit e6e4faa

Browse files
refactor(dal): remove soft delete in message collection (#8073)
1 parent 733cc2f commit e6e4faa

File tree

10 files changed

+98
-107
lines changed

10 files changed

+98
-107
lines changed

apps/api/src/app/messages/usecases/remove-messages-by-transactionId/remove-messages-by-transactionId.usecase.ts

+5-3
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
import { Injectable, NotFoundException } from '@nestjs/common';
2+
23
import { buildFeedKey, buildMessageCountKey, InvalidateCacheService } from '@novu/application-generic';
3-
import { MessageEntity, MessageRepository } from '@novu/dal';
4+
import { EnforceEnvId, MessageEntity, MessageRepository } from '@novu/dal';
5+
46
import { RemoveMessagesByTransactionIdCommand } from './remove-messages-by-transactionId.command';
57

68
@Injectable()
@@ -41,7 +43,7 @@ export class RemoveMessagesByTransactionId {
4143
}
4244
}
4345

44-
const deleteQuery: Partial<MessageEntity> = {
46+
const deleteQuery: Partial<MessageEntity> & EnforceEnvId = {
4547
transactionId: command.transactionId,
4648
_environmentId: command.environmentId,
4749
_organizationId: command.organizationId,
@@ -51,6 +53,6 @@ export class RemoveMessagesByTransactionId {
5153
deleteQuery.channel = command.channel;
5254
}
5355

54-
await this.messageRepository.deleteMany(deleteQuery);
56+
await this.messageRepository.delete(deleteQuery);
5557
}
5658
}

apps/api/src/app/widgets/dtos/feeds-response.dto.ts

-7
Original file line numberDiff line numberDiff line change
@@ -177,13 +177,6 @@ export class NotificationFeedItemDto implements INotificationDto {
177177
})
178178
seen: boolean;
179179

180-
@ApiProperty({
181-
description: 'Indicates whether the notification has been deleted.',
182-
example: false,
183-
type: Boolean,
184-
})
185-
deleted: boolean;
186-
187180
@ApiPropertyOptional({
188181
description: 'Device tokens for push notifications, if applicable.',
189182
type: [String],

apps/api/src/app/widgets/usecases/remove-message/remove-message.usecase.ts

+11-22
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
1+
import { DalException, MessageRepository, SubscriberRepository, SubscriberEntity } from '@novu/dal';
12
import { Injectable, NotFoundException, BadRequestException } from '@nestjs/common';
2-
import { MessageEntity, DalException, MessageRepository, SubscriberRepository, SubscriberEntity } from '@novu/dal';
33
import {
44
WebSocketsQueueService,
55
AnalyticsService,
@@ -22,7 +22,7 @@ export class RemoveMessage {
2222
private subscriberRepository: SubscriberRepository
2323
) {}
2424

25-
async execute(command: RemoveMessageCommand): Promise<MessageEntity> {
25+
async execute(command: RemoveMessageCommand): Promise<void> {
2626
await this.invalidateCache.invalidateQuery({
2727
key: buildFeedKey().invalidate({
2828
subscriberId: command.subscriberId,
@@ -40,41 +40,30 @@ export class RemoveMessage {
4040
const subscriber = await this.subscriberRepository.findBySubscriberId(command.environmentId, command.subscriberId);
4141
if (!subscriber) throw new NotFoundException(`Subscriber ${command.subscriberId} not found`);
4242

43-
let deletedMessage;
4443
try {
45-
await this.messageRepository.delete({
44+
const deletedMessage = await this.messageRepository.delete({
4645
_environmentId: command.environmentId,
4746
_organizationId: command.organizationId,
4847
_id: command.messageId,
4948
_subscriberId: command.subscriberId,
5049
});
51-
const item = await this.messageRepository.findDeleted({
52-
_environmentId: command.environmentId,
53-
_organizationId: command.organizationId,
54-
_id: command.messageId,
55-
});
5650

57-
// eslint-disable-next-line prefer-destructuring
58-
deletedMessage = item[0];
59-
60-
if (!deletedMessage.read) {
61-
await this.updateServices(command, subscriber, deletedMessage, MarkEnum.READ);
62-
}
63-
if (!deletedMessage.seen) {
64-
await this.updateServices(command, subscriber, deletedMessage, MarkEnum.SEEN);
51+
if (deletedMessage.deletedCount) {
52+
await Promise.all([
53+
this.updateServices(command, subscriber, command.messageId, MarkEnum.READ),
54+
this.updateServices(command, subscriber, command.messageId, MarkEnum.SEEN),
55+
]);
6556
}
6657
} catch (e) {
6758
if (e instanceof DalException) {
6859
throw new BadRequestException(e.message);
6960
}
7061
throw e;
7162
}
72-
73-
return deletedMessage;
7463
}
7564

7665
private async updateServices(command: RemoveMessageCommand, subscriber, message, marked: MarkEnum) {
77-
this.updateSocketCount(subscriber, marked);
66+
await this.updateSocketCount(subscriber, marked);
7867

7968
this.analyticsService.track(`Removed Message - [Notification Center]`, command.organizationId, {
8069
_subscriber: message._subscriberId,
@@ -83,10 +72,10 @@ export class RemoveMessage {
8372
});
8473
}
8574

86-
private updateSocketCount(subscriber: SubscriberEntity, mark: MarkEnum) {
75+
private async updateSocketCount(subscriber: SubscriberEntity, mark: MarkEnum) {
8776
const eventMessage = mark === MarkEnum.READ ? WebSocketEventEnum.UNREAD : WebSocketEventEnum.UNSEEN;
8877

89-
this.webSocketsQueueService.add({
78+
await this.webSocketsQueueService.add({
9079
name: 'sendMessage',
9180
data: {
9281
event: eventMessage,

apps/api/src/app/widgets/usecases/remove-messages-bulk/remove-messages-bulk.usecase.ts

+21-19
Original file line numberDiff line numberDiff line change
@@ -27,30 +27,32 @@ export class RemoveMessagesBulk {
2727
if (!subscriber) throw new NotFoundException(`Subscriber ${command.subscriberId} not found`);
2828

2929
try {
30-
await this.messageRepository.deleteMany({
30+
const deletedMessages = await this.messageRepository.delete({
3131
_environmentId: command.environmentId,
3232
_organizationId: command.organizationId,
3333
_subscriberId: subscriber._id,
3434
channel: ChannelTypeEnum.IN_APP,
3535
_id: { $in: command.messageIds },
3636
});
3737

38-
await this.updateServices(subscriber, MarkEnum.SEEN);
39-
await this.updateServices(subscriber, MarkEnum.READ);
40-
41-
await this.invalidateCache.invalidateQuery({
42-
key: buildFeedKey().invalidate({
43-
subscriberId: command.subscriberId,
44-
_environmentId: command.environmentId,
45-
}),
46-
});
47-
48-
await this.invalidateCache.invalidateQuery({
49-
key: buildMessageCountKey().invalidate({
50-
subscriberId: command.subscriberId,
51-
_environmentId: command.environmentId,
52-
}),
53-
});
38+
if (deletedMessages.deletedCount > 0) {
39+
await Promise.all([
40+
this.updateServices(subscriber, MarkEnum.SEEN),
41+
this.updateServices(subscriber, MarkEnum.READ),
42+
this.invalidateCache.invalidateQuery({
43+
key: buildFeedKey().invalidate({
44+
subscriberId: command.subscriberId,
45+
_environmentId: command.environmentId,
46+
}),
47+
}),
48+
this.invalidateCache.invalidateQuery({
49+
key: buildMessageCountKey().invalidate({
50+
subscriberId: command.subscriberId,
51+
_environmentId: command.environmentId,
52+
}),
53+
}),
54+
]);
55+
}
5456
} catch (e) {
5557
if (e instanceof DalException) {
5658
throw new BadRequestException(e.message);
@@ -59,10 +61,10 @@ export class RemoveMessagesBulk {
5961
}
6062
}
6163

62-
private async updateServices(subscriber, marked: string) {
64+
private async updateServices(subscriber, marked: string): Promise<void> {
6365
const eventMessage = marked === MarkEnum.READ ? WebSocketEventEnum.UNREAD : WebSocketEventEnum.UNSEEN;
6466

65-
this.webSocketsQueueService.add({
67+
await this.webSocketsQueueService.add({
6668
name: 'sendMessage',
6769
data: {
6870
event: eventMessage,

apps/api/src/app/widgets/usecases/remove-messages/remove-all-messages.usecase.ts

+25-23
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import {
66
SubscriberRepository,
77
SubscriberEntity,
88
FeedRepository,
9+
EnforceEnvId,
910
} from '@novu/dal';
1011
import { ChannelTypeEnum, WebSocketEventEnum } from '@novu/shared';
1112
import {
@@ -43,7 +44,7 @@ export class RemoveAllMessages {
4344
}
4445
}
4546

46-
const deleteMessageQuery: Partial<MessageEntity> = {
47+
const deleteMessageQuery: Partial<MessageEntity> & EnforceEnvId = {
4748
_environmentId: command.environmentId,
4849
_organizationId: command.organizationId,
4950
_subscriberId: subscriber._id,
@@ -53,32 +54,33 @@ export class RemoveAllMessages {
5354
if (feed) {
5455
deleteMessageQuery._feedId = feed._id;
5556
}
57+
const deletedMessages = await this.messageRepository.delete(deleteMessageQuery);
5658

57-
await this.messageRepository.deleteMany(deleteMessageQuery);
58-
59-
await this.updateServices(command, subscriber, MarkEnum.SEEN);
60-
await this.updateServices(command, subscriber, MarkEnum.READ);
59+
if (deletedMessages.deletedCount > 0) {
60+
await Promise.all([
61+
this.updateServices(command, subscriber, MarkEnum.SEEN),
62+
this.updateServices(command, subscriber, MarkEnum.READ),
63+
this.invalidateCache.invalidateQuery({
64+
key: buildFeedKey().invalidate({
65+
subscriberId: command.subscriberId,
66+
_environmentId: command.environmentId,
67+
}),
68+
}),
69+
this.invalidateCache.invalidateQuery({
70+
key: buildMessageCountKey().invalidate({
71+
subscriberId: command.subscriberId,
72+
_environmentId: command.environmentId,
73+
}),
74+
}),
75+
]);
76+
}
6177

6278
this.analyticsService.track(`Removed All Feed Messages - [Notification Center]`, command.organizationId, {
6379
_subscriber: subscriber._id,
6480
_organization: command.organizationId,
6581
_environment: command.environmentId,
6682
_feedId: command.feedId,
6783
});
68-
69-
await this.invalidateCache.invalidateQuery({
70-
key: buildFeedKey().invalidate({
71-
subscriberId: command.subscriberId,
72-
_environmentId: command.environmentId,
73-
}),
74-
});
75-
76-
await this.invalidateCache.invalidateQuery({
77-
key: buildMessageCountKey().invalidate({
78-
subscriberId: command.subscriberId,
79-
_environmentId: command.environmentId,
80-
}),
81-
});
8284
} catch (e) {
8385
if (e instanceof DalException) {
8486
throw new BadRequestException(e.message);
@@ -87,14 +89,14 @@ export class RemoveAllMessages {
8789
}
8890
}
8991

90-
private async updateServices(command: RemoveAllMessagesCommand, subscriber, marked: string) {
91-
this.updateSocketCount(subscriber, marked);
92+
private async updateServices(command: RemoveAllMessagesCommand, subscriber, marked: string): Promise<void> {
93+
await this.updateSocketCount(subscriber, marked);
9294
}
9395

94-
private updateSocketCount(subscriber: SubscriberEntity, mark: string) {
96+
private async updateSocketCount(subscriber: SubscriberEntity, mark: string): Promise<void> {
9597
const eventMessage = mark === MarkEnum.READ ? WebSocketEventEnum.UNREAD : WebSocketEventEnum.UNSEEN;
9698

97-
this.webSocketsQueueService.add({
99+
await this.webSocketsQueueService.add({
98100
name: 'sendMessage',
99101
data: {
100102
event: eventMessage,

apps/api/src/app/widgets/widgets.controller.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -282,7 +282,7 @@ export class WidgetsController {
282282
async removeMessage(
283283
@SubscriberSession() subscriberSession: SubscriberEntity,
284284
@Param('messageId') messageId: string
285-
): Promise<MessageEntity> {
285+
): Promise<void> {
286286
if (!messageId) throw new BadRequestException('messageId is required');
287287

288288
const command = RemoveMessageCommand.create({

libs/dal/src/repositories/message/message.entity.ts

+3
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,9 @@ export class MessageEntity {
5353

5454
archived: boolean;
5555

56+
/**
57+
* todo: remove deleted field after all the soft deletes are removed task nv-5688
58+
*/
5659
deleted: boolean;
5760

5861
email?: string;

libs/dal/src/repositories/message/message.repository.ts

+10-27
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
1-
import { SoftDeleteModel } from 'mongoose-delete';
2-
import { FilterQuery, Types } from 'mongoose';
1+
import { FilterQuery, QueryWithHelpers, Types, UpdateQuery } from 'mongoose';
32
import {
43
ActorTypeEnum,
54
ButtonTypeEnum,
@@ -27,11 +26,9 @@ const getFlatObject = (obj: object) => {
2726
};
2827

2928
export class MessageRepository extends BaseRepository<MessageDBModel, MessageEntity, EnforceEnvId> {
30-
private message: SoftDeleteModel;
3129
private feedRepository = new FeedRepository();
3230
constructor() {
3331
super(Message, MessageEntity);
34-
this.message = Message;
3532
}
3633

3734
private async getFilterQueryForMessage(
@@ -111,6 +108,14 @@ export class MessageRepository extends BaseRepository<MessageDBModel, MessageEnt
111108
return requestQuery;
112109
}
113110

111+
/**
112+
* if aggregation is needed, make sure to filter with {deleted: { $ne: true }}.
113+
* todo: aggregate method should be implemented after all the soft deletes are removed task nv-5688
114+
*/
115+
async aggregate(query: any[], options: { readPreference?: 'secondaryPreferred' | 'primary' } = {}): Promise<any> {
116+
throw new Error('Not implemented');
117+
}
118+
114119
async findBySubscriberChannel(
115120
environmentId: string,
116121
subscriberId: string,
@@ -656,28 +661,6 @@ export class MessageRepository extends BaseRepository<MessageDBModel, MessageEnt
656661
);
657662
}
658663

659-
async delete(query: MessageQuery) {
660-
return await this.message.delete({ _id: query._id, _environmentId: query._environmentId });
661-
}
662-
663-
async deleteMany(query: MessageQuery) {
664-
try {
665-
return await this.message.delete({ ...query, deleted: false });
666-
} catch (e: unknown) {
667-
if (e instanceof Error) {
668-
throw new DalException(e.message);
669-
} else {
670-
throw new DalException('An unknown error occurred');
671-
}
672-
}
673-
}
674-
675-
async findDeleted(query: MessageQuery): Promise<MessageEntity> {
676-
const res: MessageEntity = await this.message.findDeleted(query);
677-
678-
return this.mapEntity(res);
679-
}
680-
681664
async findMessageById(query: { _id: string; _environmentId: string }): Promise<MessageEntity | null> {
682665
const res = await this.MongooseModel.findOne({ _id: query._id, _environmentId: query._environmentId })
683666
.populate('subscriber')
@@ -734,7 +717,7 @@ export class MessageRepository extends BaseRepository<MessageDBModel, MessageEnt
734717
if (query.transactionId) {
735718
filterQuery.transactionId = { $in: query.transactionId };
736719
}
737-
const data = await this.MongooseModel.find(query, select, {
720+
const data = await this.MongooseModel.find(filterQuery, select, {
738721
sort: options?.sort,
739722
limit: options?.limit,
740723
skip: options?.skip,

0 commit comments

Comments
 (0)