Skip to content

Commit

Permalink
docs: refactor notification documentation into how-to, architecture a…
Browse files Browse the repository at this point in the history
…nd reference
  • Loading branch information
rekt-hard authored and kpsherva committed Jul 10, 2024
1 parent bc18937 commit ef6cf65
Show file tree
Hide file tree
Showing 5 changed files with 280 additions and 267 deletions.
172 changes: 172 additions & 0 deletions docs/develop/architecture/notifications.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,172 @@
# Notifications

_Introduced in InvenioRDM v12_

The notification system in InvenioRDM is provided via the [`invenio-notifications`](https://github.com/inveniosoftware/invenio-notifications) module.


## Service Level
Notifications are registered at the service level in the unit of work and send off to a celery task - which takes care of further processing. When dispatching this notification in the service, the context shall be as minimal as possible, to reduce passing huge chunks of data.
The implications of sending all of the required immediately would be a hit on performance and higher memory usage.
As the notification operations are registered during methods of the service level, it would increase response time for requests using these methods.
As we can offload the extension of the notification to a worker, the initial request can be processed quicker and save some precious time - leading to faster response times.

This is an example of creating a notification
```py
n = Notification(type="community-submission", context={"request": request.id})
```

After it is build, it can be dumped and send to a background task for further processing. The serialized notification will look like this
```py
{
"type": "community-submission",
"context": {
"request": 1
}
}
```


## Notification Manager
A notification manager is created, which will rely on certain [configuration values](../howtos/notifications.md#configuration-values) and provide logic to send notifications. Its task is also to call respective methods to resolve the notification context, generate recipients, filter recipients and generate the backend ids for further processing. With all information created, it will then dispatch further tasks to relay the notification to the backend, which will take care of sending the actual notification.



<details>
<summary>A recipient entity could look like this</summary>

```py
{
"data": {
"id": "5",
"created": "2023-08-03T12:37:24.353656+00:00",
"updated": "2023-08-03T12:37:24.971301+00:00",
"links": {
"self": "https://127.0.0.1:5000/api/users/5",
"avatar": "https://127.0.0.1:5000/api/users/5/avatar.svg"
},
"revision_id": 5,
"active": true,
"confirmed": true,
"is_current_user": false,
"email": "[email protected]",
"username": null,
"profile": {
"full_name": "New User",
"affiliations": "CERN"
},
"preferences": {
"visibility": "public",
"email_visibility": "restricted",
"locale": "en",
"timezone": "Europe/Zurich",
"notifications": {
"enabled": true
}
}
}
}
```
</details>

<details>
<summary>After resolving all entities, a notification could look like this</summary>

```py
{
"type": "community-invitation.submit",
"context": {
"request": {
"id": "372cd107-7b76-4a45-9c10-c339f4c2a3ac",
"created": "2023-08-03T12:32:06.251565+00:00",
"updated": "2023-08-03T12:32:06.260397+00:00",
"links": {
"actions": {
"accept": "https://127.0.0.1:5000/api/requests/372cd107-7b76-4a45-9c10-c339f4c2a3ac/actions/accept",
"decline": "https://127.0.0.1:5000/api/requests/372cd107-7b76-4a45-9c10-c339f4c2a3ac/actions/decline",
"cancel": "https://127.0.0.1:5000/api/requests/372cd107-7b76-4a45-9c10-c339f4c2a3ac/actions/cancel",
"expire": "https://127.0.0.1:5000/api/requests/372cd107-7b76-4a45-9c10-c339f4c2a3ac/actions/expire"
},
"self": "https://127.0.0.1:5000/api/requests/372cd107-7b76-4a45-9c10-c339f4c2a3ac",
"self_html": "https://127.0.0.1:5000/requests/372cd107-7b76-4a45-9c10-c339f4c2a3ac",
"comments": "https://127.0.0.1:5000/api/requests/372cd107-7b76-4a45-9c10-c339f4c2a3ac/comments",
"timeline": "https://127.0.0.1:5000/api/requests/372cd107-7b76-4a45-9c10-c339f4c2a3ac/timeline"
},
"revision_id": 2,
"type": "community-invitation",
"title": "Invitation to join \"My Community\"",
"description": "You will join as \"Reader\".",
"number": "2",
"status": "submitted",
"is_closed": false,
"is_open": true,
"expires_at": "2023-09-02T12:32:06.239340+00:00",
"is_expired": false,
"created_by": {
"id": "232d2ae9-ac03-4359-bc8e-9fa95e66ced0",
"created": "2023-08-03T12:32:05.556911+00:00",
"updated": "2023-08-03T12:32:05.595929+00:00",
"links": {
"featured": "https://127.0.0.1:5000/api/communities/232d2ae9-ac03-4359-bc8e-9fa95e66ced0/featured",
"self": "https://127.0.0.1:5000/api/communities/232d2ae9-ac03-4359-bc8e-9fa95e66ced0",
"self_html": "https://127.0.0.1:5000/communities/public",
"settings_html": "https://127.0.0.1:5000/communities/public/settings",
"logo": "https://127.0.0.1:5000/api/communities/232d2ae9-ac03-4359-bc8e-9fa95e66ced0/logo",
"rename": "https://127.0.0.1:5000/api/communities/232d2ae9-ac03-4359-bc8e-9fa95e66ced0/rename",
"members": "https://127.0.0.1:5000/api/communities/232d2ae9-ac03-4359-bc8e-9fa95e66ced0/members",
"public_members": "https://127.0.0.1:5000/api/communities/232d2ae9-ac03-4359-bc8e-9fa95e66ced0/members/public",
"invitations": "https://127.0.0.1:5000/api/communities/232d2ae9-ac03-4359-bc8e-9fa95e66ced0/invitations",
"requests": "https://127.0.0.1:5000/api/communities/232d2ae9-ac03-4359-bc8e-9fa95e66ced0/requests",
"records": "https://127.0.0.1:5000/api/communities/232d2ae9-ac03-4359-bc8e-9fa95e66ced0/records"
},
"revision_id": 2,
"slug": "public",
"metadata": {
"title": "My Community"
},
"access": {
"visibility": "public",
"member_policy": "open",
"record_policy": "open",
"review_policy": "closed"
},
"custom_fields": {}
},
"receiver": {
"id": "5",
"created": "2023-08-03T12:32:05.683922+00:00",
"updated": "2023-08-03T12:32:06.366896+00:00",
"links": {
"self": "https://127.0.0.1:5000/api/users/5",
"avatar": "https://127.0.0.1:5000/api/users/5/avatar.svg"
},
"revision_id": 6,
"active": true,
"confirmed": true,
"is_current_user": false,
"email": "[email protected]",
"username": null,
"profile": {
"full_name": "New User",
"affiliations": "CERN"
},
"preferences": {
"visibility": "public",
"email_visibility": "restricted",
"locale": "en",
"timezone": "Europe/Zurich",
"notifications": {
"enabled": true
}
}
},
"topic": {
"community": "232d2ae9-ac03-4359-bc8e-9fa95e66ced0"
}
},
"role": "Reader",
"message": null
}
}
```
</details>
Original file line number Diff line number Diff line change
@@ -1,11 +1,5 @@
# Notifications

Depending on your instance needs regarding notifications, you might want to customize the default values provided by the `invenio-notifications` module.

Specifically, we are going to have a look at templates and the following variables:

- [NOTIFICATIONS_BACKENDS](#notifications_backends)
- [NOTIFICATIONS_BUILDERS](#notifications_builders)
# How to modify notifications
This guide will show you how you can configure and modify all things related to notifications.

## Templates

Expand Down Expand Up @@ -141,10 +135,14 @@ In our case, we are happy with the content in the general template and _only_ wa

Only the specified blocks will be overriden. Other blocks stay as they are in the base template.

## NOTIFICATIONS_BACKENDS

This config variable allows to specify the available backends. For a detailed description on backends, checkout the respective [reference section](/reference/notifications/#backends).
For instance, you can provide an implementation for your institution's preferred communication tool and send notifications via this backend. For this, simply extend the `NotificationBackend` class and implement the `send` method.


## Build your own
This will show you can build your own notification classes.

### Notification Backend
Building your own notification backend can be achieved in a few steps. For instance, you can provide an implementation for your institution's preferred communication tool and send notifications via this backend. For this, simply extend the `NotificationBackend` class and implement the `send` method.

```py
from invenio_notifications.backends import JinjaTemplateLoaderMixin, NotificationBackend,
Expand All @@ -161,18 +159,7 @@ class InstitutationalBackend(NotificationBackend, JinjaTemplateLoaderMixin):
institutation_communication_tool.send_message(user_id=recipient.data["id"], template["md_body"])
```

This backend can now be specified (e.g. in `invenio.cfg`):

```py
NOTIFICATION_BACKENDS = {
EmailNotificationBackend.id: EmailNotificationBackend,
InstitutationalBackend.id: InstitutationalBackend,
}
```

## NOTIFICATIONS_BUILDERS

This config variable defines which builder class should be used for a specific notification type. For detailed description on builders, filters and generators , checkout the respective [reference section](/reference/notifications/#builders-filters-generators).
### Notification Builder
Let us assume that you want to override who will get notified in the event of a community record submission (community curator and owner) and add the previously defined backend (so recipients will get notified via whatever the base class has defined and via the `InstitutationalBackend`).
To do this, we will create a custom builder, which will inherit most of the properties from the existing base class.

Expand All @@ -193,10 +180,56 @@ class CustomSubmissionBuilder(CommunityInclusionSubmittedNotificationBuilder):
]
```

This builder can now be specified (e.g. in `invenio.cfg`):
## Configuration Values

Configuration values used in the `invenio-notifications` module can be overriden, in order to adapt instances to specific needs.


### NOTIFICATION_BACKENDS

This config variable allows to specify the available backends. For a detailed description on backends, checkout the respective [reference section](/reference/notifications/#backends).
For instance, you can provide an implementation for your institution's preferred communication tool and send notifications via this backend.

As an example, take the backend shown in [build your own backend](#notification-backend). Then you only have to specify it in the config variable (e.g. in `invenio.cfg`).

```py
NOTIFICATION_BACKENDS = {
EmailNotificationBackend.id: EmailNotificationBackend,
InstitutationalBackend.id: InstitutationalBackend,
}
```

### NOTIFICATION_BUILDERS

Specifies [notification builders](#notificationbuilder) to be used for certain types of notifications. When a notification is handled by the manager, it will lookup the type in this variable and build the notification with the provided builder class.

As an example, take the backend shown in [build your own builder](#notification-builder). Then you only have to specify it in the config variable (e.g. in `invenio.cfg`).

```py
NOTIFICATIONS_BUILDERS = {
CustomSubmissionBuilder.type: CustomSubmissionBuilder,
}
```

Since the custom builder uses the same `type` as the builder we want to override, we can use either `type` as key for the entry. The following snippet will have the same effect, but it is more clear what will be overriden.

```py
NOTIFICATIONS_BUILDERS = {
CommunityInclusionSubmittedNotificationBuilder.type: CustomSubmissionBuilder,
}
```


### NOTIFICATIONS_ENTITY_RESOLVERS

Specifies entity resolvers (not to be confused with [EntityResolve](#entityresolve)) to be used for resolving the notification context. These are usually `ServiceResultResolver` objects, which provide functionality to dump an object to a reference dictionary and later on use the dump to fetch information as seen on the API/service level (i.e. fully resolved objects with links for easy access).

```py
NOTIFICATIONS_ENTITY_RESOLVERS = [
RDMRecordServiceResultResolver(),
ServiceResultResolver(service_id="users", type_key="user"),
ServiceResultResolver(service_id="communities", type_key="community"),
ServiceResultResolver(service_id="requests", type_key="request"),
ServiceResultResolver(service_id="request_events", type_key="request_event"),
]
```
2 changes: 2 additions & 0 deletions docs/develop/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@ Step-by-step guides on how to perform certain tasks:
- [Fix a vulnerability](howtos/security-fix.md)
- [Test emails locally](howtos/dev_email.md)
- [Migrate legacy routes](howtos/route_migration.md)
- [Create and configure notifications](howtos/notifications.md)

## Architecture

Expand All @@ -66,6 +67,7 @@ choices:
- [Requests](architecture/requests.md)
- [Communities](architecture/communities.md)
- [Records](architecture/records.md)
- [Notifications](architecture/notifications.md)
- [Recommended reading](architecture/reading.md)

## Concepts
Expand Down
Loading

0 comments on commit ef6cf65

Please sign in to comment.