Skip to content
Merged
Show file tree
Hide file tree
Changes from all 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
91 changes: 91 additions & 0 deletions docs/useCases.md
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,11 @@ The different use cases currently available in the package are classified below,
- [Get Application Terms of Use](#get-application-terms-of-use)
- [Contact](#Contact)
- [Send Feedback to Object Contacts](#send-feedback-to-object-contacts)
- [Notifications](#Notifications)
- [Get All Notifications by User](#get-all-notifications-by-user)
- [Delete Notification](#delete-notification)
- [Get Unread Count](#get-unread-count)
- [Mark As Read](#mark-as-read)
- [Search](#Search)
- [Get Search Services](#get-search-services)

Expand Down Expand Up @@ -2096,6 +2101,92 @@ In ContactDTO, it takes the following information:
- **body**: the email body to send.
- **fromEmail**: the email to list in the reply-to field.

## Notifications

#### Get All Notifications by User

Returns a [Notification](../src/notifications/domain/models/Notification.ts) array containing all notifications for the current authenticated user.

##### Example call:

```typescript
import { getAllNotificationsByUser } from '@iqss/dataverse-client-javascript'

/* ... */

getAllNotificationsByUser.execute().then((notifications: Notification[]) => {
/* ... */
})

/* ... */
```

_See [use case](../src/notifications/domain/useCases/GetAllNotificationsByUser.ts) implementation_.

#### Delete Notification

Deletes a specific notification for the current authenticated user by its ID.

##### Example call:

```typescript
import { deleteNotification } from '@iqss/dataverse-client-javascript'

/* ... */

const notificationId = 123

deleteNotification.execute(notificationId: number).then(() => {
/* ... */
})

/* ... */
```

_See [use case](../src/notifications/domain/useCases/DeleteNotification.ts) implementation_.

#### Get Unread Count

Returns the number of unread notifications for the current authenticated user.

##### Example call:

```typescript
import { getUnreadNotificationsCount } from '@iqss/dataverse-client-javascript'

/* ... */

getUnreadNotificationsCount.execute().then((count: number) => {
console.log(`You have ${count} unread notifications`)
})

/* ... */
```

_See [use case](../src/notifications/domain/useCases/GetUnreadNotificationsCount.ts) implementation_.

#### Mark As Read

Marks a specific notification as read for the current authenticated user. This operation is idempotent - marking an already-read notification as read will not cause an error.

##### Example call:

```typescript
import { markNotificationAsRead } from '@iqss/dataverse-client-javascript'

/* ... */

const notificationId = 123

markNotificationAsRead.execute(notificationId).then(() => {
console.log('Notification marked as read')
})

/* ... */
```

_See [use case](../src/notifications/domain/useCases/MarkNotificationAsRead.ts) implementation_.

## Search

#### Get Search Services
Expand Down
1 change: 1 addition & 0 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,4 +8,5 @@ export * from './collections'
export * from './metadataBlocks'
export * from './files'
export * from './contactInfo'
export * from './notifications'
export * from './search'
72 changes: 72 additions & 0 deletions src/notifications/domain/models/Notification.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
export enum NotificationType {
ASSIGNROLE = 'ASSIGNROLE',
REVOKEROLE = 'REVOKEROLE',
CREATEDV = 'CREATEDV',
CREATEDS = 'CREATEDS',
CREATEACC = 'CREATEACC',
SUBMITTEDDS = 'SUBMITTEDDS',
RETURNEDDS = 'RETURNEDDS',
PUBLISHEDDS = 'PUBLISHEDDS',
REQUESTFILEACCESS = 'REQUESTFILEACCESS',
GRANTFILEACCESS = 'GRANTFILEACCESS',
REJECTFILEACCESS = 'REJECTFILEACCESS',
FILESYSTEMIMPORT = 'FILESYSTEMIMPORT',
CHECKSUMIMPORT = 'CHECKSUMIMPORT',
CHECKSUMFAIL = 'CHECKSUMFAIL',
CONFIRMEMAIL = 'CONFIRMEMAIL',
APIGENERATED = 'APIGENERATED',
INGESTCOMPLETED = 'INGESTCOMPLETED',
INGESTCOMPLETEDWITHERRORS = 'INGESTCOMPLETEDWITHERRORS',
PUBLISHFAILED_PIDREG = 'PUBLISHFAILED_PIDREG',
WORKFLOW_SUCCESS = 'WORKFLOW_SUCCESS',
WORKFLOW_FAILURE = 'WORKFLOW_FAILURE',
STATUSUPDATED = 'STATUSUPDATED',
DATASETCREATED = 'DATASETCREATED',
DATASETMENTIONED = 'DATASETMENTIONED',
GLOBUSUPLOADCOMPLETED = 'GLOBUSUPLOADCOMPLETED',
GLOBUSUPLOADCOMPLETEDWITHERRORS = 'GLOBUSUPLOADCOMPLETEDWITHERRORS',
GLOBUSDOWNLOADCOMPLETED = 'GLOBUSDOWNLOADCOMPLETED',
GLOBUSDOWNLOADCOMPLETEDWITHERRORS = 'GLOBUSDOWNLOADCOMPLETEDWITHERRORS',
REQUESTEDFILEACCESS = 'REQUESTEDFILEACCESS',
GLOBUSUPLOADREMOTEFAILURE = 'GLOBUSUPLOADREMOTEFAILURE',
GLOBUSUPLOADLOCALFAILURE = 'GLOBUSUPLOADLOCALFAILURE',
PIDRECONCILED = 'PIDRECONCILED'
}

export interface RoleAssignment {
id: number
assignee: string
definitionPointId: number
roleId: number
roleName: string
_roleAlias: string
}

export interface Notification {
id: number
type: NotificationType
subjectText?: string
messageText?: string
sentTimestamp: string
displayAsRead: boolean
installationBrandName?: string
userGuidesBaseUrl?: string
userGuidesVersion?: string
userGuidesSectionPath?: string
roleAssignments?: RoleAssignment[]
collectionAlias?: string
collectionDisplayName?: string
datasetPersistentIdentifier?: string
datasetDisplayName?: string
ownerPersistentIdentifier?: string
ownerAlias?: string
ownerDisplayName?: string
requestorFirstName?: string
requestorLastName?: string
requestorEmail?: string
dataFileId?: number
dataFileDisplayName?: string
currentCurationStatus?: string
additionalInfo?: string
objectDeleted?: boolean
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import { Notification } from '../models/Notification'

export interface INotificationsRepository {
getAllNotificationsByUser(inAppNotificationFormat?: boolean): Promise<Notification[]>
deleteNotification(notificationId: number): Promise<void>
getUnreadNotificationsCount(): Promise<number>
markNotificationAsRead(notificationId: number): Promise<void>
}
16 changes: 16 additions & 0 deletions src/notifications/domain/useCases/DeleteNotification.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import { UseCase } from '../../../core/domain/useCases/UseCase'
import { INotificationsRepository } from '../repositories/INotificationsRepository'

/**
* Use case for deleting a specific notification for the current user.
*
* @param notificationId - The ID of the notification to delete.
* @returns {Promise<void>} - A promise that resolves when the notification is deleted.
*/
export class DeleteNotification implements UseCase<void> {
constructor(private readonly notificationsRepository: INotificationsRepository) {}

async execute(notificationId: number): Promise<void> {
return this.notificationsRepository.deleteNotification(notificationId)
}
}
19 changes: 19 additions & 0 deletions src/notifications/domain/useCases/GetAllNotificationsByUser.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import { UseCase } from '../../../core/domain/useCases/UseCase'
import { Notification } from '../models/Notification'
import { INotificationsRepository } from '../repositories/INotificationsRepository'

export class GetAllNotificationsByUser implements UseCase<Notification[]> {
constructor(private readonly notificationsRepository: INotificationsRepository) {}

/**
* Use case for retrieving all notifications for the current user.
*
* @param inAppNotificationFormat - Optional parameter to retrieve fields needed for in-app notifications
* @returns {Promise<Notification[]>} - A promise that resolves to an array of Notification instances.
*/
async execute(inAppNotificationFormat?: boolean): Promise<Notification[]> {
return (await this.notificationsRepository.getAllNotificationsByUser(
inAppNotificationFormat
)) as Notification[]
}
}
19 changes: 19 additions & 0 deletions src/notifications/domain/useCases/GetUnreadNotificationsCount.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import { UseCase } from '../../../core/domain/useCases/UseCase'
import { INotificationsRepository } from '../repositories/INotificationsRepository'

export class GetUnreadNotificationsCount implements UseCase<number> {
private notificationsRepository: INotificationsRepository

constructor(notificationsRepository: INotificationsRepository) {
this.notificationsRepository = notificationsRepository
}

/**
* Use case for retrieving the number of unread notifications for the current user.
*
* @returns {Promise<number>} - A promise that resolves to the number of unread notifications.
*/
async execute(): Promise<number> {
return await this.notificationsRepository.getUnreadNotificationsCount()
}
}
20 changes: 20 additions & 0 deletions src/notifications/domain/useCases/MarkNotificationAsRead.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import { UseCase } from '../../../core/domain/useCases/UseCase'
import { INotificationsRepository } from '../repositories/INotificationsRepository'

export class MarkNotificationAsRead implements UseCase<void> {
private notificationsRepository: INotificationsRepository

constructor(notificationsRepository: INotificationsRepository) {
this.notificationsRepository = notificationsRepository
}

/**
* Use case for marking a notification as read.
*
* @param notificationId - The ID of the notification to mark as read.
* @returns {Promise<void>} - A promise that resolves when the notification is marked as read.
*/
async execute(notificationId: number): Promise<void> {
return await this.notificationsRepository.markNotificationAsRead(notificationId)
}
}
21 changes: 21 additions & 0 deletions src/notifications/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import { NotificationsRepository } from './infra/repositories/NotificationsRepository'
import { GetAllNotificationsByUser } from './domain/useCases/GetAllNotificationsByUser'
import { DeleteNotification } from './domain/useCases/DeleteNotification'
import { GetUnreadNotificationsCount } from './domain/useCases/GetUnreadNotificationsCount'
import { MarkNotificationAsRead } from './domain/useCases/MarkNotificationAsRead'

const notificationsRepository = new NotificationsRepository()

const getAllNotificationsByUser = new GetAllNotificationsByUser(notificationsRepository)
const deleteNotification = new DeleteNotification(notificationsRepository)
const getUnreadNotificationsCount = new GetUnreadNotificationsCount(notificationsRepository)
const markNotificationAsRead = new MarkNotificationAsRead(notificationsRepository)

export {
getAllNotificationsByUser,
deleteNotification,
getUnreadNotificationsCount,
markNotificationAsRead
}

export { Notification, NotificationType, RoleAssignment } from './domain/models/Notification'
57 changes: 57 additions & 0 deletions src/notifications/infra/repositories/NotificationsRepository.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
import { ApiRepository } from '../../../core/infra/repositories/ApiRepository'
import { INotificationsRepository } from '../../domain/repositories/INotificationsRepository'
import { Notification } from '../../domain/models/Notification'
import { NotificationPayload } from '../transformers/NotificationPayload'

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

public async getAllNotificationsByUser(
inAppNotificationFormat?: boolean
): Promise<Notification[]> {
const queryParams = inAppNotificationFormat ? { inAppNotificationFormat: 'true' } : undefined
return this.doGet(
this.buildApiEndpoint(this.notificationsResourceName, 'all'),
true,
queryParams
)
.then((response) => {
const notifications = response.data.data.notifications
return notifications.map((notification: NotificationPayload) => {
const { dataverseDisplayName, dataverseAlias, ...restNotification } = notification
return {
...restNotification,
...(dataverseDisplayName && { collectionDisplayName: dataverseDisplayName }),
...(dataverseAlias && { collectionAlias: dataverseAlias })
}
}) as Notification[]
})
.catch((error) => {
throw error
})
}

public async deleteNotification(notificationId: number): Promise<void> {
return this.doDelete(
this.buildApiEndpoint(this.notificationsResourceName, notificationId.toString())
)
.then(() => undefined)
.catch((error) => {
throw error
})
}

public async getUnreadNotificationsCount(): Promise<number> {
return this.doGet(
this.buildApiEndpoint(this.notificationsResourceName, 'unreadCount'),
true
).then((response) => response.data.data.unreadCount as number)
}

public async markNotificationAsRead(notificationId: number): Promise<void> {
return this.doPut(
this.buildApiEndpoint(this.notificationsResourceName, 'markAsRead', notificationId),
{}
).then(() => undefined)
}
}
30 changes: 30 additions & 0 deletions src/notifications/infra/transformers/NotificationPayload.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
import { RoleAssignment } from '../../domain/models/Notification'

export interface NotificationPayload {
id: number
type: string
subjectText?: string
messageText?: string
sentTimestamp: string
displayAsRead: boolean
installationBrandName?: string
userGuidesBaseUrl?: string
userGuidesVersion?: string
userGuidesSectionPath?: string
roleAssignments?: RoleAssignment[]
dataverseAlias?: string
dataverseDisplayName?: string
datasetPersistentIdentifier?: string
datasetDisplayName?: string
ownerPersistentIdentifier?: string
ownerAlias?: string
ownerDisplayName?: string
requestorFirstName?: string
requestorLastName?: string
requestorEmail?: string
dataFileId?: number
dataFileDisplayName?: string
currentCurationStatus?: string
additionalInfo?: string
objectDeleted?: boolean
}
Loading
Loading