Skip to content
Open
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,14 @@ This changelog follows the principles of [Keep a Changelog](https://keepachangel

### Added

- In GetAllNotificationsByUser use case, added support for filtering unread messages and pagination.

### Changed

### Fixed

- In GetAllNotificationsByUser use case, additionalInfo field is returned as an object instead of a string.

### Removed

- 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.
Expand Down
2 changes: 1 addition & 1 deletion src/notifications/domain/models/Notification.ts
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,6 @@ export interface Notification {
dataFileId?: number
dataFileDisplayName?: string
currentCurationStatus?: string
additionalInfo?: string
additionalInfo?: Record<string, unknown>
objectDeleted?: boolean
}
Original file line number Diff line number Diff line change
@@ -1,7 +1,12 @@
import { Notification } from '../models/Notification'

export interface INotificationsRepository {
getAllNotificationsByUser(inAppNotificationFormat?: boolean): Promise<Notification[]>
getAllNotificationsByUser(
inAppNotificationFormat?: boolean,
onlyUnread?: boolean,
limit?: number,
offset?: number
): Promise<Notification[]>
deleteNotification(notificationId: number): Promise<void>
getUnreadNotificationsCount(): Promise<number>
markNotificationAsRead(notificationId: number): Promise<void>
Expand Down
15 changes: 13 additions & 2 deletions src/notifications/domain/useCases/GetAllNotificationsByUser.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,22 @@ export class GetAllNotificationsByUser implements UseCase<Notification[]> {
* Use case for retrieving all notifications for the current user.
*
* @param inAppNotificationFormat - Optional parameter to retrieve fields needed for in-app notifications
* @param onlyUnread - Optional parameter to filter only unread notifications
* @param limit - Optional parameter to limit the number of notifications returned
* @param offset - Optional parameter to skip a number of notifications (for pagination)
* @returns {Promise<Notification[]>} - A promise that resolves to an array of Notification instances.
*/
async execute(inAppNotificationFormat?: boolean): Promise<Notification[]> {
async execute(
inAppNotificationFormat?: boolean,
onlyUnread?: boolean,
limit?: number,
offset?: number
): Promise<Notification[]> {
return (await this.notificationsRepository.getAllNotificationsByUser(
inAppNotificationFormat
inAppNotificationFormat,
onlyUnread,
limit,
offset
)) as Notification[]
}
}
15 changes: 12 additions & 3 deletions src/notifications/infra/repositories/NotificationsRepository.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,22 @@ export class NotificationsRepository extends ApiRepository implements INotificat
private readonly notificationsResourceName: string = 'notifications'

public async getAllNotificationsByUser(
inAppNotificationFormat?: boolean
inAppNotificationFormat?: boolean,
onlyUnread?: boolean,
limit?: number,
offset?: number
): Promise<Notification[]> {
const queryParams = inAppNotificationFormat ? { inAppNotificationFormat: 'true' } : undefined
const queryParams: Record<string, string | number> = {}

if (inAppNotificationFormat) queryParams.inAppNotificationFormat = 'true'
if (onlyUnread) queryParams.onlyUnread = 'true'
if (limit !== undefined) queryParams.limit = limit
if (offset !== undefined) queryParams.offset = offset

return this.doGet(
this.buildApiEndpoint(this.notificationsResourceName, 'all'),
true,
queryParams
Object.keys(queryParams).length ? queryParams : undefined
)
.then((response) => {
const notifications = response.data.data.notifications
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,6 @@ export interface NotificationPayload {
dataFileId?: number
dataFileDisplayName?: string
currentCurationStatus?: string
additionalInfo?: string
additionalInfo?: Record<string, unknown>
objectDeleted?: boolean
}
4 changes: 2 additions & 2 deletions test/environment/.env
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
POSTGRES_VERSION=17
DATAVERSE_DB_USER=dataverse
SOLR_VERSION=9.8.0
DATAVERSE_IMAGE_REGISTRY=docker.io
DATAVERSE_IMAGE_TAG=unstable
DATAVERSE_IMAGE_REGISTRY=ghcr.io
DATAVERSE_IMAGE_TAG=11852-notifs-api-pagination-unread
DATAVERSE_BOOTSTRAP_TIMEOUT=5m
Original file line number Diff line number Diff line change
Expand Up @@ -34,4 +34,13 @@ describe('execute', () => {
expect(notifications[0]).toHaveProperty('sentTimestamp')
expect(notifications[0]).toHaveProperty('displayAsRead')
})
test('should have correct in-app notification properties when filter and paging params are set', async () => {
const notifications = await getAllNotificationsByUser.execute(true, true, 1, 0)

expect(notifications[0]).toHaveProperty('id')
expect(notifications[0]).toHaveProperty('type')
expect(notifications[0]).toHaveProperty('sentTimestamp')
expect(notifications[0]).toHaveProperty('displayAsRead')
expect(notifications.length).toBeLessThanOrEqual(1)
})
})
2 changes: 1 addition & 1 deletion test/integration/collections/CollectionsRepository.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1381,7 +1381,7 @@ describe('CollectionsRepository', () => {
})

it('should return error when the dvObjectIdentifier of a file does not exist', async () => {
const invalidFileId = '99'
const invalidFileId = '99999999'
const newFeaturedItems: DvObjectFeaturedItemDTO[] = [
{
type: FeaturedItemType.FILE,
Expand Down
22 changes: 22 additions & 0 deletions test/integration/notifications/NotificationsRepository.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -183,4 +183,26 @@ describe('NotificationsRepository', () => {
expectedError
)
})
test('should only return unread notifications when onlyUnread is true', async () => {
const notifications: Notification[] = await sut.getAllNotificationsByUser(true, true)

expect(Array.isArray(notifications)).toBe(true)
const originalUnreadCount = notifications.length
expect(notifications.length).toBeGreaterThanOrEqual(0)

await expect(sut.markNotificationAsRead(notifications[0].id)).resolves.toBeUndefined()

const updatedNotifications: Notification[] = await sut.getAllNotificationsByUser(true, true)
expect(updatedNotifications.length).toBe(originalUnreadCount - 1)

const hasReadNotifications = notifications.some((n) => n.displayAsRead === true)
expect(hasReadNotifications).toBe(false)
})
test('should return limited number of notifications when limit is set', async () => {
const limit = 1
const notifications: Notification[] = await sut.getAllNotificationsByUser(true, false, limit, 0)

expect(Array.isArray(notifications)).toBe(true)
expect(notifications.length).toBeLessThanOrEqual(limit)
})
})
Loading