vintasend-prisma 0.2.3 → 0.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,406 @@
1
+ Object.defineProperty(exports, '__esModule', { value: true });
2
+ exports.PrismaNotificationBackendFactory =
3
+ exports.PrismaNotificationBackend =
4
+ exports.NotificationTypeEnum =
5
+ exports.NotificationStatusEnum =
6
+ void 0;
7
+ exports.NotificationStatusEnum = {
8
+ PENDING_SEND: 'PENDING_SEND',
9
+ SENT: 'SENT',
10
+ FAILED: 'FAILED',
11
+ READ: 'READ',
12
+ CANCELLED: 'CANCELLED',
13
+ };
14
+ exports.NotificationTypeEnum = {
15
+ EMAIL: 'EMAIL',
16
+ PUSH: 'PUSH',
17
+ SMS: 'SMS',
18
+ IN_APP: 'IN_APP',
19
+ };
20
+ function convertJsonValueToRecord(jsonValue) {
21
+ if (typeof jsonValue === 'object' && !Array.isArray(jsonValue)) {
22
+ return jsonValue;
23
+ }
24
+ throw new Error('Invalid JSON value. It should be an object.');
25
+ }
26
+ class PrismaNotificationBackend {
27
+ constructor(prismaClient) {
28
+ this.prismaClient = prismaClient;
29
+ }
30
+ /**
31
+ * Serialize a Prisma notification model to either DatabaseNotification or DatabaseOneOffNotification
32
+ * based on whether it has a userId or not
33
+ */
34
+ serializeNotification(notification) {
35
+ const baseData = {
36
+ id: notification.id,
37
+ notificationType: notification.notificationType,
38
+ title: notification.title,
39
+ bodyTemplate: notification.bodyTemplate,
40
+ contextName: notification.contextName,
41
+ contextParameters: notification.contextParameters ? notification.contextParameters : {},
42
+ sendAfter: notification.sendAfter,
43
+ subjectTemplate: notification.subjectTemplate,
44
+ status: notification.status,
45
+ contextUsed: notification.contextUsed,
46
+ extraParams: notification.extraParams
47
+ ? convertJsonValueToRecord(notification.extraParams)
48
+ : null,
49
+ adapterUsed: notification.adapterUsed,
50
+ sentAt: notification.sentAt,
51
+ readAt: notification.readAt,
52
+ createdAt: notification.createdAt,
53
+ updatedAt: notification.updatedAt,
54
+ };
55
+ // Check if this is a one-off notification (has emailOrPhone but no userId)
56
+ if (notification.emailOrPhone && !notification.userId) {
57
+ return {
58
+ ...baseData,
59
+ emailOrPhone: notification.emailOrPhone,
60
+ firstName: notification.firstName || '',
61
+ lastName: notification.lastName || '',
62
+ };
63
+ }
64
+ // Regular notification with userId
65
+ return {
66
+ ...baseData,
67
+ userId: notification.userId,
68
+ };
69
+ }
70
+ deserializeNotification(notification) {
71
+ return {
72
+ user: {
73
+ connect: {
74
+ id: notification.userId,
75
+ },
76
+ },
77
+ notificationType: notification.notificationType,
78
+ title: notification.title,
79
+ bodyTemplate: notification.bodyTemplate,
80
+ contextName: notification.contextName,
81
+ contextParameters: notification.contextParameters,
82
+ sendAfter: notification.sendAfter,
83
+ subjectTemplate: notification.subjectTemplate,
84
+ extraParams: notification.extraParams,
85
+ };
86
+ }
87
+ deserializeNotificationForUpdate(notification) {
88
+ return {
89
+ ...(notification.userId ? { user: { connect: { id: notification.userId } } } : {}),
90
+ ...(notification.notificationType
91
+ ? {
92
+ notificationType: exports.NotificationTypeEnum[notification.notificationType],
93
+ }
94
+ : {}),
95
+ ...(notification.title ? { title: notification.title } : {}),
96
+ ...(notification.bodyTemplate ? { bodyTemplate: notification.bodyTemplate } : {}),
97
+ ...(notification.contextName ? { contextName: notification.contextName } : {}),
98
+ ...(notification.contextParameters
99
+ ? {
100
+ contextParameters: notification.contextParameters ? notification.contextParameters : {},
101
+ }
102
+ : {}),
103
+ ...(notification.sendAfter ? { sendAfter: notification.sendAfter } : {}),
104
+ ...(notification.subjectTemplate ? { subjectTemplate: notification.subjectTemplate } : {}),
105
+ };
106
+ }
107
+ async getAllPendingNotifications() {
108
+ const notifications = await this.prismaClient.notification.findMany({
109
+ where: { status: exports.NotificationStatusEnum.PENDING_SEND },
110
+ });
111
+ return notifications.map((n) => this.serializeNotification(n));
112
+ }
113
+ async getPendingNotifications(page = 0, pageSize = 100) {
114
+ const notifications = await this.prismaClient.notification.findMany({
115
+ where: {
116
+ status: exports.NotificationStatusEnum.PENDING_SEND,
117
+ sendAfter: null,
118
+ },
119
+ skip: page * pageSize,
120
+ take: pageSize,
121
+ });
122
+ return notifications.map((n) => this.serializeNotification(n));
123
+ }
124
+ async getAllFutureNotifications() {
125
+ const notifications = await this.prismaClient.notification.findMany({
126
+ where: {
127
+ status: { not: exports.NotificationStatusEnum.PENDING_SEND },
128
+ sendAfter: { lte: new Date() },
129
+ },
130
+ });
131
+ return notifications.map((n) => this.serializeNotification(n));
132
+ }
133
+ async getFutureNotifications(page = 0, pageSize = 100) {
134
+ const notifications = await this.prismaClient.notification.findMany({
135
+ where: {
136
+ status: { not: exports.NotificationStatusEnum.PENDING_SEND },
137
+ sendAfter: { lte: new Date() },
138
+ },
139
+ skip: page * pageSize,
140
+ take: pageSize,
141
+ });
142
+ return notifications.map((n) => this.serializeNotification(n));
143
+ }
144
+ async getAllFutureNotificationsFromUser(userId) {
145
+ const notifications = await this.prismaClient.notification.findMany({
146
+ where: {
147
+ userId,
148
+ status: {
149
+ not: exports.NotificationStatusEnum.PENDING_SEND,
150
+ },
151
+ sendAfter: {
152
+ lte: new Date(),
153
+ },
154
+ },
155
+ });
156
+ // These are guaranteed to be regular notifications (not one-off) since we're filtering by userId
157
+ return notifications.map((n) => this.serializeNotification(n));
158
+ }
159
+ async getFutureNotificationsFromUser(userId, page, pageSize) {
160
+ const notifications = await this.prismaClient.notification.findMany({
161
+ where: {
162
+ userId,
163
+ status: {
164
+ not: exports.NotificationStatusEnum.PENDING_SEND,
165
+ },
166
+ sendAfter: {
167
+ lte: new Date(),
168
+ },
169
+ },
170
+ skip: page * pageSize,
171
+ take: pageSize,
172
+ });
173
+ // These are guaranteed to be regular notifications (not one-off) since we're filtering by userId
174
+ return notifications.map((n) => this.serializeNotification(n));
175
+ }
176
+ async getAllNotifications() {
177
+ const notifications = await this.prismaClient.notification.findMany({});
178
+ return notifications.map((n) => this.serializeNotification(n));
179
+ }
180
+ async getNotifications(page, pageSize) {
181
+ const notifications = await this.prismaClient.notification.findMany({
182
+ skip: page * pageSize,
183
+ take: pageSize,
184
+ });
185
+ return notifications.map((n) => this.serializeNotification(n));
186
+ }
187
+ async persistNotification(notification) {
188
+ const created = await this.prismaClient.notification.create({
189
+ data: this.deserializeNotification(notification),
190
+ });
191
+ // persistNotification takes NotificationInput which always has userId, so this is always a regular notification
192
+ return this.serializeNotification(created);
193
+ }
194
+ async persistNotificationUpdate(notificationId, notification) {
195
+ const updated = await this.prismaClient.notification.update({
196
+ where: {
197
+ id: notificationId,
198
+ },
199
+ data: this.deserializeNotificationForUpdate(notification),
200
+ });
201
+ // persistNotificationUpdate takes Partial<DatabaseNotification> which has userId, so this is always a regular notification
202
+ return this.serializeNotification(updated);
203
+ }
204
+ /* One-off notification persistence and query methods */
205
+ async persistOneOffNotification(notification) {
206
+ const created = await this.prismaClient.notification.create({
207
+ data: {
208
+ userId: null, // One-off notifications don't have a userId
209
+ emailOrPhone: notification.emailOrPhone,
210
+ firstName: notification.firstName,
211
+ lastName: notification.lastName,
212
+ notificationType: notification.notificationType,
213
+ title: notification.title,
214
+ bodyTemplate: notification.bodyTemplate,
215
+ contextName: notification.contextName,
216
+ contextParameters: notification.contextParameters,
217
+ sendAfter: notification.sendAfter,
218
+ subjectTemplate: notification.subjectTemplate,
219
+ extraParams: notification.extraParams,
220
+ status: exports.NotificationStatusEnum.PENDING_SEND,
221
+ },
222
+ });
223
+ return this.serializeNotification(created);
224
+ }
225
+ async persistOneOffNotificationUpdate(notificationId, notification) {
226
+ const data = {
227
+ ...(notification.emailOrPhone !== undefined
228
+ ? { emailOrPhone: notification.emailOrPhone }
229
+ : {}),
230
+ ...(notification.firstName !== undefined ? { firstName: notification.firstName } : {}),
231
+ ...(notification.lastName !== undefined ? { lastName: notification.lastName } : {}),
232
+ ...(notification.notificationType ? { notificationType: notification.notificationType } : {}),
233
+ ...(notification.title ? { title: notification.title } : {}),
234
+ ...(notification.bodyTemplate ? { bodyTemplate: notification.bodyTemplate } : {}),
235
+ ...(notification.contextName ? { contextName: notification.contextName } : {}),
236
+ ...(notification.contextParameters
237
+ ? { contextParameters: notification.contextParameters }
238
+ : {}),
239
+ ...(notification.sendAfter ? { sendAfter: notification.sendAfter } : {}),
240
+ ...(notification.subjectTemplate ? { subjectTemplate: notification.subjectTemplate } : {}),
241
+ };
242
+ const updated = await this.prismaClient.notification.update({
243
+ where: { id: notificationId },
244
+ data,
245
+ });
246
+ return this.serializeNotification(updated);
247
+ }
248
+ async getOneOffNotification(notificationId, _forUpdate) {
249
+ const notification = await this.prismaClient.notification.findUnique({
250
+ where: {
251
+ id: notificationId,
252
+ },
253
+ });
254
+ if (!notification || !notification.emailOrPhone || notification.userId !== null) {
255
+ return null;
256
+ }
257
+ return this.serializeNotification(notification);
258
+ }
259
+ async getAllOneOffNotifications() {
260
+ const notifications = await this.prismaClient.notification.findMany({
261
+ where: {
262
+ userId: null,
263
+ emailOrPhone: { not: null },
264
+ },
265
+ });
266
+ return notifications
267
+ .filter((n) => n.emailOrPhone !== null)
268
+ .map((n) => this.serializeNotification(n));
269
+ }
270
+ async getOneOffNotifications(page, pageSize) {
271
+ const notifications = await this.prismaClient.notification.findMany({
272
+ where: {
273
+ userId: null,
274
+ emailOrPhone: { not: null },
275
+ },
276
+ skip: page * pageSize,
277
+ take: pageSize,
278
+ });
279
+ return notifications
280
+ .filter((n) => n.emailOrPhone !== null)
281
+ .map((n) => this.serializeNotification(n));
282
+ }
283
+ async markAsSent(notificationId, checkIsPending = true) {
284
+ const whereClause = {
285
+ id: notificationId,
286
+ };
287
+ if (checkIsPending) {
288
+ whereClause.status = exports.NotificationStatusEnum.PENDING_SEND;
289
+ }
290
+ const updated = await this.prismaClient.notification.update({
291
+ where: whereClause,
292
+ data: {
293
+ status: exports.NotificationStatusEnum.SENT,
294
+ sentAt: new Date(),
295
+ },
296
+ });
297
+ return this.serializeNotification(updated);
298
+ }
299
+ async markAsFailed(notificationId, checkIsPending = true) {
300
+ const whereClause = {
301
+ id: notificationId,
302
+ };
303
+ if (checkIsPending) {
304
+ whereClause.status = exports.NotificationStatusEnum.PENDING_SEND;
305
+ }
306
+ const updated = await this.prismaClient.notification.update({
307
+ where: whereClause,
308
+ data: {
309
+ status: exports.NotificationStatusEnum.FAILED,
310
+ sentAt: new Date(),
311
+ },
312
+ });
313
+ return this.serializeNotification(updated);
314
+ }
315
+ async markAsRead(notificationId, checkIsSent = true) {
316
+ const whereClause = {
317
+ id: notificationId,
318
+ };
319
+ if (checkIsSent) {
320
+ whereClause.status = exports.NotificationStatusEnum.SENT;
321
+ }
322
+ const updated = await this.prismaClient.notification.update({
323
+ where: whereClause,
324
+ data: {
325
+ status: 'READ',
326
+ readAt: new Date(),
327
+ },
328
+ });
329
+ // markAsRead is only for in-app notifications (which are always regular notifications with userId)
330
+ return this.serializeNotification(updated);
331
+ }
332
+ async cancelNotification(notificationId) {
333
+ await this.prismaClient.notification.update({
334
+ where: {
335
+ id: notificationId,
336
+ },
337
+ data: {
338
+ status: exports.NotificationStatusEnum.CANCELLED,
339
+ },
340
+ });
341
+ }
342
+ async getNotification(notificationId, _forUpdate) {
343
+ const notification = await this.prismaClient.notification.findUnique({
344
+ where: { id: notificationId },
345
+ });
346
+ if (!notification) return null;
347
+ return this.serializeNotification(notification);
348
+ }
349
+ async filterAllInAppUnreadNotifications(userId) {
350
+ const notifications = await this.prismaClient.notification.findMany({
351
+ where: {
352
+ userId,
353
+ status: 'SENT',
354
+ readAt: null,
355
+ },
356
+ });
357
+ // These are guaranteed to be regular notifications (not one-off) since we're filtering by userId
358
+ return notifications.map((n) => this.serializeNotification(n));
359
+ }
360
+ async filterInAppUnreadNotifications(userId, page, pageSize) {
361
+ const notifications = await this.prismaClient.notification.findMany({
362
+ where: {
363
+ userId,
364
+ status: 'SENT',
365
+ readAt: null,
366
+ },
367
+ skip: page * pageSize,
368
+ take: pageSize,
369
+ });
370
+ // These are guaranteed to be regular notifications (not one-off) since we're filtering by userId
371
+ return notifications.map((n) => this.serializeNotification(n));
372
+ }
373
+ async getUserEmailFromNotification(notificationId) {
374
+ var _a;
375
+ const notification = await this.prismaClient.notification.findUnique({
376
+ where: {
377
+ id: notificationId,
378
+ },
379
+ include: {
380
+ user: true,
381
+ },
382
+ });
383
+ return (_a = notification === null || notification === void 0 ? void 0 : notification.user) ===
384
+ null || _a === void 0
385
+ ? void 0
386
+ : _a.email;
387
+ }
388
+ async storeContextUsed(notificationId, context) {
389
+ await this.prismaClient.notification.update({
390
+ where: { id: notificationId },
391
+ data: { contextUsed: context },
392
+ });
393
+ }
394
+ async bulkPersistNotifications(notifications) {
395
+ return this.prismaClient.notification.createMany({
396
+ data: notifications.map((notification) => this.deserializeNotification(notification)),
397
+ });
398
+ }
399
+ }
400
+ exports.PrismaNotificationBackend = PrismaNotificationBackend;
401
+ class PrismaNotificationBackendFactory {
402
+ create(prismaClient) {
403
+ return new PrismaNotificationBackend(prismaClient);
404
+ }
405
+ }
406
+ exports.PrismaNotificationBackendFactory = PrismaNotificationBackendFactory;