Skip to content

Commit 295b6f0

Browse files
authored
Merge pull request #387 from IQSS/386-update-notifications-use-case
Update GetAllNotificationsByUser use case
2 parents a937365 + 6eba2d8 commit 295b6f0

File tree

11 files changed

+185
-90
lines changed

11 files changed

+185
-90
lines changed

CHANGELOG.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,9 @@ This changelog follows the principles of [Keep a Changelog](https://keepachangel
2020

2121
### Fixed
2222

23+
- In GetAllNotificationsByUser use case, additionalInfo field is returned as an object instead of a string.
24+
- In GetAllNotificationsByUser use case, added support for filtering unread messages and pagination.
25+
2326
### Removed
2427

2528
- Removed date fields validations in create and update dataset use cases, since validation is already handled in the backend and SPA frontend (other clients should perform client side validation also). This avoids duplicated logic and keeps the package focused on its core responsibility.

src/notifications/domain/models/Notification.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,6 @@ export interface Notification {
6767
dataFileId?: number
6868
dataFileDisplayName?: string
6969
currentCurationStatus?: string
70-
additionalInfo?: string
70+
additionalInfo?: Record<string, unknown>
7171
objectDeleted?: boolean
7272
}
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
import { Notification } from './Notification'
2+
3+
export interface NotificationSubset {
4+
notifications: Notification[]
5+
totalNotificationCount: number
6+
}

src/notifications/domain/repositories/INotificationsRepository.ts

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,12 @@
1-
import { Notification } from '../models/Notification'
1+
import { NotificationSubset } from '../models/NotificationSubset'
22

33
export interface INotificationsRepository {
4-
getAllNotificationsByUser(inAppNotificationFormat?: boolean): Promise<Notification[]>
4+
getAllNotificationsByUser(
5+
inAppNotificationFormat?: boolean,
6+
onlyUnread?: boolean,
7+
limit?: number,
8+
offset?: number
9+
): Promise<NotificationSubset>
510
deleteNotification(notificationId: number): Promise<void>
611
getUnreadNotificationsCount(): Promise<number>
712
markNotificationAsRead(notificationId: number): Promise<void>
Lines changed: 17 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,30 @@
11
import { UseCase } from '../../../core/domain/useCases/UseCase'
2-
import { Notification } from '../models/Notification'
32
import { INotificationsRepository } from '../repositories/INotificationsRepository'
3+
import { NotificationSubset } from '../models/NotificationSubset'
44

5-
export class GetAllNotificationsByUser implements UseCase<Notification[]> {
5+
export class GetAllNotificationsByUser implements UseCase<NotificationSubset> {
66
constructor(private readonly notificationsRepository: INotificationsRepository) {}
77

88
/**
99
* Use case for retrieving all notifications for the current user.
1010
*
1111
* @param inAppNotificationFormat - Optional parameter to retrieve fields needed for in-app notifications
12-
* @returns {Promise<Notification[]>} - A promise that resolves to an array of Notification instances.
12+
* @param onlyUnread - Optional parameter to filter only unread notifications
13+
* @param limit - Optional parameter to limit the number of notifications returned
14+
* @param offset - Optional parameter to skip a number of notifications (for pagination)
15+
* @returns {Promise<NotificationSubset>} - A promise that resolves to an array of Notification instances.
1316
*/
14-
async execute(inAppNotificationFormat?: boolean): Promise<Notification[]> {
17+
async execute(
18+
inAppNotificationFormat?: boolean,
19+
onlyUnread?: boolean,
20+
limit?: number,
21+
offset?: number
22+
): Promise<NotificationSubset> {
1523
return (await this.notificationsRepository.getAllNotificationsByUser(
16-
inAppNotificationFormat
17-
)) as Notification[]
24+
inAppNotificationFormat,
25+
onlyUnread,
26+
limit,
27+
offset
28+
)) as NotificationSubset
1829
}
1930
}

src/notifications/infra/repositories/NotificationsRepository.ts

Lines changed: 15 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2,29 +2,39 @@ import { ApiRepository } from '../../../core/infra/repositories/ApiRepository'
22
import { INotificationsRepository } from '../../domain/repositories/INotificationsRepository'
33
import { Notification } from '../../domain/models/Notification'
44
import { NotificationPayload } from '../transformers/NotificationPayload'
5+
import { NotificationSubset } from '../../domain/models/NotificationSubset'
56

67
export class NotificationsRepository extends ApiRepository implements INotificationsRepository {
78
private readonly notificationsResourceName: string = 'notifications'
89

910
public async getAllNotificationsByUser(
10-
inAppNotificationFormat?: boolean
11-
): Promise<Notification[]> {
12-
const queryParams = inAppNotificationFormat ? { inAppNotificationFormat: 'true' } : undefined
11+
inAppNotificationFormat?: boolean,
12+
onlyUnread?: boolean,
13+
limit?: number,
14+
offset?: number
15+
): Promise<NotificationSubset> {
16+
const queryParams = new URLSearchParams()
17+
18+
if (inAppNotificationFormat) queryParams.set('inAppNotificationFormat', 'true')
19+
if (onlyUnread) queryParams.set('onlyUnread', 'true')
20+
if (limit !== undefined) queryParams.set('limit', limit.toString())
21+
if (offset !== undefined) queryParams.set('offset', offset.toString())
1322
return this.doGet(
1423
this.buildApiEndpoint(this.notificationsResourceName, 'all'),
1524
true,
1625
queryParams
1726
)
1827
.then((response) => {
19-
const notifications = response.data.data.notifications
20-
return notifications.map((notification: NotificationPayload) => {
28+
const notifications = response.data.data.map((notification: NotificationPayload) => {
2129
const { dataverseDisplayName, dataverseAlias, ...restNotification } = notification
2230
return {
2331
...restNotification,
2432
...(dataverseDisplayName && { collectionDisplayName: dataverseDisplayName }),
2533
...(dataverseAlias && { collectionAlias: dataverseAlias })
2634
}
2735
}) as Notification[]
36+
const totalNotificationCount = response.data.totalCount
37+
return { notifications, totalNotificationCount }
2838
})
2939
.catch((error) => {
3040
throw error

src/notifications/infra/transformers/NotificationPayload.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,6 @@ export interface NotificationPayload {
2525
dataFileId?: number
2626
dataFileDisplayName?: string
2727
currentCurationStatus?: string
28-
additionalInfo?: string
28+
additionalInfo?: Record<string, unknown>
2929
objectDeleted?: boolean
3030
}

test/functional/notifications/DeleteNotification.test.ts

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -12,13 +12,16 @@ describe('execute', () => {
1212
})
1313

1414
test('should successfully delete a notification for authenticated user', async () => {
15-
const notifications = await getAllNotificationsByUser.execute()
15+
const notificationSubset = await getAllNotificationsByUser.execute()
16+
const notifications = notificationSubset.notifications
1617
const notificationId = notifications[notifications.length - 1].id
1718

1819
await deleteNotification.execute(notificationId)
1920

20-
const notificationsAfterDelete = await getAllNotificationsByUser.execute()
21-
expect(notificationsAfterDelete.length).toBe(notifications.length - 1)
21+
const notificationsAfterDeleteSubset = await getAllNotificationsByUser.execute()
22+
const notificationsAfterDelete = notificationsAfterDeleteSubset.notifications
23+
const deletedExists = notificationsAfterDelete.some((n) => n.id === notificationId)
24+
expect(deletedExists).toBe(false)
2225
})
2326

2427
test('should throw an error when the notification id does not exist', async () => {
Lines changed: 19 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
1-
import { ApiConfig, getAllNotificationsByUser, Notification } from '../../../src'
1+
import { ApiConfig, getAllNotificationsByUser } from '../../../src'
22
import { TestConstants } from '../../testHelpers/TestConstants'
33
import { DataverseApiAuthMechanism } from '../../../src/core/infra/repositories/ApiConfig'
4-
4+
import { NotificationSubset } from '../../../src/notifications/domain/models/NotificationSubset'
55
describe('execute', () => {
66
beforeEach(async () => {
77
ApiConfig.init(
@@ -12,26 +12,40 @@ describe('execute', () => {
1212
})
1313

1414
test('should successfully return notifications for authenticated user', async () => {
15-
const notifications: Notification[] = await getAllNotificationsByUser.execute()
15+
const result: NotificationSubset = await getAllNotificationsByUser.execute()
16+
const notifications = result.notifications
1617

1718
expect(notifications).not.toBeNull()
1819
expect(Array.isArray(notifications)).toBe(true)
1920
})
2021

2122
test('should have correct notification properties if notifications exist', async () => {
22-
const notifications = await getAllNotificationsByUser.execute()
23+
const result: NotificationSubset = await getAllNotificationsByUser.execute()
24+
const notifications = result.notifications
2325

2426
expect(notifications[0]).toHaveProperty('id')
2527
expect(notifications[0]).toHaveProperty('type')
2628
expect(notifications[0]).toHaveProperty('sentTimestamp')
2729
})
2830

2931
test('should have correct in-app notification properties when inAppNotificationFormat is true', async () => {
30-
const notifications = await getAllNotificationsByUser.execute(true)
32+
const result: NotificationSubset = await getAllNotificationsByUser.execute(true)
33+
const notifications = result.notifications
34+
35+
expect(notifications[0]).toHaveProperty('id')
36+
expect(notifications[0]).toHaveProperty('type')
37+
expect(notifications[0]).toHaveProperty('sentTimestamp')
38+
expect(notifications[0]).toHaveProperty('displayAsRead')
39+
})
40+
41+
test('should have correct in-app notification properties when filter and paging params are set', async () => {
42+
const result: NotificationSubset = await getAllNotificationsByUser.execute(true, true, 1, 0)
43+
const notifications = result.notifications
3144

3245
expect(notifications[0]).toHaveProperty('id')
3346
expect(notifications[0]).toHaveProperty('type')
3447
expect(notifications[0]).toHaveProperty('sentTimestamp')
3548
expect(notifications[0]).toHaveProperty('displayAsRead')
49+
expect(notifications.length).toBeLessThanOrEqual(1)
3650
})
3751
})

0 commit comments

Comments
 (0)